LeetCode 863. 二叉树中所有距离为 K 的结点/ 641. 设计循环双端队列 / 622. 设计循环队列

Posted Zephyr丶J

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 863. 二叉树中所有距离为 K 的结点/ 641. 设计循环双端队列 / 622. 设计循环队列相关的知识,希望对你有一定的参考价值。

863. 二叉树中所有距离为 K 的结点

2021.7.28每日一题

题目描述

给定一个二叉树(具有根结点 root), 一个目标结点 target ,和一个整数值 K 。

返回到目标结点 target 距离为 K 的所有结点的值的列表。 答案可以以任何顺序返回。

示例 1:

输入:root = [3,5,1,6,2,0,8,null,null,7,4], target = 5, K = 2
输出:[7,4,1]
解释:
所求结点为与目标结点(值为 5)距离为 2 的结点,
值分别为 7,4,以及 1

注意,输入的 “root” 和 “target” 实际上是树上的结点。
上面的输入仅仅是对这些对象进行了序列化描述。

提示:

给定的树是非空的。
树上的每个结点都具有唯一的值 0 <= node.val <= 500 。
目标结点 target 是树上的结点。
0 <= K <= 1000.

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/all-nodes-distance-k-in-binary-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

如果把target节点当做根节点的话,就可以很方便的找到距离为k的节点。
那么用一个哈希表把所有节点的父节点存储起来
然后由target节点开始层序遍历
可以向三个方向遍历,用一个set存储已经被遍历过的节点,如果没有被遍历过,就扩展,直到k层

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    Map<Integer, TreeNode> map;
    public List<Integer> distanceK(TreeNode root, TreeNode target, int k) {
        //如果把target节点当做根节点的话,就可以很方便的找到距离为k的节点
        //那么用一个哈希表把所有节点的父节点存储起来
        //然后由target节点开始层序遍历?
        //可以向三个方向遍历,如果和传入的方向不同,就遍历,直到k层
        map = new HashMap<>();
        dfs(root);
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(target);
        List<Integer> res = new LinkedList<>();
        if(k == 0){
            res.add(target.val);
            return res;
        }
        Set<Integer> set = new HashSet<>();
        set.add(target.val);
        int deep = 0;
        while(!queue.isEmpty()){
            deep++;
            int size = queue.size();
            while(size-- > 0){
                TreeNode node = queue.poll();
                if(node.left != null && !set.contains(node.left.val)){
                    queue.add(node.left);
                    set.add(node.left.val);
                    if(deep == k){
                        res.add(node.left.val);
                    }
                }
                if(node.right != null && !set.contains(node.right.val)){
                    queue.add(node.right);
                    set.add(node.right.val);
                    if(deep == k){
                        res.add(node.right.val);
                    }
                }
                if(map.containsKey(node.val) && !set.contains(map.get(node.val).val)){
                    queue.add(map.get(node.val));
                    set.add(map.get(node.val).val);
                    if(deep == k){
                        res.add(map.get(node.val).val);
                    }
                }
            }
        }
        return res;
    }

    public void dfs(TreeNode root){
        if(root == null){
            return;
        }
        if(root.left != null)
            map.put(root.left.val, root);
        if(root.right != null)
            map.put(root.right.val, root);
        dfs(root.left);
        dfs(root.right);
    }
}

深度优先搜索
如果来源节点和要走的方向不同,就往这个方向走,否则就不能往这个方向走

class Solution {
    Map<Integer, TreeNode> parents = new HashMap<Integer, TreeNode>();
    List<Integer> ans = new ArrayList<Integer>();

    public List<Integer> distanceK(TreeNode root, TreeNode target, int k) {
        // 从 root 出发 DFS,记录每个结点的父结点
        findParents(root);

        // 从 target 出发 DFS,寻找所有深度为 k 的结点
        findAns(target, null, 0, k);

        return ans;
    }

    public void findParents(TreeNode node) {
        if (node.left != null) {
            parents.put(node.left.val, node);
            findParents(node.left);
        }
        if (node.right != null) {
            parents.put(node.right.val, node);
            findParents(node.right);
        }
    }

    public void findAns(TreeNode node, TreeNode from, int depth, int k) {
        if (node == null) {
            return;
        }
        if (depth == k) {
            ans.add(node.val);
            return;
        }
        if (node.left != from) {
            findAns(node.left, node, depth + 1, k);
        }
        if (node.right != from) {
            findAns(node.right, node, depth + 1, k);
        }
        if (parents.get(node.val) != from) {
            findAns(parents.get(node.val), node, depth + 1, k);
        }
    }
}


