飞道的博客

C++数据结构——栈

347人阅读  评论(0)

                                                      C++数据结构——栈

 

            最近计划再复习一遍数据结构,看到一篇博客:https://www.cnblogs.com/QG-whz/p/5170418.html#_label0

1、栈(Stack)是一种线性存储结构,它具有如下特点:

(1)栈中的数据元素遵守“先进后出"(First In Last Out)的原则,简称FILO结构。(后进先出的叫法,也是可以的)

(2)限定只能在栈顶进行插入和删除操作。

2、栈的相关概念:

(1)栈顶与栈底:允许元素插入与删除的一端称为栈顶,另一端称为栈底。

(2)压栈:栈的插入操作,叫做进栈,也称压栈、入栈。

(3)弹栈:栈的删除操作,也叫做出栈。

3、栈的常用操作为:

(1)弹栈,通常命名为pop

(2)压栈,通常命名为push

(3)求栈的大小

(4)判断栈是否为空

(5)获取栈顶元素的值

4、栈的常见分类:

(1)基于数组的栈——以数组为底层数据结构时,通常以数组头为栈底,数组头到数组尾为栈顶的生长方向

(2)基于单链表的栈——以链表为底层的数据结构时,以链表头为栈顶,便于节点的插入与删除,压栈产生的新节点将一直出现在链表的头部

5、实例分析

       使用标准库的栈时, 应包含相关头文件,在栈中应包含头文件: #include< stack > 。定义:stack< int > s;


  
  1. s.empty(); //如果栈为空则返回true, 否则返回false;
  2. s.size(); //返回栈中元素的个数
  3. s.top(); //返回栈顶元素, 但不删除该元素
  4. s.pop(); //弹出栈顶元素, 但不返回其值
  5. s.push(); //将元素压入栈顶

(1)基于数组的栈


  
  1. #include <stack>
  2. #include <iostream>
  3. using namespace std;
  4. int main()
  5. {
  6. stack< int> mystack;
  7. int sum = 0;
  8. for ( int i = 0; i <= 10; i++){
  9. mystack.push(i);
  10. }
  11. cout << "size is " << mystack.size() << endl;
  12. while (!mystack.empty()){
  13. cout << " " << mystack.top();
  14. mystack.pop();
  15. }
  16. cout << endl;
  17. system( "pause");
  18. return 0;
  19. }
  20. //size is 11
  21. // 10 9 8 7 6 5 4 3 2 1 0

(2)基于单链表的栈


  
  1. #include <iostream>
  2. using namespace std;
  3. template< class T>class Stack
  4. {
  5. private:
  6. struct Node
  7. {
  8. T data;
  9. Node *next;
  10. };
  11. Node *head;
  12. Node *p;
  13. int length;
  14. public:
  15. Stack()
  16. {
  17. head = NULL;
  18. length = 0;
  19. }
  20. void push(T n)//入栈
  21. {
  22. Node *q = new Node;
  23. q->data = n;
  24. if (head == NULL)
  25. {
  26. q->next = head;
  27. head = q;
  28. p = q;
  29. }
  30. else
  31. {
  32. q->next = p;
  33. p = q;
  34. }
  35. length++;
  36. }
  37. T pop()//出栈并且将出栈的元素返回
  38. {
  39. if (length <= 0)
  40. {
  41. abort();
  42. }
  43. Node *q;
  44. T data;
  45. q = p;
  46. data = p->data;
  47. p = p->next;
  48. delete(q);
  49. length--;
  50. return data;
  51. }
  52. int size()//返回元素个数
  53. {
  54. return length;
  55. }
  56. T top()//返回栈顶元素
  57. {
  58. return p->data;
  59. }
  60. bool isEmpty()//判断栈是不是空的
  61. {
  62. if (length == 0)
  63. {
  64. return true;
  65. }
  66. else
  67. {
  68. return false;
  69. }
  70. }
  71. void clear()//清空栈中的所有元素
  72. {
  73. while (length > 0)
  74. {
  75. pop();
  76. }
  77. }
  78. };
  79. int main()
  80. {
  81. Stack< char> s;
  82. s.push( 'a');
  83. s.push( 'b');
  84. s.push( 'c');
  85. while (!s.isEmpty())
  86. {
  87. cout << s.pop() << endl;
  88. }
  89. system( "pause");
  90. return 0;
  91. }

 

