栈和队列(数据结构与算法java代码)

第一题 用栈实现队列

点击跳转力扣

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(pushpoppeekempty):

实现 MyQueue 类:

  • void push(int x) 将元素 x 推到队列的末尾
  • int pop() 从队列的开头移除并返回元素
  • int peek() 返回队列开头的元素
  • boolean empty() 如果队列为空,返回 true ;否则,返回 false 

说明:

  • 你 只能 使用标准的栈操作 —— 也就是只有 push to toppeek/pop from topsize, 和 is empty 操作是合法的。
  • 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。

 双栈

一个输入栈,一个输出栈,因为栈的特点是先入后出(FILO),队列是先入先出(FIFO),那么使用两个栈,一个栈用于“输入”,一个栈用于“输出”,输出当要出队或访问队首时输入栈弹出元素压入输入栈。

class MyQueue {
    Stack<Integer> stackIn;
    Stack<Integer> stackOut;

    public MyQueue() {
        stackIn = new Stack<>(); // 输入栈
        stackOut = new Stack<>(); // 输出栈        
    }
    
    public void push(int x) {
        stackIn.push(x);
    }
    
    public int pop() {
        dumpstackIn();
        return stackOut.pop();        
    }
    
    public int peek() {
        dumpstackIn();
        return stackOut.peek();
    }
    
    public boolean empty() {
        return stackIn.isEmpty() && stackOut.isEmpty();
    }

    // 如果stackOut为空,那么将stackIn中的元素全部放到stackOut中
    private void dumpstackIn(){
        if (!stackOut.isEmpty()) 
            return; 
        while (!stackIn.isEmpty()){
            stackOut.push(stackIn.pop());
        }
    }
}

/**
 * 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();
 */

第二题 用队列实现栈

点击跳转力扣

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(pushtoppop 和 empty)。

实现 MyStack 类:

  • void push(int x) 将元素 x 压入栈顶。
  • int pop() 移除并返回栈顶元素。
  • int top() 返回栈顶元素。
  • boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。

双队列

添加元素先加到que2后面,然后把que1的元素都备份到que2,再交换que2和que1,这时第一个元素位于que1队首,相当于栈顶元素,通过que2添加元素,que1删除元素或者读取元素,从而实现栈的功能。

class MyStack {

    Queue<Integer> queue1; // 和栈中保持一样元素的队列
    Queue<Integer> queue2; // 辅助队列

    /** Initialize your data structure here. */
    public MyStack() {
        queue1 = new LinkedList<>();
        queue2 = new LinkedList<>();
    }
    
    /** Push element x onto stack. */
    public void push(int x) {
        queue2.offer(x); // 先放在辅助队列中
        while (!queue1.isEmpty()){
            queue2.offer(queue1.poll());
        }
        Queue<Integer> queueTemp;
        queueTemp = queue1;
        queue1 = queue2;
        queue2 = queueTemp; // 最后交换queue1和queue2,将元素都放到queue1中
    }
    
    /** Removes the element on top of the stack and returns that element. */
    public int pop() {
        return queue1.poll(); // 因为queue1中的元素和栈中的保持一致,所以这个和下面两个的操作只看queue1即可
    }
    
    /** Get the top element. */
    public int top() {
        return queue1.peek();
    }
    
    /** Returns whether the stack is empty. */
    public boolean empty() {
        return queue1.isEmpty();
    }
}

单队列

和上面双队列的逻辑相同,但是是在自身上操作,把自己的队首加到队尾,从而让要加入的元素放到队首,实现栈的“后入先出”。

class MyStack {

    Queue<Integer> queue;

    public MyStack() {
        queue = new LinkedList<>();
    }

    //每 offer 一个数(A)进来,都重新排列,把这个数(A)放到队列的队首
    public void push(int x) {
        queue.offer(x);
        int size = queue.size();
        //移动除了 A 的其它数
        while (size-- > 1)
            queue.offer(queue.poll());
    }

