本文整理自:https://www.cnblogs.com/33debug/p/7252371.html
一、常见题型
1. 求两个节点的最近公共祖先;
2. 求二叉树中最远的两个节点的距离;
3. 由前序遍历和中序遍历重建二叉树(如:前序序列:1 2 3 4 5 6 - 中序序列 :3 2 4 1 6 5);
4. 判断一棵树是否是完全二叉树 ;
5. 将二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向;
6.求二叉树的宽度;
7. 判断一棵二叉树是否是平衡二叉树;
8.判断一颗二叉树是否是另一颗树的子树。
二、解题思路分析
1.两个节点的最近公共祖先
求两个节点的最近公共祖先可分为三种情况,分别为:
(1)搜索二叉树,根据搜索二叉树的性质,左子树的所有节点比根节点小,右子树的所有节点比跟节点大。
如果两个节点都比根节点小,则递归左子树 ;
如果两个节点都比跟节点大,则递归右子树 ;
否则,两个节点一个在左子树,一个在右子树,则当前节点就是最近公共祖先节点。
Node* GetAncestor(Node* root, Node* x1, Node* x2)//1.该二叉树为搜索二叉树 { assert(x1 && x2); if (x1->_data <= root->_data && x2->_data <= root->_data) { return GetAncestor(root->_left, x1, x2);//两个节都小于根节点,最近公共祖先在左子树中 } else if (x1->_data > root->_data && x2->_data > root->_data) { return GetAncestor(root->_right, x1, x2);//两个节都大于根节点,最近公共祖先在左子树中 } else return root; //一个在左子树,一个在右子树,找到公共祖先 }
(2)三叉链,二叉树节点有指向父节点的指针。
给定的两个节点都含有父节点,因此,可将这两个节点看做是两个链表的头结点,将求两个节点的最近公共祖先节点转化为求两链表的交点,这两个链表的尾节点都是根节点。
int Hight(BinaryNode* root, BinaryNode* node) { int len = 0; for (; node != NULL; node = node->_parent) len++; return len; } BinaryNode* GetLastCommonAncestor(BinaryNode* root, BinaryNode* node1, BinaryNode* node2) { if (root == NULL || node1 == NULL || node2==NULL) return NULL; int len1 = Hight(root,node1); int len2 = Hight(root,node2); for (; len1 > len2; len1--) node1 = node1->_parent; for (; len2 > len1; len2--) node2 = node2->_parent; while (node1 && node2 && node1 != node2) { node1 = node1->_parent; node2 = node2->_parent; } if (node1 == node2) return node1; else return NULL; }
3)普通二叉树
下面的方法时间复杂度为O(N),但是需要额外的空间来存储路径。
1) 找到从根到node1的路径,并存储在一个向量或数组中。
2)找到从根到node2的路径,并存储在一个向量或数组中。
3) 遍历这两条路径,直到遇到一个不同的节点,则前面的那个即为最低公共祖先.
bool GetNodePaths(Node* root, Node* node, stack<Node *>& s) { if (root == NULL) { return false; } s.push(root); if (root == node) { return true; } bool inleft = GetNodePaths(root->_left, node, s); if (inleft) { return true; } bool inright = GetNodePaths(root->_right, node, s); if (inright) { return true; } s.pop(); return false; } Node* GetAncestor(Node* root, Node* x1, Node* x2); { assert(x1 && x2); stack<Node*> paths1, paths2; if (!GetNodePaths(root->_left, x1, paths1) || !GetNodePaths(root->_right, x2, paths2)) { return NULL; } else{ while(paths1.size()>paths2.size()){ paths1.pop(); } while(paths1.size()<paths2.size()){ paths2.pop(); } while(!paths1.empty() && !paths2.empty() && paths1.top()!=paths2.top()){ if(paths1.top()==paths2.top()) return paths1.top(); paths1.pop(); paths2.pop(); } } return NULL; }
2.最远的两个节点的距离
第一种情况最远的两个节点的距离为它们到根节点的路径长度之和,又有可能距离最远的两个节点之间的路径不经过根节点,如图所示:
时间复杂度为O(N)的解法:
size_t _MaxLen(Node* root, size_t maxlen) //O(N) { if (root == NULL) { return; } size_t left = _MaxLen(root->_left, maxlen); size_t right = _MaxLen(root->_right, maxlen); if (right+left>maxlen) { maxlen = right + left; } return left > right ? left + 1 : right + 1; }
3. 前序遍历和中序遍历重建二叉树
这个题是要用一颗二叉树的前序遍历序列和中序遍历序列,如:前序序列:1 2 3 4 5 6 - 中序序列 :3 2 4 1 6 5,来重新构建二叉树。可以利用前序序列和中序序列中根节点的位置特性作为重建依据。图示解析过程如下:
创建右子树的方法与左子树的方法完全相同。当 prev 遍历完前序序列,即二叉树创建完成。代码如下:
//由前序遍历和中序遍历重建二叉树(如:前序序列:1 2 3 4 5 6 - 中序序列 :3 2 4 1 6 5) Node* RebulidTree(char* prev, char* inbgein, char* inend) { assert(prev && inbgein && inend); if (inbgein > inend || prev == '