练习1、实现一个特殊的栈,在实现栈的基本功能的基础上,再实现返回栈中最小元素的操作。

          解法参考博客:https://blog.csdn.net/cherrydreamsover/article/details/79475925,具体过程如下:

(1)使用两个栈,一个栈用来保存当前的元素,记做:stackData,一个栈用来保存压入操作每一步的最小元素,记做:stackMin

(2)入栈:当stackData栈中压入一个数据时,判断satckMin中是否为空。若为空,将该元素压入stackMin栈中。若不空,判断两者之间的大小,当前者小于或等于后者时,将前者中的数据压入后者中;当前者大于后者时,

不进行任何操作。

(3)出栈:保证stackMin中栈顶的元素是stackData中最小的。


  
  1. #include<iostream>
  2. #include <stack>
  3. #include <cassert>
  4. using namespace std;
  5. //方法一: 一个辅助栈,如果这个栈为空,直接将元素入这个栈,如果辅助栈中有元素,将压入的元素和辅助栈顶元素比较,
  6. //压入两者中较小的那个元素使得辅助栈总是维持栈顶元素为最小值。
  7. //class Stack
  8. //{
  9. //public:
  10. // void Push(int data)
  11. // {
  12. // stackData.push(data);
  13. // if (stackMin.empty())
  14. // {
  15. // stackMin.push(data);
  16. // }
  17. // else
  18. // {
  19. // int tmp = stackMin.top();
  20. // int min = data > tmp ? tmp : data;
  21. // stackMin.push(min);
  22. // }
  23. // }
  24. //
  25. // void Pop()
  26. // {
  27. // assert(!stackData.empty() && !stackMin.empty());
  28. // stackData.pop();
  29. // stackMin.pop();
  30. // }
  31. //
  32. // int GetMin()
  33. // {
  34. // assert(!stackMin.empty());
  35. // return stackMin.top();
  36. // }
  37. //
  38. //private:
  39. // stack<int> stackData;
  40. // stack<int> stackMin;
  41. //};
  42. //方法二: 一个辅助栈,如果这个栈为空,直接将元素入这个栈,如果辅助栈中有元素,将压入的元素和辅助栈顶元素比较,
  43. //如果压入的元素小于等于辅助栈顶元素,者将这个元素入辅助栈,否则无操作,出栈的时候判断要出栈的元素是否等于辅助
  44. //栈顶元素,如果是,也将辅助栈顶元素出栈。否则无操作。
  45. class Stack
  46. {
  47. public:
  48. void Push(int data)
  49. {
  50. stackData.push(data);
  51. if (stackMin.empty())
  52. {
  53. stackMin.push(data);
  54. }
  55. else
  56. {
  57. if (data <= stackMin.top())
  58. {
  59. stackMin.push(data);
  60. }
  61. }
  62. }
  63. void Pop()
  64. {
  65. assert(!stackData.empty() && !stackMin.empty());
  66. if (stackData.top() == stackMin.top())
  67. {
  68. stackMin.pop();
  69. }
  70. stackData.pop();
  71. }
  72. int GetMin()
  73. {
  74. assert(!stackMin.empty());
  75. return stackMin.top();
  76. }
  77. private:
  78. stack< int> stackData;
  79. stack< int> stackMin;
  80. };
  81. int main()
  82. {
  83. Stack s;
  84. //s.Push(5);
  85. s.Push( 36);
  86. s.Push( 15);
  87. s.Push( 95);
  88. s.Push( 50);
  89. s.Push( 53);
  90. cout << s.GetMin() << endl;
  91. system( "pause");
  92. return 0;
  93. } //15

(3)栈的应用举例

1)进制转换

2)括号匹配的检验

3)行编辑程序

4)迷宫求解、汉诺塔等经典问题

5)表达式求值

6)栈与递归的实现

 

练习2、剑指offer面试题30——包含min函数的栈

题目描述