    public int pop() {
        return queue.poll();
    }

    public int top() {
        return queue.peek();
    }

    public boolean empty() {
        return queue.isEmpty();
    }
}

双向队列

因为题目要求,不能通过双向队列中栈的功能去实现栈。

Deque 接口继承了 Queue 接口,所以 Queue 中的 add、poll、peek等效于 Deque 中的 addLast、pollFirst、peekFirst。

因此,使用Deque和Qeque没啥差别


第三题 队列的实现

链队列

基于链表实现的队列

public class LinkedListQuque {

    public class ListNode { // 链表节点
        int val;
        ListNode next;
        ListNode(){}
        ListNode(int val){
            this.val = val;
        }
    }

    private ListNode front, rear; // 头,尾
    private int queSize = 0; // 长度

    public LinkedListQuque() { // 构造器
        front = null;
        rear = null;
    }
    
    public void offer(int val) { // 入队
        ListNode node = new ListNode(val);
        if (front == null) {
            front = node;
            rear = node;
        } else {
            rear.next = node;
            rear = rear.next;
        }
        queSize++;
    }
    
    public int poll() { // 出队
        int n;
        if (front == null) {
            throw new IndexOutOfBoundsException();
        } else {
            n = front.val;
            front = front.next;
            queSize--;
        }
        return n;
    }
}

顺序队列

基于环形数组实现的队列

因为数组中删除首元素的时间效率太低,所以通过“取余操作”实现环形数组。

要想扩容,可以添加动态数组。

package com.practiceA;

public class ArrayQuque {

    private int[] nums;
    private int front; // 指向队首
    private int queSize;


    public ArrayQuque(int capacity) { // 构造器
        nums = new int[capacity];
        front = queSize = 0;
    }


    public void offer(int val) { // 入队
        if (queSize == nums.length) {
            System.out.println("满了");
            return;
        } 
        // 取余是为了后面满了能回到头部
        int rear = (front + queSize) % nums.length;
        queSize++;
    }

    public int poll(boolean isFront) { // 出队
        if (queSize == 0) {
            throw new IndexOutOfBoundsException();
        }
        int val = nums[front];
        // 为了后面满时能回到头部
        front = (front + 1) % nums.length;
        queSize--;
        return val;
    }
}

链式双向队列

基于双向链表实现的双向队列

public class LinkedListDuque {

    public class ListNode { // 链表节点
        int val;
        ListNode next, prev;
        ListNode(){}
        ListNode(int val){
            this.val = val;
        }
    }

    private ListNode front, rear; // 头,尾
    private int queSize = 0; // 长度

    public LinkedListDuque() { // 构造器
        front.next = rear;
        rear.prev = front;
    }

    public void addFirst(int num) {
        push(num, true);
    }

    public void addLast(int num) {
        push(num, false);
    }

    public void removeFirst(int num) {
        pop(true);
    }

    public void removeLast(int num) {
        pop(false);
    }

    private void push(int val, boolean isFront) { // 入队
        ListNode node = new ListNode(val);
        if (isEmpty()) {
            front = node;
            rear = node;
        } else if (isFront) { // 队首入队
            node.next = front;
            front.prev = node;
            front = node;
        } else { // 队尾入队
            rear.next = node;
            node.prev = rear;
            rear = node;
        }
        queSize++;
    }

    private int pop(boolean isFront) { // 出队
        int n;
        if (isEmpty()) {
            throw new IndexOutOfBoundsException();
        } else if (isFront){ // 队首出队
            n = front.val;
            front = front.next;
            queSize--;
        } else {
            n = rear.val;
            rear = rear.prev;
            queSize--;
        }
        return n;
    }

    public boolean isEmpty() {
        return queSize == 0;
    }
}

数组双向队列

基于环形数组实现

public class ArrayDuque {

