LeetBook-队列&栈

49 阅读12分钟

队列&栈

队列 & 栈 - LeetBook - 力扣(LeetCode)全球极客挚爱的技术成长平台

设计循环队列

解法1

参考书本

class MyCircularQueue {
    
    private int[] data;
    private int head;
    private int tail;
    private int size;
​
    /** Initialize your data structure here. Set the size of the queue to be k. */
    public MyCircularQueue(int k) {
        data = new int[k];
        head = -1;
        tail = -1;
        size = k;
    }
    
    /** Insert an element into the circular queue. Return true if the operation is successful. */
    public boolean enQueue(int value) {
        if (isFull() == true) {
            return false;
        }
        if (isEmpty() == true) {
            head = 0;
        }
        tail = (tail + 1) % size;
        data[tail] = value;
        return true;
    }
    
    /** Delete an element from the circular queue. Return true if the operation is successful. */
    public boolean deQueue() {
        if (isEmpty() == true) {
            return false;
        }
        if (head == tail) {
            head = -1;
            tail = -1;
            return true;
        }
        head = (head + 1) % size;
        return true;
    }
    
    /** Get the front item from the queue. */
    public int Front() {
        if (isEmpty() == true) {
            return -1;
        }
        return data[head];
    }
    
    /** Get the last item from the queue. */
    public int Rear() {
        if (isEmpty() == true) {
            return -1;
        }
        return data[tail];
    }
    
    /** Checks whether the circular queue is empty or not. */
    public boolean isEmpty() {
        return head == -1;
    }
    
    /** Checks whether the circular queue is full or not. */
    public boolean isFull() {
        return ((tail + 1) % size) == head;
    }
}
​
/**
 * 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();
 */

岛屿数量

解法1

递归

  1. 遍历网格

  2. 如果碰到1就count自加1并且进入递归:

    • 按照上右下左的方向判断是否为1,如果为1再次调用自己
  3. 返回count计数

class Solution {
    public int numIslands(char[][] grid) {
        int count = 0;
        for (int i = 0; i < grid.length; i++) {
            for (int j = 0; j < grid[0].length; j++) {
                if (grid[i][j] == '1') {
                    remove(grid, i, j);
                    count++;
                }
            }
        }
        return count;
    }
    public void remove(char[][] grid, int i, int j) {
        if (i > 0 && grid[i - 1][j] == '1') {//上
            grid[i - 1][j] = 0;
            remove(grid, i - 1, j);
        }
        if (j < grid[0].length - 1 && grid[i][j + 1] == '1') {//右
            grid[i][j + 1] = 0;
            remove(grid, i, j + 1);
        }
        if (i < grid.length - 1 && grid[i + 1][j] == '1') {//下
            grid[i + 1][j] = 0;
            remove(grid, i + 1, j);
        }
        if (j > 0 && grid[i][j - 1] == '1') {//左
            grid[i][j - 1] = 0;
            remove(grid, i, j - 1);
        }
    }
}

打开转盘锁

解法1

双向bfs

  1. 将死亡数字放到一个set里面

  2. 将起始0000和最后的密码分别放到两个队列里面

  3. new两个HashMap分别存放正反两个方向的数字和旋转次数的map

  4. 当两个队列都不为空时进行循环(如果有队列为空,那就说明找穷尽了没有找到密码,返回-1)

  5. 在size较小的队列里取出队头进行更新:

    1. 对于队头元素的每一位都有+1-1两种情况一共8种
    2. 如果某种情况在死亡数字里面,或者在本队列的map里,则忽略此情况
    3. 如果某种情况在另一个队列的map里,则返回(另一个队列map的key对应的value+本队列map的key对应的value + 1)即为最小旋转次数
    4. 否则继续进行循环
    5. 如果遍历完所有情况,没有返回,则返回-1
  6. 对于更新的返回值如果不为-1则返回更新的返回值

  7. 如果循环结束仍没有返回,则返回-1(没有解)

class Solution {
    String start,end;
    Set<String> dead = new HashSet<>();
    public int openLock(String[] deadends, String target) {
        start = "0000";
        end = target;
        for (String de: deadends) {
            dead.add(de);
        }
        if (start.equals(target)) {
            return 0;
        }
        if (dead.contains(start)) {
            return -1;
        }
        Deque<String> queue_head = new ArrayDeque<>();
        Deque<String> queue_tail = new ArrayDeque<>();
        Map<String, Integer> map_head = new HashMap<>();
        Map<String, Integer> map_tail = new HashMap<>();
        queue_head.addLast(start);
        queue_tail.addLast(end);
        map_head.put(start, 0);
        map_tail.put(end, 0);
        while(!queue_head.isEmpty() && !queue_tail.isEmpty()) {
            int result = -1;
            if (queue_head.size() <= queue_tail.size()) {
                result = update(queue_head, map_head, map_tail);
            } else {
                result = update(queue_tail, map_tail, map_head);
            }
            if (result != -1) {
                return result;
            }
        }
        return -1;
    }
    public int update(Deque<String> queue, Map<String, Integer> this_map, Map<String, Integer> other_map) {
        String firstStr = queue.pollFirst();
        char[] first = firstStr.toCharArray();
        int step = this_map.get(firstStr);
        for (int i = 0; i < 4; i++) {
            for (int j = -1; j <= 1; j += 2) {
                int origin = first[i] - '0';
                int next = (origin + j) % 10;
                if (next == -1) next = 9;
​
                char[] clone = first.clone();
                clone[i] = (char)(next + '0');
                String back = String.valueOf(clone);
                /*
                if (first[i] == '0' && j == -1) {
                    first[i] = '9';
                } else if (first[i] == '9' && j == 1) {
                    first[i] = '0';
                } else {
                    first[i] = (char)(first[i] + j);
                }
                String back = String.valueOf(first);
                */
                if (dead.contains(back)) {
                    continue;
                }
                if (this_map.containsKey(back)) {
                    continue;
                }
                if (other_map.containsKey(back)) {
                    return step + 1 + other_map.get(back);
                } else {
                    queue.addLast(back);
                    this_map.put(back, step + 1);
                }
            }
        }
        return -1;
    }    
}

完全平方数

解法1

拉格朗日四平方和定理

拉格朗日四平方和定理说明任何一个数,都可以由小于等于4个的完全平方数相加得到。

n=(8b+7)4nn是由4个完全平方数得到否则n则为13个完全平方数组成由一个或者两个完全平方数组成的n很容易判断剩下的即为由3个完全平方数组成的数\begin{align} &n=(8b+7)*4^n\quad n是由4个完全平方数得到\\ &否则n则为1到3个完全平方数组成\\ &由一个或者两个完全平方数组成的n很容易判断\\ &剩下的即为由3个完全平方数组成的数 \end{align}

优化:

可以采用位运算来达到更快的速度

class Solution {
    public int numSquares(int n) {
        int number = n;
        if (two(n)) {
            return 1;
        }
        while (n % 4 == 0) {
            n = n / 4;
        }
        if (n % 8 == 7) {
            return 4;
        }
        for (int i = 1; i <= (int) (Math.sqrt(number)); i++) {
            if (two(number - i * i)) {
                return 2;
            }
        }
        return 3;
    }
    public boolean two(int n) {
        int half = (int) (Math.sqrt(n));
        return n == half * half;
    }
}

最小栈

解法1

参考书本

class MinStack {
    private Stack<StackNode> stack = new Stack<>();
​
    //压栈
    public void push(int x) {
        if (empty()) {
            stack.push(new StackNode(x, x));
        } else {
            stack.push(new StackNode(x, Math.min(x, getMin())));
        }
    }
​
    //出栈
    public void pop() {
        if (empty())
            throw new IllegalStateException("栈为空……");
        stack.pop();
    }
​
    public int top() {
        if (empty())
            throw new IllegalStateException("栈为空……");
        return stack.peek().val;
    }
​
    public int getMin() {
        if (empty())
            throw new IllegalStateException("栈为空……");
        return stack.peek().min;
    }
​
    //判断栈是否为空
    private boolean empty() {
        return stack.isEmpty();
    }
}
​
class StackNode {
    public int val;
    public int min;
​
    public StackNode(int val, int min) {
        this.val = val;
        this.min = min;
    }
}
​
/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack obj = new MinStack();
 * obj.push(val);
 * obj.pop();
 * int param_3 = obj.top();
 * int param_4 = obj.getMin();
 */

有效的括号

解法1

  1. 将字符串转为char数组
  2. 遍历数组
  3. 左括号进栈,右括号出栈
  4. 如果碰到不匹配或者该出栈的适合栈为空,返回false
  5. 循环结束后如果栈为空返回true,否则返回false
class Solution {
    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<>();
        char[] ss = s.toCharArray();
        for (int i = 0; i < s.length(); i++) {
            if (ss[i] == '('|| ss[i] == '[' || ss[i] == '{') {
                stack.push(ss[i]);
            }
            if (ss[i] == ')') {
                if (stack.isEmpty() || stack.pop() != '('){
                    return false;
                }
            }
            if (ss[i] == ']') {
                if (stack.isEmpty() || stack.pop() != '['){
                    return false;
                }
            }
            if (ss[i] == '}') {
                if (stack.isEmpty() || stack.pop() != '{'){
                    return false;
                }
            }
        }
        if (stack.isEmpty()) {
            return true;
        }
        return false;
    }
}

每日温度

解法1

  1. new一个Stack
  2. 遍历数组
  3. 开启一个新循环,如果当前值大于栈顶的下标对应的值,给result的栈顶下标赋值为当前下标减去栈顶下标,并将栈顶出栈
  4. 新循环结束后,将此元素的下标入栈
  5. 循环结束后返回result
class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        Stack<Integer> stack = new Stack<>();
        int[] result = new int[temperatures.length];
        for (int i = 0;i < temperatures.length; i++) {
            while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {
                int index = stack.pop();
                result[index] = i - index;
            }
            stack.push(i);
        }
        return result;
    }
}

解法2

逆向遍历

  1. 从后往前进行遍历查找
  2. 对元素向后遍历,步长为已经记录的某个元素的result值(如果某个元素比当前元素小,那么那个元素的下一个更高温才有可能大于此元素)
  3. 如果碰到某个元素小于等于当前元素并且没有下一个更高温,则可以直接赋0
  4. 如果遇到了下一个更高温,则可以赋值后跳出
  5. 返回结果
class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        int[] result = new int[temperatures.length];
        for (int i = temperatures.length - 1; i >= 0; i--) {
            int j = i + 1;
            while (j < temperatures.length) {
                if (temperatures[j] > temperatures[i]) {
                    result[i] = j - i;
                    break;
                } else if (result[j] == 0) {
                    result[i] = 0;
                    break;
                } else {
                    j += result[j];
                }
            }
        }
        return result;
    }
}

逆波兰表达式求值

解法1

  1. 遍历数组
  2. 如果碰到符号则取出两个栈顶数字进行运算
  3. 如果碰到数字则入栈
  4. 最后栈顶则为结果,返回栈顶
class Solution {
    public int evalRPN(String[] tokens) {
        List<String> list = Arrays.asList("+", "-", "*", "/");
        Stack<Integer> stack = new Stack<>();
        for (int i = 0; i < tokens.length; i++) {
            if (!list.contains(tokens[i])) {
                stack.push(Integer.valueOf(tokens[i]));
            } else {
                int o2 = stack.pop();
                int o1 = stack.pop();
                if (tokens[i].equals("+")) {
                    stack.push(o1 + o2);
                }
                else if (tokens[i].equals("-")) {
                    stack.push(o1 - o2);
                }
                else if (tokens[i].equals("*")) {
                    stack.push(o1 * o2);
                }
                else if (tokens[i].equals("/")) {
                    stack.push(o1 / o2);
                }
            }
        }
        if (stack.isEmpty()) {
            return 0;
        }
        return stack.pop();
    }
}

克隆图

解法1

  1. new一个HashMap来存放对应节点

  2. new一个新节点

  3. 递归克隆neighbors

    1. 如果map里有节点邻居对应的key,则取出对应的value放到邻居列表中
    2. 如果没有,则将neighbor(key)和new一个新节点(value)放进map,然后递归克隆新节点的neighbors
  4. 返回新节点

/*
// Definition for a Node.
class Node {
    public int val;
    public List<Node> neighbors;
    public Node() {
        val = 0;
        neighbors = new ArrayList<Node>();
    }
    public Node(int _val) {
        val = _val;
        neighbors = new ArrayList<Node>();
    }
    public Node(int _val, ArrayList<Node> _neighbors) {
        val = _val;
        neighbors = _neighbors;
    }
}
*/class Solution {
    Map<Node,Node> map = new HashMap<>();
    public Node cloneGraph(Node node) {
        if(node == null){
            return null;
        }
        Node node1 = new Node();
        node1.val = node.val;
        map.put(node, node1);
        node1.neighbors = cope(node);
        return node1;
    }
    public List<Node> cope(Node node){
        List<Node> neighbors = node.neighbors;
        List<Node> list = new ArrayList<>();
        for (Node neighbor : neighbors) {
            if (map.containsKey(neighbor)){
                list.add(map.get(neighbor));
            }else {
                Node node1 = new Node();
                node1.val = neighbor.val;
                map.put(neighbor, node1);
                List<Node> cope1 = cope(neighbor);
                node1.neighbors = cope1;
                list.add(node1);
            }
        }
        return list;
    }
}

目标和

参考文章

分享|深度讲解背包问题:面试中每五道动态规划就有一道是背包模型 ... - 力扣(LeetCode)

解法1

动态规划五部曲

解析题目意思:就是分成两堆数组left和right,其中有left-right=target,left+right=sum,联立求解有left=(traget+sum)/2。即背包0-1问题,找到一个(target+sum)/2的总和的左数组,即背包重量,数组元素是物品,权重和价值都是元素值nums[i]

1、确定dp数组以及下标的含义——这里dp[i]是指背包重量是i,子集可以凑成的该重量的所有方法数

2、确定递推公式,典型背包公式递推,dp[j] += dp[j-nums[i]]——一维,滚动数组

3、dp数组初始化,dp[0]=1,不放东西也是一种方法

4、确定遍历顺序,双层for循环遍历,i从0到numss.length(遍历所有石头),j从bagSize到当前物品的重量nums[i]

5、举例推导dp数组

6、最后返回dp[bagSize]

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int sum = 0;
        for(int num : nums){
            sum += num;
        }
        if(target > sum) return 0;
        if((target+sum) < 0) return 0;
        if((target+sum) % 2 == 1) return 0;
        int bagSize = (target + sum) / 2;
        int[] dp = new int[bagSize+1];
        dp[0] = 1;
        for(int i = 0;i < nums.length;i++){
            for(int j = bagSize;j >= nums[i];j--){
                dp[j] += dp[j - nums[i]];
            }
        }
        return dp[bagSize];
    }
}

二叉树的中序遍历

解法1

  1. 递归调用自己,参数为左节点
  2. 把自己加入List
  3. 递归调用自己,参数为右节点
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    List result = new ArrayList<Integer>();
    boolean flag = false;
    public List<Integer> inorderTraversal(TreeNode root) {
        if (root == null) {
            if (!flag) {
                return result;
            }
            flag = true;
            return null;
        }
        inorderTraversal(root.left);
        result.add(root.val);
        inorderTraversal(root.right);
        return result;
    }
}

解法2

  1. 递归
  2. 左根右
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        inOrder(result, root);
        return result;
    }
    public void inOrder(List<Integer> result, TreeNode node) {
        if (node == null) {
            return;
        }
        inOrder(result, node.left);
        result.add(node.val);
        inOrder(result, node.right);
    }
}

用栈实现队列

解法1

使用双栈

class MyQueue {
    private Stack<Integer> stack1 = new Stack<>();
    private Stack<Integer> stack2 = new Stack<>();
​
    /** Initialize your data structure here. */
    public MyQueue() {
​
    }
    
    /** Push element x to the back of queue. */
    public void push(int x) {
        stack1.push(x);
    }
    
    /** Removes the element from in front of queue and returns that element. */
    public int pop() {
        if(stack2.isEmpty()) {
            stack1Tostack2(stack1, stack2);
        }
        return stack2.pop();
    }
    
    /** Get the front element. */
    public int peek() {
        if(stack2.isEmpty()) {
            stack1Tostack2(stack1, stack2);
        }
        return stack2.peek();
    }
    
    /** Returns whether the queue is empty. */
    public boolean empty() {
        return stack1.isEmpty() && stack2.isEmpty();
    }
​
    private void stack1Tostack2(Stack stack1, Stack stack2) {
        while(!stack1.isEmpty()) {
           stack2.push(stack1.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();
 */

用队列实现栈

解法1

使用双栈

class MyStack {
​
    Queue<Integer> queue1 = new LinkedList<>();
    Queue<Integer> queue2 = new LinkedList<>();
​
    /** Initialize your data structure here. */
    public MyStack() {
​
    }
    
    /** Push element x onto stack. */
    public void push(int x) {
        queue2.offer(x);
        while(!queue1.isEmpty()) {
            queue2.offer(queue1.poll());
        }
        Queue<Integer> temp = queue1;
        queue1 = queue2;
        queue2 = temp;
    }
    
    /** Removes the element on top of the stack and returns that element. */
    public int pop() {
        return queue1.poll();
    }
    
    /** Get the top element. */
    public int top() {
        return queue1.peek();
    }
    
    /** Returns whether the stack is empty. */
    public boolean empty() {
        return queue1.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();
 */

字符串解码

解法1

双栈

  1. 一个栈放字符串,一个栈放数字
  2. 如果碰到数字,开始计数
  3. 碰到‘[’,将数字入栈,将目前的字符串入栈,字符串置空,数字归零
  4. 碰到‘]’,取出栈顶字符串,将当前字符串循环添加到栈顶字符串后,循环次数为数字栈的栈顶(出栈)
  5. 其他情况往字符串后面添加当前元素(用StringBuilder的append方法)
  6. 返回result.toString()
class Solution {
    public String decodeString(String s) {
        char[] st = s.toCharArray();
        int number = 0;
        Stack<Integer> stack_number = new Stack<>();
        Stack<StringBuilder> stack_st = new Stack<>();
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            if (st[i] >= '0' && st[i] <= '9') {
                number = number * 10 + st[i] - '0';
            } else if (st[i] == '[') {
                stack_st.push(result);
                stack_number.push(number);
                result = new StringBuilder();
                number = 0;
            } else if (st[i] == ']') {
                StringBuilder head = stack_st.pop();
                int n = stack_number.pop();
                for (int j = 0; j < n; j++) {
                    head.append(result);
                }
                result = head;
            } else {
                result.append(st[i]);
            }
        }
        return result.toString();
    }
}

图像渲染

解法1

解法同岛屿数量那道题,可以直接看那道题

class Solution {
    public int[][] floodFill(int[][] image, int sr, int sc, int color) {
        if (image[sr][sc] == color) {
            return image;
        }
        int oldColor = image[sr][sc];
        image[sr][sc] = color;
        color(image, sr, sc, color, oldColor);
        return image;
    }
    public void color(int[][] grid, int i, int j, int newColor, int oldColor) {
        if (i > 0 && grid[i - 1][j] == oldColor) {//上
            grid[i - 1][j] = newColor;
            color(grid, i - 1, j, newColor, oldColor);
        }
        if (j < grid[0].length - 1 && grid[i][j + 1] == oldColor) {//右
            grid[i][j + 1] = newColor;
            color(grid, i, j + 1, newColor, oldColor);
        }
        if (i < grid.length - 1 && grid[i + 1][j] == oldColor) {//下
            grid[i + 1][j] = newColor;
            color(grid, i + 1, j, newColor, oldColor);
        }
        if (j > 0 && grid[i][j - 1] == oldColor) {//左
            grid[i][j - 1] = newColor;
            color(grid, i, j - 1, newColor, oldColor);
        }
    }
}

01矩阵

解法1

  1. 初始化一个result数组,原数组为0的位置赋0,其他位置赋MAX_VALUE/2
  2. 对原数组进行两次遍历,一次从左上到右下,一次从右下到左上,填充距离矩阵
  3. return
class Solution {
    public int[][] updateMatrix(int[][] mat) {
        int[][] result = new int[mat.length][mat[0].length];
        for (int i = 0; i < mat.length; i++) {
            for (int j = 0; j < mat[0].length; j++) {
                if (mat[i][j] == 0) {
                    result[i][j] = 0;
                } else {
                    result[i][j] = Integer.MAX_VALUE / 2;
                }
            }
        }
        for (int i = 0; i < mat.length; i++) {
            for (int j = 0; j < mat[0].length; j++) {
                if (i - 1 >= 0) {
                    result[i][j] = Math.min(result[i][j], result[i - 1][j] + 1);
                }
                if (j - 1 >= 0) {
                    result[i][j] = Math.min(result[i][j], result[i][j - 1] + 1);
                }
            }
        }
        for (int i = mat.length - 1; i >= 0; i--) {
            for (int j = mat[0].length - 1; j >= 0; j--) {
                if (i + 1 < mat.length) {
                    result[i][j] = Math.min(result[i][j], result[i + 1][j] + 1);
                }
                if (j + 1 < mat[0].length) {
                    result[i][j] = Math.min(result[i][j], result[i][j + 1] + 1);
                }
            }
        }
        return result;
    }
}

钥匙和房间

解法1

  1. 建立一个boolean的数组对应每个房间,访问过标记为true否则为flase,定义一个flag记录访问了几个房间
  2. 递归每个房间的钥匙,如果没访问过则将boolean数组对应值置为true,并访问该房间
  3. 返回flag是否等于数组长度
class Solution {
    public int go = 0;
    public boolean[] visited;
    public boolean canVisitAllRooms(List<List<Integer>> rooms) {
        visited = new boolean[rooms.size()];
        gogogo(rooms, 0);
        return go == visited.length;
    }
    public void gogogo(List<List<Integer>> rooms, int door) {
        visited[door] = true;
        go++;
        for (int room: rooms.get(door)) {
            if (!visited[room]) {
                gogogo(rooms, room);
            }
        }
    }
}