启迪思维:顺序栈
借用觉先老大说的一句话“窃以为,做一个程序员,一要钻下去,积累技术,二要跳出来,影响世界(虽然只是一点点)”作为开篇,数据结构和算法这门学科是有些枯燥和难学,需要大家在没有苍小姐的夜晚慢慢思考和实践,一直来我都希望用更幽默的方式来总结这方面的知识,这样大家阅读文章的时候也轻松点,无奈程序写的时间长了和小学语文没有毕业(经常被女朋友鄙视),文字功底更差.......分享两个最近学习的小知识
1、为什么在二进制、八进制、十进制、十六进制中只有十进制被人类广泛应用?举起双手看看一共有多少个指头,就能找到一个相对合理的解释(万物存在必有因);
2、程序中为什么用两个等号(==)表示相等呢,为什么不直接用一个等号(=)?这个是因为在程序中用赋值的概率远高于判断两个变量相等,这样设计是减少大家多写等号的机会。
一:概念
栈作为一种数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。它按照后进先出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。栈具有记忆作用,对栈的插入与删除操作中,不需要改变栈底指针。栈是允许在同一端进行插入和删除操作的特殊线性表。允许进行插入和删除操作的一端称为栈顶(top),另一端为栈底(bottom);栈底固定,而栈顶浮动;栈中元素个数为零时称为空栈。插入一般称为进栈(push),删除则称为退栈(pop)。栈也称为后进先出表。
二:示例图
顺便空栈示例图如下:
顺便非空栈示例图如下:
三:栈的应用
1、函数的返回地址和参数;
2、临时变量:包括函数的非静态局部变量以及编译器自动生成的其他临时变量;
3、二进制转换其他进制(后面有相关代码分析);
4、解析程序的中的括号匹配问题;
5、逆波兰计算器;
栈从代码的角度看,实现起来非常简单,但栈在程序的运行中有着举足轻重的作用(这个就和苍老师在我们屌丝心中一样重要),从这能看出来在平时项目开发中,写代码应该保持简单、易懂,这样才是最受欢迎的工程师。
四:代码分析
1、元素入栈:
学习小甲鱼老师的原则"no pic you say a J8",上图:
代码分析如下:
1 /** 2 *元素入栈 3 */ 4 void Push(const T &e){ 5 6 //如果当前栈位置已经满,动态扩展栈长度为原链表2倍 7 if(GetSize() == size){ 8 //创建一个新的指针数组 9 T *newBase = new T[size*2]; 10 //复制原空间的数据到新创建空间 11 memcpy(newBase,base,sizeof(base)+1); 12 //修改栈顶指针值 13 top = size + base; 14 //修改栈大小 15 size = size *2; 16 //这种删除内存很危险,更多请参考boost里shared_array 17 delete[] base; 18 //指向新空间 19 base = newBase; 20 } 21 //1、压入元素到栈顶;2、改变栈顶指向 22 *top++ = e; 23 }
2、元素出栈
学习小甲鱼老师的原则"no pic you say a J8",上图:
代码分析如下:
1 /** 2 *元素出栈 3 */ 4 void Pop(T &e){ 5 //1、获取栈顶元素;2、改变栈顶指向 6 //--top和top--的区别:top--在内部创建一个临时变量, 7 //--top直接减后直接返回,可以总结--top效率稍微高 8 e = *--top; 9 } 10 11 /** 12 *获取栈顶元素 13 */ 14 void GetTop(T &e){ 15 //如果栈不为空,取出栈顶元素 16 if(top > base){ 17 e = *(--top); 18 } 19 }
3、清空链表
1 /** 2 * 清空栈数据 3 */ 4 void Clear(){ 5 //直接改变栈顶指向栈顶,这个也解释为什么硬盘格式化后,高手还可以通过某种技术恢复数据 6 top = base; 7 }
4、判断是否为空
1 /** 2 *判断栈是否为空 3 */ 4 bool IsEmpty(){ 5 return top == base; 6 }
5、计算栈的大小
1 /** 2 *获取栈的大小,关于指针的知识请阅读相关资料 3 */ 4 int GetSize(){ 5 return top - base; 6 }
6、测试相关
测试代码如下:
1 /** 2 *测试栈的相关方法 3 */ 4 void test(){ 5 std::cout<<"-----------push stack begin------------"<<std::endl; 6 for(size_t i = 0; i < 5; ++i){ 7 Push(i); 8 } 9 std::cout<<"-----------push stack end------------"<<std::endl; 10 11 std::cout<<"frist stack length="<<GetSize()<<std::endl; 12 13 std::cout<<"-----------pop stack begin------------"<<std::endl; 14 T e; 15 for(size_t i = 0; i < 3; ++i){ 16 Pop(e) ; 17 std::cout<<"e is value = "<<e<<"\n"; 18 } 19 std::cout<<"-----------pop stack end------------"<<std::endl; 20 21 std::cout<<"secend stack size="<<GetSize()<<std::endl; 22 Clear(); 23 std::cout<<"third stack size="<<GetSize()<<std::endl; 24 }
运行结果如下图:
7、完整代码
1 /* 2 * ArrayStack.h 3 * 4 * Created on: May 3, 2013 5 * Author: sunysen 6 */ 7 8 #ifndef ARRAYSTACK_H_ 9 #define ARRAYSTACK_H_ 10 11 template <class T> 12 class ArrayStack{ 13 private: 14 int size;//栈的大小 15 T *base;//栈底 16 T *top; //栈顶 17 public: 18 /** 19 *初始化一个栈,根据指定大小创建一个指针数组,栈顶等于栈低 20 *explicit 只对构造函数起作用,用来抑制隐式转换 21 */ 22 explicit ArrayStack(int size):size(size),base(new T[size]){ 23 top = base; 24 } 25 ~ArrayStack(){ 26 //这种删除内存很危险,更多请参考boost里shared_array 27 delete[] base; 28 } 29 /** 30 *获取栈的大小,关于指针的知识请阅读相关资料 31 */ 32 int GetSize(){ 33 return top - base; 34 } 35 36 /** 37 *判断栈是否为空 38 */ 39 bool IsEmpty(){ 40 return top == base; 41 } 42 /** 43 * 清空栈数据 44 */ 45 void Clear(){ 46 //直接改变栈顶指向栈顶,这个也解释为什么硬盘格式化后,高手还可以通过某种技术恢复数据 47 top = base; 48 } 49 50 /** 51 *元素出栈 52 */ 53 void Pop(T &e){ 54 //1、获取栈顶元素;2、改变栈顶指向 55 //--top和top--的区别:top--在内部创建一个临时变量, 56 //--top直接减后直接返回,可以总结--top效率稍微高 57 e = *--top; 58 } 59 60 /** 61 *获取栈顶元素 62 */ 63 void GetTop(T &e){ 64 e = *top; 65 } 66 67 /** 68 *元素入栈 69 */ 70 void Push(const T &e){ 71 72 //如果当前栈位置已经满,动态扩展栈长度为原链表2倍 73 if(GetSize() == size){ 74 //创建一个新的指针数组 75 T *newBase = new T[size*2]; 76 //复制原空间的数据到新创建空间 77 memcpy(newBase,base,size); 78 //修改栈顶指针值 79 top = size + base; 80 //修改栈大小 81 size = size *2; 82 //这种删除内存很危险,更多请参考boost里shared_array 83 delete[] base; 84 } 85 //1、获取栈顶元素;2、改变栈顶指向 86 *top++ = e; 87 } 88 89 /** 90 *测试栈的相关方法 91 */ 92 void test(){ 93 std::cout<<"-----------push stack begin------------"<<std::endl; 94 for(size_t i = 0; i < 5; ++i){ 95 Push(i); 96 } 97 std::cout<<"-----------push stack end------------"<<std::endl; 98 99 std::cout<<"frist stack length="<<GetSize()<<std::endl; 100 101 std::cout<<"-----------pop stack begin------------"<<std::endl; 102 T e; 103 for(size_t i = 0; i < 3; ++i){ 104 Pop(e) ; 105 std::cout<<"e is value = "<<e<<"\n"; 106 } 107 std::cout<<"-----------pop stack end------------"<<std::endl; 108 109 std::cout<<"secend stack size="<<GetSize()<<std::endl; 110 Clear(); 111 std::cout<<"third stack size="<<GetSize()<<std::endl; 112 } 113 114 }; 115 116 117 #endif /* ARRAYSTACK_H_ */
五:环境
1、运行环境:Ubuntu 10.04 LTS+VMware8.0.4+gcc4.4.3;
2、开发工具:Eclipse+make
六:题记
1、上面的代码难免有bug,如果你发现代码写的有问题,请你帮忙指出,让我们一起进步,让代码变的更漂亮和更健壮;
2、我自己能手动写上面代码,离不开郝斌、高一凡、侯捷、严蔚敏等老师的书籍和视频指导,在这里感谢他们;
3、鼓励自己能坚持把更多数据结构方面的知识写出来,让自己掌握更深刻,也顺便冒充下"小牛";
欢迎继续阅读“启迪思维:数据结构和算法”系列