    private int[] nums;
    private int front; // 指向队首
    private int queSize;


    public ArrayDuque(int capacity) { // 构造器
        nums = new int[capacity];
        front = queSize = 0;
    }

    // 计算索引
    private int index(int i) {
        return (i + nums.length) % nums.length;
    }

    public void offerFirst(int val) { // 队首入队
        if (queSize == nums.length) {
            System.out.println("满了");
            return;
        }
        // 队首的前一位
        front = index(front - 1);
        nums[front] = val;
        queSize++;
    }

    public void offerLast(int val) { // 队尾入队
        if (queSize == nums.length) {
            System.out.println("满了");
            return;
        }
        // 队尾的后一位
        int rear = index(front + queSize);
        nums[rear] = val;
        queSize++;
    }

    public int pollFirst(boolean isFront) { // 队首出队
        if (queSize == 0) {
            throw new IndexOutOfBoundsException();
        }
        int val = nums[front];
        // 队首后一位
        front = index(front + 1);
        queSize--;
        return val;
    }

    public int pollLast(boolean isFront) { // 队尾出队
        if (queSize == 0) {
            throw new IndexOutOfBoundsException();
        }
        // 队尾
        int rear = index(front + queSize - 1);
        int val = nums[rear];
        // 队尾会被覆盖,当他不存在
        queSize--;
        return val;
    }
}

第四题 循环队列的实现

点击跳转力扣

设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。

循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。

你的实现应该支持如下操作:

  • MyCircularQueue(k): 构造器,设置队列长度为 k 。
  • Front: 从队首获取元素。如果队列为空,返回 -1 。
  • Rear: 获取队尾元素。如果队列为空,返回 -1 。
  • enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
  • deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
  • isEmpty(): 检查循环队列是否为空。
  • isFull(): 检查循环队列是否已满。

链表

class MyCircularQueue {
    private ListNode front, rear;
    private int queSize = 0; // 长度
    private int capacity = 0; // 容量

    // 构造器,设置队列长度为 k
    public MyCircularQueue(int k) {
        // 循环链表
        front = null;
        rear = null;
        capacity = k;
    }

    // 向循环队列插入一个元素。如果成功插入则返回真    
    public boolean enQueue(int value) {
        if (isFull()) {
            return false;
        }
        ListNode node = new ListNode(value);
        if (front == null) {
            rear = front = node;
        } else {
            // 队尾插
            rear.next = node;
            rear = node;
            rear.next = front;
        }
        queSize++;
        return true;
    }
    
    // 从循环队列中删除一个元素。如果成功删除则返回真
    public boolean deQueue() {
        if (isEmpty()) {
            return false;
        }
        // 当被删到只有一个时只写else里面的会出错
        if (queSize == 1) {
            front = rear = null;
        } else {
            // 队头删
            front = front.next;
            rear.next = front;
        }
        queSize--;
        return true;
    }
    
    // 从队首获取元素。如果队列为空,返回 -1 
    public int Front() {
        if (isEmpty()) {
            return -1;
        } else {
            return front.val;
        }
    }
    
    //  获取队尾元素。如果队列为空,返回 -1
    public int Rear() {
        if (isEmpty()) {
            return -1;
        } else {
            return rear.val;
        }
    }
    
    // 检查循环队列是否为空
    public boolean isEmpty() {
        return queSize == 0;
    }

    // 检查循环队列是否已满 
    public boolean isFull() {
        return queSize == capacity;
    }
}

/**
 * Your MyCircularQueue object will be instantiated and called as such:
 * MyCircularQueue obj = new MyCircularQueue(k);
 * boolean param_1 = obj.enQueue(value);
 * boolean param_2 = obj.deQueue();
 * int param_3 = obj.Front();
 * int param_4 = obj.Rear();
 * boolean param_5 = obj.isEmpty();
 * boolean param_6 = obj.isFull();
 */

环形数组