定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数。

  
  1. class Solution {
  2. public:
  3. stack< int> stackData; //保存数据用的栈stackData
  4. stack< int> stackMin; //保存最小的数的栈stackMin,其中它的栈顶始终为最小的数
  5. void push(int value) {
  6. stackData.push(value);
  7. if(stackMin.empty())
  8. stackMin.push(value); //如果stackMin为空,则value是最小的值,入栈
  9. else{
  10. if(stackMin.top()>=value)
  11. stackMin.push(value); //否则当value小于等于stackMin的栈顶元素时,入栈(等于的时候也入栈是因为我考虑有相同的数)
  12. }
  13. }
  14. void pop() {
  15. if(stackData.top()==stackMin.top()) //如果出栈的数刚好是最小的数,那么stackMin也应该出栈
  16. stackMin.pop();
  17. stackData.pop();
  18. }
  19. int top() {
  20. return stackData.top(); //栈顶元素应返回stackData的栈顶元素
  21. }
  22. int min() {
  23. return stackMin.top(); //stackMin的栈顶元素即是最小的数
  24. }
  25. };

运行结果:

练习3、剑指offer面试题31——栈的压入、弹出序列

       参考博客:https://blog.csdn.net/budf01/article/details/76232497,解题思路为:创建一个栈进行压入、弹出操作。具体操作如下: 
(1)当栈为空或者栈顶元素和popV当前元素不相等时,将pushV当前元素压入; 
(2)当栈顶元素与popV当前元素相等时,将栈顶元素弹出,并移至popV下一个元素; 
(3)如果需要压入的元素个数大于pushV的元素个数,说明popV不可能是pushV的弹出序列。

代码为:


  
  1. class Solution {
  2. public:
  3. bool IsPopOrder(vector<int> pushV,vector<int> popV) {
  4. //特殊输入测试
  5. if(pushV.empty() || popV.empty() || pushV.size()!=popV.size())
  6. return false;
  7. stack< int> mystack; //定义一个辅助栈
  8. int index= 0;
  9. for( int i= 0;i<popV.size();i++){
  10. //当辅助栈为空或者栈顶元素和popV当前元素不相等时,将pushV当前元素压入
  11. while(mystack.empty()||mystack.top()!=popV[i]){
  12. if(index>=pushV.size())
  13. //如果需要压入的元素个数大于pushV的元素个数,说明popV不可能是pushV的弹出序列
  14. return false;
  15. mystack.push(pushV[index++]);
  16. }
  17. //当栈顶元素与popV当前元素相等时,将栈顶元素弹出,并移至popV下一个元素
  18. if(mystack.top()==popV[i])
  19. mystack.pop();
  20. }
  21. return true;
  22. }
  23. };

运行结果:

 

 

当然可以利用其他思想解决,如引入哈希或直接利用向量的方式求解。


  
  1. class Solution {
  2. public:
  3. bool IsPopOrder(vector<int> pushV,vector<int> popV) {
  4. if(pushV.empty() && popV.empty() && pushV.size() != popV.size()){
  5. return false;
  6. }
  7. map< int, int> Hash; //用map做一个映射,入栈顺序的值不一定是递增
  8. for( int i= 0;i<pushV.size();i++){
  9. Hash[pushV[i]]=i+ 1;
  10. }
  11. int now=Hash[popV[ 0]]; //当前最靠后入栈的键值,例如题目给的4 3 5 1 2,now先等于4,再等于5
  12. for( int i= 0;i<popV.size();i++){
  13. //如果入栈序列中没有这个值
  14. if(Hash[popV[i]]== 0){
  15. return false;
  16. }
  17. if(Hash[popV[i]]>=now){
  18. now=Hash[popV[i]];
  19. }
  20. else if(Hash[popV[i]]<=Hash[popV[i- 1]]){
  21. continue ;
  22. }
  23. else{
  24. return false;
  25. }
  26. }
  27. return true;
  28. }
  29. };

练习4、简单的括号匹配判断

       例如,爱奇艺的一道实习在线编程题:当输入为()(())(),返回true;当输入为)()()()()),返回false,时间15min。(不能使用栈)

1、假设可以使用栈(15min可以完成)

