情况1. 节点只有left/right,没有parent指针,root已知
情况2. root未知,但是每个节点都有parent指针
情况3. 二叉树是个二叉查找树,且root和两个节点的值(a, b)已知
虽然情况一是第一个情况,但是看上去比较复杂,我们放到最后来说,先从第二个情况开始说。
10
/ \
6 14
/ \ / \
4 8 12 16
/ \
3 5
画一个二叉树来做例子。如果我们要找3和8这两个节点的公共父亲节点,我们的做法是首先找到3到根节点的路劲,然后找到8到根节点的路径。
10
// \
6 14
/ \ / \
4 8 12 16
/ \
3 5
3的路径用红色表示,8的用绿色表示,可以看到, 这里的问题实际上是另一个我们熟知的问题,有2个相交的单链表,找出它们的相交点!
只要把这个二叉树的图片倒过来看,或者把脖子倒过来看就知道了:)那个方法也是传统的求出linkedList A的长度lengthA, linkedList B的长度LengthB。然后让长的那个链表走过abs(lengthA-lengthB)步之后,齐头并进,就能解决了。
- int getLength (bstNode* pNode)
- {
- int length = 0;
- bstNode* pTemp = pNode;
- while (pTemp)
- {
- length ++ ;
- pTemp = pTemp->pParent;
- }
- return length;
- }
- bstNode* findLCACase2(bstNode* pNode1, bstNode* pNode2)
- {
- int length1 = getLength(pNode1);
- int length2 = getLength(pNode2);
-
-
- bstNode* pIter1 = NULL;
- bstNode* pIter2 = NULL;
- int k=0;
- if (length1>=length2)
- {
- bstNode* pTemp = pNode1;
- while (k++<length1-length2)
- {
- pTemp = pTemp->pParent;
- }
- pIter1 = pTemp;
- pIter2 = pNode2;
- }
- else
- {
- bstNode* pTemp = pNode1;
- while (k++<length2-length1)
- {
- pTemp = pTemp->pParent;
- }
- pIter1 = pNode1;
- pIter2 = pTemp;
- }
-
- while (pIter1&&pIter2&&pIter1!= pIter2)
- {
- pIter1 = pIter1->pParent;
- pIter2 = pIter2->pParent;
- }
- return pIter1;
- }
自己写了个代码,总觉得有些拖沓冗余,希望有缘人看到文章之后能帮我改写的更和谐一些。
还是原来这个图,情况三,如果是个二叉搜索树,而且root和a, b已知,我们这个case假设a,b=3,8。从知道根这个条件我们很自然联想到递归(当然不递归也可以)地往下找。关键是收敛条件,什么情况下可以判断 出当然检查的这个节点是最近父亲节点呢?其实从这个例子已经可以看出一些端倪了,如果当前访问的节点比a,b来的都小,肯定不行。如果比a,b都大,也不 行。那也就是说,这个节点只有在a<=node<=b的区间内才成立(我们假定a<b这里)。这样的问题,网上广为流传着类似的代码:
- bstNode* findLCACase3(bstNode* pNode, int value1, int value2)
- {
- bstNode* pTemp = pNode;
- while (pTemp)
- {
- if (pTemp->data>value1 && pTemp->data>value2)
- pTemp = pTemp->pLeft;
- else if (pTemp->data<value1 && pTemp->data<value2)
- pTemp = pTemp->pRight;
- else
- return pTemp;
- }
- return NULL;
- }
好,前面的问题都解决了,我们再回过头来看第一个情况,只有ROOT和left, right节点,没有parent也不是排序树,怎么办?网络上也流传着很多所谓的LCA,RMQ算法,我们不暇找个最合适的,尤其是在面试的时候,特定 时间空间下你很难写出一个逻辑非常复杂的东西(比如你会在面试的时候去实现一个Suffix Tree还是用动态规划来求最长公共子串,哪怕效率不同,我也选择动态规划:))。所以这里,碰到类似的问题的时候,我选择简单的记录找到node1和 node2的路径,然后再把它们的路径用类似的情况二来做分析,比如还是node1=3,node2=8这个case.我们肯定可以从根节点开始找到3这 个节点,同时记录下路径3,4,6,10,类似的我们也可以找到8,6,10。我们把这样的信息存储到两个vector里面,把长的vector开始的多 余节点3扔掉,从相同剩余长度开始比较,4!=8, 6==6, coooool,我们找到了我们的答案。下面的代码完全按照这个思路写成
- #include <vector>
- bool nodePath (bstNode* pRoot, int value, std::vector<bstNode*>& path)
- {
- if (pRoot==NULL) return false ;
- if (pRoot->data!=value)
- {
- if (nodePath(pRoot->pLeft,value,path))
- {
- path.push_back(pRoot);
- return true ;
- }
- else
- {
- if (nodePath(pRoot->pRight,value,path))
- {
- path.push_back(pRoot);
- return true ;
- }
- else
- return false ;
- }
- }
- else
- {
- path.push_back(pRoot);
- return true ;
- }
- }
- bstNode* findLCACase1(bstNode* pNode, int value1, int value2)
- {
- std::vector<bstNode*> path1;
- std::vector<bstNode*> path2;
- bool find = false ;
- find |= nodePath(pNode, value1, path1);
- find &= nodePath(pNode, value2, path2);
- bstNode* pReturn=NULL;
- if (find)
- {
- int minSize = path1.size()>path2.size()?path2.size():path1.size();
- int it1 = path1.size()-minSize;
- int it2 = path2.size()-minSize;
- for (;it1<path1.size(),it2<path2.size();it1++,it2++)
- {
- if (path1[it1]==path2[it2])
- {
- pReturn = path1[it1];
- break ;
- }
- }
- }
- return pReturn;
- }
这段代码经历了大概30分钟的修改和debug,然后才逐渐稳定下来,真的很难想象如果是在面试的环境下,在纸笔之上会有如何的表现,可能真是只有天知道了。
分享到:
相关推荐
- **最短路径算法**:Dijkstra算法、Bellman-Ford算法,用于找出图中两个节点间的最短路径。 - **最小生成树算法**:Prim算法、Kruskal算法,用于找寻连接图中所有节点的权值最小的树。 - **动态规划**:通过分解...
二叉树是最简单的树形结构,每个节点最多有两个子节点。图是由节点和连接这些节点的边组成,可以用来表示复杂的网络关系。 接下来,我们讨论这些数据结构的操作。例如,查找操作在数组中通常可以通过索引直接完成,...
二叉排序树(BST)是一种特殊的二叉树,满足左子节点小于父节点,右子节点大于父节点。演示可能会详细解释删除节点时如何保持树的平衡和排序性质。 7. **B树的生长过程.swf**: B树是一种自平衡的多路搜索树,...
- 最长公共子序列(LCS):寻找两个序列中最长的不降序的公共子序列,用于比较文本相似性。 - 状态转移方程:根据问题特性构建状态转移矩阵或方程,以解决复杂问题,如矩阵链乘法。 5. **字符串处理**: - KMP...
2.将铺平的左子树赋予根节点的右子树,再创建右子树的temp 并遍历到末尾 在末尾将铺平的右子树插入(根节点右子树中) 3.返回根节点 4.(Android) 一对多关系 找寻decorView下的特定id的子view 思想: 获取其子view的...
查找算法在实际应用中无处不在,例如在数据库中寻找特定记录、在字典中查找单词或在电话簿中找寻联系人。根据描述,该压缩包可能包含了第九章的例题解答,这些解答可以帮助读者更好地理解和掌握查找算法的实现。 ...
- 最长公共子序列:寻找两个序列中没有顺序关系的最长相同子序列。 - 矩阵链乘法:优化多个矩阵相乘的计算过程,降低计算复杂度。 5. 图论算法: - 广度优先搜索(BFS):用于遍历无权图,常用于寻找最短路径...
- 树和二叉树:树是一种层次结构,二叉树是特殊的树,每个节点最多有两个子节点。它们在文件系统、编译器设计和图形结构中有广泛应用。例如,Huffman编码利用二叉树实现数据压缩,拓扑排序用于任务调度。 - 图:由...
- **二叉树的构建与遍历**:从文件中读取二叉树节点信息并建立二叉树,可能还需要进行前序、中序或后序遍历。 3. **形式语言与自动机理论**: - **正规式**:题目要求找到一个正规式,使得其表示的语言与给定的...
- **链表**:由一系列节点组成,每个节点包含数据和指向下一个节点的引用。 - **栈**:后进先出(LIFO)的数据结构,常用于函数调用、表达式求值等。 - **队列**:先进先出(FIFO)的数据结构,用于模拟各种等待...
- 最长公共子序列:寻找两个序列最长的子序列,它在原序列中都存在,但不一定连续。 - 矩阵链乘法:通过优化运算顺序减少计算矩阵乘法的时间复杂度。 4. **图论算法**: - 深度优先搜索(DFS):沿着图的一条...
- **KMP算法**:在两个字符串中寻找最长公共子序列,避免了多余的回溯。 - **Rabin-Karp滚动哈希**:用于字符串匹配,通过哈希函数快速计算子串的哈希值,减少比较次数。 5. **图论**: - **最小生成树(MST)**:...
- Dijkstra算法:求解单源最短路径问题,每次都选择当前未访问节点中距离源节点最近的一个进行处理。 - Huffman编码:用于数据压缩,通过构建最优的二叉树来实现字符的高效编码。 贪婪算法的优势在于其简单性和效率...
- **快速排序**:采用分治策略,以一个元素为基准,将数组分为两部分,然后递归排序,平均时间复杂度为O(n log n)。 - **归并排序**:同样采用分治策略,将数组合并成有序序列,时间复杂度为O(n log n)。 - **...
- **链表**:每个节点包含数据和指向下一个节点的引用,便于插入和删除,但随机访问较慢。 - **栈**:后进先出(LIFO)的数据结构,主要用于函数调用、表达式求值等场景。 - **队列**:先进先出(FIFO)的数据...
- **找寻树**和**平衡二叉树**:虽然不直接用于贪吃蛇,但在类似游戏设计中可能用于优化查找和存储。 - **队列**:可以用于实现蛇的移动序列,确保每次只移动头部,然后更新身体位置。 - **串**(字符串):可能...