作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/all-nodes-distance-k-in-binary-tree/solution/er-cha-shu-zhong-suo-you-ju-chi-wei-k-de-qbla/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

早上来了看了三叶姐建图的方法,晚上想的时候,完全没有想到和图有什么关系,这次又好像明白了点什么
然后建图,邻接表,我想着就是那种二维数组的形式,但是又觉得树里面的节点大小不固定,当然可以开超过最大限制范围的数组,但是那样建表的话后面遍历又很慢
然后就看三叶姐怎么建图的,她没有写具体思路,但是根据我的理解是这样的
(看不太懂,已经求问了,明白了再来补充)

三步:
he[a]和e[idx]相邻
第一步先将每个节点放在一个数组e中,第几个出现的下标就是几
第二步ne应该是邻接的意思,就是说,下标为idx的数,和哪个点是相连接的
第三步

	int N = 1010, M = N * 2;
    int[] he = new int[N], e = new int[M], ne = new int[M];
    int idx;
    void add(int a, int b) {
        e[idx] = b;
        ne[idx] = he[a];
        he[a] = idx++;
    }

641. 设计循环双端队列

题目描述

设计实现双端队列。
你的实现需要支持以下操作:

MyCircularDeque(k):构造函数,双端队列的大小为k。
insertFront():将一个元素添加到双端队列头部。 如果操作成功返回 true。
insertLast():将一个元素添加到双端队列尾部。如果操作成功返回 true。
deleteFront():从双端队列头部删除一个元素。 如果操作成功返回 true。
deleteLast():从双端队列尾部删除一个元素。如果操作成功返回 true。
getFront():从双端队列头部获得一个元素。如果双端队列为空,返回 -1。
getRear():获得双端队列的最后一个元素。 如果双端队列为空,返回 -1。
isEmpty():检查双端队列是否为空。
isFull():检查双端队列是否满了。
示例:

MyCircularDeque circularDeque = new MycircularDeque(3); // 设置容量大小为3
circularDeque.insertLast(1); // 返回 true
circularDeque.insertLast(2); // 返回 true
circularDeque.insertFront(3); // 返回 true
circularDeque.insertFront(4); // 已经满了,返回 false
circularDeque.getRear(); // 返回 2
circularDeque.isFull(); // 返回 true
circularDeque.deleteLast(); // 返回 true
circularDeque.insertFront(4); // 返回 true
circularDeque.getFront(); // 返回 4

提示:

所有值的范围为 [1, 1000]
操作次数的范围为 [1, 1000]
请不要使用内置的双端队列库。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/design-circular-deque
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

开了会员,上周力扣发邮件推荐了这个题,是有什么暗示吗哈哈

双向链表很好实现,单链表在队尾删除的时候可能会有点问题,需要遍历,还是用数组来写
创建数组时,使数组长度比要求的长度大1,即空一格
然后使left指向数据开头位置,right指向最后一个数据的后一个位置,这个是为了方便判断队列为空,或者队列为满
队列为空,则left==right;队列为满,则(right+1)%len == left
如果直接模拟的话,判断条件会比较复杂

class MyCircularDeque {
    //链表很好实现,还是用数组来做
    //还是空一个位置
    int len;
    int[] deque;
    int left;
    int right;
    /** Initialize your data structure here. Set the size of the deque to be k. */
    public MyCircularDeque(int k) {
        len = k + 1;
        deque = new int[len];
        left = 0;
        right = 0;
    }
    
    /** Adds an item at the front of Deque. Return true if the operation is successful. */
    public boolean insertFront(int value) {
        if(isFull())
            return false;
        left = (left - 1 + len) % len;
        deque[left] = value;
        return true;
    }
    
    /** Adds an item at the rear of Deque. Return true if the operation is successful. */
    public boolean insertLast(int value) {
        if(isFull())
            return false;
        deque[right] = value;
        right = (right + 1) % len;
        return true;
    }
    
    /** Deletes an item from the front of Deque. Return true if the operation is successful. */
    public boolean deleteFront() {
        if(isEmpty())
            return false;
        left = (left + 1) % len;
        return true;
    }
    
    /** Deletes an item from the rear of Deque. Return true if the operation is successful. */
    public boolean deleteLast() {
        if(isEmpty())
            return false;
        right = (right - 1 + len) % len;
        return true;
    }
    
