1.定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数。
方法:1.使用两个栈 stackData,stackMin,一个记录数据,另一个栈确保栈顶是当前数据栈的最小元素
2.入栈:若stackMin空,则直接入,否则如果当前元素小于等于栈顶元素,入栈,否则,不入。
3.出栈:如果从stackData出来的元素于当前stackMin元素栈顶相同,stackMin进行一次出栈。
private Stack<Integer> stack=new Stack<>(); private Stack<Integer> min=new Stack<>(); public void push(int node) { stack.push(node); if(min.isEmpty()||node<=min.peek()) min.push(node); } public void pop() { int value=stack.pop(); if(!min.isEmpty()&&value==min.peek()) min.pop(); } public int top() { return stack.peek(); } public int min() { return min.peek(); }
2.用栈实现队列的功能
问题:用两个栈结构实现队列,支持队列的基本操作(push,pop,peek)
方法:使用一个栈压入,使用另一个栈弹出。如队列1 2 3 4 5,压入栈时依次入栈,
在栈内就是1 2 3 4 5 ,5是栈顶,将此栈元素倒入另一个栈,
在栈内就是5 4 3 2 1,1是栈顶,依次出栈就是一个队列。
要点:将元素从压入栈倒入弹出栈时,需确保弹出栈为空且压入栈已全部倒入。
3.将栈逆序,要求不能使用额数据结构,只能使用递归函数
方法:1.使用一个get函数,每次取得并移除栈底元素,其他元素顺序不变。
2.使用一个reverse函数,每一层都调用一次get,当每一层返回时,将get得到的元素入栈,最后即得到逆序栈。
public int get(Stack<Integer> stk) { int value=stk.pop(); int last=value; if(!stk.isEmpty()) { last=get(stk); stk.push(value); } return last; } public void reverse(Stack<Integer> stk) { int ele=get(stk); if(!stk.isEmpty()) reverse(stk); stk.push(ele); }
4.按升序对栈进行排序(即最大元素位于栈顶),要求最多只能使用一个额外的栈存放临时数据
分析:类似于第一个带最值的栈的问题,使用一个辅助栈,
从数据栈依次弹出元素,如果不大于辅助栈栈顶,则入辅助栈,否则,依次从辅助栈弹入到数据栈,直到能够入辅助栈。
最后数据栈全部压入辅助栈,则将辅助栈全部倒入数据栈,完成排序。
5.滑动窗口求每个窗口的最大值
问题描述:
有一个整型数组 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,第五个窗口[3,3,6]的最大值为6。第六个窗口[3,6,7]的最大值为7。所以最终返回[5,5,5,4,6,7]。
分析:
1.普通方法遍历每个窗口,则时间复杂度为O(N*W)。
2.利用一个双端队列,存储最近一个窗口内出现的最大值,其中队列内元素按降序排列。
方法:以上题为例,遍历数组,当队列为空时,则将当前下标i入队,如果队内有元素,则当队尾下标对应的元素比要入队的下标对应的元素大时,直接入队,
否则队内元素出队,直接可入队,即保持队内的下标对应的元素是降序。
当入队成功时,判断当前队头的下标是否等于i-w,如果等于,则已过期(画个图就明白了),把过期的元素弹出。
判断完过期后,判断是否形成了窗口,满足则当前窗口最大值即为队头下标对应的元素。
public static int[] slide(int[] arr, int n, int w) { // write code here Deque<Integer> deque=new LinkedList<Integer>(); int[] res=new int[n-w+1]; int k=0; for(int i=0;i<arr.length;i++) { while(!deque.isEmpty()&&arr[deque.peekLast()]<arr[i]) deque.removeLast(); deque.offer(i); if(deque.getFirst()==i-w) deque.poll(); if(i>=w-1) res[k++]=arr[deque.getFirst()]; } return res; }
6.数组建树
问题描述:
对于一个没有重复元素的整数数组,请用其中元素构造一棵MaxTree,MaxTree定义为一棵二叉树,其中的节点与数组元素一一对应,同时对于MaxTree的每棵子树,它的根的元素值为子树的最大值。
现有一建树方法,对于数组中的每个元素,其在树中的父亲为数组中它左边比它大的第一个数和右边比它大的第一个数中更小的一个。若两边都不存在比它大的数,那么它就是树根。请设计O(n)的算法实现这个方法。
给定一个无重复元素的数组A和它的大小n,请返回一个数组,其中每个元素为原数组中对应位置元素在树中的父亲节点的编号,若为根则值为-1。
测试样例:[3,1,4,2],4
返回:[2,0,-1,2]
分析:此题重点是寻找每个元素左边比其大的一个数和右边比其大的第一个数。
方法:寻找右边时,维护一个栈,从右往左遍历数组
如果当前栈为空,则无比其大的数,并将次数入栈。
如果栈不为空,且栈顶元素比该数小时,出栈,直到可入。则栈顶为右边比起大的第一个数。若栈空,则无比其大的数。
public int[] buildMaxTree(int[] arr, int n) { // write code here int[] right=new int[arr.length]; Stack<Integer> stk=new Stack<>(); for(int i=arr.length-1;i>=0;i--) { while(!stk.isEmpty()&&arr[i]>arr[stk.peek()]) { stk.pop(); } if(stk.isEmpty()) right[i]=-1; else right[i]=stk.peek(); stk.push(i); }stk.clear(); for(int i=0;i<arr.length;i++) { while(!stk.isEmpty()&&arr[i]>arr[stk.peek()]) { stk.pop(); } if(stk.isEmpty()) ; else { if(right[i]==-1) right[i]=stk.peek(); else right[i]=arr[stk.peek()]<arr[right[i]]?stk.peek():right[i]; } stk.push(i); } return right; }