LeetCode:863. All Nodes Distance K in Binary Tree
很有意思的一道树的题目了:
1.自己的做法。
首先是自己想出来的。自己拿个图画一下就知道了:找到与target相距K的节点,要么是target的子节点,要么是target的祖先节点以及它们的子节点当中去找。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> distanceK(TreeNode* root, TreeNode* target, int K) {
unordered_map<TreeNode*, int> m;
int height = -1;
getOriginNodes(m, root, target, height, K);
vector<int> ret;
for (auto item : m) {
auto node = item.first;
auto len = item.second;
if (len == K) {
ret.push_back(node->val);
continue;
}
if (m.find(node->left) == m.end())
dfs(node->left, len+1, ret, K);
if (m.find(node->right) == m.end())
dfs(node->right, len+1, ret, K);
}
return ret;
}
private:
void getOriginNodes(unordered_map<TreeNode*, int>& m, TreeNode* node, TreeNode* target, int& height, int K) {
if (target == node) {
height = 0;
m[node] = height;
return;
}
if (!node)
return;
getOriginNodes(m, node->left, target, height, K);
if (height >= 0) {
++height;
if (height <= K)
m[node] = height;
return;
}
getOriginNodes(m, node->right, target, height, K);
if (height >= 0) {
++height;
if (height <= K)
m[node] = height;
return;
}
}
void dfs(TreeNode* node, int len, vector<int>& ret, int K) {
if (!node || len > K)
return;
if (len == K) {
ret.push_back(node->val);
return;
}
dfs(node->left, len+1, ret, K);
dfs(node->right, len+1, ret, K);
}
};
//主要做法是这样:找到从root到target节点路径上的所有节点,记录下它们与target节点的距离。然后将它们中不在map中的子树进行遍历,找到长度为K的节点。
这属于这题的特殊做法。可以看到编码比较繁琐,而且容易出错的地方也比较多。
- 图的bfs
从评论区看来的:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> distanceK(TreeNode* root, TreeNode* target, int K) {
unordered_map<int, vector<int>> conn;
getConn(root, nullptr, conn);
vector<int> ret, aux;
ret.push_back(target->val);
unordered_set<int> visited;
visited.insert(target->val);
for (int d = 0; d < K; ++d) {
for (auto u : ret)
for (auto v : conn[u]) {
if (visited.count(v))
continue;
visited.insert(v);
aux.push_back(v);
}
ret.clear();
ret.swap(aux);
}
return ret;
}
private:
void getConn(TreeNode* node, TreeNode* parent, unordered_map<int, vector<int>>& conn) {
if (!node)
return;
if (parent) {
conn[node->val].push_back(parent->val);
conn[parent->val].push_back(node->val);
}
getConn(node->left, node, conn);
getConn(node->right, node, conn);
}
};
//这种解法也有意思,把所有有关联的关系都放起来,相当于一个邻接表吧。然后采用bfs的方法来做。
//这种将树看成图的做法属于比较通用,应该容易想到的做法。实际上,都应该想到图的bfs,dfs等。只不过之前没想过可以用这种保存相邻节点的关系来构成邻接表的做法。
//也可以把它的方法和我的结合起来。可以记录下亲子关系,儿子->父亲,可以一路向上找到源路径上的点。不过我的这种方法属于特殊方法,不一定能够想得起来。
3.结合第二种对第一种稍作修改,使编码更加简单。
主要用了记录每个节点的父节点的想法。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> distanceK(TreeNode* root, TreeNode* target, int K) {
unordered_map<TreeNode*, TreeNode*> m;
getParent(root, nullptr, m);
int len = 0;
auto node = target;
unordered_set<TreeNode*> visited;
vector<int> ret;
while (node) {
visited.insert(node);
if (len == K) {
ret.push_back(node->val);
return ret;
}
if (visited.find(node->left) == visited.end())
dfs(ret, node->left, len+1, K);
if (visited.find(node->right) == visited.end())
dfs(ret, node->right, len+1, K);
++len;
node = m[node];
}
return ret;
}
private:
//有点浪费空间了,把所有节点的父节点都加进来了,但是相对简单一点。
void getParent(TreeNode* node, TreeNode* parent, unordered_map<TreeNode*, TreeNode*>& m) {
if (!node)
return;
m[node] = parent;
getParent(node->left, node, m);
getParent(node->right, node, m);
}
void dfs(vector<int>& ret, TreeNode* node, int len, int K) {
if (len > K || !node)
return;
if (len == K) {
ret.push_back(node->val);
return;
}
dfs(ret, node->left, len+1, K);
dfs(ret, node->right, len+1, K);
}
};
最后,总的来说,第二种方法是最好的:
一三是针对这一题的特殊解法,可以看出比较难想出来(target的祖先节点的子节点这一点不容易想到),然后第二种做法是相当于首先把树转化为一个图,得出他的邻接表,这样很容易就把树的问题转化为一个图的问题。而一个图中距离一个点K个单位的点很容易就可以求出来了。
特殊的解法不好想出来,把思路带到熟悉的问题上最好。
从这题学到的两种手法:
1.记录一个节点的父和子节点,相当于创建一个邻接表。
2.记录一个节点的父节点,可以用来向上回溯。
从这题学到的思想:
可以把数看成图。
其实只要有这种想法,很快就可以想出是图论的问题。