一起养成写作习惯!这是我参与「掘金日新计划 · 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;
}
本章完。