二叉搜索树
二叉搜索树的中序遍历是有序的。
中序遍历的利用
JZ36
二叉搜索树与双向链表
充分利用中序遍历的有序性构造有序的双向链表。

改造原始的中序遍历,避免构造额外的空间。核心的逻辑是:左中右中,中的前驱是左,后继是右子树。所以可以使用一共pre指针指向左节点,然后root的left指向pre,pre的right指向root。pre更新到root,pre设置为全局的变量。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 
 | 
 
 
 
 
 
 
 
 class Solution {
 public:
 TreeNode *  pre;
 TreeNode* Convert(TreeNode* pRootOfTree) {
 if(pRootOfTree==nullptr){
 return nullptr;
 }
 pre=nullptr;
 
 TreeNode * ret;
 ret=pRootOfTree;
 while(ret->left!=nullptr){
 ret=ret->left;
 }
 inorder(pRootOfTree);
 return ret;
 }
 void inorder(TreeNode * root){
 if(root->left!=nullptr){
 inorder(root->left);
 }
 
 root->left=pre;
 if(pre!=nullptr){
 pre->right=root;
 }
 pre=root;
 if(root->right!=nullptr){
 inorder(root->right);
 }
 }
 };
 
 
 | 
JZ8
二叉树的下一个结点
描述:
给定一个二叉树其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的next指针
要求:**空间复杂度 O*(1) **,时间复杂度 O(n)
分析可能存在的三种情况:
- 如果给出的结点有右子节点,则最终要返回的下一个结点即右子树的最左下的结点
- 如果给出的结点无右子节点,且当前结点是其父节点的左子节点,则返回其父节点
- 如果给出的结点无右子节点,且当前结点是其父节点的右子节点,则先要沿着左上方父节点爬树,一直爬到当前结点是其父节点的左子节点为止,返回的就是这个父节点;或者没有满足上述情况的则返回为NULL

| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 
 | 
 
 
 
 
 
 
 
 
 
 class Solution {
 public:
 TreeLinkNode* GetNext(TreeLinkNode* pNode) {
 if(pNode==nullptr)
 return nullptr;
 
 if(pNode->right!=nullptr){
 TreeLinkNode* cur=pNode->right;
 TreeLinkNode* pre=cur;
 while(cur!=nullptr){
 pre=cur;
 cur=cur->left;
 }
 return pre;
 }
 
 if(pNode->next==nullptr)
 return nullptr;
 if(pNode->next->left==pNode){
 return pNode->next;
 }
 
 TreeLinkNode* cur=pNode;
 while(cur->next!=nullptr&&(cur!=cur->next->left)){
 cur=cur->next;
 }
 if(cur==cur->next->left)
 return cur->next;
 else
 return nullptr;
 }
 
 };
 
 
 | 
利用搜索二叉树的有序性
JZ68
二叉搜索树的最近公共祖先

解答:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 
 | 
 
 
 
 
 
 
 class Solution {
 public:
 int lowestCommonAncestor(TreeNode* root, int p, int q) {
 
 queue<TreeNode *>myqueue;
 myqueue.push(root);
 int rettmp;
 int minnum=min(p,q);
 int maxnum=max(p,q);
 TreeNode *cur=root;
 
 while (cur!=nullptr) {
 if(maxnum<cur->val)
 cur=cur->left;
 else if(minnum>cur->val)
 cur=cur->right;
 else if(cur->val>=minnum&&cur->val<=maxnum){
 rettmp=cur->val;
 break;
 }
 
 }
 return rettmp;
 
 }
 
 };
 
 | 
JZ86
在二叉树中找到两个节点的最近公共祖先
递归思路:
分析遍历到任意的一个结点(之前没有找到过那两个结点),可能的情况分为四类:

- 该节点的左树上没找到目标节点---》说明结果在右子树上,继续遍历右子树
- 该节点的右树上没找到目标节点---》说明结果在左子树上,继续遍历左子树
- 该节点的左树上找到了目标节点,右边也找到了节点---》返回该节点
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 
 | 
 
 
 
 
 
 
 class Solution {
 public:
 int lowestCommonAncestor(TreeNode* root, int o1, int o2) {
 
 TreeNode * ret=subtask(root,o1,o2);
 return ret->val;
 }
 TreeNode * subtask(TreeNode* root, int o1, int o2){
 if(root==nullptr||root->val==o1||root->val==o2){
 return root;
 }
 TreeNode *left=subtask(root->left,  o1,  o2);
 TreeNode  * right=subtask(root->right,o1,o2);
 if(left==nullptr){
 return right;
 }
 if(right==nullptr){
 return left;
 }
 return root;
 }
 };
 
 | 
