这是今晚做的英伟达的一道笔试题,思路不是很难,但是写起来却写出了很多bug。突然想起来同类型的题目之前面试的时候也出现过,但是没有放在心上,面试完就忘了。今天重新写一点都不熟练。
我的思路很直接,就是先找出最大的叶节点和最小的叶节点,然后分别找出从root到这两个节点的路径。然后根据路径上的节点是否相同,判读路径是否出现分叉,从而根据此计算两节点之间的距离。
做题的时候出现了两个bug:
1、求最大节点和最小节点忘了要传值引用,导致找完回到调用函数值又没了。一个比较低级的错误。
2、找路径的方法不太对。因为前序遍历的代码是现成的,我的思路就在前序遍历的框架上找路径,但是后来发现不太对,出栈的顺序和路径差别还挺大的,不好改,改了半天发现都不太对。
结束之后重新写了找路径的函数,舍弃迭代还是用了递归,比较容易想清楚。如果在一个节点的左子树或者右子树能找到目标节点,那么该节点就在路径上,将其加入到一个列表中。递归基就是当前节点是目标节点的情况,将当前节点加入列表,并返回。
#include <iostream>
#include<vector>
#include<string>
#include<sstream>
#include<list>
#include<stack>
#include<algorithm>
using namespace std;
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
//迭代的前序遍历获取最大值最小值节点
void preorder(TreeNode* p, TreeNode*& pMin, TreeNode*& pMax) {
if (p == NULL)
return;
stack<TreeNode*> s;
while (p != NULL || !s.empty()) {
while (p) {
s.push(p);
//要求是叶子节点才需要左右都是空
if (p->left == NULL && p->right == NULL && p->val > pMax->val) {
pMax = p;
}
if (p->left == NULL && p->right == NULL && p->val < pMin->val) {
pMin = p;
}
p = p->left;
}
p = s.top()->right;
s.pop();
}
}
//获取从根到树中某个节点的路径
bool getPath(TreeNode* p, TreeNode* target, vector<TreeNode*>& arr) {
bool left = false;
bool right = false;
if (p == target) {
arr.push_back(p);
return true;
}
if (p->left) {
left = getPath(p->left, target, arr);
}
if (p->right) {
right = getPath(p->right, target, arr);
}
if (left || right) {
arr.push_back(p);
}
return left || right;
}
int getDis(TreeNode* root) {
// write code here
//先找到最大最小的节点
TreeNode* pMin = new TreeNode(INT_MAX);
TreeNode* pMax = new TreeNode(INT_MIN);
preorder(root, pMin, pMax);
vector<TreeNode*> list1;
bool tmp = getPath(root, pMin, list1);
vector<TreeNode*> list2;
tmp = getPath(root, pMax, list2);
int len1 = list1.size();
int len2 = list2.size();
reverse(list1.begin(), list1.end());
reverse(list2.begin(), list2.end());
int result = len1 + len2 - 2;
for (int i = 1; i < min(len1, len2); i++) {
if (list1[i] == list2[i]) {
result -= 2;
}
else {
break;
}
}
return result;
}
int main() {
TreeNode* root = new TreeNode(12);
root->left = new TreeNode(10);
root->right = new TreeNode(16);
root->left->left = new TreeNode(3);
root->left->right = new TreeNode(19);
root->right->left = new TreeNode(22);
root->right->right = new TreeNode(15);
root->left->left->left = new TreeNode(1);
root->left->left->right = new TreeNode(6);
root->left->right->left = new TreeNode(2);
root->left->right->right = new TreeNode(9);
root->right->left->left = new TreeNode(7);
root->right->left->right = new TreeNode(20);
root->right->right->left = new TreeNode(18);
root->right->right->right = new TreeNode(8);
root->left->left->left->left = new TreeNode(13);
root->left->left->left->right = new TreeNode(14);
root->left->left->right->left = new TreeNode(11);
root->left->left->right->right = new TreeNode(23);
root->left->right->left->left = new TreeNode(4);
root->left->right->left->right = new TreeNode(17);
root->left->right->right->left = new TreeNode(21);
root->left->right->right->right = new TreeNode(5);
int result = getDis(root);
cout << result << endl;
return 0;
}
除了这种直接找出路径的,在根据路径上节点是否相同来判断是否分叉,从而计算距离之外,还可以直接计算最低公共祖先的办法来计算距离。两个节点之间的距离就是他们分别到最低公共祖先的距离之和。最低公共祖先的代码如下,是网上找的。
LCA的思路
如果给定root是NULL,即空树,则返回的公共节点是NULL;
如果给定的root与两个节点中的任何一个相同,说明,root就是要找的两个节点之一,则直接返回root,表明在当前链路中找到至少一个节点;
如果给定root不是两个节点中任何一个,则说明,需要在root的左右子树中重新查找,此时有三种情况:两个节点都在左子树上;两个节点都在右子树上;一个在左子树上,一个在右子树上;具体来说,就是:
- 如果左子树查找出来的公共节点是NULL,则表明从左子树根节点开始到左子树的虽有叶节点等所有的叶节点中,没有找到两个节点中的任何一个,这就说说明,这两个节点不在左子树上,则必定都在右子树上。
- 如果右子树查找的公共节点是NULL,则两个节点必都在左子树上;
- 如果左右子树查找的公共节点都不是NULL,说明左右子树中各包含一个节点,则当前节点root就是最低公共节点,直接返回。
TreeNode* LCA(TreeNode* root, TreeNode* p1, TreeNode* p2){
if(root == NULL){
return NULL;
}
if(root == p1 || root == p2){
return root;
}
TreeNode* left = LCA(root->left, p1, p2);
TreeNode* right = LCA(root->right, p1, p2);
if(left == NULL){
return right;
}
if(right == NULL){
return left;
}
return root;
}