FIFO队列

248 阅读4分钟

FIFO队列

二叉树的层次遍历

题目: 从上到下按层打印二叉树,同一层结点按从左到右的顺序打印,每一层打印到一行。

class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;
}

输出: [[3], [9, 8], [6, 7]]

解法1: 利用队列, 时间复杂度O(n) 空间复杂度O(n)

private static List<List<Integer>> levelOrder1(TreeNode root) {
        Queue<TreeNode> queue = new LinkedList<>();
        if (root != null) {
            queue.offer(root);
        }

        List<List<Integer>> resultList = new ArrayList<>();
        while (queue.size() > 0) {
            List<Integer> temp = new ArrayList<>();
            for (int i = 0; i < resultList.size(); i++) {
                TreeNode cur = queue.poll();
                temp.add(cur.val);
                if (null != cur.left) {
                    queue.offer(cur.left);
                }
                if (null != cur.right) {
                    queue.offer(cur.right);
                }
            }
            resultList.add(temp);
        }
        return resultList;
    }

解法2: 使用两层List

private static List<List<Integer>> levelOrder2(TreeNode root) {
        List<List<Integer>> resultList = new ArrayList<List<Integer>>();
        List<TreeNode> curList = new ArrayList<TreeNode>();

        if (null != root) {
            curList.add(root);
        }

        while (curList.size() > 0) {
            List<TreeNode> nextList = new ArrayList<TreeNode>();
            List<Integer> curResultList = new ArrayList<Integer>();
            for (TreeNode cur : curList) {
                curResultList.add(cur.val);
                if (cur.left != null) {
                    nextList.add(cur.left);
                }
                if (cur.right != null) {
                    nextList.add(cur.right);
                }
            }
            curList = nextList;
            resultList.add(curResultList);
        }
        return resultList;
    }

提示: 当具备广度遍历(层级遍历) 和顺序输出特点时,应该想到用FIFO队列。

二叉树next指针指向

题目: 给定一个二叉树, 修改二叉树中的所有next指针, 使其指向右边的结点,如果右边没有结点, 设置为空指针。

class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;
    TreeNode next = null;
}
private static TreeNode connect(TreeNode root) {
        TreeNode node = null;
        if (root != null) {
            node = root;
        }

        while (node != null) {
            TreeNode nextPreNode = null;
            TreeNode nextHeadNode = null;
            TreeNode curNode = node;
            while (curNode != null) {
                if(curNode.left != null){
                    if(nextPreNode != null){
                        nextPreNode.next = curNode.left;
                    }
                    nextPreNode = curNode.left;
                    if(nextHeadNode == null){
                        nextHeadNode = curNode.left;
                    }
                }

                if(curNode.right != null){
                    if(nextPreNode != null){
                        nextPreNode.next = curNode.right;
                    }
                    nextPreNode = curNode.right;
                    if(nextHeadNode == null){
                        nextHeadNode = curNode.right;
                    }
                }
                curNode = curNode.next;
            }
            node = nextHeadNode;
        }
        return root;
    }

设计一个循环队列

题目: 设计一个可以容纳 k 个元素的循环队列。需要实现以下接口:

class CircularQueue {
    // 参数k表示这个循环队列最多只能容纳k个元素
    public MyCircularQueue(int k);
    // 将value放到队列中, 成功返回true
    public boolean enQueue(int value);
    // 删除队首元素,成功返回true
    public boolean deQueue();
    // 得到队首元素,如果为空,返回-1
    public int Front();
    // 得到队尾元素,如果队列为空,返回-1
    public int Rear();
    // 看一下循环队列是否为空
    public boolean isEmpty();
    // 看一下循环队列是否已放满k个元素
    public boolean isFull();
}

解法一: 三个变量 front, rear, used 来控制循环队列的使用

class CircularQueue {

    private int used;

    private int front = 0;

    private int rear = 0;

    private int capacity = 0;

    private int[] arr = null;

    /**
     * 参数k表示这个循环队列最多只能容纳k个元素
     */
    CircularQueue(int k) {
        capacity = k;
        arr = new int[k];
    }

    /**
     * 将value放到队列中, 成功返回true
     */
    public boolean enQueue(int value) {
        if (isFull()) {
            return false;
        }
        arr[rear] = value;
        rear = (rear + 1) % capacity;
        used++;
        return true;
    }

    /**
     * 删除队首元素,成功返回true
     */
    public boolean deQueue() {
        if (isEmpty()) {
            return false;
        }

        int ret = arr[front];
        front = (front + 1) % capacity;
        used--;
        return true;
    }

