【数据结构】栈的实现 | 入栈和出栈 | 返回栈顶数据 | 判断栈是否为空 | 计算栈的大小

249 阅读7分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情

前言:

在上一篇中,我们把数据结构栈的部分开了一个头。本篇讲继续讲解栈的实现。

0x02 判断栈是否为空(StackIfEmpty)

💬 Stack.h

bool StackIsEmpty(Stack* pst);

🔑 解读:布尔值,返回 true 或 false

💬 Stack.c

/* 判断栈是否为空*/
bool StackIsEmpty(Stack* pst) {
	assert(pst);  //防止pst为空

	if (pst->top == 0)
		return true;
	else
		return false;
}

🔑 解读:首先防止 pst 为空。思路很简单,只需要看 top 是不是 0 即可,如果 top 是 0 就说明栈内没有数据。

⚡ 这里甚至可以直接返回,巧妙地利用布尔类型的特性:

bool StackIfEmpty(Stack* pst) {
	assert(pst);  //防止pst为空

	return pst->top == 0; //等于0就是空,就是真
}

0x03 入栈(StackPush)

💬 Stack.h

void StackPush(Stack* pst, StackDataType x);

💬 Stack.c

/* 进栈 */
void StackPush(Stack* pst, StackDataType x) {
	assert(pst); // 防止pst为空

	// 检查是否需要增容
	if (pst->top == pst->capacity) {
		int new_capacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
		StackDataType* tmp_arr = realloc(pst->array, sizeof(StackDataType) * new_capacity);
		// 防止realloc翻车
		if (tmp_arr == NULL) {
			printf("realloc failed!\n");
			exit(-1);
		}
		// 更新
		pst->array = tmp_arr;
		pst->capacity = new_capacity;
	}

	// 填入数据
	pst->array[pst->top] = x;
	pst->top++;
}

🔑 解读:

这里和顺序表尾插的实现没有任何区别。首先防止 pst 为空,随后检查是否要增容,如果要增容就进行增容操作。最后再填入数据即可。

【顺序表尾插的讲解】根据我们刚才分析的三种情况,首先我们需要判断是空间是否足够。判断思路如下:如果 size == capacity(有效数据个数等于实际能存的最大空间容量),我们进行扩容操作。

如果空间不足,我们就创建一个变量 new_capacity 用来存放 "新容量" ,int new_capacity = pst->capacity 首先把 capacity 的值赋值给这个 "新容量" ,因为考虑到第一次使用 capacity 大小为0,翻倍会出问题(0乘以任何数都是0),这里巧妙地使用三目操作符:int new_capacity = pst->capacity == 0 ? 4 : pst->capacity*2 , 如果 capacity 为 0 (第一次使用大小是0),就把4赋值给它;如果不为0,就把 capacity 的值翻一倍(x2)。

0x04 出栈(StackPop)

💬 Stack.h

void StackPop(Stack* pst);

💬 Stack.c

/* 出栈 */
void StackPop(Stack* pst) {
	assert(pst);  //防止pst为空
	//assert(pst->top > 0);  //防止top为空

	assert(!StackIsEmpty(pst));
	
	pst->top--;
}

🔑 解读:

① 首先防止 pst 为空。这里要注意的是,不能让栈内没有数据,必须要有东西能 "出栈" 才行。

② 出栈是非常简单的,就是把数据吐出来。直接将 top-- ,直接一刀砍就能直接达到目的。后进先出,后进的直接被 top-- 干掉了。

0x05 返回栈顶数据(StackTop)

💬 Stack.h

StackDataType StackTop(Stack* pst);

🔑 解读:为了方便打印出数据,我们需要设计一个返回栈顶数据的接口。既然是返回数据,我们的函数类型自然是 StackDataType 了。

💬 Stack.c

/* 返回栈顶数据 */
StackDataType StackTop(Stack* pst) {
	assert(pst);  //防止psl为空
	//assert(pst->top > 0);  //防止top为空
	assert(!StackIsEmpty(pst));

	return pst->array[pst->top - 1];
}

🔑 解读:

① 首先防止 pst 为空。同样地,不能让栈内没有数据。

② 我们直接返回栈顶数据就可以了,pst->array[pst->top-1] 。

❓ 为什么这里要 -1?

💡 这里 -1 的原因是我们初始化栈的时候定义 top 时给的值是 0,意味着 top 指向的是栈顶数据的下一个(无数据),所以这里既然要返回的是栈顶数据,自然需要 -1。这里的代码到底要不要 -1,主要看我们初始化 top 的时候给的是什么值,如果我们当时给的是 -1,那么 top 将指向栈顶数据,自然这里就不需要 -1,就应该是 pst->array[pst->top-1] 了。

(修正:图中的 psl 应该是 pst ,笔误)

💬 Test.c

#include "Stack.h"

void TestStack1() {
	Stack st;
	StackInit(&st);
	StackPush(&st, 1);
	StackPush(&st, 2);
	StackPush(&st, 3);
	StackPush(&st, 4);

	StackPop(&st);
	StackPop(&st);
	StackPop(&st);
	StackPop(&st);
	//StackPop(&st);
	//printf("%d", StackTop(&st));

	StackDestroy(&st);
}

int main() {
	TestStack1();

	return 0;
}

0x06 计算栈的大小(StackSize)

💬 Stack.h

int StackSize(Stack* pst);

💬 Stack.c

/* 计算栈的大小 */
int StackSize(Stack* pst) {
	assert(pst);  //防止pst为空
	
	// 因为我们设定 top 是指向栈顶的下一个,所以top就是size
	return pst->top; 
}