    /** Get the front item from the deque. */
    public int getFront() {
        if(isEmpty())
            return -1;
        return deque[left];
    }
    
    /** Get the last item from the deque. */
    public int getRear() {
        if(isEmpty())
            return -1;
        return deque[(right - 1 + len) % len];
    }
    
    /** Checks whether the circular deque is empty or not. */
    public boolean isEmpty() {
        return left == right;
    }
    
    /** Checks whether the circular deque is full or not. */
    public boolean isFull() {
        return (right + 1) % len == left;
    }
}

/**
 * Your MyCircularDeque object will be instantiated and called as such:
 * MyCircularDeque obj = new MyCircularDeque(k);
 * boolean param_1 = obj.insertFront(value);
 * boolean param_2 = obj.insertLast(value);
 * boolean param_3 = obj.deleteFront();
 * boolean param_4 = obj.deleteLast();
 * int param_5 = obj.getFront();
 * int param_6 = obj.getRear();
 * boolean param_7 = obj.isEmpty();
 * boolean param_8 = obj.isFull();
 */

622. 设计循环队列

题目描述

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

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

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

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

示例:

MyCircularQueue circularQueue = new MyCircularQueue(3); // 设置长度为 3
circularQueue.enQueue(1); // 返回 true
circularQueue.enQueue(2); // 返回 true
circularQueue.enQueue(3); // 返回 true
circularQueue.enQueue(4); // 返回 false,队列已满
circularQueue.Rear(); // 返回 3
circularQueue.isFull(); // 返回 true
circularQueue.deQueue(); // 返回 true
circularQueue.enQueue(4); // 返回 true
circularQueue.Rear(); // 返回 4

提示:

所有的值都在 0 至 1000 的范围内;
操作数将在 1 至 1000 的范围内;
请不要使用内置的队列库。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/design-circular-queue
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

官解的方法,是存储了首部的指针和当前元素的个数
还有一种实现是建一个容量加1的数组,right指针指向的是末尾加1的位置

class MyCircularQueue {
    //用数组实现一下,空出一个位置
    int[] queue;
    int left;   //指向队列头部
    int right;  //尾部加1的位置,添加到队尾
    int len;
    public MyCircularQueue(int k) {
        len = k + 1;
        queue = new int[k + 1];
        left = 0;   
        right = 0;
    }
    
    public boolean enQueue(int value) {
        if(isFull())
            return false;
        queue[right] = value;
        right = (right + 1) % len;
        return true;
    }
    
    public boolean deQueue() {
        if(isEmpty())
            return false;
        left = (left + 1) % len;
        return true;
    
    }
    
    public int Front() {
        if(isEmpty())
            return -1;
        return queue[left];
    }
    
    public int Rear() {
        if(isEmpty())
            return -1;
        return queue[(right - 1 + len) % len];
        
    }
    
    public boolean isEmpty() {
        return left == right ? true : false;
    }
    
    public boolean isFull() {
        return (right + 1) % len == left ? true : false;
    }
}

/**
 * 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 Node head, tail;
    private int count;
    private int capacity;
    // Additional variable to secure the access of our queue
    private ReentrantLock queueLock = new ReentrantLock();

    /** Initialize your data structure here. Set the size of the queue to be k. */
    public MyCircularQueue(int k) {
        this.capacity = k;
    }

    /** Insert an element into the circular queue. Return true if the operation is successful. */
    public boolean enQueue(int value) {
        // ensure the exclusive access for the following block.
        queueLock.lock();
        try {
        if (this.count == this.capacity)
            return false;

        Node newNode = new Node(value);
        if (this.count == 0) {
            head = tail = newNode;
        } else {
            tail.nextNode = newNode;
            tail = newNode;
        }
        this.count += 1;

        } finally {
        queueLock.unlock();
        }
        return true;
    }
}

以上是关于LeetCode 863. 二叉树中所有距离为 K 的结点/ 641. 设计循环双端队列 / 622. 设计循环队列的主要内容,如果未能解决你的问题,请参考以下文章

leetcode中等863二叉树中所有距离为 K 的结点

力扣863. 二叉树中所有距离为 K 的结点

LeetCode 863. 二叉树中所有距离为 K 的结点/ 641. 设计循环双端队列 / 622. 设计循环队列

LeetCode 863 二叉树中所有距离为K的节点[Map 二叉树 DFS] HERODING的LeetCode之路

863. 二叉树中所有距离为 K 的结点

863. 二叉树中所有距离为 K 的结点