    /**
     * 得到队首元素,如果为空,返回-1
     */
    public int front() {
        if (isEmpty()) {
            return -1;
        }
        return arr[front];
    }

    /**
     * 得到队尾元素,如果队列为空,返回-1
     */
    public int rear() {
        if (isEmpty()) {
            return -1;
        }
        int tail = (rear - 1 + capacity) % capacity;
        return arr[tail];
    }

    /**
     * 看一下循环队列是否为空
     */
    public boolean isEmpty() {
        return used == 0;
    }

    /**
     * 循环队列是否已放满k个元素
     */
    public boolean isFull() {
        return used == capacity;
    }
}

解法2: 使用 k+1 个元素的空间,两个变量 front, rear 来控制循环队列的使用

class CircularQueue2 {

    private int front = 0;
    private int rear = 0;
    private int[] arr = null;
    private int capacity = 0;


    /**
     * 参数k表示这个循环队列最多只能容纳k个元素
     */
    CircularQueue2(int k) {
        capacity = k + 1;
        arr = new int[k + 1];
    }

    /**
     * 将value放到队列中, 成功返回true
     */
    public boolean enQueue(int value) {
        if (isFull()) {
            return false;
        }
        arr[rear] = value;
        rear = (rear + 1) % capacity;
        return true;
    }

    /**
     * 删除队首元素,成功返回true
     */
    public boolean deQueue() {
        if (isEmpty()) {
            return false;
        }

        int ret = arr[front];
        front = (front + 1) % capacity;
        return true;
    }

    /**
     * 得到队首元素,如果为空,返回-1
     */
    public int front() {
        if (isEmpty()) {
            return -1;
        }
        return arr[front];
    }

    /**
     * 得到队尾元素,如果队列为空,返回-1
     */
    public int rear() {
        if (isEmpty()) {
            return -1;
        }
        int tail = (rear - 1 + capacity) % capacity;
        return arr[tail];
    }

    /**
     * 看一下循环队列是否为空
     */
    public boolean isEmpty() {
        return front == rear;
    }

    /**
     * 循环队列是否已放满k个元素
     */
    public boolean isFull() {
        return (rear + 1) % capacity == front;
    }

单调队列 & 滑动窗口的最大值

每次入队的时候,为了保证队列的单调性,还要剔除掉尾部的元素**。**直到尾部的元素大于等于入队元素(因为是单调递减队列)。

单调队列在出队时,也与普通的队列出队方式不一样。出队时,需要给出一个 value,如果 value 与队首相等,才能将这个数出队

入队与出队的组合,可以在 O(1) 时间得到某个区间上的最大值。

private static int[] maxSlidingWindow(int[] arr, int k) {

    List<Integer> resultList = new ArrayList<>();
    ArrayDeque<Integer> queue = new ArrayDeque<>();
    for (int i = 0; i < arr.length; i++) {
        push(arr[i], queue);
        if (i < k - 1) {
            continue;
        }
        resultList.add(queue.getFirst());
        pop(arr[i - k + 1], queue);
    }
    return resultList.stream().mapToInt(Integer::valueOf).toArray();
}

private static void push(int val, ArrayDeque<Integer> queue) {
    while (!queue.isEmpty() && queue.getLast() < val) {
        queue.remove();
    }
    queue.addLast(val);
}

private static void pop(int val, ArrayDeque<Integer> queue) {
    if (!queue.isEmpty() && queue.getFirst() == val) {
        queue.removeFirst();
    }
}

捡金币

题目: 给定一个数组 A[],每个位置 i 放置了金币 A[i],小明从 A[0] 出发。当小明走到 A[i] 的时候,下一步他可以选择 A[i+1, i+k](当然,不能超出数组边界)。每个位置一旦被选择,将会把那个位置的金币收走(如果为负数,就要交出金币)。请问,最多能收集多少金币?

输入:[1,-1,-100,-1000,100,3], k = 2

输出:4

private static int maxResult(int[] arr, int k) {

    if (null == arr || arr.length == 0 || k < 0) {
        return 0;
    }

    int length = arr.length;
    int[] maxArr = new int[length];
    ArrayDeque<Integer> queue = new ArrayDeque<>();
    for (int i = 0; i < length; i++) {
        if (i - k > 0) {
            if (!queue.isEmpty() && queue.getFirst() == maxArr[i - k - 1]) {
                queue.removeFirst();
            }
        }

        int old = queue.isEmpty() ? 0 : queue.getFirst();
        maxArr[i] = old + arr[i];

        while (!queue.isEmpty() && queue.getLast() < maxArr[i]) {
            queue.removeLast();
        }
        queue.addLast(maxArr[i]);
    }
    return maxArr[length - 1];
}