二叉树进阶——二叉树OJ

为了巩固我们对于二叉树的理解本篇文章我们来练习一些二叉树的OJ题

一,根据二插入创建字符串

本题链接:根据二叉树创建字符串

在这里插入图片描述

1.2解析

根据上述本题可以转换成三句话

  • 创建字符串
  • 加入括号
  • 删除多余括号
    前面两步都比较简单,我们以第一个例子来看,在这里插入图片描述
    没有删除括号的是这样的,现在我们要开始删括号,删括号就分为了左右,转化一下思路,删括号==有条件的加括号
    对于左树来说,左数不为空要加括号,
    左数不为空分为了
  • 右树为空
  • 右树不为空

if(root->left || root->right)//这就涵盖了上面的两种

而对于右树来说就是,右树不为空就要加括号

1.3代码

class Solution {
public:
    string tree2str(TreeNode* root) {
        if(root==nullptr)
            return "";
        string str=to_string(root->val);
        //去括号
        //1.左右都为空
        //2.右为空
        if(root->left!=nullptr||root->right!=nullptr)//左为空or左不为空,右为空
        {
            str+='(';
            str+=tree2str(root->left);
            str+=')';
        }

        if(root->right!=nullptr)
        {
            str+='(';
            str+=tree2str(root->right);
            str+=')';
        }
        return str;
    }
};

二,二叉树的最近公共祖先

本题链接:二叉树的最近公共祖先

在这里插入图片描述

2.2解析

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.3代码

class Solution {
public:
    bool IsInTree(TreeNode* root, TreeNode* x)
    {
        if(root==nullptr)
            return false;
        
        return root==x
            ||IsInTree(root->left,x)
            ||IsInTree(root->right,x);
            
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(p==root||q==root)
            return root;
        bool qInleft,qInright,pInleft,pInright;
        qInleft=IsInTree(root->left,p);
        qInright=!qInleft;
        pInleft=IsInTree(root->left,q);
        pInright=!pInleft;

        //一个在左一个在右
        //两个都在左
        //两个都在右
        if((qInleft && pInright)||(qInright && pInleft))
        {
            return root;
        }
        else if(qInleft && pInleft)
        {
            return lowestCommonAncestor(root->left,p,q);
        }
        else if(qInright && pInright)
        {
            return lowestCommonAncestor(root->right,p,q);
        }
        return NULL;
    }
};

2.4第二种方法

我们可以记录下从根到p,q的路径,然后把他们各自的路径保存在栈中,栈中最先相等的值就是他们的最近公共祖先。
在这里插入图片描述
例如上图,我们取出了路径之后,让他们长的先走知道长度相同再一起走,直到出的值相同就得出祖先了.

class Solution {
public:
    bool GetPath(TreeNode* root, TreeNode* x,stack<TreeNode*>&Path)
    {
        if(root==nullptr)
            return false;
        Path.push(root);
        if(root==x)
            return true;
        if(GetPath(root->left,x,Path))
            return true;
        if(GetPath(root->right,x,Path))
            return true;  
        //走到这就是都没有
        Path.pop();  
        return false;    
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        stack<TreeNode*> Path1,Path2;
        GetPath(root,p,Path1);
        GetPath(root,q,Path2);
        //大的先走
        while(Path1.size()!=Path2.size())
        {
            if(Path1.size()>Path2.size())
            {
                Path1.pop();
            }
            else
            {
                Path2.pop();
            }
        }
        while(Path1.top()!=Path2.top())
        {
            Path1.pop();
            Path2.pop();
        }
        return Path1.top();
    }
};

三,二叉搜索树与双向链表

本题链接: 二叉树搜索树转换成排序双向链表

在这里插入图片描述

3.2解析

在这里插入图片描述
当前节点(cur)的左指向前一个节点
前一个节点(prev)的右指向当前节点
再把cur给prev向上走

3.3代码

class Solution {
public:
	void InOrderConvert(TreeNode*cur,TreeNode*&prev)
	{
		if(cur==nullptr)
			return;
		InOrderConvert(cur->left,prev);

		//当前节点的左指向前一个节点
		cur->left=prev;
		//前一个节点的右指向当前节点
		if(prev)
			prev->right=cur;

		prev=cur;

		InOrderConvert(cur->right,prev);
	}
    TreeNode* Convert(TreeNode* pRootOfTree) {
        TreeNode*prev=nullptr;
		InOrderConvert(pRootOfTree,prev);
		TreeNode* head=pRootOfTree;
		while(head->left&&head)
		{
			head=head->left;
		}
		return head;
    }
};

