堆/栈/队列
用两个栈实现队列
描述 用两个栈来实现一个队列,使用n个元素来完成 n 次在队列尾部插入整数(push)和n次在队列头部删除整数(pop)的功能。 队列中的元素为int类型。保证操作合法,即保证pop操作时队列内已有元素。
解题思路:
借助栈的先进后出规则模拟实现队列的先进先出 1、当插入时,直接插入 stack1 2、当弹出时,当 stack2 不为空,弹出 stack2 栈顶元素,如果 stack2 为空,将 stack1 中的全部数逐个出栈入栈 stack2,再弹出 stack2 栈顶元素 代码展示:
public class Solution {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack1.push(node);
}
public int pop() {
if (stack2.size() <= 0){
while (stack1.size() != 0){
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
包含min函数的栈
描述
定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的 min 函数,输入操作时保证 pop、top 和 min 函数操作时,栈中一定有元素。
此栈包含的方法有: push(value):将value压入栈中 pop():弹出栈顶元素 top():获取栈顶元素 min():获取栈中最小元素
数据范围:操作数量满足 0 \le n \le 300 \0≤n≤300 ,输入的元素满足 |val| \le 10000 \∣val∣≤10000
进阶:栈的各个操作的时间复杂度是 O(1)\O(1) ,空间复杂度是 O(n)\O(n)
方法:双栈法(推荐使用)
知识点:栈
栈是一种仅支持在表尾进行插入和删除操作的线性表,这一端被称为栈顶,另一端被称为栈底。元素入栈指的是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;元素出栈指的是从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
思路:
我们都知道栈结构的push、pop、top操作都是O(1)O(1)O(1),但是min函数做不到,于是想到在push的时候就将最小值记录下来,由于栈先进后出的特殊性,我们可以构造一个单调栈,保证栈内元素都是递增的,栈顶元素就是当前最小的元素。
具体做法:
- step 1:使用一个栈记录进入栈的元素,正常进行push、pop、top操作。
- step 2:使用另一个栈记录每次push进入的最小值。
- step 3:每次push元素的时候与第二个栈的栈顶元素比较,若是较小,则进入第二个栈,若是较大,则第二个栈的栈顶元素再次入栈,因为即便加了一个元素,它依然是最小值。于是,每次访问最小值即访问第二个栈的栈顶。
Java实现代码: public class Solution {
Stack<Integer> s1 = new Stack<Integer>();
//用于存储最小min
Stack<Integer> s2 = new Stack<Integer>();
public void push(int node) {
s1.push(node);
//空或者新元素较小,则入栈
if(s2.isEmpty() || s2.peek() > node)
s2.push(node);
else
//重复加入栈顶
s2.push(s2.peek());
}
public void pop() {
s1.pop();
s2.pop();
}
public int top() {
return s1.peek();
}
public int min() {
return s2.peek();
}
}
有效括号序列
描述
给出一个仅包含字符'(',')','{','}','['和']',的字符串,判断给出的字符串是否是合法的括号序列
括号必须以正确的顺序关闭,"()"和"()[]{}"都是合法的括号序列,但"(]"和"([)]"不合法。
数据范围:字符串长度 0\le n \le 100000≤n≤10000
要求:空间复杂度 O(n)O(n),时间复杂度 O(n)O(n) 算法思想二:栈
解题思路:
建立一个栈,存储遍历的字符串再进行对比 1、遍历字符串遇到 ‘(’ '[' '{' 左括号,就把相应的右括号(‘)’ ']' '}')入栈 2、如果遍历遇到右括号 ‘)’ ']' '}' ,则判断是否和栈顶元素相同 1、不同则直接返回false
2、相同则将栈顶元素出栈
3、遍历结束判断栈是否为空 代码如下: public class Solution {
/**
*
* @param s string字符串
* @return bool布尔型
*/
public boolean isValid (String s) {
// write code here
Stack<Character>stack = new Stack<Character>();
for(char c: s.toCharArray()){
// 碰到左括号,就把相应的右括号入栈
if(c=='(')stack.push(')');
else if(c=='[')stack.push(']');
else if(c=='{')stack.push('}');
// 如果是右括号判断是否和栈顶元素匹配
else if(stack.isEmpty()||c!=stack.pop())return false;
}
return stack.isEmpty();
}
}
最小的K个数
描述
给定一个长度为 n 的可能有重复值的数组,找出其中不去重的最小的 k 个数。例如数组元素是4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4(任意顺序皆可)。 数据范围:0\le k,n \le 100000≤k,n≤10000,数组中每个数的大小0 \le val \le 10000≤val≤1000 要求:空间复杂度 O(n)O(n) ,时间复杂度 O(nlogn)O(nlogn) 方法一:堆排序(推荐使用)
知识点:优先队列
优先队列即PriorityQueue,是一种内置的机遇堆排序的容器,分为大顶堆与小顶堆,大顶堆的堆顶为最大元素,其余更小的元素在堆下方,小顶堆与其刚好相反。且因为容器内部的次序基于堆排序,因此每次插入元素时间复杂度都是O(log2n)O(log_2n)O(log2n),而每次取出堆顶元素都是直接取出。
思路:
要找到最小的k个元素,只需要准备k个数字,之后每次遇到一个数字能够快速的与这k个数字中最大的值比较,每次将最大的值替换掉,那么最后剩余的就是k个最小的数字了。
如何快速比较k个数字的最大值,并每次替换成较小的新数字呢?我们可以考虑使用优先队列(大根堆),只要限制堆的大小为k,那么堆顶就是k个数字的中最大值,如果需要替换,将这个最大值拿出,加入新的元素就好了。
具体做法:
- step 1:利用input数组中前k个元素,构建一个大小为k的大顶堆,堆顶为这k个元素的最大值。
- step 2:对于后续的元素,依次比较其与堆顶的大小,若是比堆顶小,则堆顶弹出,再将新数加入堆中,直至数组结束,保证堆中的k个最小。
- step 3:最后将堆顶依次弹出即是最小的k个数。
代码如下: public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> res = new ArrayList<Integer>();
//排除特殊情况
if(k == 0 || input.length == 0)
return res;
//大根堆
PriorityQueue<Integer> q = new PriorityQueue<>((o1, o2)->o2.compareTo(o1));
//构建一个k个大小的堆
for(int i = 0; i < k; i++)
q.offer(input[i]);
for(int i = k; i < input.length; i++){
//较小元素入堆
if(q.peek() > input[i]){
q.poll();
q.offer(input[i]);
}
}
//堆中元素取出入数组
for(int i = 0; i < k; i++)
res.add(q.poll());
return res;
}
}
解题思路:
代码展示:
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack1.push(node);
}
public int pop() {
if (stack2.size() <= 0){
while (stack1.size() != 0){
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
包含min函数的栈
描述
进阶:栈的各个操作的时间复杂度是 O(1)\O(1) ,空间复杂度是 O(n)\O(n)
方法:双栈法(推荐使用)
知识点:栈
栈是一种仅支持在表尾进行插入和删除操作的线性表,这一端被称为栈顶,另一端被称为栈底。元素入栈指的是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;元素出栈指的是从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
思路:
我们都知道栈结构的push、pop、top操作都是O(1)O(1)O(1),但是min函数做不到,于是想到在push的时候就将最小值记录下来,由于栈先进后出的特殊性,我们可以构造一个单调栈,保证栈内元素都是递增的,栈顶元素就是当前最小的元素。
具体做法:
- step 1:使用一个栈记录进入栈的元素,正常进行push、pop、top操作。
- step 2:使用另一个栈记录每次push进入的最小值。
- step 3:每次push元素的时候与第二个栈的栈顶元素比较,若是较小,则进入第二个栈,若是较大,则第二个栈的栈顶元素再次入栈,因为即便加了一个元素,它依然是最小值。于是,每次访问最小值即访问第二个栈的栈顶。
public class Solution { Stack<Integer> s1 = new Stack<Integer>(); //用于存储最小min Stack<Integer> s2 = new Stack<Integer>(); public void push(int node) { s1.push(node); //空或者新元素较小,则入栈 if(s2.isEmpty() || s2.peek() > node) s2.push(node); else //重复加入栈顶 s2.push(s2.peek()); } public void pop() { s1.pop(); s2.pop(); } public int top() { return s1.peek(); } public int min() { return s2.peek(); } }
有效括号序列
描述
给出一个仅包含字符'(',')','{','}','['和']',的字符串,判断给出的字符串是否是合法的括号序列
括号必须以正确的顺序关闭,"()"和"()[]{}"都是合法的括号序列,但"(]"和"([)]"不合法。
数据范围:字符串长度 0\le n \le 100000≤n≤10000
要求:空间复杂度 O(n)O(n),时间复杂度 O(n)O(n) 算法思想二:栈
解题思路:
建立一个栈,存储遍历的字符串再进行对比 1、遍历字符串遇到 ‘(’ '[' '{' 左括号,就把相应的右括号(‘)’ ']' '}')入栈 2、如果遍历遇到右括号 ‘)’ ']' '}' ,则判断是否和栈顶元素相同 1、不同则直接返回false
2、相同则将栈顶元素出栈
3、遍历结束判断栈是否为空 代码如下: public class Solution {
/**
*
* @param s string字符串
* @return bool布尔型
*/
public boolean isValid (String s) {
// write code here
Stack<Character>stack = new Stack<Character>();
for(char c: s.toCharArray()){
// 碰到左括号,就把相应的右括号入栈
if(c=='(')stack.push(')');
else if(c=='[')stack.push(']');
else if(c=='{')stack.push('}');
// 如果是右括号判断是否和栈顶元素匹配
else if(stack.isEmpty()||c!=stack.pop())return false;
}
return stack.isEmpty();
}
}
最小的K个数
描述
给定一个长度为 n 的可能有重复值的数组,找出其中不去重的最小的 k 个数。例如数组元素是4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4(任意顺序皆可)。 数据范围:0\le k,n \le 100000≤k,n≤10000,数组中每个数的大小0 \le val \le 10000≤val≤1000 要求:空间复杂度 O(n)O(n) ,时间复杂度 O(nlogn)O(nlogn) 方法一:堆排序(推荐使用)
知识点:优先队列
优先队列即PriorityQueue,是一种内置的机遇堆排序的容器,分为大顶堆与小顶堆,大顶堆的堆顶为最大元素,其余更小的元素在堆下方,小顶堆与其刚好相反。且因为容器内部的次序基于堆排序,因此每次插入元素时间复杂度都是O(log2n)O(log_2n)O(log2n),而每次取出堆顶元素都是直接取出。
思路:
要找到最小的k个元素,只需要准备k个数字,之后每次遇到一个数字能够快速的与这k个数字中最大的值比较,每次将最大的值替换掉,那么最后剩余的就是k个最小的数字了。
如何快速比较k个数字的最大值,并每次替换成较小的新数字呢?我们可以考虑使用优先队列(大根堆),只要限制堆的大小为k,那么堆顶就是k个数字的中最大值,如果需要替换,将这个最大值拿出,加入新的元素就好了。
具体做法:
- step 1:利用input数组中前k个元素,构建一个大小为k的大顶堆,堆顶为这k个元素的最大值。
- step 2:对于后续的元素,依次比较其与堆顶的大小,若是比堆顶小,则堆顶弹出,再将新数加入堆中,直至数组结束,保证堆中的k个最小。
- step 3:最后将堆顶依次弹出即是最小的k个数。
代码如下: public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> res = new ArrayList<Integer>();
//排除特殊情况
if(k == 0 || input.length == 0)
return res;
//大根堆
PriorityQueue<Integer> q = new PriorityQueue<>((o1, o2)->o2.compareTo(o1));
//构建一个k个大小的堆
for(int i = 0; i < k; i++)
q.offer(input[i]);
for(int i = k; i < input.length; i++){
//较小元素入堆
if(q.peek() > input[i]){
q.poll();
q.offer(input[i]);
}
}
//堆中元素取出入数组
for(int i = 0; i < k; i++)
res.add(q.poll());
return res;
}
}
给出一个仅包含字符'(',')','{','}','['和']',的字符串,判断给出的字符串是否是合法的括号序列
括号必须以正确的顺序关闭,"()"和"()[]{}"都是合法的括号序列,但"(]"和"([)]"不合法。
数据范围:字符串长度 0\le n \le 100000≤n≤10000
算法思想二:栈
解题思路:
建立一个栈,存储遍历的字符串再进行对比public class Solution { /** * * @param s string字符串 * @return bool布尔型 */ public boolean isValid (String s) { // write code here Stack<Character>stack = new Stack<Character>(); for(char c: s.toCharArray()){ // 碰到左括号,就把相应的右括号入栈 if(c=='(')stack.push(')'); else if(c=='[')stack.push(']'); else if(c=='{')stack.push('}'); // 如果是右括号判断是否和栈顶元素匹配 else if(stack.isEmpty()||c!=stack.pop())return false; } return stack.isEmpty(); } }
最小的K个数
描述
方法一:堆排序(推荐使用)
知识点:优先队列
优先队列即PriorityQueue,是一种内置的机遇堆排序的容器,分为大顶堆与小顶堆,大顶堆的堆顶为最大元素,其余更小的元素在堆下方,小顶堆与其刚好相反。且因为容器内部的次序基于堆排序,因此每次插入元素时间复杂度都是O(log2n)O(log_2n)O(log2n),而每次取出堆顶元素都是直接取出。
思路:
要找到最小的k个元素,只需要准备k个数字,之后每次遇到一个数字能够快速的与这k个数字中最大的值比较,每次将最大的值替换掉,那么最后剩余的就是k个最小的数字了。
如何快速比较k个数字的最大值,并每次替换成较小的新数字呢?我们可以考虑使用优先队列(大根堆),只要限制堆的大小为k,那么堆顶就是k个数字的中最大值,如果需要替换,将这个最大值拿出,加入新的元素就好了。
具体做法:
- step 1:利用input数组中前k个元素,构建一个大小为k的大顶堆,堆顶为这k个元素的最大值。
- step 2:对于后续的元素,依次比较其与堆顶的大小,若是比堆顶小,则堆顶弹出,再将新数加入堆中,直至数组结束,保证堆中的k个最小。
- step 3:最后将堆顶依次弹出即是最小的k个数。
public class Solution { public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) { ArrayList<Integer> res = new ArrayList<Integer>(); //排除特殊情况 if(k == 0 || input.length == 0) return res; //大根堆 PriorityQueue<Integer> q = new PriorityQueue<>((o1, o2)->o2.compareTo(o1)); //构建一个k个大小的堆 for(int i = 0; i < k; i++) q.offer(input[i]); for(int i = k; i < input.length; i++){ //较小元素入堆 if(q.peek() > input[i]){ q.poll(); q.offer(input[i]); } } //堆中元素取出入数组 for(int i = 0; i < k; i++) res.add(q.poll()); return res; } }