第4章 栈与队列
4.1 栈
栈(stack)是存放数据对象的一种特殊容器,其中的数据元素按线性的逻辑次序排列,故也可定义首、末元素。不过,尽管栈结构也支持对象的插入和删除操作,但其操作的范围仅限于栈的某一特定端。也就是说,若约定新的元素只能从某一端插入其中,则反过来也只能从这一端删除已有的元素。禁止操作的另一端,称作盲端。
规律:“后进先出”(last-in-first-out, LIFO)
模板类:
0001 #include "Vector/Vector.h" //以向量为基类,派生出栈模板类
0002 template <typename T> class Stack: public Vector<T> { //将向量的首/末端作为栈底/顶
0003 public: //原有接口一概沿用
0004 void push ( T const& e ) { insert ( e ); } //入栈:等效于将新元素作为向量的末元素插入
0005 T pop() { return remove ( size() - 1 ); } //出栈:等效于删除向量的末元素
0006 T& top() { return ( *this ) [size() - 1]; } //取顶:直接返回向量的末元素
0007 };
4.2 栈与递归
递归:
- 优:通过递归来提高代码的简洁度和可读性。
- 缺:往往导致程序的空间效率不高甚至极低。
既然递归本身就是操作系统隐式地维护一个调用栈而实现的,我们自然也可以通过显式地模拟调用栈的运转过程,实现等效的算法功能。采用这一方式,程序员可以精细地裁剪栈中各帧的内容,从而尽可能降低空间复杂度的常系数(即可优化)。尽管算法原递归版本的高度概括性和简洁性将大打折扣,但毕竟在空间效率方面可以获得足够的补偿。
4.3 栈的典型应用
4.3.1 逆序输出
进制转换问题:任给十进制整数n,将其转换为λ进制的表示形式。
新进制下的各数位须按由低到高次序逐位算出,但确需要由高到低次序去顺序输出
故利用栈后进先出的特性:
引入一个栈并将算得的数位依次入栈,则在计算结束后只需通过反复的出栈操作即可由高到低地将其顺序输出。
算法:递归实现 空间消耗O(n)(递归深度O(n))
0001 void convert ( Stack<char>& S, __int64 n, int base ) { //整数n的1<base<=16进制打印(递归版)
0002 static char digit[] = "0123456789ABCDEF"; //数位符号,如有必要可相应扩充
0003 if ( 0 < n ) { //在尚有余数之前,反复地
0004 S.push ( digit[n % base] ); //逆向记录当前最低位,再
0005 convert ( S, n / base, base ); //通过递归得到所有更高位
0006 }
0007 } //新进制下由高到低的各数位,自顶而下保存于栈S中
算法:迭代实现 空间消耗:O(1)
0001 void convert ( Stack<char>& S, __int64 n, int base ) { //整数n的1<base<=16进制打印(迭代版)
0002 char digit[] = "0123456789ABCDEF"; //数位符号,如有必要可相应扩充
0003 while ( n > 0 ) { //由低到高,逐一计算出新进制下的各数位
0004 S.push ( digit[ n % base ] ); //余数(当前位)入栈
0005 n /= base; //n更新为其对base的除商
0006 }
0007 } //新进制下由高到低的各数位,自顶而下保存于栈S中
“开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 7 天,点击查看活动详情”