4.4 双栈队列练习题编写一个类,只能用两个栈结构实现队列,支持队列的基本操作(push,pop)。给定一个操作序列ope及它的长度n,其中元素为正数代表push操作,为0代表pop操作,保证操作序列合法且一定含pop操作,请返回pop的结果序列。测试样例:[1,2,3,0,4,0],6返回:[1,2]
思路: 简单题,注意一点就是pop(出队操作时候)的时候,要把第一个栈中元素全部放入到第二个栈中。 之后再全部放回。class TwoStack {public: stack<int>s1; stack<int>s2; vector<int> twoStack(vector<int> ope, int n) { // write code here vector<int> res; for(int i=0;i<n;i++){ if(ope[i]>0) push(ope[i]); else res.push_back(pop()); } return res; } void push(int a){ s1.push(a); } int pop(){ while(!s1.empty()){ s2.push(s1.top()); s1.pop(); } int res = s2.top(); s2.pop(); while(!s2.empty()) { s1.push(s2.top()); s2.pop(); } return res; }};
延伸一个两个队列实现栈。思路: 用两个队列q1 q2 ,push 操作很简单,直接放入到 q1中pop操作需要先遍历q1,把q1最后一个弹出,其他的存入q2,若q1为空的时候遍历q2 ,把q2最后一个弹出,其他的再存回q。
4.5 栈的反转练习题
实现一个栈的逆序,但是只能用递归函数(系统栈)和这个栈本身的push操作来实现,而不能自己申请另外的数据结构。给定一个整数数组A即为给定的栈,同时给定它的大小n,请返回逆序后的栈。测试样例:[4,3,2,1],4返回:[1,2,3,4]
思路和分析:此题目比较复杂。条件比较苛刻要充分利用递归的性质,第一点要想到如何获得当前栈的最后一个元素和去掉最后一个元素的栈。之后再递归地对每个去掉最后一个元素的栈进行找最后一个元素的操作即可。核心代码如下 int getBottom(stack<int> &sck){ //得到栈的最后一个元素,同时返回去掉最后一个元素的栈。 int result=sck.top();sck.pop(); if(sck.empty()) return result; else { int last=getBottom(sck); sck.push(result); //回溯法,把之前的弹出结果再入栈。 return last; //返回最后计算的结果 } } void reverse(stack<int> &sck){ //递归地对栈进行翻转 if(sck.empty()) return; int last=getBottom(sck); reverse(sck); sck.push(last); }
4.6 双栈排序练习题请编写一个程序,按升序对栈进行排序(即最大元素位于栈顶),要求最多只能使用一个额外的栈存放临时数据,但不得将元素复制到别的数据结构中。给定一个int[] numbers(C++中为vector<int>),其中第一个元素为栈顶,请返回排序后的栈。请注意这是一个栈,意味着排序过程中你只能访问到第一个元素。测试样例:[1,2,3,4,5]返回:[5,4,3,2,1]
思路:1,每次把原始的栈弹出的时候,去看下辅助栈里面的元素,若辅助栈里元素大于弹出值,直接压入辅助栈,否则,把辅助栈的元素比当前元素大的(有可能为全部)弹回初始栈,把当前元素再压入辅助栈。直到原始的栈为空。2,接着把辅助栈元素全部再弹回即可得到升序的结果。class TwoStacks {public: vector<int> twoStacksSort(vector<int> numbers) { vector<int> help; while(!numbers.empty()) { int current=numbers.back(); numbers.pop_back(); while(!help.empty()&&help.back()>current) { numbers.push_back(help.back()); help.pop_back(); } help.push_back(current); } while(!help.empty()) { numbers.push_back(help.back()); help.pop_back(); } return numbers; }};
4.8 滑动窗口练习题有一个整型数组 arr 和一个大小为 w 的窗口从数组的最左边滑到最右边,窗口每次向右边滑一个位置。 返回一个长度为n-w+1的数组res,res[i]表示每一种窗口状态下的最大值。 以数组为[4,3,5,4,3,3,6,7],w=3为例。因为第一个窗口[4,3,5]的最大值为5,第二个窗口[3,5,4]的最大值为5,第三个窗口[5,4,3]的最大值为5。第四个窗口[4,3,3]的最大值为4。第五个窗口[3,3,6]的最大值为6。第六个窗口[3,6,7]的最大值为7。所以最终返回[5,5,5,4,6,7]。给定整形数组arr及它的大小n,同时给定w,请返回res数组。保证w小于等于n,同时保证数组大小小于等于500。测试样例:[4,3,5,4,3,3,6,7],8,3返回:[5,5,5,4,6,7]
暴力法不可取,会超时。思路:使用双端队列,保证每一个节点最多进出队列1次。 我们可以发现,窗口是滑动形成的,最大值可能是以前窗口中的最大值,或者是新值。因此我们只要保存着以前窗口的最大值。我们使用双端队列容器存储最大值在数组中的索引。队列头部是以前窗口中最大值的最大值。
- 当以前窗口的最大值被滑出窗口时,需要从队列头部删除。
- 当新值大于队列中数字最大的数字时,删除队列中的所有数字,存储新值到队列头。(新值索引靠后,老值已经没有可能成为新窗口的最大值)
- 当新值不大于原有最大值时,当滑动新窗口时,仍有可能成为最大值,首先从队列尾依次删除比新值小的数字,然后存储到新值队列尾。
4.9 数组变树练习题(构建MaxTree)对于一个没有重复元素的整数数组,请用其中元素构造一棵MaxTree,MaxTree定义为一棵二叉树,其中的节点与数组元素一一对应,同时对于MaxTree的每棵子树,它的根的元素值为子树的最大值。现有一建树方法,对于数组中的每个元素,其在树中的父亲为数组中它左边比它大的第一个数和右边比它大的第一个数中 更小的一个。若两边都不存在比它大的数,那么它就是树根。请设计O(n)的算法实现这个方法。给定一个无重复元素的数组A和它的大小n,请返回一个数组,其中每个元素为原数组中对应位置元素在树中的父亲节点的编号,若为根则值为-1。测试样例:
[3,1,4,2],4返回:[2,0,-1,2]分析:对于这种没见过的题目,读题就懵逼了。。。首先看下定义:MaxTree的每棵子树,它的根的元素值为子树的最大值,根元素大于子树节点的值一个数组的MaxTree定义如下:1,数组必须没有重复元素;2,MaxTree是一棵二叉树,数组的每一个值对应一个二叉树节点;3,包括MaxTree树在内并且在其中的每一颗子树上,值最大的节点都是树的头;
具体的方法是: 对于数组中的每个元素,其在树中的父亲 为数组中它左边比它大的第一个数和右边比它大的第一个数中 更小的一个 实现手段即为记录每个位置的左边最大值和右边最小值(下标),栈用于辅助作用。
class MaxTree {public: vector<int> buildMaxTree(vector<int> arr, int n) { // write code here vector<int> leftMax(n),rightMin(n); //必须指定vector的大小,否则越界错误。 stack<int> s; // for (int i=0; i<n; i++) { while (!s.empty()&&arr[s.top()]<=arr[i]) { s.pop(); } if(s.empty()) leftMax[i] = -1; else leftMax[i] = s.top(); s.push(i); } while (!s.empty()) s.pop(); for (int i=n-1; i>=0; --i) { while (!s.empty()&&arr[s.top()]<=arr[i]) { s.pop(); } if(s.empty()) rightMin[i] = -1; else rightMin[i] = s.top(); s.push(i); } for (int i=0; i<n; ++i) { if(rightMin[i]==-1) continue; if(leftMax[i]==-1&&rightMin[i]!=-1) leftMax[i] = rightMin[i]; else{ if(arr[leftMax[i]]>arr[rightMin[i]]) leftMax[i] = rightMin[i]; //to find the smaller value } } return leftMax; } };