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 的结点/ 641. 设计循环双端队列 / 622. 设计循环队列