第三讲---栈
-
复习
上次我们讲到了线性表的链式表示,这里我们做一个简单的复习:
线性表的链式表示有哪些特点?
- 访问方面
- 存储方面
- 不同操作的复杂度方面
线性表有几种表示方式?
链表中你能想到几种结构?(最简单的有单链表,还有什么链表,分别有什么特点)
链表的插入、删除中,不同链表结构下,在p结点后面、前面插入一个结点s的操作是什么?
一、栈
栈: 只允许在一端进行插入或删除操作的线性表。首先栈是一种线性表,但是限定这种线性表只能在某一端进行插入和 删除操作。
栈顶:线性表允许进行插入和删除的那一端。
栈底:固定的,不允许进行插入和删除的另一端。
空栈:不含任何元素的空表。
举个栗子:
上次一将到唐僧成立了一家传销公司,后来公安局不允许他这么干,所以改成了下线也可以知道上线的模式。如此一来,公司就很透明了。唐僧决定应该组织一次团建,来团结人心,所以他们决定去春游~
非常不幸,春游的路上有一口枯井(栈),里面什么都没有(空栈)。唐僧这个人已经飘飘然不行了,所以完全没有注意到枯井。所以,咔~唐僧(栈底)掉下去了。然后猴哥说,师傅你别怕,我来救你-----咔,猴哥主动跳下去了(唐僧:woxxxxx~)。八戒一看,哇,猴哥这么会拍马屁,我也拍一个,咔~八戒跳下去了(唐僧激动的说不出话来)。然后,沙僧和小白龙(栈顶)也相继跳下去了。所以现在是这个状态:
可以看到:
1.这口枯井只能从上面跳下来,不能从下面钻出去….
(只允许在一端进行插入或删除操作)
2.如果唐僧想出去,先得马马出去,沙僧出去,八戒出去,猴哥出去,他才能出去。
(后进先出)
二、栈的基本操作(请坐下):
1.老规矩,我们先看下他的结构体:
#define MaxSize 50 //最大栈的容量(井里最多放50人)
typedef struct{
int data[MaxSize]; //我们用线性表的顺序表示方式去存取内容
//当然,你也可以用链式表示存储,如果你不嫌麻烦的话
//顺序表示有个好处,因为我们只涉及到尾部的增加和删除
//所以增删都是0(1)
int top;//用来表示当前栈顶的位置
}SqStack;
2.InitStack(&S):初始化栈
void InitStack(SqStack &S){
S.top = -1;//用-1表示栈为空的状态
}
3.StackEmpty(S):判断S是否是空栈
bool StackEmpty(SqStack S){
if(S.top == -1){
return true;//注意,空的时候返回的是true
}
return false;
}
4.Push(&S,x):将x进栈
bool Push(SqStack &S,int x){
//1.判断
if(S.top == MaxSize-1){//想想为什么有-1
//如果你想写的更好的话
//栈满时候,我们一般会用remalloc扩加,并修改MaxSzie
//这里一般不作扩加要求
return false;
}
//2.修改
S.top++;//比如初始化是top=-1,++以后top=0
//3.赋值
//这样我们才能放在第0个下标(第一个位子)上
S.data[top] = x;
//4.返回true
return true;
}
5.Pop(&S,&x):将栈顶元素出栈并赋值给x
bool Pop(SqStack &S,int &x){
//想想为什么x要加地址符
//1.判断
if(S.top == -1){
return false;//如果是空栈的话,就出不了了
}
//2.赋值
x = S.data[top];
//3.修改
top--;
//4.返回成功
return true;
}
6.GetTop(S,&x):将栈顶元素赋值给x(想想和5的区别):
bool Pop(SqStack S,int &x){
//1.判断
if(S.top == -1){
return false;//如果是空栈的话,就出不了了
}
//2.赋值
x = S.data[top];
//3.返回成功
return true;
}
7.GetNum(S):获取栈内元素个数(我自己加的,有助于理解top):
int GetSum(SqStack S){
return S.top+1;
//想想为什么要加+1
//同样是加1
//为什么不是++S.top
//或者为什么不是S.top++
}
三、共享栈和栈的链式存储结构
1.共享栈
我们发现,一个栈开辟50的初试空间,但是有很多时间用不了这么多,但是我们又担心那些极少数用到40多空间的情况。所以我们可以创建一个共享栈来解决问题,共享栈相当于一个T型的井:
这种情况下,栈的一些操作就需要发生变化了:
1.S1进栈:top1++,S1出栈:top1--;
2.S2进栈:top2--,S2出栈:top2++;
3.S1元素个数:top1 +1,s2元素个数:MaxSize-top2(这个比较难算,举个栗子就出来了);
4.S1栈空:top=-1,S2栈空:top=MaxSize;
5.栈满:top1 = top2-1;
2.栈的链式表示
这个比较好理解,因为顺序表示在栈满时候就加不进去了,而链式表示可以随便加。就比较方便操作。要注意的是链栈是采用不带头结点的头插法的。因为我们存储的是链表的头结点,每次都得遍历到最后一个结点。可以想想有没有解决的思路。这个一般不考。
四、习题
8.向一个栈顶指针为top的链栈中插入一个x结点,则执行()
因为链栈采用的是不带头结点的头插法。所以top指的就是栈顶元素,插入一个结点x,x->next =top。而这时候x为栈顶元素,所以要更新top,即top=x;
9.链栈执行Pop操作,并将出栈的元素存在x中,应该执行()
因为链栈采用的是不带头结点的头插法。所以top指的就是栈顶元素,Pop指的是返回栈顶元素,并将出栈。所以x = top->data,可以将栈顶元素存在x中。出栈的话,因为是头插法,所以top = top->next;
10.经过一下栈的操作后,变量x的值为()
InitStack(st);Push(st,a);Push(st,b);Pop(st,x);Top(st,x);
Push(st,a)后,栈内有a;Push(st,b)后,栈内有b,a(b在栈顶);Pop(st,x)表示把栈顶元素赋值给x,然后出栈,这时栈内只剩a,x = b;Top(st,x)表示把栈顶元素赋值给x,这时候x = a;
11.3个不同的元素依次进栈,能得到()种不同的出栈序列
这个就是死记硬背的东西,考试时候,如果数目小的话,可以硬枚举,公式考试前记一记就好了,也不太会考这么偏的。
24.设栈的初试状态为空,当字符序列“n 1 _”作为栈的输入时,输出长度为3,且可用作C语言标识符的序列有()个
这种情况,一共三个,所以可以暴力枚举,枚举的话可以主要看n的位置,我们先看栈能输出哪几种:
a)最后输出是nxx时候,可以发现“n 1 _ ” 和“n _ 1”,都可以通过栈输出出来,
b)最后输出是xnx时候,也就是n是第二个输出的,可以想到,第一个输出的只能是1,所以结果只能是“1 n _”
c)最后输出是xxn时候,n是最后一个输出的,结果可能是 “1 _ n”、“ _ 1 n”
OK,所有可能的结果都找出来了,我们看看:
“n 1 _ ” ,“n _ 1”,“1 n _”,“1 _ n”,“ _ 1 n”
可以看到,“1 n _”,“1 _ n”不可以,其他都可以,选C
27.判断共享栈满的条件是 top2 = top1 +1