class MyCircularQueue {
    private int[] nums;
    private int front, rear;
    private int queSize = 0; // 长度
    private int capacity = 0; // 容量

    // 构造器,设置队列长度为 k
    public MyCircularQueue(int k) {
        nums = new int[k];
        capacity = k;
        front = rear = 0;
    }

    // 计算环形数组索引
    private int index(int i) {
        return (i + capacity) % capacity;
    }

    // 向循环队列插入一个元素。如果成功插入则返回真    
    public boolean enQueue(int value) {
        if (isFull()) {
            return false;
        }
        rear = index(front + queSize);
        nums[rear] = value;
        queSize++;
        return true;
    }
    
    // 从循环队列中删除一个元素。如果成功删除则返回真
    public boolean deQueue() {
        if (isEmpty()) {
            return false;
        }
        front = index(front + 1);
        queSize--;
        return true;
    }
    
    // 从队首获取元素。如果队列为空,返回 -1 
    public int Front() {
        if (isEmpty()) {
            return -1;
        } else {
            return nums[front];
        }
    }
    
    //  获取队尾元素。如果队列为空,返回 -1
    public int Rear() {
        if (isEmpty()) {
            return -1;
        } else {
            return nums[rear];
        }
    }
    
    // 检查循环队列是否为空
    public boolean isEmpty() {
        return queSize == 0;
    }

    // 检查循环队列是否已满 
    public boolean isFull() {
        return queSize == capacity;
    }
}

第四题 有效的括号

点击跳转力扣

给定一个只包括 '('')''{''}''['']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 每个右括号都有一个对应的相同类型的左括号。

解一

class Solution {
    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<>();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (stack.isEmpty()) {
                stack.push(s.charAt(i));
                continue;
            }
            if (stack.peek() == ')' || stack.peek() == ']' || stack.peek() == '}') {
                return false;
            } else if (stack.peek() == '(') {
                if (c == ')') {
                    stack.pop();
                } else {
                    stack.push(c);
                }
            } else if (stack.peek() == '[') {
                if (c == ']') {
                    stack.pop();
                } else {
                    stack.push(c);
                }
            } else if (stack.peek() == '{') {
                if (c == '}') {
                    stack.pop();
                } else {
                    stack.push(c);
                }
            }
        }
        return stack.isEmpty();
    }
}

解二

class Solution {
    public boolean isValid(String s) {
        int n = s.length();
        // 不对称,直接返回
        if (n % 2 == 1) {
            return false;
        }
        // 使用匿名内部类的方式初始化了一个HashMap实例,并添加了三个键值对
        Map<Character, Character> pairs = new HashMap<Character, Character>() {{
            put(')', '(');
            put(']', '[');
            put('}', '{');
        }};
        Stack<Character> stack = new Stack<Character>();
        for (int i = 0; i < n; i++) {
            char ch = s.charAt(i);
            // 遇到右括号出栈
            if (pairs.containsKey(ch)) {
                if (stack.isEmpty() || stack.peek() != pairs.get(ch)) {
                    return false;
                }
                stack.pop();
            } else { // 左括号入栈
                stack.push(ch);
            }
        }
        return stack.isEmpty();
    }
}

解三

class Solution {
    public boolean isValid(String s) {
        if (s.isEmpty())
            return true;
        Stack<Character> stack = new Stack<>();
        for (char c : s.toCharArray()) {
            if (c == '(')
                stack.push(')');
            else if (c == '{') 
                stack.push('}');
            else if (c == '[') 
                stack.push(']');
            else if (stack.empty() || c != stack.pop()) {
                return false;
            }
        }
        return stack.isEmpty();
    }
}

第五题 每日温度

点击跳转力扣

给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。

