======================= **基础知识** =======================

1. FILO(先入后出) : 出栈 更多意义上是指 指针移动(与队列出队类似,都是指针的移动);

 2. 括号匹配问题引出 思路进阶过程:记录左右括号个数 -> 影响的其实左右括号差  ->  本质是 ( 等价 发生,) 等价 结束,()才是一件事情完整;

        ( ( ) ) 可以看作 是事件与事件之间的完全包含关系: 等价于: funcA{ funcB; funcC}; 二叉树: 一个节点,包含两个子结点, 前序遍历);

        注意: 完全包含关系中,隐含了顺序要求(具体例子看下面经典问题1);

3. 所以对于 完全包含关系 问题,都可以考虑用栈来解决:表达式求值,递归问题,二叉树遍历 ...

4. C++ STL 中stack 采用deque 实现(adaptor), 但是没有将iterator 封装出来,所以没法进行不改变式遍历;

  注意: 有时候为了实现随即访问要求,可以通过vector,deque... 等容器实现stack 理念,但是还是满足随即访问需求,只要注意要FILO 即可;(具体例子见先面经典问题4)
======================= **代码演示** =======================

stack 实现:

 1 class Stack {
 2     public:
 3         Stack(int s = 100): size(s) {
 4             pos = -1; //通常栈0 指针设为-1, 防止没有分配内存时候,指针溢出?
 5             ps = (int*) malloc(sizeof(int) * s);
 6         }
 7         bool isEmpty() {
 8             return pos == -1;
 9         }
10         bool isFull(){
11             return pos == size - 1;
12         }
13         int len(){
14             return pos + 1;
15         }
16         bool push(int val) {
17             if(isFull()) return false;
18             pos += 1;
19             *(ps + pos) = val;
20             return true;
21         }
22         bool pop() {
23             if(isEmpty()) return false;
24             pos -= 1;
25             return true;
26         }
27         int top() {
28             return *(ps + pos);
29         }
30         void output(){
31             if(isEmpty()) {
32                 cout << "stack is empty;\n";
33                 return;
34             }
35             for(int i = pos; i >= 0; --i)  cout << *(i + ps) << " " ;
36             cout << endl;
37             return;
38         }
39 
40     private:
41         int size;
42         int pos;
43         int *ps;
44 
45 };
Stack

