堆栈和队列知识总结
2016-05-28 22:37
225 查看
堆栈和队列是编程过程中经常使用的数据结构,为了学习数据结构知识和掌握一定的堆栈和队列的基本知识,总结记录。
堆栈和队列相关的题目主要有:
1、 构造一个O(1)访问最小值的栈结构
2、 用两个栈结构实现队列
3、 **用递归实现的栈的反转
4、 借助另一个栈实现当前栈的排序问题
5、 滑动窗口记录最大值问题
6、 *MaxTree的构造问题
内容详细描述
1、 构造一个访问最小值时间复杂度为O(1)的栈结构
描述:编写一个类,实现一个能够访问栈中最小值的自定义栈结构,要求时间复杂度为O(1)
分析:栈是只能访问栈顶元素,而不能直接访问其他栈中元素的。由于普通栈的限制,因此只能借助辅助栈的方式来解决该问题。解题思路是:自定义一个栈的类,类中有两个栈,一个负责存储数据S1,另一个负责存储最小值元素S2。具体操作如下:一个元素item到来的时候,首先判断栈是否为空,如果为空,那么分别将值压入到S1和S2;如果不为空,则先把item压入S1,再判断item与S2栈顶元素大小,item小于S2栈顶元素则将item压入S2,否则将S2栈顶元素重复压入一次。
示例代码:无
总结:无
2、 用两个栈结构实现队列
描述:编写一个类,只能用两个栈结构实现队列,支持队列的基本操作(push,pop)。
分析:栈结构是先进后出的结构,而队列是先进先出的结构,如何将先进后出的结构构造先进先出的结构呢?首先,规定这两个栈S1和S2,S1负责push数据,S2负责pop数据。另外,添加一下限制,只有当S2为空同时要做pop操作时,才将S1中的数据全部pop出来并push到S2中,然后返回S2的pop元素;否则返回S2的pop元素。
示例代码:无
总结:无
3、 用递归实现的栈的反转
描述:实现一个栈的逆序,但是只能用递归函数和这个栈本身的pop操作来实现,而不能自己申请另外的数据结构。
分析:解决思路是首先写一个递归函数F1返回栈底元素,和递归函数F2调用F1获取栈底元素Bi,然后递归调用F2,当返回到当前状态时,再将Bi压入栈中。
F1函数 F2函数
图中g代表获得(get),r代表返回(return),b代表调用F1函数返回底部元素(bottom),p代表压入栈(push)。
示例代码:
总结:递归嵌套有时候会很轻松地解决问题,在学习中应该努力尝试使用。
4、 借助另一个栈实现当前栈的排序问题
描述:只能借助一个辅助栈的情况下,实现栈的从大到小排列
分析:按照题目要求,设计一个思路:借助当前栈S1,实现辅助栈S2从小到大的排序,然后将S2中元素pop并push的S1就解决了问题。那么,如何能够实现S2的从小到大排序呢?首先从S1中pop出一个元素cur,如果cur大于S2栈顶元素或者S2为空,那么将cur压入S2;否则,pop出S2中元素同时push到S1,知道S2中栈顶元素大于cur或者是S2 pop空,然后将cur压入S2。重复以上操作直到S1为空,那么就完成了S2的从小到大排序。
示例代码:
public ArrayList<Integer> twoStacksSort(int[] numbers) {
ArrayList<Integer> arrList = new ArrayList<Integer>();
Stack<Integer> stack1 = new Stack<Integer>();
for (int i = numbers.length - 1; i >= 0; i--) {
stack1.push(numbers[i]);
}
Stack<Integer> stack2 = new Stack<Integer>();
while (!stack1.isEmpty()) {
int s1top = stack1.pop();
if (stack2.isEmpty() || s1top < stack2.peek()) {
stack2.push(s1top);
} else {
while (!stack2.isEmpty() && s1top > stack2.peek()) {
stack1.push(stack2.pop());
}
stack2.push(s1top);
}
}
while (!stack2.isEmpty()) {
stack1.push(stack2.pop());
}
while (!stack1.isEmpty()) {
arrList.add(stack1.pop());
}
return arrList;
}
总结:无
5、 滑动窗口记录最大值问题
描述:使用大小为k的滑动窗口,滑动到数组结束,返回每次滑动过程中窗口中最大值构成的数组。给出数组和k值,返回窗口最大值数组。
输入样例:[7,3,2,5,4] 3
返回结果:[7,5,5]
分析:本题借助双端队列Deque来完成,双端队列中记录数组的下标(队头位置的值对应数组中的值就是当前窗口的最大值)。当Deque为空或者队尾值所对应的元素大于下标为i的值时,则将i压入Deque;当下标为i的值大于队尾值所对应的元素时,则弹出队尾元素,直到出现Deque为空或者队尾值所对应的元素大于下标为i的值的情况,将i压入Deque;同时,也要关注队首值是否过期,即队首值不在当前窗口范围内。
示例代码:
6、 MaxTree的构造问题
描述:输入数组,返回该数组对应的MaxTree数组。
输入样例:[3,1,4,2]
返回结果:[2,0,-1,2]
分析
4000
:MaxTree结构类似于二叉树,要求每个节点的值都大于他们的子节点的值。如何求取每个节点的父节点是解决该问题的关键?只要求导当前节点左右临近大于当前节点值且较小那个节点就是该节点的父节点。基于这个思想,设两个哈希表分别记录每个元素的左侧第一个比当前值大的值,和右侧第一个比当前值大的值。根据这两个哈希表就能找到每个节点的父节点。
示例代码:
总结:无
堆栈和队列相关的题目主要有:
1、 构造一个O(1)访问最小值的栈结构
2、 用两个栈结构实现队列
3、 **用递归实现的栈的反转
4、 借助另一个栈实现当前栈的排序问题
5、 滑动窗口记录最大值问题
6、 *MaxTree的构造问题
内容详细描述
1、 构造一个访问最小值时间复杂度为O(1)的栈结构
描述:编写一个类,实现一个能够访问栈中最小值的自定义栈结构,要求时间复杂度为O(1)
分析:栈是只能访问栈顶元素,而不能直接访问其他栈中元素的。由于普通栈的限制,因此只能借助辅助栈的方式来解决该问题。解题思路是:自定义一个栈的类,类中有两个栈,一个负责存储数据S1,另一个负责存储最小值元素S2。具体操作如下:一个元素item到来的时候,首先判断栈是否为空,如果为空,那么分别将值压入到S1和S2;如果不为空,则先把item压入S1,再判断item与S2栈顶元素大小,item小于S2栈顶元素则将item压入S2,否则将S2栈顶元素重复压入一次。
示例代码:无
总结:无
2、 用两个栈结构实现队列
描述:编写一个类,只能用两个栈结构实现队列,支持队列的基本操作(push,pop)。
分析:栈结构是先进后出的结构,而队列是先进先出的结构,如何将先进后出的结构构造先进先出的结构呢?首先,规定这两个栈S1和S2,S1负责push数据,S2负责pop数据。另外,添加一下限制,只有当S2为空同时要做pop操作时,才将S1中的数据全部pop出来并push到S2中,然后返回S2的pop元素;否则返回S2的pop元素。
示例代码:无
总结:无
3、 用递归实现的栈的反转
描述:实现一个栈的逆序,但是只能用递归函数和这个栈本身的pop操作来实现,而不能自己申请另外的数据结构。
分析:解决思路是首先写一个递归函数F1返回栈底元素,和递归函数F2调用F1获取栈底元素Bi,然后递归调用F2,当返回到当前状态时,再将Bi压入栈中。
F1函数 F2函数
图中g代表获得(get),r代表返回(return),b代表调用F1函数返回底部元素(bottom),p代表压入栈(push)。
示例代码:
public int F1(Stack<Integer> stack) { //F1函数 int top = stack.pop(); if (stack.isEmpty()) { return top; } int i = get(stack); stack.push(top); return i; } public void F2(Stack<Integer> stack) { //F2函数 if (stack.isEmpty()) { return; } int bottom = get(stack); reverse(stack); stack.push(bottom); }
总结:递归嵌套有时候会很轻松地解决问题,在学习中应该努力尝试使用。
4、 借助另一个栈实现当前栈的排序问题
描述:只能借助一个辅助栈的情况下,实现栈的从大到小排列
分析:按照题目要求,设计一个思路:借助当前栈S1,实现辅助栈S2从小到大的排序,然后将S2中元素pop并push的S1就解决了问题。那么,如何能够实现S2的从小到大排序呢?首先从S1中pop出一个元素cur,如果cur大于S2栈顶元素或者S2为空,那么将cur压入S2;否则,pop出S2中元素同时push到S1,知道S2中栈顶元素大于cur或者是S2 pop空,然后将cur压入S2。重复以上操作直到S1为空,那么就完成了S2的从小到大排序。
示例代码:
public ArrayList<Integer> twoStacksSort(int[] numbers) {
ArrayList<Integer> arrList = new ArrayList<Integer>();
Stack<Integer> stack1 = new Stack<Integer>();
for (int i = numbers.length - 1; i >= 0; i--) {
stack1.push(numbers[i]);
}
Stack<Integer> stack2 = new Stack<Integer>();
while (!stack1.isEmpty()) {
int s1top = stack1.pop();
if (stack2.isEmpty() || s1top < stack2.peek()) {
stack2.push(s1top);
} else {
while (!stack2.isEmpty() && s1top > stack2.peek()) {
stack1.push(stack2.pop());
}
stack2.push(s1top);
}
}
while (!stack2.isEmpty()) {
stack1.push(stack2.pop());
}
while (!stack1.isEmpty()) {
arrList.add(stack1.pop());
}
return arrList;
}
总结:无
5、 滑动窗口记录最大值问题
描述:使用大小为k的滑动窗口,滑动到数组结束,返回每次滑动过程中窗口中最大值构成的数组。给出数组和k值,返回窗口最大值数组。
输入样例:[7,3,2,5,4] 3
返回结果:[7,5,5]
分析:本题借助双端队列Deque来完成,双端队列中记录数组的下标(队头位置的值对应数组中的值就是当前窗口的最大值)。当Deque为空或者队尾值所对应的元素大于下标为i的值时,则将i压入Deque;当下标为i的值大于队尾值所对应的元素时,则弹出队尾元素,直到出现Deque为空或者队尾值所对应的元素大于下标为i的值的情况,将i压入Deque;同时,也要关注队首值是否过期,即队首值不在当前窗口范围内。
示例代码:
public int[] slide(int[] arr, int n, int w) { if (arr == null || n < w) { return null; } int[] ret = new int[n - w + 1]; Deque<Integer> dque = new LinkedList<Integer>(); int index = 0; for (int i = 0; i < n; i++) { while (!dque.isEmpty() && arr[i] > arr[dque.peekLast()]) { dque.pollLast(); } if (!dque.isEmpty() && dque.peekFirst() <= (i - w)) { dque.pollFirst(); } dque.offerLast(i); if (i >= (w - 1)) { ret[index++] = arr[dque.peekFirst()]; } } return ret; }总结:辅助队列方式解决问题。
6、 MaxTree的构造问题
描述:输入数组,返回该数组对应的MaxTree数组。
输入样例:[3,1,4,2]
返回结果:[2,0,-1,2]
分析
4000
:MaxTree结构类似于二叉树,要求每个节点的值都大于他们的子节点的值。如何求取每个节点的父节点是解决该问题的关键?只要求导当前节点左右临近大于当前节点值且较小那个节点就是该节点的父节点。基于这个思想,设两个哈希表分别记录每个元素的左侧第一个比当前值大的值,和右侧第一个比当前值大的值。根据这两个哈希表就能找到每个节点的父节点。
示例代码:
public int[] buildMaxTree(int[] A, int n) { if (A == null || n < 1) {return null; } HashMap<Integer, Integer> lmap = new HashMap<Integer, Integer>(); HashMap<Integer, Integer> rmap = new HashMap<Integer, Integer>(); Stack<Integer> stack = new Stack<Integer>(); for (int i = 0; i < n; i++) { while (!stack.isEmpty() && A[stack.peek()] < A[i]) { stack.pop(); } if (stack.isEmpty()) { lmap.put(A[i], -1); } else { lmap.put(A[i], stack.peek()); } stack.push(i); } stack.clear(); for (int i = n - 1; i >= 0; i--) { while (!stack.isEmpty() && A[stack.peek()] < A[i]) { stack.pop();} if (stack.isEmpty()) { rmap.put(A[i], -1); } else { rmap.put(A[i], stack.peek()); } stack.push(i); } int[] ret = new int ; for (int i = 0; i < n; i++) { if (lmap.get(A[i]) == -1 && rmap.get(A[i]) == -1) { ret[i] = -1; } else if (lmap.get(A[i]) == -1) { ret[i] = rmap.get(A[i]); } else if (rmap.get(A[i]) == -1) { ret[i] = lmap.get(A[i]); } else { ret[i] = A[lmap.get(A[i])] > A[rmap.get(A[i])] ? rmap.get(A[i]) : lmap.get(A[i]); } } return ret; }
总结:无
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树