刷题时间:2021.7.20-2021.7.22
栈:先进后出线性表
push()把项压入堆栈顶部
pop()移除堆栈顶部的对象,并作为此函数的值返回该对象
peek()查看堆栈顶部的对象,但不从堆栈中移除它
队列:先进先出线性表
offer()方法往队列添加元素如果队列已满直接返回false,队列未满则直接插入并返回true
poll()方法取出并删除队头的元素,当队列为空,返回null
peek()方法直接取出队头的元素,并不删除
PriorityQueue是基于优先堆的一个无界队列,这个优先队列中的元素可以默认自然排序或者通过提供的Comparator(比较器)在队列实例化的时排序。PriorityQueue保存队列元素的顺序不是它们加入队列的顺序,而是他们的大小(默认poll的顺序是从小到大)。所以使用peek()、poll()函数取出队列中的元素时,不是取出最先存进去的元素而是所有元素中最小的元素。同时PriorityQueue不允许插入null元素,他存储的元素必须是可以比较的对象,否则要指明比较器。
PriorityQueue实现自定义比较器: PriorityQueue中的元素是从小到大顺序排列的,如果我希望里面的元素从大到小逆序排列可以这么写:
PriorityQueue pq = new PriorityQueue<>((a, b) -> (b - a));
二叉堆:最小(大)值先出的完全二叉树
1、LeetCode225用队列实现栈
思路:在STACK push元素时,利用临时队列调换元素次序
class MyStack {
Queue<Integer> data_queue;
Queue<Integer> temp_queue;
/** Initialize your data structure here. */
public MyStack() {
data_queue = new LinkedList<Integer>();//data_queue数据队列存储元素的顺序就是栈存储元素的顺序
temp_queue = new LinkedList<Integer>();//临时队列,利用该队列进行原始data_queue元素与新元素的次序调换
}
/** Push element x onto stack. */
public void push(int x) {
temp_queue.offer(x);
while (!data_queue.isEmpty()) {
temp_queue.offer(data_queue.poll());//将原队列内容offer进入临时队列
}
while(!temp_queue.isEmpty()){
data_queue.offer(temp_queue.poll());//将临时队列内容offer进入数据队列
}
}
/** Removes the element on top of the stack and returns that element. */
public int pop() {
return data_queue.poll();//取出并删除队头的元素,当队列为空,返回null
}
/** Get the top element. */
public int top() {
return data_queue.peek();//直接取出队头的元素,并不删除
}
/** Returns whether the stack is empty. */
public boolean empty() {
return data_queue.isEmpty();
}
}
/**
* Your MyStack object will be instantiated and called as such:
* MyStack obj = new MyStack();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.top();
* boolean param_4 = obj.empty();
*/
2、LeetCode232用栈实现队列
思路:在队列push元素时,利用临时栈调换元素次序
class MyQueue {
Deque<Integer> temp_Stack;
Deque<Integer> data_Stack;
/** Initialize your data structure here. */
public MyQueue() {
temp_Stack = new LinkedList<Integer>();//设置临时栈用来调换数据栈中的元素次序
data_Stack = new LinkedList<Integer>();
}
/** Push element x to the back of queue. */
public void push(int x) {
while (!data_Stack.isEmpty()) {
temp_Stack.push(data_Stack.pop());//将原数据栈内容push进入临时栈
}
temp_Stack.push(x);//将新数据push进入临时栈
while(!temp_Stack.isEmpty()){
data_Stack.push(temp_Stack.pop());//将临时栈内容push进入数据栈
}
}
/** Removes the element from in front of queue and returns that element. */
public int pop() {//弹出栈顶并返回头部元素
return data_Stack.pop();
}
/** Get the front element. */
public int peek() {//取出头部元素,并不删除
return data_Stack.peek();
}
/** Returns whether the queue is empty. */
public boolean empty() {
return data_Stack.isEmpty();
}
}
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue obj = new MyQueue();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.peek();
* boolean param_4 = obj.empty();
*/
3、LeetCode155最小栈
思路:用另一个栈,存储各个状态的最小值
class MinStack {
Deque<Integer> xStack;
Deque<Integer> minStack;
public MinStack() {
xStack = new LinkedList<Integer>();
minStack = new LinkedList<Integer>();
}
public void push(int x) {
xStack.push(x);//将数据压入数据栈
if(minStack.isEmpty()){
minStack.push(x);
}else{
if(x > minStack.peek()){
x = minStack.peek();
}
minStack.push(x);
}
}
public void pop() {//同时弹出
xStack.pop();
minStack.pop();
}
public int top() {
return xStack.peek();
}
public int getMin() {
return minStack.peek();
}
}
/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(x);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.getMin();
*/
4、LeetCode946验证栈序列
思路:出栈结果存储在队列order中。按元素顺序,将元素push进入栈。每push一个元素,即检查是否与队列首部元素相同,若相同则弹出队列首元素和栈顶元素,直到两元素不同结束。若最终栈为空,说明序列合法,否则不合法。
class Solution {
public boolean validateStackSequences(int[] pushed, int[] popped) {
Stack<Integer> stack = new Stack<>();//stack为模拟栈序列
int index = 0;
for(int i = 0; i < pushed.length; i++){
stack.push(pushed[i]);
while(!stack.isEmpty() && stack.peek() == popped[index]){
stack.pop();
index++;
}
}
return stack.isEmpty();
}
}
5、LeetCode224基本栈序列
class Solution {
public int calculate(String s) {
Stack<Integer> stack = new Stack<Integer>();
// sign 代表正负
int sign = 1, res = 0;
int length = s.length();
for (int i = 0; i < length; i++) {
char ch = s.charAt(i);
if (Character.isDigit(ch)) {
int cur = ch - '0';//将字符串转成数字
while (i + 1 < length && Character.isDigit(s.charAt(i + 1)))//数字不止一位
cur = cur * 10 + s.charAt(++i) - '0';
res = res + sign * cur;
} else if (ch == '+') {
sign = 1;
} else if (ch == '-') {
sign = -1;
} else if (ch == '(') {
stack.push(res);
res = 0;
stack.push(sign);
sign = 1;
} else if (ch == ')') {
res = stack.pop() * res + stack.pop();
}
}
return res;
}
}
6、LeetCode215数组中的第K个最大元素
思路:维护一个K大小的最小堆,堆中元素个数小于K时,新元素直接进入堆;否则,当堆顶小于新元素时,弹出堆顶,将新元素加入堆。 解释:由于堆是最小堆,堆顶是堆中最小元素,新元素都会保证比堆顶小(否则新元素替换堆顶),故堆中K个元素是已扫描的元素里最大的K个;堆顶即为第K大的数。
class Solution {
public int findKthLargest(int[] nums, int k) {//最小堆
PriorityQueue<Integer> data_queue = new PriorityQueue<Integer>();//优先级队列
for(int i = 0;i < nums.length;i++){
if(data_queue.size() < k){
data_queue.offer(nums[i]);
}else if(data_queue.peek()<nums[i]){//如果堆顶比新元素小,弹出堆顶,offer进入新元素
data_queue.poll();
data_queue.offer(nums[i]);
}
}
return data_queue.peek();
}
}
7、LeetCode295数据流的中位数
在数据流中,数据会不断涌入结构中,那么也就面临着需要多次动态调整以获得中位数。 因此实现的数据结构需要既需要快速找到中位数,也需要做到快速调整。首先能想到就是二叉搜索树,在平衡状态下,树顶必定是中间数,然后再根据长度的奇偶性决定是否取两个数。此方法效率高,但是手动编写较费时费力。根据只需获得中间数的想法,可以将数据分为左右两边,一边以最大堆的形式实现,可以快速获得左侧最大数, 另一边则以最小堆的形式实现。其中需要注意的一点就是左右侧数据的长度差不能超过1。 这种实现方式的效率与AVL平衡二叉搜索树的效率相近,但编写更快。
思路:动态维护一个最大堆与一个最小堆,最大堆存储一半数据,最小堆存储一半数据, 维持最大堆的堆顶比最小堆的堆顶小。
class MedianFinder {
PriorityQueue<Integer> small_queue;//最小堆
PriorityQueue<Integer> big_queue;//最大堆
/** initialize your data structure here. */
public MedianFinder() {
small_queue = new PriorityQueue<Integer>();
big_queue = new PriorityQueue<Integer>((a, b) -> b - a);
}
public void addNum(int num) {
if(big_queue.isEmpty()){
big_queue.offer(num);
return;
}
if(big_queue.size()==small_queue.size()){
if(num<big_queue.peek()){
big_queue.offer(num);
}else{
small_queue.offer(num);
}
}else if(big_queue.size()>small_queue.size()){
if(num>big_queue.peek()){
small_queue.offer(num);
}else{
small_queue.offer(big_queue.peek());
big_queue.poll();
big_queue.offer(num);
}
}else{
if(num<small_queue.peek()){
big_queue.offer(num);
}else{
big_queue.offer(small_queue.peek());
small_queue.poll();
small_queue.offer(num);
}
}
}
public double findMedian() {
if (big_queue.size() == small_queue.size()){
return (big_queue.peek() + small_queue.peek()) / 2.0;
}
else if(big_queue.size() > small_queue.size()){
return big_queue.peek();
}
return small_queue.peek();
}
}
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder obj = new MedianFinder();
* obj.addNum(num);
* double param_2 = obj.findMedian();
*/