一、栈的顺序存储结构
1.基本概念
栈顶就是表尾。
栈顶是栈插入和删除的地方。
栈就是只允许在表尾进行添加或删除,是顺序存储结构线性表的特例或者说简化。
为什么说是简化呢?相对于顺序存储结构来说栈只允许在表尾插入和删除,而顺序存储结构线性表是可以在任何位置插入和删除的。所以简化了,我固定位置了。
栈或者队列是固定插入和删除的位置的顺序存储结构的线性表。
具体区分:
栈是固定一端的位置进行插入和删除的顺序存储结构的线性表。
队列是将插入和删除分在两端的顺序存储结构的线性表。
队列:在一端添加,在另一端删除。
2.栈的生长方向
假如栈的长度为n,则数组下标范围为0 ~(n-1)
向上生长:入栈时,栈顶元素下标递增,栈底是数组元素0。空栈的判断一般是栈顶指针是否为-1,当然也有另一种栈的实现方法就是以0为空栈的判断依据。
向下生长:入栈时,栈顶元素下标递减,栈底是数组元素n-1。空栈的判断一般是栈顶指针是否为n,当然也有另外一种栈的,实现方法是以n-1为空栈的判断依据。
因为向上生长的栈,入栈1个元素时,栈顶是元素0,空栈时-1;
而向下生长的栈,入栈1个元素时,栈顶为元素n-1,那空栈时就是n了。
第一,向上生长方向,入栈时是递增方向,出栈时是递减。即递增入栈,递减出栈。
(1)向上生长方向的第1种实现方法。——栈顶本身指向有出栈入栈的数据,它是出栈入栈的最后一个数据指向,本身指向出栈入栈的数据。
以栈顶指向-1为空栈,以栈顶指向n-1为栈满。
它入栈push思路:先判断是否栈满(栈顶为n-1),满的话就返回错误。栈没满,栈顶先自增1,再把数据写入栈里。
它出栈pop思路:先判断是否空栈(栈顶为-1),空栈了就没啥可出了,返回错误。否则,先把栈顶内容读出,栈顶指针再自减1。
(2)向上生长方向的第2种实现方法。——数据在栈顶下面,每次入栈出栈栈顶指向事屋顶一样,屋顶下面才是入栈出栈的数据,栈顶本身的指向只是个界限。
以栈顶指向0为空栈,以栈顶指向n为栈满。
它入栈push思路:先判断是否栈满(栈顶为n),满的话就返回错误。栈没满,先把数据写入栈里,之后栈顶才自增1。
它出栈pop思路:先判断是否空栈(栈顶为n),空栈了就没啥可出了,返回错误。否则,栈顶指针先自减1,再把栈顶内容读出。
第二向下生长方向n-0。递减入栈,递增出栈。
以栈顶指向n为空栈,以栈顶指向0为栈满。 它入栈push思路:先判断是否栈满(栈顶为0),满的话就返回错误。栈没满,栈顶先自减1往下移,再把数据写入栈里。 它出栈pop思路:先判断是否空栈(栈顶为0),空栈了就没啥可出了,返回错误。否则,先把栈顶内容读出,栈顶指针再自增1。
以上情况如下图。
当然,还有下面这种情况:
三、链栈
链栈不需要头结点的。
栈顶在链表表头,也就是链表的第一个节点。
3.1有个问题
问题:形参linkstack *S,这个传入之前肯定要定义一个栈顶节点,这个就固定不变了,之更新栈顶top指针就完事了,假如是linkstack a,那么传入push(&a,3),在传入之前是不是要初始化a?
答:要初始化为0。比如
linkstack a={0,0};
push(&a,3);
再问:为啥?linkstack 是个结构体,里面有个链表节点的指针的,这个链表节点的指针变量要赋为0?那这个链表节点指针为零,那它又是个结构体内部有数据域和指针域,也需要全赋为0吧?
答:只是一个指针,地址。指针是一个存储单元,4个字节
答:我明白了,
linkstack a={0,0};
虽然linkstack是个结构体,但是里面就是2个元素:一个是地址变量(指针),一个计数(普通变量)。
定义a的目的是保存链栈的栈顶指针,也就是固定这个a来保存链栈的头结点,那么初始化为0,是指链栈的栈顶的指针为空,就是目前这个链栈为空么,没有链表,所以栈顶指向为空。——还是指针没学好。
3.2顺序栈和链栈
顺序栈和链栈的出栈入栈的时间复杂度都为O(1),因为没有循环。
在空间上,若空间确定就用顺序栈,若不确定就用链栈。
如果使用过程中元素变化不可预料,有时很小,有时非常大,那么最好是用链擒,反之,如果它的变化在可控范围内,建议使用顺序棋会更好一些。