线性表的一种特殊的存储结构。与学习过的
由于栈只有一边开口存取数据,称开口的那一端为“栈顶”,封死的那一端为“栈底”(类似于盛水的木桶,从哪进去的最后还得从哪出来)。
栈的“先进后出”原则
使用栈存储数据元素,对数据元素的“存”和“取”有严格的规定:数据按一定的顺序存储到栈中,当需要调取栈中某数据元素时,需要将在该数据元素之后进栈的先出栈,该数据元素才能从栈中提取出来。
如图 1 ,栈中存放了 4 个数据元素,进栈的顺序是 A 先进栈,然后 B 进,然后 C 进,最后 D 进栈;当需要调取 A 时,首先 D 出栈,然后 C 出栈,然后 B 出栈,最后 A 才能出栈被调用。
就好比只有一个门的车库(每次仅允许一辆车通过),每辆车好比一个数据元素,只有离门最近的车先开出来,里边的车才能出来;最里边的车是最先开进去的,注定要最后出来。
栈操作数据元素的方法
栈操作数据元素只有两种动作:
数据元素用栈的数据结构存储起来,称为“入栈”,也叫“压栈”。
数据元素由于某种原因需要从栈结构中提取出来,称为“出栈”,也叫“弹栈”。
栈的两种表示方式
既然栈也是线性表,那么它就同样有线性表的两种表示形式:链式栈(简称“)。
两者的区别在于存储的数据元素在物理结构上是否是相互紧挨着的。顺序栈存储元素预先申请连续的存储单元;链栈需要即申请,数据元素不紧挨着。
栈的“上溢”和“下溢”
栈存储结构调取栈中数据元素时,要避免出现“上溢”和“下溢”的情况:
“上溢”:在栈已经存满数据元素的情况下,如果继续向栈内存入数据,栈存储就会出错。
“下溢”:在栈内为空的状态下,如果对栈继续进行取数据的操作,就会出错。
栈的“上溢”和“下溢”,可以总结为:栈满还存会“上溢”,栈空再取会“下溢”。
对于栈的两种表示方式来说,顺序栈两种情况都有可能发生;而链栈由于“随时需要,随时申请空间”的存储结构,不会出现“上溢”的情况。
顺序栈
顺序栈的实现采用的是“空栈”;只要数据元素进栈,top 就加 1 ;数据元素出栈, top 就减 1 。
例如,使用顺序栈的存储结构将(’a’,’b’,’c’,’d’)四个元素逐个压栈并输出栈顶元素。
实现代码:
#include
//元素elem进栈
int push(char* a,int top,char elem){
a[++top]=elem;
return top;
}
//数据元素出栈
int pop(char * a,int top){
if (top==-1) {
printf("空栈");
return -1;
}
printf("弹栈元素:%c\n",a[top]);
top--;
return top;
}
int main() {
char a[100];
int top=-1;
top=push(a, top, 'a');
top=push(a, top, 'b');
top=push(a, top, 'c');
top=push(a, top, 'd');
top=pop(a, top);
top=pop(a, top);
top=pop(a, top);
top=pop(a, top);
top=pop(a, top);
return 0;
}
输出结果:
弹栈元素:d
弹栈元素:c
弹栈元素:b
弹栈元素:a
空栈
链栈
链栈,用线性表的链式存储结构实现。
链栈一般不需要创建头结点,头结点会增加程序的复杂性,只需要创建一个头指针就可以了。
用#include
#include
typedef struct lineStack{
char data;
struct lineStack * next;
}lineStack;
lineStack* push(lineStack * stack,char a){
lineStack * line=(lineStack*)malloc(sizeof(lineStack));
line->data=a;
line->next=stack;
stack=line;
return stack;
}
lineStack * pop(lineStack * stack){
if (stack) {
lineStack * p=stack;
stack=stack->next;
printf("弹栈元素:%c ",p->data);
if (stack) {
printf("栈顶元素:%c\n",stack->data);
}else{
printf("栈已空\n");
}
free(p);
}else{
printf("栈内没有元素");
return stack;
}
return stack;
}
int main() {
lineStack * stack=NULL;
stack=push(stack, 'a');
stack=push(stack, 'b');
stack=push(stack, 'c');
stack=push(stack, 'd');
stack=pop(stack);
stack=pop(stack);
stack=pop(stack);
stack=pop(stack);
stack=pop(stack);
return 0;
}
输出结果:
弹栈元素:d 栈顶元素:c
弹栈元素:c 栈顶元素:b
弹栈元素:b 栈顶元素:a
弹栈元素:a 栈已空
栈内没有元素
总结
实际生活中使用手机时,屏幕页面的跳转使用的就是栈结构(跳转页面时,前一个页面会被存储到栈中;做回退操作时会回到上一个页面,这是进栈页面出栈的效果)。另外在求 n! 时,可以通过函数的递归来实现,这个过程的底层就用到了栈结构。
除此之外,数制转换和括号匹配问题也可以用栈来解决(下节介绍)。