class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        int length = temperatures.length;
        int[] answer = new int[length];
        Stack<Integer> stack = new Stack<>();
        for (int i = 0; i < length; i++) {
            // 当前温度比栈顶温度高,进入循环
            while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {
                // 找到栈顶温度对应的更高温度
                int prevIndex = stack.pop();
                answer[prevIndex] = i - prevIndex;
            }
            // 当前温度比栈顶温度低,找不到更高温度
            // 要么更高温度在后面,要不找不到更高温度,默认值为0
            // 当前栈内温度从栈底到栈顶依次降低,所以当找到栈顶的更高温度时,栈可以遍历完毕
            stack.push(i);
        }
        return answer;
    }
}

非栈

速度比用栈快

class Solution {
   public int[] dailyTemperatures(int[] temperatures) {
       int n = temperatures.length;
       int[] ans = new int[n];
       ans[n - 1] = 0;
       for (int i = n - 2; i >= 0; i--) {
           int j = i + 1;
           // ans[j] 即temperatures[j]对应更高温度比temperatures[j]多的天数
           // ans[j] 等于0时,说明temperatures[j]后面找不到更高温度了
           // temperatures[i]比temperatures[j]高,那就得找更高的temperatures[ans[j]]
           while (temperatures[j] <= temperatures[i] && ans[j] != 0) {
               j += ans[j];
           }
           // 找到更高温度
           if (temperatures[j] > temperatures[i]) {
               ans[i] = j - i;
           }
           // 找不到更高温度,即temperatures[j] <= temperatures[i] && ans[j] == 0
           // 数组默认值0
       }
       return ans;
   }
}

错误解

没用栈,超时了,有一个测试案例是前面一堆99,最后写个100,得逆序才行

class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        int[] answer = new int[temperatures.length];

        for(int i = 0; i < temperatures.length; i++) {
            if (i == temperatures.length - 1) {
                answer[i] = 0;
                break;    
            }    
            int count = 1; // 记录天数  
            int j = i + 1;
            if (temperatures[i] <= temperatures[j]) {
                while (j < temperatures.length) {
                    count++;
                    if (temperatures[i] > temperatures[j]) {
                        answer[i] = count;
                        break;
                    }   
                    j++;
                }
            } else if (temperatures[i] > temperatures[j]) {
                answer[i] = count;
            }
        }
        return answer;
    }
}

第六题 删除字符串中的所有重复项

点击跳转力扣

给出由小写字母组成的字符串 S重复项删除操作会选择两个相邻且相同的字母,并删除它们。

在 S 上反复执行重复项删除操作,直到无法继续删除。

在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。

class Solution {
    public String removeDuplicates(String s) {
        Stack<Character> stack = new Stack<>();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            // i == 0时直接入栈
            // 不这样写报错访问空栈
            if (i == 0 || stack.isEmpty()) {
                stack.push(c);
            } else if (stack.peek() == c) {
                stack.pop();
            } else {
                stack.push(c);
            }
        }
        StringBuilder sb = new StringBuilder();
        for (Character c : stack) {
            sb.append(c);
        }
        return sb.toString();
        // 直接写输出的是"[c, a]",而题目要求"ca"
        // return stack.toString();
    }
}

双指针法

图片结合代码

a6203407c77c45d28d943e03fcc26f8e.png

class Solution {
    public String removeDuplicates(String s) {
        char[] chars = s.toCharArray();
        int slow = 0, fast = 0;
        while(fast < s.length()){
            chars[slow] = chars[fast];
            // 跳过第一位
            // 如果chars[slow]等于它的前一位,让它等于slow[fast]
            if( slow > 0 && chars[slow] == chars[slow - 1]){
                slow--;
            }else{
                slow++;
            }
            fast++;
        }
        return new String(chars).substring(0,slow);
    }
}

第七题 逆波兰表达式求和

点击跳转力扣

给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。

请你计算该表达式。返回一个表示表达式值的整数。

两个答案一样

