数据结构之初识stack
栈结构一定要结合具体应用场景去理解。。。
主要参考邓俊辉老师的数据结构课程,基于c++的实现。
一 基本概念
栈(stack)是受限的序列:只能在栈顶(top)插入和删除,栈底为盲端。
特点:先进后出(FILO),后进先出(LIFO)
基本接口:size() / empty()
push() 入栈
pop() 出栈
top() 查顶
拓展接口:getMAX()...
基本实现:
栈也属于序列的特例,故可由向量或者列表派生
1 template <typename> class Stack :: public Vector<T> { //由向量派生的栈模板类 2 3 pubilic: //size(),empty()以及其他开放接口均可直接沿用 4 void push(T const &e) {insert(size(),e);} //入栈 5 T pop() {return remove(size() - 1);} //出栈 6 T &top() {return (*this)[size()-1];} //取顶 7 }
上述三种基本接口实现均只需Ο(1)时间。
二 应用场景
什么时候用到栈?需满足两个条件:1.需要保存状态;2.状态输出有先后顺序
典型应用场合
逆序输出 :输出次序与处理过程颠倒;递归深度和输出长度不易预知。实例:进制转换
延迟缓冲 :线性扫描算法模式中,在预读足够长之后,方能确定可处理的前缀。实例:括号匹配
三 实例
1.进制转换
算法实现
1 void convert( Stack<char> & S,_int64 n,int base) { 2 static char digit[] = //新进制下的数位符号,可视base取值范围适当扩充 3 {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; 4 while(n > 0) { //由低到高,逐一计算出新进制下的各数位 5 S.push(digit[n % base]); //余数(当前的数位)入栈 6 n /= base; //n更新为其对base的除商 7 } 8 9 } 10 main() { 11 Stack<char> S; convert( S, n, base); //用栈记录转换得到的各数位 12 while ( !S.empty()) printf("%c", S.pop()); //逆序输出 13 }
2.括号匹配
算法实现
1 bool paren( const char exp[], int lo, int hi) { //exp[lo,hi) 2 Stack<char> S; //使用栈记录已发现但尚未匹配的左括号 3 for ( int i = lo; i < hi; i++) //逐一检查当前字符 4 if ( '(' == exp[i]) S.push( exp[i] ); //遇左括号,入栈 5 else if (!S.empty()) S.pop(); //遇右括号,若栈非空,则弹出左括号 6 else return false; //否则栈已空,括号不匹配 7 return S.empty(); //最终,当且仅当匹配时栈空 8 9 10 }
四 栈混洗
混洗总数:SP(n) = (2n)! / (n+1)! / (n)!
不含“禁形”的混洗排列都是合法的。原排列为<...i <j<k...],混洗排列为[...k,....,i,....,j....,....>这种是“禁形”。