C++代码:


  
  1. #include <iostream>
  2. #include <stack>
  3. #include <vector>
  4. using namespace std;
  5. bool isRight(vector<char> &vec){
  6. stack< char> stack1;
  7. bool index = false;
  8. if (vec.size() <= 1 || vec[ 0]!= '(' || vec.size()% 2!= 0){
  9. return index;
  10. }
  11. for ( int i = 0; i < vec.size(); i++){
  12. if (vec[i] == '(')
  13. stack1.push(vec[i]);
  14. else if (vec[i] == ')')
  15. stack1.pop();
  16. }
  17. if (stack1.empty())
  18. index = true;
  19. return index;
  20. }
  21. int main(){
  22. //输入不定长的括号
  23. vector< char> vec;
  24. char tmpCh;
  25. char t;
  26. cout << "输入一串括号为:";
  27. do{
  28. cin >> tmpCh;
  29. vec.push_back(tmpCh);
  30. } while ((t = cin.get()) != '\n');
  31. //调用isRight函数
  32. bool myRes = isRight(vec);
  33. cout << myRes << endl;
  34. system( "pause");
  35. return 0;
  36. }

运行结果:

python代码:


  
  1. def isRight(str1):
  2. index = False
  3. tmp = []
  4. if(len(str1)>= 2 and len(str1)% 2== 0 and str1[ 0]== '('):
  5. for id in range(len(str1)):
  6. if str1[id] == '(':
  7. tmp.append(str1[id])
  8. else:
  9. tmp.pop()
  10. if len(tmp)== 0:
  11. index = True
  12. return index
  13. if __name__ == "__main__":
  14. try:
  15. while True:
  16. str1 = [i for i in input().split()]
  17. print(isRight(str1)) # 返回判断结果
  18. except:
  19. pass

运行结果:

2、不能使用栈(15min,不太好想,mad,笔试那会儿就没想到!)

       以下是我的想法,具体的过程如下:

      (1)由于不能使用栈,将左括号定义为数值1,右括号定义为数值-1,存放到向量id(C++)或列表tmp (Python)中;

      (2)初始化变量sum,用于判断总的求和结果是否等于0,若不等于0,则肯定不正确,若等于0,不一定正确;

      (3)循环遍历输入的括号向量vec,判断当前括号属性的同时,进行累加求和,如果求和值小于等于-1,break(跳出循环);

      (4)最后再检查sum是否等于0,此时若等于0,则为正确。

C++代码:


  
  1. #include <iostream>
  2. #include <vector>
  3. using namespace std;
  4. bool isRight(vector<char> &vec){
  5. vector<int> id(vec.size()); //用于存放左右括号的属性:左括号用1表示,右括号用-1表示
  6. int sum = 0;
  7. bool index = false;
  8. if (vec.size() <= 1 || vec[ 0]!= '(' || vec.size()% 2!= 0){
  9. return index;
  10. }
  11. for ( int i = 0; i < vec.size(); i++){
  12. if (vec[i] == '('){
  13. id.push_back( 1);
  14. sum = id[i] + sum;
  15. }
  16. else if (vec[i] == ')'){
  17. id.push_back( -1);
  18. sum = id[i] + sum;
  19. if (sum <= -1)
  20. break;
  21. }
  22. }
  23. if (sum == 0)
  24. index = true;
  25. return index;
  26. }
  27. int main(){
  28. //输入不定长的括号
  29. vector< char> vec;
  30. char tmpCh;
  31. char t;
  32. cout << "输入一串括号为:";
  33. do{
  34. cin >> tmpCh;
  35. vec.push_back(tmpCh);
  36. } while ((t = cin.get()) != '\n');
  37. //调用isRight函数
  38. bool myRes = isRight(vec);
  39. cout << myRes << endl;
  40. system( "pause");
  41. return 0;
  42. }

运行结果同上

python代码:


  
  1. def isRight(str1):
  2. index = False
  3. sum = 0
  4. tmp = []
  5. if(len(str1)>= 2 and len(str1)% 2== 0 and str1[ 0]== '('):
  6. for id in range(len(str1)):
  7. if str1[id] == '(':
  8. tmp.append( 1)
  9. sum += tmp[id]
  10. else:
  11. tmp.append( -1)
  12. sum += tmp[id]
  13. if sum<= -1:
  14. break
  15. if sum == 0:
  16. index = True
  17. return index
  18. if __name__ == "__main__":
  19. try:
  20. while True:
  21. str1 = [i for i in input().split()]
  22. print(isRight(str1)) # 返回判断结果
  23. except:
  24. pass

运行结果同上。


转载:https://blog.csdn.net/zichen_ziqi/article/details/80807989
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场