【例子1】132 Pattern

  https://leetcode.com/problems/132-pattern/description/

  Given a sequence of n integers a1, a2, ..., an, a 132 pattern is a subsequence ai, aj, ak such that i < j < k and ai < ak < aj. Design an algorithm that takes a list of n numbers as input and checks whether there is a 132 pattern in the list.

  Note: n will be less than 15,000.

 思路:求一个list中是否有132模式。利用一个栈,从后往前遍历数组,再设置一个second变量。栈中记录的是当前遍历到的最大值,而second则用来记录目前第二大的数。只要目前遍历的数,小于栈顶的最大值和second,这时候就刚好满足132模式。

 代码:

    bool find132pattern(vector<int>& nums) {if(nums.size()<3)return false; stack<int> s;int second=INT_MIN;for(int i=nums.size()-1;i>=0;i--){if(nums[i]<second)return true;while(!s.empty()&&nums[i]>s.top()){second = s.top();s.pop();}s.push(nums[i]);}return false;}

View Code

【例子2】直方图中的最大矩形

  https://leetcode.com/problems/largest-rectangle-in-histogram/description/

  Given n non-negative integers representing the histogram's bar height where the width of each bar is 1, find the area of largest rectangle in the histogram.

                   

思路:这道题目有多种解法,例如:动态规划、分治法、线段树等等。这里主要讲一下用栈解决的思路。那么,最大矩形什么时候出现呢?

首先,最大矩形必定恰好包含了图中的一个直方,例如上面的右图。因此,我们只要计算出恰好包含第一个直方的最大矩形、恰好包含第二个直方的.最大矩形..然后得到其中的最大值就行了。

接着,考虑如何计算这些矩形的面积,它们的高就是恰好包含的直方的高h,只要计算各个矩形的跨度(L,R)即可,L表示矩形开始的位置,R表示结束位置。这时候的矩形面积就是h*(R-L+1)。如上面的右图的阴影部分,R=3,L=2。

最后,最大矩形的右边界很容易确定,只要出现一个直方h[j]小于当前直方h[i],就说明恰好包含h[i]的最大矩阵的右边界是j-1。但是开始的位置在哪里呢?这就需要用到栈,用栈来记录恰好包含第i个直方的最大矩阵的左边界。具体的计算过程,从左至右先将直方的下标逐一入栈s,如果遇到h[i]<s.top(),那么,就说明恰好包含h=s.top()的最大矩阵在R=i-1的位置结束,而它的左边界就是在s.pop()之后,L=s.top()+1,这时候的面积就是h*(R-L+1)。计算的过程中,与之前的结果比较大小,得到直方图的最大矩阵面积。

注意:按照上面的思路,我们在遍历h数组之后,最后的栈中并不一定为空。这是因为,存在直方的高度小于最右边的直方。在开始阶段,往数组h最右边插入一个0作为结束,就能够计算到每一个直方的最大矩阵面积了。

代码:

int largestRectangleArea(vector<int>& h) {h.push_back(0);int res = 0;vector<int> pos;for(int i=0;i<h.size();i++){while(!pos.empty()&&h[i]<=h[pos.back()]){int cur = h[pos.back()];pos.pop_back();//出栈int sidx = pos.empty() ? -1:pos.back();res = max(res, cur*(i-sidx-1));}pos.push_back(i);}return res;}

View Code

【例子3】最大全1子矩阵

https://leetcode.com/problems/remove-duplicate-letters/description/

Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing only 1's and return its area.

For example, given the following matrix:

1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0

Return 6.  

思路:这个题目本质上和上一题是相同的,可以相互转化,上一题中可以转化为一个0-1矩阵,求最大全1子矩阵的大小即可。这一个问题也可以将各行分别看作直方图的x坐标,求出当前的最大子矩阵,然后比较得到各行当中的最大值即可。例如,第1-1行可以看作是一个直方图,第1-2行可以看作是一个直方图....矩阵中的最大全1子矩阵就从各个直方图的最大矩阵中去找。不多说了,上代码:

代码:

    int maximalRectangle(vector<vector<char>>& m) {if(m.empty()) return 0;vector<int> h(m[0].size(),0);int res = 0;for(int i=0;i<m.size();i++){for(int j=0;j<m[0].size();j++){if(m[i][j]=='0') h[j] = 0;else h[j]++;}res = max(res,largestRectangleArea(h));} return res;}int largestRectangleArea(vector<int>& h) {h.push_back(0);int res = 0;vector<int> pos;for(int i=0;i<h.size();i++){while(!pos.empty()&&h[i]<=h[pos.back()]){int cur = h[pos.back()];pos.pop_back();int sidx = pos.empty() ? -1:pos.back();res = max(res, cur*(i-sidx-1));}pos.push_back(i);}return res;}

View Code

【例子4】Verify Preorder Serialization of a Binary Tree

  https://leetcode.com/problems/verify-preorder-serialization-of-a-binary-tree/description/

One way to serialize a binary tree is to use pre-order traversal. When we encounter a non-null node, we record the node's value. If it is a null node, we record using a sentinel value such as #.

     _9_/   \3     2/ \   / \4   1  #  6
/ \ / \   / \
# # # #   # #

For example, the above binary tree can be serialized to the string "9,3,4,#,#,1,#,#,2,#,6,#,#", where # represents a null node.

Given a string of comma separated values, verify whether it is a correct preorder traversal serialization of a binary tree. Find an algorithm without reconstructing the tree.

思路:验证是否为二叉树的先序序列。从左到右将各字符入栈,如果遇到栈顶字符是“#”,当前待压入栈中的字符是“#”,就说明栈顶的前一个字符是叶子节点,这时候,连续两个pop(),消去这个叶子节点。这时候,叶子节点的位置就是null,需要将“#”压入栈中。如果满足前面的条件,继续消去。如果遍历完整个字符串之后,栈中留下的只有一个“#”,就说明是先序序列。不过,如果出现连续三个“#”或者栈中只剩两个“#”的情况,这时候肯定不是先序序列,这要在栈的操作过程中加以判断。

另外,参考别人的discuss,二叉树只有度为0和度为2的两种节点,而根据公式:n0 = n2 + 1。如果在遍历的过程中遇到了n0>=n2+1(这时候遍历还未结束),就说明这个序列有问题了。同样,遍历完之后,如果n0!= n2 + 1,这也说明不是先序序列。

代码:

    bool isValidSerialization(string pre) {char tmp;bool isNum = false;pre += ',';vector<char> s;for(auto tmp:pre){if(tmp=='#'){while(!s.empty()&&s.back()=='#'){s.pop_back();if(s.empty()||s.back()=='#')return false;//case“###”
                    s.pop_back();}s.push_back('#');}else if(tmp==','){if(isNum)s.push_back('n');isNum = false;}else{isNum = true;}}return s.back()=='#'&&s.size()==1;   }

View Code

【例子5】二叉树的后序遍历(用栈)

思路:二叉树的用栈后序遍历,比用栈前序、中序遍历都要复杂,方法也比较多。

  思路一,根据(左右中)的顺序,先将r压栈,再出栈,将r->left压栈,将r->right压栈,再将栈顶(r->right)出栈....这样就能够遍历到二叉树的每个节点,最后栈为空。可以发现,这样的出栈序列和二叉树的后序序列刚好相反。所以,只要在出栈的时候讲val存入vector数组,再反转即可,也可以采用insert函数,将每一次的val插入vector的首位。

  思路二,如果目的仅仅是输出后序序列,那么,是否输出当前节点,只需要看看它的左右节点是否输出,或者左右节点是否为空。所以,添加一个pre指针,指向前一次输出的节点。在栈不为空的情况下,按照s.top()->right、s.top()->left的方式依次入栈。当目前栈顶元素的左右节点都未null,或者,pre节点是左节点且右子树为null,pre节点是右节点的时候,就输出栈顶val,再出栈,pre就等于当前出栈节点。这样循环下去,直到栈为空。

  思路三,先从p节点一直往左下走,直到p->left=NULL,这时候,再判断p->right,是否为NULL,如果是则将p出栈,否则,需要将p->right入栈。如果p->right是叶子节点,则出栈,这时候需要添加一个指针为q,指向p->right,代表此节点已经被访问。这时候回退到了p节点,如果p->right==q,就知道它的右子节点已经被访问了,故继续出栈,访问p节点,这时候的q指针就指向p节点。

思路四,和前面两种思路差不多,都用到pre前驱节点,对于每一个栈顶节点(初始是root),看看是否需要继续添加它的子节点入栈,不需要的话就输出这个节点,并出栈,否则就分别就左右子节点入栈。判断是否需要的关键在于当前的pre节点是哪个。

  总结起来,可以用q来表示已经访问过的指针,q初始值为NULL,只有在visit(p->right)之后,q=p->right,所以,当p->right = q时,这时候就需要访问p节点了。

//思路一:
vector<int> postorderTraversal(TreeNode* r) {vector<int> v;if (!r) return v;stack<TreeNode*> s;s.push(r);while(!s.empty()){TreeNode* cur = s.top();v.insert(v.begin(),cur->val);s.pop();if(cur->left)s.push(cur->left);if(cur->right)s.push(cur->right);}return v;}//思路二:
vector<int> postorderTraversal(TreeNode* r) {vector<int> v;if (!r) return v;stack<TreeNode*> s;TreeNode* pre;s.push(r);while(!s.empty()){TreeNode* cur = s.top();if((!cur->left&&!cur->right)||(pre == cur->left&&!cur->right)||(pre&&pre==cur->right)){v.push_back(cur->val);s.pop();pre = cur;}else{if(cur->right)s.push(cur->right);if(cur->left)s.push(cur->left);}}return v;}
//思路三:vector<int> postorderTraversal(TreeNode* r) {vector<int> v;if (!r) return v;stack<TreeNode*> s;TreeNode* p = r;do{while(p){     //0,最开始将左节点全部入栈
                s.push(p);p=p->left;}TreeNode* pre = NULL;while(!s.empty()){p = s.top();if(p->right==pre){//1,访问过、或者右子树为空才出栈v.push_back(p->val);pre = p;s.pop();}else{//2,没有访问过、跳出继续将p->right的一路左子树入栈,执行0p = p->right;break;}}}while(!s.empty());return v;}

View Code

 vector<int> postorderTraversal(TreeNode* r) {vector<int> v;if (!r) return v;stack<TreeNode*> s;s.push(r);//pre表示后序序列中节点的前驱//最开始的pre不能为空,要将pre和cur的左右子节点进行比较//当访问最左边节点时,如果pre=NULL且右节点==NULL,这一点就不能够入栈TreeNode *pre =new TreeNode(0);while(!s.empty()){TreeNode* cur = s.top();//有左节点,且左右节点都还没访问过,入栈if(cur->left&&pre!=cur->left&&pre!=cur->right){s.push(cur->left);//有右节点,且没有访问过,而左节点访问过}else if(cur->right&&pre!=cur->right&&(!cur->left||cur->left == pre)){s.push(cur->right);}else{//其他情况,由于栈后进先出,栈顶出栈即可,这时候pre就指向curv.push_back(cur->val);pre = cur;s.pop();}}return v;}

思路四

【例子6】中缀表达式求值

思路:中缀就是我们平常所见的表达式序列。直接进行中缀求值需要用两个栈,一个栈存操作数,一个栈存放操作符,包括“+-*/()”。如果遍历的操作符优先级不比栈顶操作符大,就将栈顶操作符出栈,同时将两个操作数出栈,计算得到的结果再入栈。

为了减少操作数压栈入栈的次数,设置一个cur变量,代表当前得到的操作数。在需要计算的时候,只需将一个操作数出栈,计算得到的结果仍然是cur,直到目前的操作符优先级大于当前栈顶的优先级,才将cur压入栈中。

另外,考虑到遍历之后,两个栈中仍然有数据需要计算,这样就会有比较重复的代码了。为了解决这个问题,只需在最开始的时候,给表达式加上括号,这样在遍历之后,两个栈全部清空,这时候表达式的值就是cur。

#include<iostream>
#include<cstring>
#include<stack>
#include<unordered_map>
#include<unordered_set>
#include<functional>
using namespace std;unordered_map<char, function<int  (int, int )>> op_map = {{ '+' , [] (int  a, int  b) { return a + b; } },{ '-' , [] (int  a, int  b) { return a - b; } },{ '*' , [] (int  a, int  b) { return a * b; } },{ '/' , [] (int  a, int  b) { return a / b; } }};
unordered_map<char, int> priority = {{'+',1},{'-',1},{'*',2},{'/',2},{'(',0}
};/* 利用两个栈进行模拟计算 */
int Compute(string s)
{stack<char> opstk;    //操作符栈stack<int> numstk;  //操作数栈int pos = 0;s = "(" + s + ")";int cur;while(pos < s.length()){if (s[pos] == '('){opstk.push('(');if(s[++pos]=='-')cur = 0;//负号必定出现在'('的后面,变成减法}else if (s[pos] == ')'){//括号内计算得到的cur不需要入栈while (opstk.top() != '('){cur = op_map[opstk.top()](numstk.top(), cur);//只需拿cur与栈顶数字进行计算
                numstk.pop();opstk.pop();}opstk.pop();  //删除'('pos++;}else if (s[pos] >= '0' && s[pos] <= '9'){int integer = 0;while (s[pos] >= '0' && s[pos] <= '9'){integer = integer*10 + (s[pos++] - '0');}cur = integer;}else{while(priority[s[pos]] <= priority[opstk.top()]){cur = op_map[opstk.top()](numstk.top(), cur);numstk.pop();opstk.pop();}numstk.push(cur);opstk.push(s[pos++]);}}return cur;
}int main()
{string s = "-2+3*(-2+3*4)-(-(-2))";cout << "结果为:" << Compute(s) << endl;//输出结果为26return 0;
}

View Code

【例子7】 用栈模拟汉诺塔问题

  就是将一个递归问题用栈写出来。

代码:

#include <queue>
#include<iostream>
#include<stack>
#include<cstdio>
using namespace std;/*定义状态*/
typedef struct Statute{char left,right,mid;int n;Statute(){};Statute(char L,char m,char r,int n):left(L),mid(m),right(r),n(n){};
}Statute;/*用栈*/
void HanNouWei(Statute s){stack<Statute> stk;stk.push(s);int cnt = 0;while(!stk.empty()){Statute cur = stk.top();stk.pop();if(cur.n==1){cout << " from "<< cur.left << " to " << cur.right  << endl;}else{stk.push(Statute(cur.mid,cur.left,cur.right,cur.n-1));//此处并不能够直接输出结果,而应该将这一步的状态放入栈中//cout << " from "<< cur.left << " to " << cur.right  << endl;stk.push(Statute(cur.left,cur.mid,cur.right,1));stk.push(Statute(cur.left,cur.right,cur.mid,cur.n-1));}}}/*递归*/
void hanoi(Statute s){Statute cur = s;if(cur.n == 1) {cout << " from "<< cur.left << " to " << cur.right  << endl;return;} else {hanoi(Statute(cur.left,cur.right,cur.mid,cur.n-1));cout << " from "<< cur.left << " to " << cur.right  << endl;hanoi(Statute(cur.mid,cur.left,cur.right,cur.n-1));}}int main(){Statute s = Statute('A','B','C',4);HanNouWei(s);cout << "==============================="<< endl;hanoi(s);return 0;
}

View Code

总结:如果问题的结构类似于(A(B)(C(D(E)))),其中A代表原问题,B、C、D分别代表嵌套的子问题,一个括号表示一个完整子问题的左右边界,完整意味着这个子问题的解可能是最后的最优解,或者说是最终解中的一部分。在这里原问题的解决需要先解决嵌套的子问题,这时候就可以用栈来解决问题。这里我总结出一个不成熟的方法,既然括号的匹配是最典型的的栈问题,那么,我们可以用括号来划分那些完整的子问题。就拿例子2来说,2,1,5,6,2,3,可以划分为( (2), 1, ((5, (6)), 2, (3))),每一个括号里面只有一个单独的数字,括号就刚好代表该高度的矩形的跨度。即使不知道能否用栈,只要我们能够标明括号,来划分完整的子问题,这就很容易得到用栈的解决方法了。左括号入栈,右括号出栈,其他操作.....这样执行下去,直到找到答案。另外,拿例子1来说,1, 3, 6, 4, 2,用栈的话就是(((2), 4), 6), 3, 1,当然,能够这样做的前提就是,我们需要意识到数组从右至左最大值和次大值的重要性。

【例子8】[LeetCode] Closest Binary Search Tree Value II 二分搜索树中离target最近的k个值

Given a non-empty binary search tree and a target value, find k values in the BST that are closest to the target.

Note:

  • Given target value is a floating point.
  • You may assume k is always valid, that is: k ≤ total nodes.
  • You are guaranteed to have only one unique set of k values in the BST that are closest to the target.

Follow up:
Assume that the BST is balanced, could you solve it in less than O(n) runtime (where n = total nodes)?

思路:这道题是寻找二分搜索树中和target最接近的k个点(突然想起了最近邻法)。这里的思路还是比较简单的,二分搜索树的中序序列,恰好是从小到大,所以,我们只要中序遍历,用一个vector(在这里其实是充当队列)来存储得到的k个点,如果遍历到第k+1个位置,就看看k该节点和vector中第一个节点,哪个里target最近。。。所以,这里其实就是在中序遍历上面加点visit()代码即可,既可以用到栈,也可以用递归。这里时间复杂度o(n),空间复杂度是o(k)。

这里还有一个lgn的思路,用到两个栈,一个栈用来存储比tartget小的若干个数,另一个存储比target大的若干个数。先init两个栈,找到离target最近的两个值(一个小于或者等于,一个大于)分别入栈。接着,归并这两个栈,如果还没有达到k个,则需要从这两个栈中的节点出发,再取寻找那些“稍远”的点入栈,越到栈顶,离target越近。继续归并。

如果简化这道题,只用寻找一个点的话,则可以用到二分法,时间复杂度是lgn。

代码:

【例子9】Ternary Expression Parser三元表达式解析

Given a string representing arbitrarily nested ternary expressions, calculate the result of the expression. You can always assume that the given expression is valid and only consists of digits 0-9?:T and F (T and Frepresent True and False respectively).

Note:

  1. The length of the given string is ≤ 10000.
  2. Each number will contain only one digit.
  3. The conditional expressions group right-to-left (as usual in most languages).
  4. The condition will always be either T or F. That is, the condition will never be a digit.
  5. The result of the expression will always evaluate to either a digit 0-9T or F.

Example 1:

Input: "T?2:3"Output: "2"Explanation: If true, then result is 2; otherwise result is 3.

Example 2:

Input: "F?1:T?4:5"Output: "4"

思路:这里从后往前遍历,将数全部入栈,忽略冒号,如果遇到?,则看看前面的字符是T还是F,有选择地进行出栈即可。如果要从左往右遍历的话,那么,加得等到所有?的index都入栈之后,才能够从最后一个?开始计算。

这里还有一个比较巧妙的递归解法,考虑到合法表达式中?和:的数目总是相等的,所以,可以设置一个cnt1计算?的个数,cnt2计算:的个数,当cnt1 == cnt2时,根据?前面的‘T’或‘F’选择:前面的子串或者后面的子串进行计算。

还有一个使用STL中内置函数find_last_of的简单方法,每次只需找到字符串中最后一个?,这时候后面必定紧跟着A:B,计算出这一步的结果,再放回字符串中,循环下去就能够得到整个式子的结果了。

【例子10】Verify Preorder Sequence in Binary Search Tree

验证一个list是不是一个BST的preorder traversal sequence。

Given an array of numbers, verify whether it is the correct preorder traversal sequence of a binary search tree.

You may assume each number in the sequence is unique.

Follow up:
Could you do it using only constant space complexity?

思路:二分数例如,123  4  567,它的先序序列是421 3 65 7,所以可以从左开始依次将数字入栈,如果遇到比top大的数字,则pop,将比当前数字小的都出栈。这时将这个节点坐标的节点都出栈了,同时记住最后一个出栈的数字,为min,min就是这个数字的父母节点。再将当前数字入栈,如果按照先序序列的话,后面的数字必定都比min大。例如,4 2 1  3 -> 4 3(min 2)-> 4 3 6 -> 6 (min 4) -> 6 5 7 -> 7 (min 6)。

另一种思路,使用常量空间。直接在序列上面进行修改,其实也只需改掉遍历时的前一个数字即可。另外,这个问题同样可以用递归来解决。

转载于:https://www.cnblogs.com/hello-new-world/p/7289615.html

leetcode 456. 132 Pattern 132模式 题解(栈)相关推荐

  1. LeetCode 456 132 Pattern

    问题:给出一个数组a,问是否有这样的子序列,满足i<j<k,a[i]<a[k]<a[j] 思路:1.首先计算前缀的最小值. 2.从后往前遍历,当元素大于前缀最小值时.看栈顶元素 ...

  2. 判断给定数组是否包含132模式 132 Pattern

    为什么80%的码农都做不了架构师?>>>    问题: Given a sequence of n integers a1, a2, ..., an, a 132 pattern i ...

  3. [Leetcode456]132模式 - 单调栈

    [Leetcode456]132模式 - 单调栈 给你一个整数数组 nums ,数组中共有 n 个整数.132 模式的子序列 由三个整数 nums[i].nums[j] 和 nums[k] 组成,并同 ...

  4. lintcode(636)132 Pattern

    描述 : Given a sequence of n integers a1, a2, ..., an, a 132 pattern is a subsequence ai, aj, ak such ...

  5. 技能树升级——Chrome Headless模式 - 全栈客栈 - SegmentFault

    技能树升级--Chrome Headless模式 - 全栈客栈 - SegmentFault TNPM

  6. Leetcode刷题 232题:用栈实现队列(基于python3和c++两种语言)

    Leetcode刷题 232题:用栈实现队列(基于python3和c++两种语言) 题目: 使用栈实现队列的下列操作: push(x) – 将一个元素放入队列的尾部. pop() – 从队列首部移除元 ...

  7. Leetcode刷题 155题: 最小栈(基于python3和c++两种语言)

    ** Leetcode刷题 155题: 最小栈(基于python3和c++两种语言) ** ** 题目: ** 设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈. ...

  8. Leetcode刷题 1441题: 用栈操作构建数组(基于python3和c++两种语言)

    Leetcode刷题 1441题: 用栈操作构建数组(基于python3和c++两种语言) ** 题目: ** 给你一个目标数组 target 和一个整数 n.每次迭代,需要从 list = {1,2 ...

  9. 【LeetCode】【HOT】155. 最小栈(辅助栈)

    [LeetCode][HOT]155. 最小栈 文章目录 [LeetCode][HOT]155. 最小栈 package hot;import java.util.ArrayDeque; import ...

最新文章

  1. testem方便的web tdd 测试框架使用
  2. Win8/Win8.1值得做的十多项优化方法
  3. 工作中用的linux命令
  4. 【NLP】发现一篇专门吐槽 NLP 内卷现状的 ACL 论文 ...
  5. 双十一大促技术只做两件事情?来看看阿里巴巴的技术之道
  6. 中运用_钢琴教学中指法的安排与运用
  7. python中实现多线程的几种方式
  8. outlook qr码在哪里_高能手办团兑换码有哪些 高能手办团哪里兑换
  9. layer自动补全 select
  10. P5231 [JSOI2012]玄武密码
  11. FTRL之FM和LR实战(使用稀疏数据进行实战)
  12. 计算机毕业设计 SSM健康知识信息平台 健康自检平台 健康体检管理系统Java
  13. informix 访问mysql_C语言访问INFORMIX数据库
  14. 时间序列及异常检测综述(资料)
  15. 天道酬勤-一篇短文却能激励大家!-chinajftang
  16. ubuntu18.04下EnlightenGAN运行过程记录
  17. Check It Again: Progressive Visual Question Answeringvia Visual Entailment(SAR)
  18. 用于推荐的隐式反馈去噪
  19. 虚拟主机到底哪家比较好呢?
  20. 跟着狂神老师配置Dubbo

热门文章

  1. IE8的css hack
  2. Java线程安全策略
  3. Pandas入门教程(六)
  4. Phoenix+Hbase二级索引
  5. logstash mysql 准实时同步到 elasticsearch
  6. 防火墙(ASA)高级配置之URL过滤、日志管理、透明模式
  7. java-信息安全(九)-基于DH,非对称加密,对称加密等理解HTTPS
  8. 自适应布局 的 解决方案
  9. 用 ReactJs 创建Mac版的 keep
  10. 编写自定义的字符串一致性匹配方法,只要两个字符串包含同样的字符, 不管字符的顺序如何,都认为两个字符串一致,如:”aabbcc”和”abcabc”被认为是一致的...