一:前言
对于栈的操作,虽不及其他数据结构一样多,但是栈的实际应用却是十分广泛。比如在我们进行代码编写的编译器中,对于函数调用、递归操作、表达式求值以及编译器的括号匹配等问题均是通过反复的入栈和出栈操作进行控制的。栈结构在计算机科学的历史上,地位是举重若轻的,值得我们好好进行研究!
在顺序栈和链栈的实现中,我总是将top指针指向栈的下一位置的,这样做是为了以下优点考虑:
1. 简化入栈操作
- 减少指针调整:当top指针指向入栈元素的下一个位置时,新元素可以直接存储在top指针当前指向的位置,然后top指针简单地递增(或增加相应的偏移量)以指向下一个空位置。这样,在入栈操作中就不需要额外调整top指针来指向新元素的位置,因为新元素已经存储在了top指针原本指向的下一个位置。
- 避免覆盖风险:如果top指针指向入栈元素的位置,那么在入栈前需要先确保该位置是空的,否则可能会覆盖原有数据。而将top指针指向下一个位置则自然避免了这种风险,因为该位置在入栈前肯定是空的。
2. 便于判断栈空和栈满
- 栈空判断:当top指针指向入栈元素的下一个位置时,如果栈为空,则top指针和栈底指针(base指针)会指向相同的位置或相邻的位置(取决于具体实现)。这样,通过比较top指针和base指针就可以很容易地判断栈是否为空。
- 栈满判断(针对动态分配的顺序栈):在动态分配的顺序栈中,如果top指针指向入栈元素的下一个位置,那么当栈满时,top指针和栈的当前容量边界(通常是base指针加上栈的当前分配大小)之间将没有空余位置。这样,就可以通过比较top指针和容量边界来判断栈是否已满,从而决定是否需要进行扩容操作。
如图:
二:定义
栈其实算是属于操作受限的线性表,规定栈只能在栈顶进行插入与删除,相较于其他如数组与链表这样的线性表,栈的操作受到了一定的限制。数据结构中的栈是一种遵循后进先出的结构,又被称为LIFO(last in first out)结构。在这种数据结构中,元素的添加(称为“入栈”或“压栈”)和移除(称为“出栈”或“弹栈”)操作都仅发生在栈的同一端,这一端被称为栈顶,而另一端则被称为栈底。栈底是栈中固定不变的一端,栈顶是随着元素的加入和移除而动态变化的一端。可以理解为栈相当于一个放在地上的水缸,栈底是挨着地面的水缸底部,而栈顶是水缸中水的水位线,只能在水缸的缸口进行添水和取水。
说明:在实际的计算机内存分配中,计算机对与栈的内存分配实际上是从高地址到低地址的方向进行分配的,这与堆的内存分配方向是相向的,具体原因在这不做赘述,感兴趣可以自行搜索。但是在我们日常的操作中,我们习惯性的将栈进行从下到上的方向为操作方向,因为这符合我们大部分人的实际逻辑,在实际问题上其实并无影响。在这里我们将采取从下到上的方向进行操作。
下图为计算机为栈分配内存空间的逻辑,这只是一个简略图,实际情况要比这复杂的多,主要是为了说明系统在为栈分配空间时,其所分配的内存空间的地址是由高到低的,其栈底是在高地址上。
下图是我们编写代码所使用的逻辑 ,我们通常将栈顶视为栈的开始或结束,并假设栈是从下&#