🔑 解读:首先防止 pst 为空。因为我们之前初始化时 top 给的是0,指向栈顶的下一个。所以 top 就是栈的大小,直接 return top 即可。

0x07 代码测试

① 全部入栈完再出栈

#include "Stack.h"

void TestStack2() {
	// 入栈:1 2 3 4
	Stack st;
	StackInit(&st);
	StackPush(&st, 1);
	StackPush(&st, 2);
	StackPush(&st, 3);
	StackPush(&st, 4);

	// 出栈:4 3 2 1
	while (!StackIfEmpty(&st)) {
		printf("%d ", StackTop(&st));
		StackPop(&st);
	}
	
	StackDestroy(&st);
}

int main() {
	TestStack2();

	return 0;
}

🚩 运行代码:

② 入栈的同时伴随着出栈

#include "Stack.h"

void TestStack3() {
	// 入栈:1 2 3 4
	Stack st;
	StackInit(&st);
	StackPush(&st, 1);
	StackPush(&st, 2);
	StackPush(&st, 3);
	StackPush(&st, 4);

	// 提前出栈:4 3
	printf("%d ", StackTop(&st));
	StackPop(&st);
	printf("%d ", StackTop(&st));
	StackPop(&st);

	StackPush(&st, 5);
	StackPush(&st, 6);

	// 出栈:6 5 2 1
	while (!StackIfEmpty(&st)) {
		printf("%d ", StackTop(&st));
		StackPop(&st);
	}

	StackDestroy(&st);
}

int main() {
	TestStack3();

	return 0;
}

🚩 运行结果:

0x08 完整代码

💬 Stack.h

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>

typedef int StackDataType;

typedef struct Stack {
	StackDataType* array;  //数组
	int top;               //栈顶
	int capacity;          //容量
} Stack;

void StackInit(Stack* pst);
void StackDestroy(Stack* pst);
bool StackIsEmpty(Stack* pst);
void StackPush(Stack* pst, StackDataType x);
void StackPop(Stack* pst);
StackDataType StackTop(Stack* pst);
int StackSize(Stack* pst);

💬 Stack.c

#include "Stack.h"

/* 初始化 */
void StackInit(Stack* pst) {
	assert(pst);   // 防止pst为空
	pst->array = NULL;
	pst->top = 0;  // pst->top = -1
	pst->capacity = 0;
}

/* 销毁 */
void StackDestroy(Stack* pst) {
	assert(psl);   // 防止pst为空
	
	free(pst->array);
	pst->array = NULL;
	pst->capacity = pst->top = 0;
}

/* 判断栈是否为空*/
bool StackIsEmpty(Stack* pst) {
	assert(pst);  //防止pst为空

	return pst->top == 0; //等于0就是空,就是真
}

/* 进栈 */
void StackPush(Stack* pst, StackDataType x) {
	assert(pst); // 防止pst为空

	// 检查是否需要增容
	if (pst->top == pst->capacity) {
		int new_capacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
		StackDataType* tmp_arr = realloc(pst->array, sizeof(StackDataType) * new_capacity);
		// 防止realloc翻车
		if (tmp_arr == NULL) {
			printf("realloc failed!\n");
			exit(-1);
		}
		// 更新
		pst->array = tmp_arr;
		pst->capacity = new_capacity;
	}

	// 填入数据
	pst->array[pst->top] = x;
	pst->top++;
}


/* 出栈 */
void StackPop(Stack* pst) {
	assert(pst);  //防止pst为空
	//assert(pst->top > 0);  //防止top为空
	assert(!StackIsEmpty(pst));
	
	pst->top--;
}

/* 返回栈顶数据 */
StackDataType StackTop(Stack* pst) {
	assert(pst);  //防止pst为空
	//assert(pst->top > 0);  //防止top为空
	assert(!StackIsEmpty(pst));

	return pst->array[pst->top - 1];
}

/* 计算栈的大小 */
int StackSize(Stack* pst) {
	assert(pst);  //防止pst为空
	
	// 因为我们设定 top 是指向栈顶的下一个,所以top就是size
	return pst->top; 
}

💬 Test.c(测试用)

#include "Stack.h"

void TestStack1() {
	Stack st;
	StackInit(&st);
	StackPush(&st, 1);
	StackPush(&st, 2);
	StackPush(&st, 3);
	StackPush(&st, 4);

	StackPop(&st);
	StackPop(&st);
	StackPop(&st);
	StackPop(&st);
	//StackPop(&st);

	//printf("%d", StackTop(&st));

	StackDestroy(&st);
}


void TestStack2() {
	// 入栈:1 2 3 4
	Stack st;
	StackInit(&st);
	StackPush(&st, 1);
	StackPush(&st, 2);
	StackPush(&st, 3);
	StackPush(&st, 4);

	// 出栈:4 3 2 1
	while (!StackIfEmpty(&st)) {
		printf("%d ", StackTop(&st));
		StackPop(&st);
	}
	
	StackDestroy(&st);
}

void TestStack3() {
	// 入栈:1 2 3 4
	Stack st;
	StackInit(&st);
	StackPush(&st, 1);
	StackPush(&st, 2);
	StackPush(&st, 3);
	StackPush(&st, 4);

	// 提前出栈:4 3
	printf("%d ", StackTop(&st));
	StackPop(&st);
	printf("%d ", StackTop(&st));
	StackPop(&st);

	StackPush(&st, 5);
	StackPush(&st, 6);

	// 出栈:6 5 2 1
	while (!StackIfEmpty(&st)) {
		printf("%d ", StackTop(&st));
		StackPop(&st);
	}

	StackDestroy(&st);
}

int main() {
	TestStack3();

	return 0;
}



本章完。