class Solution {
    public int evalRPN(String[] tokens) {
        Stack<Integer> stack = new Stack<>();
        for (int i = 0; i < tokens.length; i++) {
            String token = tokens[i];
            if (token.equals("+")) {
                int num2 = stack.pop();
                int num1 = stack.pop();
                stack.push(num1 + num2);
            } else if (token.equals("-")) {
                int num2 = stack.pop();
                int num1 = stack.pop();
                stack.push(num1 - num2);
            } else if (token.equals("*")) {
                int num2 = stack.pop();
                int num1 = stack.pop();
                stack.push(num1 * num2);
            } else if (token.equals("/")) {
                int num2 = stack.pop();
                int num1 = stack.pop();
                stack.push(num1 / num2);
            } else {
                stack.push(Integer.parseInt(token));
            }  
        }
        return stack.peek();
    }
}
class Solution {
    public int evalRPN(String[] tokens) {
        Stack<Integer> stack = new Stack<>();
        for (int i = 0; i < tokens.length; i++) {
            String token = tokens[i];
            if (isNumber(token)) {
                stack.push(Integer.parseInt(token));
            } else {
                int num2 = stack.pop();
                int num1 = stack.pop();
                switch (token) {
                    case "+" -> {
                        stack.push(num1 + num2);
                    }
                    case "-" -> {
                        stack.push(num1 - num2);
                    }
                    case "*" -> {
                        stack.push(num1 * num2);
                    }
                    case "/" -> {
                        stack.push(num1 / num2);
                    }
                    default ->{}
                }
            }
        }
        return stack.peek();
    }
    
    public boolean isNumber(String token) {
        return !("+".equals(token) || "-".equals(token) || "*".equals(token) || "/".equals(token));
    }
}

第九题 滑动窗口最大值

点击跳转力扣

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值 

自定义数组(单调队列)

class MyQueue {
    Deque<Integer> deque = new LinkedList<>();
    // 记录要弹出元素是否等于队列出口的元素
    // 即,如果要弹出的应该当前的最大值,那么就弹出,否则不弹出
    void poll (int val) {
        if (!deque.isEmpty() && val == deque.peek())
            deque.poll();
    }
    // 添加元素时,弹出从后往前所有队尾元素比val小的数,再加入val
    // 这样就保证了最大值始终在队首,队列内元素依次减小
    void add (int val) {
        while (!deque.isEmpty() && val > deque.getLast())
            deque.removeLast();
        deque.add(val);
    }
    // 队首,即最大值
    int peek() {
        return deque.peek();
    }
}

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        // 先排除特殊情况
        if (nums.length == 1) 
            return nums;
        // 返回的数组
        int[] res = new int[nums.length - k + 1];
        // 记录最大值索引
        int num = 0;
        MyQueue que = new MyQueue();
        // 前k个元素
        for (int i = 0; i < k; i++)
            que.add(nums[i]);
        res[num++] = que.peek();
        // 后面的元素
        for (int i = k; i < nums.length; i++) {
            que.poll(nums[i - k]); // 窗口最左侧
            que.add(nums[i]);
            res[num++] = que.peek();
        }
        return res;
    }
}

双向队列(单调队列)

和上个答案类似,但是代码更简洁,运行更快。

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        ArrayDeque<Integer> deque = new ArrayDeque<>();
        int[] res = new int[nums.length - k + 1];
        int index = 0;
        for (int i = 0; i < nums.length; i++) {
            // 队列头结点在窗口内,否则弹出
            while (!deque.isEmpty() && deque.peek() < i - k + 1) 
                deque.poll();
            // 单调,即保证每次放进去的数字都比末尾大
            while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i])
                deque.pollLast();
            deque.offer(i);
            // 窗口每滑一格,都将队首所对应元素放入结果中
            if (i >= k - 1) 
                res[index++] = nums[deque.peek()];
        }
        return res;
    }
}

优先队列(堆)

还没学。


第十题 前k个高频元素

点击跳转力扣

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

大顶堆

优先级队列  -> 披着队列外衣的堆

-> 一颗完全二叉树,树中每个节点的值都不大于(或不小于)其左右孩子的值

大顶堆 (堆头最大)

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        Map<Integer, Integer> map = new HashMap<>();
        // key -> 元素值 value -> 出现次数
        for (int num : nums) 
            map.put(num, map.getOrDefault(num, 0) + 1);
        // 创建了优先级队列,其中元素是整数数组
        // 其比较器(Comparator)是根据数组的第二个元素(即value)降序排序
        PriorityQueue<int[]> pq = new PriorityQueue<>((p1, p2) -> p2[1] - p1[1]);
        // 将map中的元素存入优先级队列
        for (Map.Entry<Integer, Integer> entry : map.entrySet())
            // 大顶堆排序
            pq.add(new int[] {entry.getKey(), entry.getValue()});
        int[] ans = new int[k];
        for (int i = 0; i < k; i++) 
            ans[i] = pq.poll()[0];
        return ans;
    }
}

小顶堆

小顶堆(堆头最小)

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        Map<Integer, Integer> map = new HashMap<>();
        // key -> 元素值 value -> 出现次数
        for (int num : nums) 
            map.put(num, map.getOrDefault(num, 0) + 1);
        // 创建了优先级队列,其中元素是整数数组
        // 其比较器(Comparator)是根据数组的第二个元素(即value)降序排序
        PriorityQueue<int[]> pq = new PriorityQueue<>((p1, p2) -> p1[1] - p2[1]);
        // 将map中的元素存入优先级队列
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            // 小顶堆排序
            // 队列长度不足k位,直接加
            if (pq.size() < k) 
                pq.add(new int[] {entry.getKey(), entry.getValue()});
            else // 长度到了k
                // 如果当前元素出现次数比队首元素(即最少次数的元素)多
                // 那么直接弹出队首,将该元素插入队列
                if (entry.getValue() > pq.peek()[1]) {
                    pq.poll();
                    pq.add(new int[] {entry.getKey(), entry.getValue()});
                }
                // 没影响,那就跳过
        }   
        int[] ans = new int[k];
        for (int i = k - 1; i >= 0; i--) 
            ans[i] = pq.poll()[0];
        return ans;
    }
}

第十一题 根据身高重建队列

点击跳转力扣

假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。

请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue ,其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。

思路:先按身高从大到小排,然后按k依次往前插

排序

class Solution {
    public int[][] reconstructQueue(int[][] people) {
        // 排序规则是先比较每个子数组的第一个元素
        // 不相等降序排列;相等,则比较第二个元素,按照升序排列
        Arrays.sort(people, new Comparator<int[]>() {
            public int compare (int[] p1, int[] p2) {
                if (p1[0] != p2[0])
                    return p2[0] - p1[0];
                else
                    return p1[1] - p2[1];
            }
        });
        List<int[]> ans = new ArrayList<int[]>();
        for (int[] p : people) 
            ans.add(p[1], p);
        return ans.toArray(new int[ans.size()][]);
    }
}

简化

class Solution {
    public int[][] reconstructQueue(int[][] people) {
        // 排序规则是先比较每个子数组的第一个元素
        // 不相等降序排列;相等,则比较第二个元素,按照升序排列
        Arrays.sort(people, (x, y) -> x[0] == y[0] ? x[1] - y[1] : y[0] - x[0]);
        List<int[]> ans = new ArrayList<int[]>();
        for (int[] p : people) 
            ans.add(p[1], p);
        return ans.toArray(new int[0][0]);
    }
}

目录

第一题 用栈实现队列

 双栈

第二题 用队列实现栈

双队列

单队列

双向队列

第三题 队列的实现

链队列

顺序队列

链式双向队列

数组双向队列

第四题 有效的括号

解一

解二

解三

第五题 每日温度

非栈

错误解

第六题 


  • 26
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值