堆栈
堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶)对数据项进行插入和删除。
特殊的操作使得堆栈中的元素满足 LIFO (后进先出),如图所示:
本文所涉及的代码在这。
堆栈可以使用顺序存储结构和链式存储结构来实现。
顺序结构 实现
堆栈的顺序存储实现借助了数组,同时引入了 top 来标志栈内元素的个数,当元素为空时,一般地,我们使用 top = -1 来表示。
初始化
使用数组来存储堆栈数据。同时使用 top 标志堆栈的空状态。
/// 构建一个空栈
/// @param s 结果带回
Status stackInit(Stack *s){
s->top = -1;
return SUCCESS;
}
/// 清空一个栈,顺序存储空间不能释放,将所有位置都标记为未使用就是清空。
/// @param s 结果带回
Status stackClear(Stack *s){
s->top = -1;
return SUCCESS;
}
/// 判断栈是否为空
/// @param s 栈
Status stackIsEmpty(Stack s){
return -1 == s.top ? TRUE : FALSE;
}
增
堆栈的插入又叫做压栈或入栈,它只能在栈顶进行。
/// 压栈
/// @param s 堆栈
/// @param e 新元素
Status stackPush(Stack *s, ElementType e){
if (MAXSIZE == stackGetCount(*s)) {
printf("栈已经满了!\n");
return ERROR;
}else{
// 栈顶 +1 ,并且将新的数据放入堆栈。
s->data[++s->top] = e;
}
return SUCCESS;
}
删
堆栈的删除又叫做弹栈或者出栈,它只能在栈顶进行操作。
/// 弹栈
/// @param s 栈
/// @param e 元素值带回
Status stackPop(Stack *s, ElementType *e){
if (-1 == s->top) {
printf("栈已经空了!\n");
return ERROR;
}else{
// 取出当前元素带回,栈顶 -1。
*e = s->data[s->top--];
}
return SUCCESS;
}
链表结构 实现
链式堆栈的机构如图所示:
初始化
本部分还实现了一些辅助型的功能,例如,栈的遍历和判空等。
/// 初始化堆栈
/// @param s 堆栈
Status stackInit(ChainStack *s){
s->top = NULL;
s->count = 0;
return SUCCESS;
}
/// 清空堆栈 清空堆栈的时候需要将栈内的结点都释放掉。
/// @param s 堆栈
Status stackClear(ChainStack *s){
StackNodePtr p, temp;
p = s->top;
while (p) {
temp = p;
p = p->next;
free(temp);
}
s->count = 0;
s->top = NULL;
return SUCCESS;
}
/// 判断堆栈是否为空
/// @param s 堆栈
Status stackIsEmpty(ChainStack s){
return 0 == s.count;
}
/// 获取堆栈的长度
/// @param s 堆栈
Status stackGetCount(ChainStack s){
return s.count;
}
/// 遍历输出堆栈
/// @param s 堆栈
Status stackPrint(ChainStack s){
StackNodePtr p;
p = s.top;
printf("链表信息:");
while (p) {
printf("%d, ", p->data);
p = p->next;
}
printf("\n\n");
return SUCCESS;;
}
压栈
压栈操作需要注意的是对于top指针的重新定位:
/// 压栈
/// @param s 堆栈
/// @param e 入栈元素
Status stackPush(ChainStack *s, ElementType e){
StackNodePtr k;
k = (StackNodePtr)malloc(sizeof(StackNode));
k->data = e;
k->next = s->top;
s->top = k;
s->count++;
return SUCCESS;
}
弹栈
弹栈操作需要将top指针重新定位,还要将出栈结点释放,将值带回:
/// 弹栈
/// @param s 堆栈
/// @param e 出栈元素带回
Status stackPop(ChainStack *s, ElementType *e){
if (!stackGetCount(*s)) {
printf("堆栈已经空了!");
return ERROR;
}
StackNodePtr k;
k = s->top;
*e = k->data;
s->top = s->top->next;
free(k);
return SUCCESS;
}
写在最后
栈的应用不仅仅是在于数据结构上的应用,在许多问题的算法解决上堆栈的FIFO 思想都贯穿其中,例如,在程序运行时,函数的嵌套调用时,运行现场以及临时变量的保留底层都是使用了堆栈的思想。一些算法题:例如括号匹配,字符串的匹配都可以使用堆栈的思想来解决。
递归
函数的递归调用就是用到了堆栈来保留现场,所以递归是是分浪费空间的。
理论上,所有的递归都能改成循环来实现。
下⾯面3种情况下,我们会使⽤用到递归来解决问题:
- 定义是递归的
- 数据结构是递归的
- 问题的解法是递归的