一、线性表的操作增加约束条件
线性表元素和元素逻辑的逻辑关系1:1 插入 删除 可以在任意位置进行操作
insertTable(Table *tab, int pos, Element e);
如果对插入 删除进行约束
栈 : 只能在一端操作
元素操作行为上: 先入后出(后入先出)
羽毛球桶 子弹夹
队列 : 只能在两端操作
栈的适应场景:
1. 用户场景:
场景1:
像下图的迷宫(A是入口,F是出口),我们总是先向下,再向右,再向上,最后向左(穷举)。根据这个规律,A->B->C->G->H,发现走不通了,然后倒退到C,再到D->E->F(成功找到出口)。这个倒退的过程就要用到栈。
场景2:
历史记录 ctrl+z 撤销(WPS,Word) 倒退
场景3:
软件编程的时候,函数保护现场和恢复现场使用
局部变量
函数返回后,局部变量消失(不准确的说法)
全局变量
函数返回后,全局变量存在,程序退出后,彻底消失
例如,在执行fun1()函数第20行代码时,我们要调用abc()函数,但最终调用完之后要回到这里,我们必须记住这个位置(或者记录第21行的位置),等调用完之后继续执行21行,这里就需要用到栈。
2.操作行为:
栈顶 top 可以加 也可以减
先取元素 后取元素
栈的代码不用背,了解原理就可以根据前面的写出后面的。
4种栈:
满栈 空栈 top指针指向的元素是 有效 和 待入栈 的位置
递增 递减 入栈时,top是加++ 减 --
例如空递增
C语言自动的(天然的)实现了一个栈:
满递减栈
二、顺序存储栈的实现(容易栈溢出)
满递增
Element data[MaxSize];
int top;
push
top++;
data[top] = e;
pop
*e = data[top];
top--;
三、链式存储栈的实现
栈只能在一段操作,所以我们需要维护一个指向某个节点的指针,这个指针一般就叫 node *top;
如上图所示,要插入一个新元素,我们先将新元素指向栈顶,然后更新top。
newNode->next = top;
top = newNode;
出栈:(备份思想)
tmp = top;
top = top->next;
free(tmp);
四、代码实现:
由于顺序栈和链式栈写入一个文件,故先写一个头文件common.h来存放都要用的部分。
common.h
#ifndef COMMON_H
#define COMMON_H
#include<stdio.h>
#include<stdlib.h>
typedef int Element;
#endif //COMMON_H
顺序栈:
arrayStack.h
#ifndef ARRAYSTACK_H
#define ARRAYSTACK_H
/* 顺序栈 满递增栈*/
#define MaxStackSize 5 //宏定义栈大小
#include "common.h"
typedef struct {
Element data[MaxStackSize];
int top;
}ArrayStack;
//申请栈
ArrayStack *createArrayStack();
//释放栈
void releaseArrayStack(ArrayStack *stack);
//压栈push(把哪个元素e压入哪个栈stack)
int pushArrayStack(ArrayStack *stack, Element e);
//出栈pop
int popArrayStack(ArrayStack *stack, Element *e);
#endif //ARRAYSTACK_H
arrayStack.c
#include <stdlib.h>
#include <stdio.h>
#include "arrayStack.h"
/* 顺序栈 满递增栈*/
ArrayStack *createArrayStack() {
ArrayStack *stack = (ArrayStack *) malloc(sizeof(ArrayStack));
if (stack == NULL) {
printf("malloc failed\n");
return NULL;
}
stack->top = -1;//因为是满栈,初始化为-1.如果是空栈,初始化为0.
return stack;
}
/* 数据能存放的位置 : [0, 1, ... max-1] */
int pushArrayStack(ArrayStack *stack, Element e) {
// 上溢出(overflow)的问题排除
if (stack->top >= MaxStackSize - 1) { //判断条件也可以写成stack->top+1 >MaxStackSize - 1
printf("OverFlow\n");
return -1;
}
stack->data[++stack->top] = e;//等价于stack->top++; stack->data[stack->top]=e;
return 0;
}
int popArrayStack(ArrayStack *stack, Element *e) {
if (stack->top < 0) {
printf("UnderFlow!\n");
return -1;
}
*e = stack->data[stack->top--];//等价于*e = stack->data[stack->top]; top--;
return 0;
}
void releaseArrayStack(ArrayStack *stack) {
if (stack) {
free(stack);
}
}
链栈:
linkStack.h
#ifndef LINKSTACK_H
#define LINKSTACK_H
/* 链式存储栈,关注节点结构,栈的操作结构 */
#include "common.h"
// 节点结构
typedef struct stackNode {
Element e;
struct stackNode *next;
}StackNode;
// 链式栈的表头结构
typedef struct {
StackNode *top; //栈顶
int count; // 统计栈里的元素
}LinkStack;
LinkStack *createLinkStack();
void releaseLinkStack(LinkStack *stack);
int pushLinkStack(LinkStack *stack, Element e);
int popLinkStack(LinkStack *stack, Element *e);
#endif //LINKSTACK_H
linkStack.c
#include "linkStack.h"
LinkStack *createLinkStack() {
LinkStack *stack = (LinkStack *) malloc(sizeof(LinkStack));//申请表头
if (stack == NULL) {
printf("malloc failed!\n");
return NULL;
}
stack->top = NULL; //刚开始没元素,为空
stack->count = 0; //
return stack;
}
int pushLinkStack(LinkStack *stack, Element e) {
StackNode *node = (StackNode *) malloc(sizeof(StackNode));//申请栈空间
if (node == NULL) {
printf(".....!\n");
return -1;
}
node->e = e;
node->next = stack->top;
stack->top = node;
stack->count++;
return 0;
}
int popLinkStack(LinkStack *stack, Element *e) {
if (stack->top == NULL) {
printf(",,,,,,,,\n");
return -1;
}
*e = stack->top->e;
//利用备份思想
StackNode *tmp = stack->top;
stack->top = tmp->next;
free(tmp);
stack->count--;
return 0;
}
void releaseLinkStack(LinkStack *stack) {
if (stack) {
// 以top指向的元素逐个删除,top为空时停止
while (stack->top) {
StackNode *tmp = stack->top;
stack->top = tmp->next;
free(tmp);
stack->count--;
}
printf("stack count : %d\n", stack->count);
//所有节点释放完之后,释放表头
free(stack);
}
}
main.c来测试结果
顺序栈和链栈的测试结果用===============划分
#include <stdio.h>
#include "arrayStack.h"
#include "linkStack.h"
/*顺序栈的测试*/
int test01() {
ArrayStack *stack = createArrayStack();
Element e;
if (stack == NULL) {
return -1;
}
//压五个入栈[100,101,...104]
for (int i = 0; i < 5; ++i) {
pushArrayStack(stack, i + 100);
}
//再压会溢出
pushArrayStack(stack, 500);
printf("pop:");
for (int i = 0; i < 5; ++i) {
popArrayStack(stack, &e);
printf("\t%d", e);
}
printf("\n");
popArrayStack(stack, &e);
releaseArrayStack(stack);
return 0;
}
/* 链式栈的测试 */
int test02() {
LinkStack *stack = createLinkStack();
Element e;
if (stack == NULL) {
return -1;
}
//压入5个
for (int i = 0; i < 5; ++i) {
pushLinkStack(stack, i + 50);
}
printf("stack node: %d\n", stack->count);
printf("pop:");
//弹出5个
for (int i = 0; i < 5; ++i) {
popLinkStack(stack, &e);
printf("\t%d", e);
}
printf("\n");
popLinkStack(stack, &e);//已经弹完了,再弹会报错
releaseLinkStack(stack);
return 0;
}
int main() {
test01();
printf("=================\n");
test02();
return 0;
}
在CLion上运行结果如下: