LeetCode中Queue & Stack章节

2019-04-24  本文已影响0人  s1991721

Queue

队列的特性是先进先出FIFO

Design Circular Queue

设计环形队列的数据操作类,环形队列的优势在于节省空间,但指针的控制复杂,设计思路重点看isFull & isEmpty这两个方法

class MyCircularQueue {

    private int[] queue;
    private int headP;
    private int tailP;

    public MyCircularQueue(int k) {
        queue = new int[k + 1];
        headP = 0;
        tailP = 0;
        for (int i = 0; i < k + 1; i++) {
            queue[i] = -1;
        }
    }
    //3、既然tailP指向空位,入队列就简单了
    public boolean enQueue(int value) {

        if (!isFull()) {
            queue[tailP] = value;
            tailP = (tailP + 1) % queue.length;
            return true;
        }

        return false;
    }

    //4、出队列时要清空数据(非必须、但有造成内存泄漏的风险)
    public boolean deQueue() {
        if (!isEmpty()) {
            queue[headP] = -1;
            headP = (headP + 1) % queue.length;
            return true;
        }

        return false;
    }

    public int Front() {
        return queue[headP];
    }
    //5、注意指针越界
    public int Rear() {
        return queue[(tailP + queue.length - 1) % queue.length];
    }

    //1、tailP指向的永远是空位,headP指向头元素
    public boolean isEmpty() {
        return headP == tailP;
    }
    //2、这就会浪费一个空间,用来区分Empty、Full的状态
    public boolean isFull() {
        return (tailP + 1) % queue.length == headP;
    }

}

Queue and BFS

由于队列FIFO的性质,BFS广度优先查找一般由Queue来实现

先处理顶层,记录同层的数量,依次处理此层的数据,与其相关的下层加入队尾,从而实现广度优先查找

Number of Islands

用二维数组表示平面,其中1代表岛屿,0代表海,相连岛屿算一个岛,计算平面内共有多少座岛屿

class Solution {

    public int numIslands(char[][] grid) {

        int count = 0;
        for (int i = 0; i < grid.length; i++) {
            for (int j = 0; j < grid[i].length; j++) {
                if (grid[i][j] != '0') {//找到第一个岛
                    export(grid, i, j);//探索该岛
                    count++;
                }
            }

        }

        return count;
    }

    private void export(char[][] grid, int x, int y) {
        if (x < 0 || y < 0) {//超出地图范围
            return;
        }

        if (x < grid.length && y < grid[x].length && grid[x][y] == '1') {
            grid[x][y] = '0';//将岛屿打小
        } else {
            return;
        }

        //上下左右各方探索
        export(grid, x, y + 1);
        export(grid, x, y - 1);
        export(grid, x + 1, y);
        export(grid, x - 1, y);
    }
    
}

Open the Lock

四位数字密码锁,每次转动一下,已知密码和死亡触发,求解最少的解锁步骤

这种最少的求解算法,多数要用到BFS

class Solution {
    public int openLock(String[] deadends, String target) {

        Set<String> visited = new HashSet<String>();
        Set<String> deadEnds = new HashSet<String>(deadends.length);

        deadEnds.addAll(Arrays.asList(deadends));

        Queue<String> queue = new LinkedList();
        queue.offer("0000");//起始位置
        visited.add("0000");
        int level = 0;

        while (!queue.isEmpty()) {

            int length = queue.size();//每一层要查找的次数

            for (int j = 0; j < length; j++) {

                String poll = queue.poll();
                if (poll.equals(target)) {//命中
                    return level;
                }
                if (deadEnds.contains(poll)) continue;//陷阱!避开

                String cur = new String(poll);

                for (int i = 0; i < 4; i++) {//一共四位,走向每一位的前后
                    char c = cur.charAt(i);
                    String next = cur.substring(0, i) + (c == '9' ? 0 : c - '0' + 1) + cur.substring(i + 1);
                    String pre = cur.substring(0, i) + (c == '0' ? 9 : c - '0' - 1) + cur.substring(i + 1);
                    //避开陷阱和重复
                    if (!visited.contains(pre) && !deadEnds.contains(pre)) {
                        queue.offer(pre);
                        visited.add(pre);
                    }
                    if (!visited.contains(next) && !deadEnds.contains(next)) {
                        queue.offer(next);
                        visited.add(next);
                    }
                }
            }
            level++;

        }

        return -1;
    }
}

Perfect Squares

给定数字n,求它由最少多少个完全平方数的和组成

两种解法:
一种是纯数学定理【四平方和定理】;
第二种则是动态规划(所采取的),将0-n全部数字找出完全平方数的最少个数

class Solution {
    public int numSquares(int n) {
        int[] dp = new int[n + 1];
        dp[0] = 0;

        for (int i = 1; i < dp.length; i++) {//遍历0-n

            int j = 1;
            int min = 5;
            while (i - j * j >= 0) {//计算第i位数字i,完全平方数的最少个数
                min = Math.min(min, dp[i - j * j] + 1);
                j++;
            }
            dp[i] = min;

        }

        return dp[n];
    }
}

Stack

栈的特性是后进先出LIFO

Min Stack

设计简单的栈结构类,能够检索最小值。

难点:正常情况下,入栈时比较最小值,能够保留最小值。问题发生在最小值出栈后,剩余数据中的最小值怎么获取??

思路:既然次小值、次次小值无法确定,那就想方法将其保留。当出栈为最小值时,栈顶是次小值的话问题就解决了

class MinStack {

    int min = Integer.MAX_VALUE;
    Stack<Integer> stack;

    public MinStack() {
        stack = new Stack();
    }

    public void push(int x) {
        if (x <= min) {//1、入栈发现最小值
            stack.push(min);//2、将次小值先入栈
            min = x;//3、记录最小值
        }
        stack.push(x);//4、再将最小值入栈;5、如果是一般值,则正常入栈,不经过1、2、3
    }//由此就保证了栈内最小值的下方为次小值

    public void pop() {
        if (stack.pop() == min)//出栈值为最小值
            min = stack.pop();//取出次小值,为最小值
    }

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

    public int getMin() {
        return min;
    }
}

Valid Parentheses

判断输入的括号是否配对

class Solution {
    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<Character>();
        if (s.length() % 2 != 0) return false;//奇数项的话一定不能配对

        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);

            if (c == '(') {//反向入栈,看后续内容是否匹配栈内内容
                stack.push(')');
            } else if (c == '{') {
                stack.push('}');
            } else if (c == '[') {
                stack.push(']');
            } else if (stack.isEmpty() || stack.pop() != c) {//不匹配则不能配对
                return false;
            }

        }

        return stack.isEmpty();

    }
}

Daily Temperatures

给出连续几天的温度,计算出,还要等几天,温度才比现在的温度高。

class Solution {
    public int[] dailyTemperatures(int[] T) {
        Stack<Integer> stack = new Stack<Integer>();//没有找到结果的集合,栈内栈顶永远小于栈底,从上到下是升序
        int[] waits = new int[T.length];//结果集合

        for (int i = 0; i < T.length; i++) {//从第一天开始遍历
            while (!stack.isEmpty() && T[i] > T[stack.peek()]) {//今天大于栈内最小元素,则栈顶元素可计算出结果
                int index = stack.pop();
                waits[index] = i - index;//保存结果
            }
            stack.push(i);
        }
        return waits;

    }
}

Evaluate Reverse Polish Notation

这是计算机编译原理中的概念,书中是由计算公式引申到栈内的表示,而这里题目则是反向,给出了栈内表示反推计算公式的结果

class Solution {
    public int evalRPN(String[] tokens) {
        Stack<Integer> stack = new Stack<Integer>();

        for (String str : tokens) {
            if (str.equals("+") || str.equals("-") || str.equals("*") || str.equals("/")) {
                int num2 = stack.pop();//先出栈的是num2
                int num1 = stack.pop();
                int result = 0;
                if (str.equals("+")) {
                    result = num1 + num2;
                }
                if (str.equals("-")) {
                    result = num1 - num2;
                }
                if (str.equals("*")) {
                    result = num1 * num2;
                }
                if (str.equals("/")) {
                    result = num1 / num2;
                }
                stack.push(result);
            } else {
                stack.push(Integer.valueOf(str));
            }
        }
        return stack.pop();
    }
}

Stack and DFS

由于栈LIFO的性质,DFS深度优先查找一般由Stack来实现

逮到一根线深入(即入栈),碰到南墙就回头(出栈),遇到分叉口再次深入(即入栈),知道得出结果,因此Stack与DFS密不可分

Clone Graph

将无向图深度复制

因为是无向图,所以同一节点可以被多个节点关联,可以使用Map避免同一节点被多次复制的问题

/*
// Definition for a Node.
class Node {
    public int val;
    public List<Node> neighbors;

    public Node() {}

    public Node(int _val,List<Node> _neighbors) {
        val = _val;
        neighbors = _neighbors;
    }
};
*/
class Solution {
    public Node cloneGraph(Node node) {

        Stack<Node> stack = new Stack<Node>();
        HashMap<Node, Node> map = new HashMap<Node, Node>();

        Node root = new Node();//先复制根节点
        root.val = node.val;
        root.neighbors = new ArrayList<Node>();

        map.put(node, root);//记录已复制的节点
        stack.push(node);//记录将要复制其子节点的节点

        while (!stack.isEmpty()) {
            Node node1 = stack.pop();
            for (Node node2 : node1.neighbors) {//子节点
                if (!map.containsKey(node2)) {//已有子节点,避免重复复制
                    Node newNode = new Node();
                    newNode.val = node2.val;
                    newNode.neighbors = new ArrayList<Node>();
                    map.put(node2, newNode);
                    stack.push(node2);
                }
                map.get(node1).neighbors.add(map.get(node2));

            }
        }

        return map.get(node);
    }
}

Target Sum

给定一正整型数组和一目标值,只能通过加减法遍历操作数组内所有项使其结果为目标值,求共有多少种操作方式。

class Solution {
    int result = 0;

    public int findTargetSumWays(int[] nums, int S) {
        helper(nums, 0, S);
        return result;
    }

    private void helper(int[] nums, int position, int S) {//数组、第几位开始、当前结果
        if (position >= nums.length) {//最后一位
            if (S == 0) {//且结果为0
                result++;
            }
            return;
        }

        helper(nums, position + 1, S - nums[position]);//DFS
        helper(nums, position + 1, S + nums[position]);
    }
}

Implement Queue using Stacks

通过使用栈来实现队列,即利用LIFO的数据结构实现FIFO的操作方式

重点:在peek方法,通过左右手倒换来实现

    class MyQueue {

        Stack<Integer> inStack;
        Stack<Integer> outStack;

        public MyQueue() {
            inStack=new Stack<Integer>();
            outStack=new Stack<Integer>();
        }

        public void push(int x) {
            inStack.push(x);
        }

        public int pop() {
            peek();
            return outStack.pop();
        }

        public int peek() {
            if (outStack.isEmpty()) {//将入栈的栈底元素,转到了栈顶
                while (!inStack.isEmpty()) {
                    outStack.push(inStack.pop());
                }
            }
            return outStack.peek();
        }

        public boolean empty() {
            return inStack.isEmpty()&&outStack.isEmpty();
        }
    }

Implement Stack using Queues

通过使用队列来实现栈

class MyStack {

        Queue<Integer> q = new LinkedList<Integer>();

        public void push(int x) {
            q.add(x);

            int n = q.size();
            while (n-- > 1)//每入队一元素,将其前面的元素出队并再次入队,达到新进元素在队首的效果
                q.add(q.poll());
        }

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

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

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

Decode String

看效果比题目描述更直观

s = "3[a]2[bc]", return "aaabcbc".
s = "3[a2[c]]", return "accaccacc".
s = "2[abc]3[cd]ef", return "abcabccdcdcdef".

class Solution {
    public static String decodeString(String s) {
        String res = "";
        Stack<Integer> countStack = new Stack();//记录数字
        Stack<String> resStack = new Stack();//记录字符
        int index = 0;

        while (index < s.length()) {

            if (Character.isDigit(s.charAt(index))) {
                int count = 0;
                while (Character.isDigit(s.charAt(index))) {//多位整数
                    count = count * 10 + (s.charAt(index) - '0');
                    index++;
                }
                countStack.push(count);
            } else if (s.charAt(index) == '[') {//字符的开始
                resStack.push(res);
                res = "";
                index++;
            } else if (s.charAt(index) == ']') {//字符的结束,表示要开始重复拼接字符
                StringBuffer temp = new StringBuffer(resStack.pop());
                int repeat = countStack.pop();
                for (int i = 0; i < repeat; i++) {
                    temp.append(res);
                }
                res = temp.toString();
                index++;
            } else {
                res += s.charAt(index++);
            }

        }

        return res;
    }
}

Flood Fill

泛洪算法,即指定一点,将该点周围与其相同的点统一变换

class Solution {
    public int[][] floodFill(int[][] image, int sr, int sc, int newColor) {

        int oldColor = image[sr][sc];

        fun(image, sr, sc, newColor, oldColor);

        return image;
    }

    private void fun(int[][] image, int sr, int sc, int newColor, int oldColor) {
        if (sr < 0 || sc < 0) {//越界处理
            return;
        }

        if (sr >= image.length || sc >= image[0].length) {
            return;
        }
        if (oldColor == newColor) {//已处理过,或不需处理的相邻点
            return;
        }
        if (image[sr][sc] == oldColor) {//必须是相同的点
            image[sr][sc] = newColor;
            //四个方向拓展
            fun(image, sr + 1, sc, newColor, oldColor);
            fun(image, sr - 1, sc, newColor, oldColor);
            fun(image, sr, sc + 1, newColor, oldColor);
            fun(image, sr, sc - 1, newColor, oldColor);

        }
    }
}

01 Matrix

二位数组中,求出所有点距离最近0点的距离

class Solution {
    public int[][] updateMatrix(int[][] matrix) {
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[0].length; j++) {
                if (matrix[i][j] == 0) {//当前点本身是0点
                    continue;
                } else {
                    int count = find(matrix, i, j);//找到此点距最近0点的距离
                    matrix[i][j] = count;
                }
            }
        }
        return matrix;
    }

    private int find(int[][] matrix, int r, int c) {//BFS

        Queue<String> queue = new LinkedList<String>();

        queue.offer(r + "-" + c);

        int count = 0;

        while (!queue.isEmpty()) {

            int loop = queue.size();
            for (int i = 0; i < loop; i++) {

                String[] temp = queue.poll().split("-");
                int row = Integer.parseInt(temp[0]);
                int col = Integer.parseInt(temp[1]);

                if (matrix[row][col] == 0) {
                    return count;
                }

                if (row + 1 < matrix.length) {
                    queue.offer((row + 1) + "-" + col);
                }
                if (col + 1 < matrix[0].length) {
                    queue.offer(row + "-" + (col + 1));
                }
                if (row > 0) {
                    queue.offer((row - 1) + "-" + col);
                }
                if (col > 0) {
                    queue.offer(row + "-" + (col - 1));
                }
            }
            count += 1;
        }

        return count;
    }
}

Keys and Rooms

有多间房,每间房内存在开启其他房间的钥匙,问是否能进入所有房间

class Solution {
    public boolean canVisitAllRooms(List<List<Integer>> rooms) {

        Queue<Integer> queue = new LinkedList<Integer>();//手上有的钥匙
        Set<Integer> visited = new HashSet<Integer>();//已进过的房间

        for (int i = 0; i < rooms.get(0).size(); i++) {
            queue.offer(rooms.get(0).get(i));//拿到房间0的所有钥匙
        }
        visited.add(0);//去过房间0

        while (!queue.isEmpty()) {

            int room = queue.poll();
            visited.add(room);//拿钥匙去其他房间
            List<Integer> keys = rooms.get(room);
            for (int i = 0; i < keys.size(); i++) {//并将其他房间的钥匙拿入手中
                if (!visited.contains(keys.get(i))) {
                    queue.offer(keys.get(i));
                }
            }

        }

        return visited.size() == rooms.size();//去过的房间数等于房间总数则表示能够遍历所有房间,反之不能
    }
}
上一篇下一篇

猜你喜欢

热点阅读