简单了解 栈
『 栈 』 是计算机存储中一种常见的简单数据结构
对 『 栈 』结构常进行的数据操作我们常称为 『 出栈 』( 读取或是删除数据 ) 或是 『 入栈 』( 插入数据 )
有个很形象的比喻 ,把 『 栈 』看成是一个子弹夹将数据压入和弹出,恰好的表达了『 栈 』的特性 『 后进先出 , 先进后出 』,而『 递归 』的使用就是和 『 栈 』紧密相连。( 递归 : 不断调用函数自己 。)
如何理解 栈
这是 王争 老师在 《 数据结构与算法之美 》 专栏中 『 栈:如何实现浏览器的前进和后退功能? 』稍作修改变成的 C# 代码 。( 两门语言因为历史原因惊人的相似 , 却因开源和闭源的问题而有了极大的差别的开发者数量 )
// 基于数组实现的顺序栈
public class ArrayStack
{
private String[] items; // 定义一个数组
private int count; // 栈中元素个数
private int n; // 栈的大小
// 初始化数组,申请一个大小为 n 的数组空间
public ArrayStack(int n)//构造函数 , 每次实例化对象时需传入参数 n
{
this.items = new String[n];// 初始化栈数组的长度
this.n = n; // 初始化私有变量 n 的值 , this.n 是指类中的变量 n
this.count = 0; // 初始化 count 计数器的值为
}
// 入栈操作
public bool push(String item)
{
// 数组空间不够了,直接返回 false,入栈失败。
if (count == n) return false;
// 将 item 放到下标为 count 的位置,并且 count 加一
items[count] = item;
++count;
return true;
}
// 出栈操作
public String pop()
{
// 栈为空,则直接返回 null
if (count == 0) return null;
// 返回下标为 count-1 的数组元素,并且栈中元素个数 count 减一
String tmp = items[count - 1];
--count;
return tmp;
}
}
这是一个 C# 实现的 栈 类 ,表达的是数组实现的『 顺序栈 』,完全按照数据的录入顺序来执行压栈 。
代码解读 :
count 为计数器 , 但数组索引从 0 开始 , 所以每次录入数据之后 计数器 +1 ,而最后一个元素则为 items[ count-1 ] 。
而 栈 的结构也可以用链表来表示 , 实际上就是在链表头结点插入数据 , 又因为链表的从『 首到尾 』遍历特性每次从 起始位 也就是首结点开始读取数据 。 相比较数组实现栈 , 链表的实现更有灵活性, 便于动态扩充数据长度 , 而数组相对而言会节省内存, 灵活性稍差 。( 如果是动态数组 , 则会因为动态扩容增加算法的时间复杂度 , 在平摊时间复杂度分析法中 动态数组实现栈也是 O( 1 ) ,因为只有一种特殊情况 数据长度超出初始数组长度时执行扩容操作 , 特殊情况的时间复杂度为 O( n ) ,执行了一次数据拷贝 )
栈在函数中的应用
函数总是被嵌套 , 例如 其他函数总是被 main 函数( 主函数 )所嵌套 , 可以用『 栈 』的定义来思考函数嵌套。
/* 递归实现累加 , 假设 n 一定为正整数 */
int recursion(int n)
{
if (n == 1)
return 1;
else
n=(n+recursion(n-1));
return n;
}
在这个累加递归函数中 ,recursion 函数被不断调用 , 直到 n == 1 ;因为 『 栈 』的数据操作是 『 后进先出 』所以在计算机中 , 依次执行的是 1+ 2 + 3 .... + n ,而其他被嵌套的函数也是大致这样的过程 。先执行最里层的函数将数据不断返回到最外层 ,最终产生我们所需要的结果。
==栈的结构未必是计算机执行函数嵌套必然的选择 , 但是这样的结构更利于我们去理解 ,因为递归的思想符合我们人思考的逻辑 。也符合函数执行『 后进先出 』的特性。将二者联系一起考虑是一种好的方式。==
计算机中通过栈来实现算术运算
王争老师在课中将计算机执行算术运算用 『 栈 』的思想给我们讲解了一下。
例如 1 + 2 × 3 - 4
计算机执行运算的过程中将 数字 和 运算符分开存储在两个栈里。
先按输入顺序存放 , 在读取到 × 号等运算级别较高的算术运算符时 , 根据算术运算符将运算符 升到栈顶( 即执行 出栈 、 出栈 的一端),相应的 ,数字栈中对应的两端的数据上升。( 王争老师数字这一块的操作未指出,如有误导请 指正 )
按照『 栈 』思想运算顺序应当为 ① 2*3 ② 6-4 ③ 2+1