模拟 简单计算器(leetcode227):  ( 如果考虑括号,可以把 '(' 强制入op,到了')' 强制 出到第一个‘('; 

 1 class Solution {
 2 public:
 3 
 4     string removeSpace(string s) {
 5         string ret = "";
 6         for(auto x : s) {
 7             if(' ' == x) continue;
 8             ret += x;
 9         }
10         return ret;
11     }
12 
13     int priority(char op) {
14         switch (op) {
15             case '+' :
16             case '-' : return 1;
17             case '*' :
18             case '/' : return 2;
19             default : cout << "error op : " << op << endl;
20         }
21         return -1;
22     }
23 
24     int basicCal(int a, int b, char op) {
25         switch (op) {
26             case '+' :  return a + b;
27             case '-' :  return a - b;
28             case '*' :  return a * b;
29             case '/' :  return a / b;
30             default: cout << "error op : " << op << endl;
31         }
32         return  0;
33     }
34 
35 
36     int calculate(string s) {
37         stack<int> nums;
38         stack<char> op;
39         int temp = 0;
40 
41         s = removeSpace(s);
42 
43         for(auto x : s) {
44             if(x >= '0' && x <= '9') {
45                 temp = temp * 10 + (x - '0'); 
46                 continue;
47             }
48 
49             nums.push(temp);
50             temp = 0;
51             while(op.size() && priority(op.top()) >= priority(x)) {
52                 int b = nums.top(); nums.pop();
53                 int a = nums.top(); nums.pop();
54                 nums.push( basicCal(a, b, op.top()));
55                 op.pop();
56             }
57             op.push(x);
58         }
59         nums.push(temp);
60 
61         while(op.size()) {
62             int b = nums.top(); nums.pop();
63             int a = nums.top(); nums.pop();
64             nums.push( basicCal(a, b, op.top()));
65             op.pop();
66         }
67         return nums.top();
68     }
69 };
calculate

 

======================= **经典问题** =======================

1.括号匹配(leetcode20): 不仅有左右括号数目相等,还有顺序要求;这也是完全包含中包含的意思;

 1 class Solution {
 2 public:
 3     bool isValid(string s) {
 4         stack<char> s1;
 5         for(auto x : s) {
 6             switch (x) {
 7                 case '(' :  {
 8                                 s1.push('(');
 9                             } break;
10                 case '[' :  {
11                                 s1.push('[');
12                             } break;
13                 case '{' :  {
14                                 s1.push('{');
15                             } break;
16                 case ')' :  {
17                                 if(s1.empty() || s1.top() != '(') return false;
18                                 s1.pop();
19                             } break;
20                 case ']' :  {
21                                 if(s1.empty() || s1.top() != '[') return false;
22                                 s1.pop();
23                             } break;
24                 case '}' :  {
25                                 if(s1.empty() || s1.top() != '{') return false;
26                                 s1.pop();
27                             } break;
28                 default: cout << "error" << endl; break;
29             }
30         }
31         return s1.empty();
32 
33 //下面方式无法保证先入括号类型一定会后出
34 //        int cnt[3]  = {0};
35 //        for(auto x : s) {
36 //            switch (x) {
37 //                case '(' : cnt[0] += 1; break;
38 //                case '[' : cnt[1] += 1; break;
39 //                case '{' : cnt[2] += 1; break;
40 //                case ')' : cnt[0] -= 1; break;
41 //                case ']' : cnt[1] -= 1; break;
42 //                case '}' : cnt[2] -= 1; break;
43 //                default: break;
44 //            }
45 //            for(int i = 0; i < 3; ++i) {
46 //                if(cnt[i] < 0) return false;
47 //                continue;
48 //            }
49 //        }
50 //        for(int i = 0; i < 3; ++i) {
51 //            if(cnt[i] != 0) return false;
52 //            continue;
53 //        }
54 //        return true;
55     }
56 };
括号匹配

2. 递归;

引申:如何将递归程序通过迭代算法实现:(leetcode145):

 注意点:a.保存当前递归层中参数,通过stack 实现,如果对应多个参数,就需要多个栈 依次操作;

b. 迭代过程,如何记录每个递归层做到哪一步骤(对于单个递归层有多个操作程序,返回递归结果时,是返回到当前递归层中调用函数位置的,知道执行的位置的);

 1 /**
 2  * Definition for a binary tree node.
 3  * struct TreeNode {
 4  *     int val;
 5  *     TreeNode *left;
 6  *     TreeNode *right;
 7  *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 8  *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 9  *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
10  * };
11  */
12 class Solution {
13 public:
14     vector<int> postorderTraversal(TreeNode* root) {
15         if(!root) return vector<int>();
16         vector<int> ans;
17         stack<TreeNode*> s1;
18         stack<int> pri; //(0: left, 1: right, 2:root)
19         s1.push(root), pri.push(0);
20 
21         while(s1.size()) {
22             int status = pri.top();
23             pri.pop();
24             if(status == 0) {
25                 pri.push(1);
26                 if(s1.top()->left) {
27                     pri.push(0);
28                     s1.push(s1.top()->left);
29                 }
30                 continue;
31             }
32             if(status == 1) {
33                 pri.push(2);
34                 if(s1.top()->right){
35                     pri.push(0);
36                     s1.push(s1.top()->right);
37                 }
38                 continue;
39             }
40             if(status == 2) {
41                 ans.push_back(s1.top()->val);
42                 s1.pop();
43             }
44         }
45         return ans;
46     }
47 };
递归转迭代

3. 对于问题处理中,完整性问题体现:(leetcode636)

 1 class Solution {
 2 public:
 3     vector<int> exclusiveTime(int n, vector<string>& logs) {
 4         stack<int> s1;  //记录前一次id
 5         vector<int> ans(n, 0);
 6         for(int i = 0, pre = -1, I = logs.size(); i < I; ++i) {
 7             int i1 = logs[i].find_first_of(":"), i2 = logs[i].find_last_of(":");
 8             int  id = stoi(logs[i].substr(0, i1)), num = stoi(logs[i].substr(i2 + 1));
 9             if('s' == logs[i][i1 + 1]) {
10                 if(s1.size()) ans[s1.top()] += (num - pre - 1);
11                 pre = num - 1;  //如何定义pre 也是归纳各种情况结果,优化后的方法;
12                 s1.push(id);
13             } else {
14                 ans[id] += (num - pre);
15                 pre = num;
16                 s1.pop();
17             }
18         } 
19         return ans;
20     }
21 // 这里对题意理解有偏差,所以存储的数据不够,这里就需要考虑一个问题完整性, 完全包含性,用栈来实现会更方便;
22 //    void process(string &s, int *op) {
23 //        int ind = 0, i = 0, len = s.size();
24 //        while(i < len) {
25 //            if(1 == ind) {
26 //                op[ind] = ('s' != s[i]);  // start: 0; end : 1;
27 //
28 //                while(':' != s[i]) i += 1;
29 //                ind += 1;
30 //                i += 1;
31 //                continue;
32 //            }
33 //
34 //            int temp = 0;
35 //            while(i < len && ':' != s[i]) {
36 //                temp = temp * 10 + (s[i] - '0');
37 //                i += 1;
38 //            }
39 //            op[ind++] = temp;
40 //            i += 1;
41 //        }
42 //        return ;
43 //    }
44 //
45 //    vector<int> exclusiveTime(int n, vector<string>& logs) {
46 //        vector<int> ans(n, 0);
47 //        int pre[2][3];
48 //        process(logs[0], pre[0]);
49 //
50 //        cout << pre[0][0] << " ," << pre[0][1] << " ," << pre[0][2] << " ," << endl;
51 //
52 //        for(int i = 1, I = logs.size(); i < I; ++i){
53 //            int ind = i % 2, p_ind = (i + 1) % 2;
54 //            process(logs[i], pre[ind]);
55 //
56 //            if(pre[ind][0] == pre[p_ind][0] && pre[p_ind][1] == 0 && pre[ind][1] == 1) {
57 //                ans[pre[ind][0]] += (pre[ind][2] - pre[p_ind][2] + 1);
58 //            } else if(!pre[ind][1] && !pre[p_ind][1]) {   //now is start, pre is start;
59 //                ans[pre[p_ind][0]] += (pre[ind][2] - pre[p_ind][2]);
60 //            } else if(pre[ind][1] && pre[p_ind][1]){    //now is end, pre is endl;
61 //                ans[pre[ind][0]] += (pre[ind][2] - pre[p_ind][2]);
62 //            }
63 //        }
64 //
65 //        return ans;
66 //    }
67 };
完整性

4. 使用其他容器,但是利用FILO 思想,同时又能实现随机访问的需求;(leetcode331)

 1 class Solution {
 2 public:
 3     bool isValidSerialization(string preorder) {
 4         deque<string> process;
 5         for(int i = 0, pre = 0, I = preorder.size(); i < I; ++i) {
 6             pre = i;
 7             if(',' == preorder[i]) continue;
 8             while(i < I && preorder[i] >= '0' && preorder[i] <= '9') ++i;
 9             if(pre != i)  process.push_back(preorder.substr(pre, i - pre));
10             else process.push_back("#");
11 
12 
13             while(process.size() >= 3 && "#" == *process.rbegin()
14                     && "#" == *(++process.rbegin()) && "#" != *(++++process.rbegin())) {
15                     process.pop_back();
16                     process.pop_back();
17                     process.pop_back();
18                     process.push_back("#");
19                     }
20         }
21 
22         return process.size() == 1 && process[0] == "#";
23     }
24 };
其他容器实现FILO

======================= **应用场景** =======================

1. 操作系统中的线程栈;  当前系统中每个stack size : 8192; 

所以在多线程编程中,每个线程都会占用8M 内存,需要注意对硬件的需求;

爆栈概念: 当递归函数调用过深,导致当前线程栈超出系统上限,就会引起爆栈;结果如下图中运行爆栈程序时候的结果:

 1 using namespace std;
 2 #define MAX_N 1000000
 3 void funcA(int n) {
 4     if(n > 10) return;
 5     cout << "n is: " << n << endl;
 6     char num[MAX_N];
 7 
 8     funcA(n + 1);
 9     return;
10 }
11 
12 int main()
13 {
14     funcA(0);
15     return 0;
16 }
爆栈

 ----------> 

 

2. 这里的二叉树表达形式, 再想想 trie tree(在实现的理念上类似);

 

3. 自动机中的状态设定思维(还涉及到广义表);

  449. 序列化和反序列化二叉搜索树

 1 /**
 2  * Definition for a binary tree node.
 3  * struct TreeNode {
 4  *     int val;
 5  *     TreeNode *left;
 6  *     TreeNode *right;
 7  *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 8  * };
 9  */
10 class Codec {
11 public:
12 
13     // Encodes a tree to a single string.
14     string serialize(TreeNode* root) {
15         if(!root) return "";
16         string ret = "";
17         stringstream ss;
18         ss << root->val;
19         ss >> ret;
20 
21         if(!root->left && !root->right) return ret;
22         ret += "(";
23         if(root->left) ret += serialize(root->left);
24         ret += ",";
25         if(root->right) ret += serialize(root->right);
26         ret += ")";
27         return ret;
28     }
29 
30     // Decodes your encoded data to tree.
31     TreeNode* deserialize(string data) {
32         stack<TreeNode*> s;
33         int opcode = 0, idx = 0, LR_flag = 0;
34         TreeNode *p = nullptr, *root = nullptr;
35 
36         while(idx < data.size()) {
37             switch (opcode) {
38                 case 0: {
39                             if(data[idx] >= '0' && data[idx] <= '9') opcode = 1;
40                             else if(data[idx] == '(') opcode = 2;
41                             else if(data[idx] == ',') opcode = 3;
42                             else if(data[idx] == ')') opcode = 4;
43                         } break;
44                 case 1: {  //num
45                             int val = 0;
46                             while(data[idx] >= '0' && data[idx] <= '9') val = val * 10 + (data[idx++] - '0');
47                             p = new TreeNode(val);
48                             if(!root) root = p; 
49                             if(LR_flag == 1) s.top()->left = p;
50                             else if(LR_flag == 2) s.top()->right = p;
51                             opcode = 0;
52                         } break;
53                 case 2: {  //"("
54                             s.push(p);
55                             LR_flag = 1, idx++, opcode = 0;
56                         } break;
57                 case 3: {  //","
58                             LR_flag = 2, idx++, opcode = 0;
59                         } break;
60                 case 4: {  //")"
61                             s.pop(), idx++, opcode = 0;
62                         } break;
63             }
64         }
65         return root;
66     }
67 };
68 
69 // Your Codec object will be instantiated and called as such:
70 // Codec* ser = new Codec();
71 // Codec* deser = new Codec();
72 // string tree = ser->serialize(root);
73 // TreeNode* ans = deser->deserialize(tree);
74 // return ans;
状态机

 

posted on 2021-12-26 23:06  学海一扁舟  阅读(174)  评论(0编辑  收藏  举报