本题还可以使用hashmap和hashset暴力求解(思路比较简单此处不做陈述)
这种用hashmap记录的一条可以反向回去的链条的方法也是比较经典值得思考的。
代码如下:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 
 | #include <unordered_map>class Solution {
 public:
 int lowestCommonAncestor(TreeNode* root, int o1, int o2) {
 
 
 unordered_map<int,int> mymap;
 mymap[root->val]=-1;
 queue<TreeNode *>myqueue;
 myqueue.push(root);
 TreeNode * cur;
 while(!myqueue.empty()&&(mymap.find(o1)==mymap.end()||mymap.find(o2)==mymap.end())){
 cur=myqueue.front();
 myqueue.pop();
 if(cur->left!=nullptr){
 mymap[cur->left->val]=cur->val;
 myqueue.push(cur->left);
 }
 if(cur->right!=nullptr){
 mymap[cur->right->val]=cur->val;
 myqueue.push(cur->right);
 }
 }
 unordered_set<int> ancestor;
 
 int curnum=o1;
 while(mymap.find(curnum)!=mymap.end()){
 ancestor.insert(curnum);
 curnum=mymap[curnum];
 }
 curnum=o2;
 while(ancestor.find(curnum)==ancestor.end()){
 curnum=mymap[curnum];
 }
 return curnum;
 }
 
 };
 
 | 
利用层次遍历
JZ28
对称的二叉树
给定一棵二叉树,判断其是否是自身的镜像(即:是否对称)
对称的二叉树每一层都是回文的情况。那我们从左往右遍历一层(包括空节点),和从右往左遍历一层(包括空节点),应该得到相同的结果。而且我们不需要两个层次遍历都完整地遍历二叉树,只需要一半就行了,从左往右遍历左子树,从右往左遍历右子树,各自遍历一半相互比对,因为遍历到另一半都已经检查过了。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 
 | 
 
 
 
 
 
 
 class Solution {
 public:
 
 
 
 
 
 
 
 bool isSymmetrical(TreeNode* pRoot) {
 
 queue<TreeNode*> myqueue1,myqueue2;
 if(pRoot==nullptr)
 return true;
 myqueue1.push(pRoot->left);
 myqueue2.push(pRoot->right);
 int num=1;
 int numnew=0;
 while(!myqueue1.empty()){
 if(myqueue2.empty()){
 return false;
 }
 while(num!=0){
 num--;
 TreeNode * cur1=myqueue1.front();
 TreeNode * cur2=myqueue2.front();
 myqueue1.pop();
 myqueue2.pop();
 if(cur1==nullptr&&cur2==nullptr){
 continue;
 }
 if((cur1==nullptr&&cur2!=nullptr)||(cur1!=nullptr&&cur2==nullptr))
 return false;
 if(cur1->val!=cur2->val)
 return false;
 if(cur1->left!=nullptr&&cur2->right!=nullptr){
 myqueue1.push(cur1->left);
 myqueue2.push(cur2->right);
 numnew++;
 }
 if(cur2->left!=nullptr&&cur1->right!=nullptr){
 myqueue2.push(cur2->left);
 myqueue1.push(cur1->right);
 numnew++;
 }
 if((cur2->left==nullptr&&cur1->right!=nullptr)||(cur2->left!=nullptr&&cur1->right==nullptr)||(cur1->left==nullptr&&cur2->right!=nullptr)||
 ( cur1->left!=nullptr&&cur2->right==nullptr))
 return false;
 }
 num=numnew;
 numnew=0;
 
 }
 return true;
 }
 };
 
 | 
路径和问题
JZ82
二叉树中和为某一值的路径(一)
只判断路径是否存在的:

使用简单的递归就可以解决:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 
 | class Solution {public:
 
 
 
 
 
 
 
 
 bool hasPathSum(TreeNode* root, int sum) {
 if(root==nullptr){
 return false;
 }
 return subtask(root,sum);
 }
 bool subtask(TreeNode* root, int sum) {
 
 
 if(root->left==nullptr&&root->right==nullptr){
 if(sum-root->val==0)
 return true;
 else
 return false;
 }
 
 bool lefttry=false;
 bool righttry=false;
 if(root->left!=nullptr)
 lefttry=subtask(root->left,  sum-root->val);
 
 if(root->right!=nullptr)
 righttry=subtask(root->right,  sum-root->val);
 return lefttry||righttry;
 }
 };
 
 | 
JZ34
二叉树中和为某一值的路径(二)
进阶版本,需要给出所有的路径,需要用深度优先搜索有恢复现场的步骤
这个在DFS深度优先搜索博文中介绍过,如下链接:
解析博客
JZ84
二叉树中和为某一值的路径(三)
不再限制是不是从根节点到叶子节点的路径。要计算路径的数量

感觉有点动态规划的意思,不过这里不用改成动态规划,用递归解决即可
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 
 | 
 
 
 
 
 
 
 #include <cstdio>
 class Solution {
 public:
 
 
 
 
 
 
 
 
 int FindPath(TreeNode* root, int sum) {
 if(root==nullptr)
 return 0;
 
 int ret=0;
 ret+=subtask(root,  sum);
 if(root->right!=nullptr)
 ret+=FindPath(root->right,sum);
 if(root->left!=nullptr)
 ret+=FindPath(root->left,sum);
 return ret;
 }
 int subtask(TreeNode * root ,int target){
 
 
 
 
 
 
 
 
 
 
 int ret=0;
 if(root==nullptr)
 return 0;
 target-=root->val;
 if(target==0)
 ret++;
 ret+=subtask(root->right,target);
 ret+=subtask(root->left,target);
 return ret;
 }
 };
 
 |