四,从前序与中序遍历序列构造二叉树

本题链接:从前序与中序遍历序列构造二叉树

在这里插入图片描述

4.2解析

前序:根,左子树,右子树
中序:左子树,根,右子树
前序确定根,中序确定左右子树
在这里插入图片描述
3为根,那么他的左右子树就确定了下来。
3的左边界为[0,1-1=0],右边界为[1+1=2,4]
同理我们在看9,
9的左边界就是[0,0-1=-1],右边界就是[0+1=1,0],很显然边界错误。
所以我们判断它是否有左右子树的条件就出来了。
在这里插入图片描述
我们用prei走前序,然后用rooti表示在中序中找到指定的根,找到了rooti的位置我们也就成功的把中序分成了三个部分,inbegin和inend就用来表示边界。
代码中的注释也可以方便理解。

4.3代码

class Solution {
public:
    TreeNode*_buildTree(vector<int>& preorder, vector<int>& inorder,int&prei,int inbegin,int inend)
    {
        if(inbegin>inend)
            return nullptr;
        TreeNode*root=new TreeNode(preorder[prei++]);
        //找到中序中指定的根
        int rooti=inbegin;
        while(rooti<=inend)
        {
            if(inorder[rooti]==root->val)
                break;
            else
                rooti++;
        }
        //此时我们就成功把中序分成了三个部分
        //[inbegin,rooti-1] rooti [rooti+1,inend]
        root->left=_buildTree(preorder, inorder,prei,inbegin,rooti-1);
        root->right=_buildTree(preorder, inorder,prei,rooti+1,inend);
        return root;
    }
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        int i=0;
        TreeNode* root=_buildTree(preorder, inorder,i,0,preorder.size()-1);
        return root;
    }
};

五,前序,中序,后序遍历(非递归)

5.1前序非递归

本题链接:
1 前序遍历非递归

在这里插入图片描述

5.1.2解析

在这里插入图片描述
前序遍历:根->左子树->右子树;我们先遍历左路节点,把他们存到栈中,然后取出各节点遍历右树。

5.1.3代码

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        stack<TreeNode*>s;
        vector<int> v;
        TreeNode* cur=root;
        while(cur||!s.empty())
        {
            //取左路节点
            while(cur)
            {
                v.push_back(cur->val);
                s.push(cur);
                cur=cur->left;
            }
            //获取左路节点
            TreeNode*top=s.top();
            s.pop();
            //遍历子问题
            cur=top->right;
        }
        return v;
    }
};

5.2中序非递归

2中序遍历非递归

在这里插入图片描述

5.2.2解析

这个和前序基本一样,但是要注意插入的位置,因为是先左子树再根,所以我们的vector的位置是在左路节点都进入栈之后才开始的。

5.2.3代码

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        stack<TreeNode*> s;
        vector<int> v;
        TreeNode* cur=root;
        while(cur||!s.empty())
        {
            //遍历左节点
            while(cur)
            {
                s.push(cur);
                cur=cur->left;
            }
            //获取左节点
            TreeNode* top=s.top();
            s.pop();
            //当我们取栈里面的数的时候,左子树就访问完了

            v.push_back(top->val);
            //开始遍历右树——子问题
            cur=top->right;
        }
        return v;
    }
};

5.3后续非递归

3 后续遍历非递归

在这里插入图片描述

5.3.2解析

在这里插入图片描述
后续更麻烦一些,例如上图我们还是先访问左路径,6右为空,返回6,到5的时候不能返回,因为他还有右子树,第二次到五才能返回,因为他的右子树访问结束了
所以我们可以有一个prev来记录上一个访问的节点,如果上一个访问的节点为现在这个节点的右子树说明他已经结束了

5.3.3代码

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        stack<TreeNode*> s;
        vector<int> v;
        TreeNode* cur=root;
        TreeNode* prev=nullptr;
        while(cur||!s.empty())
        {
            //遍历左节点
            while(cur)
            {
                s.push(cur);
                cur=cur->left;
            }
            //获取左节点
            TreeNode* top=s.top();
            //当我们取栈里面的数的时候,左子树就访问完了
            //右子树为空,或top的右为上一个访问的节点
            if(top->right==nullptr||top->right==prev)
            {
                v.push_back(top->val);
                s.pop();
                prev=top;
            }
            else
            {
                //开始遍历右树——子问题
                cur=top->right;
            }

        }
        return v;        
    }
};
  • 35
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tpoog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值