今天来介绍一下栈。本文先从栈的基本概念和结构入手,接着讲解基本操作,最后通过一个回文数的例子来巩固今天学的栈操作
一、栈的基本概念
栈: 只允许在一端进行插入或删操作的线性表。首先栈是一种线性表,但是限定这种线性表只能在某一段进行插入和删除操作
栈顶:线性表允许进行插入和删除的那一端
栈底:固定的,不允许进行插入和删除的另一端
空栈:不含任何元素的空表
二、栈的操作
队列是FIFO(先进先出),栈是FILO(先进后出 或者 后进先出)
最先进栈的元素,是不是就只能是最后出栈呢
答案是不一定,要看什么情况。栈对线性表的插入和删除的位置进行了限制,并没有对元素进出的时间进行限制,也就是说,在不是所有元素都进栈的情况下,进去的元素也可以出栈,只要保证是栈顶元素出栈就可以,那么变化就很多了,举例来说:
如果我们有3个整形数字元素1、2、3依次进栈,会有哪些出栈次序呢?
- 第一种:1、2、3进,再3、2、1出。这事最简单的最好理解的一种,出栈次序为321
- 第二种:1进,1出,2进,2出,3进,3出,出栈次序为123
- 第三种:1进,2进,2出,1出,3进,3出,出栈次序为213
- 第四种:1进,1出,2进,3进,3出,2出,出栈次序为132
- 第五种:1进,2进,2出,3进,3出,1出,出栈次序为231
有没有可能是312这样的次序出栈呢,答案是不可能的,因为3先出栈,就意味着3曾经进栈,既然3进栈了,那也意味着1和2已经进栈了,此时,2一定是在1的上面,就是更接近栈顶,那么出栈只可能是321,不然不满足123一次进栈的要求,所以此时不会发生1比2先出栈的情况
栈的基本操作
InitStack(&S):初始化一个空栈S
StackEmpty(S):判断一个栈是否为空,若栈S为空返回true,否则返回false
Push(&S,x):进栈,若栈S未满,将x加入使之成为新栈顶
Pop(&S,&x):出栈,若栈S非空,弹出栈顶元素,并用x返回
GetTop(S,&x):读栈顶元素,若栈S非空,用x返回栈顶元素
ClearStack(&S):销毁栈,并释放栈S占用的存储空间。
(符号“&”是C艹特有的,用来宝石引用,有的采用C语言中的指针类型“*”,也可以达到传址的目的)
如果没有特殊要求,可以直接用这些基本的操作函数
三、栈的存储结构
3.1、顺序存储结构
#define MaxSize 50 //定义栈中元素的最大个数 typedef struct{ Elemtype data[MaxSize]; //存放栈中元素 int top; //栈顶指针 }SqStack;
共享栈
利用栈底位置相对不变的特性,可以让两个顺序栈共享一个一位数据空间,将两个栈的栈底分别设置在共享空间的两端,两个栈向共享空间的中间延伸。
两个栈的栈顶指针都指向栈顶元素,top0=-1时0号栈为空,top1=MaxSize时1号栈为空,当且仅当两个栈顶的指针相邻时,判断为占满。当0号栈进栈时top0先加1再复制,1号栈进栈时top1先减1再复制;出栈时刚好相反。
共享栈是为了更有效地利用存储空间,两个栈的空间相互调节,只有在整个存储空间被占满时才发生上溢。其存取数据的时间复杂度均为O(1)所以对存取效率没有什么影响。
事实上使用遮掩的数据结构,通常都是当两个栈的空间需求有相反关系时,也就是一个栈增长时另一个栈在缩短的情况。就像买卖股票一样,你买入时,一定是有一个你不知道的人在做卖出操作。有人赚钱,就一定有人赔钱。这样使用两栈共享空间存储方法再有比较大的意义。
当然,这里指两个具有相同数据类型的栈,类型不同不适用这种方法。
3.2、栈的链式存储结构(链栈)
typedef struct Linknode{ ElemType data; //数据域 struct Linknode *next; //指针域 } *LinkStack; //栈类型定义
四、实战
以上就是栈的基本介绍,其实主要就记住三点------栈的FILO、栈的顺序存储结构和栈的链式存储结构
下面来,利用栈的操作,判断输入的字符串是否为回文数
(所谓的回文数,就是输入的字符串是否对称),下面采用两种方法判别
4.1、不采用栈来操作(个人第一反应的方法),代码如下
#include<stdio.h> #include<string.h> #define N 101 // 判断是否为回文数 int main() { char str[N]; int i,middle,length,flag; while(gets(str)) { length = strlen(str); middle = length/2; // 取中位数 flag = 1; // 用0标记不是回文数 1标记回文数 for(i=0;i<middle;++i) { if(str[i]!=str[length-i-1]) { flag = 0; break; } } if(flag==1) printf("YES "); else printf("NO "); } return 0; }
4.2、采用栈(顺序栈)来操作
#include<stdio.h> #include<string.h> #define MaxSize 101 // 采用栈来判断回文数 typedef struct{ char data[MaxSize]; // 存放栈中元素 int top; // 栈顶指针 }Stack; int main() { Stack s; char array[MaxSize]; int i,middle,len,next; while(gets(array)) { len = strlen(array); middle = len/2; s.top = 0; // 栈初始化 // 将middle 前的字符依此入栈 for(i=0;i<middle;++i) { s.data[++s.top] = array[i]; } // 判断字符串的长度是奇数还是偶数,并找出需要进行字符匹配的起始下标 if(len%2==0) next = middle + 1; else next = middle + 2; // 开始匹配 for(i=next-1;i<len;++i) { if(array[i]!=s.data[s.top]) break; s.top--; } // 如果top的值为0,则说明栈呢所有字符都被一一匹配了 if(s.top==0) printf("YES "); else printf("NO "); } return 0; }
不管是队列还是栈,一般采用顺序存储的较多。就跟上面的例子一样,不采用栈来的更快,更简洁些,代码风格因人而异!本人菜鸟一枚,欢迎拍砖赐教