二叉树(05)

题目一

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回

1、分析:关于树的遍历和重建基本都是将其进行不断的分解成规模更小,结构类似的问题。主要用到的是递归的思想来进行解决。

2、代码如下:

/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
        int len=pre.size();   // 元素的个数
        // 三种遍历的元素个数是相同的
        if(len<=0||pre.size()!=vin.size()){
            return NULL;
        }
        TreeNode* RootNode=new TreeNode(pre[0]);
        // 根节点在中序遍历中的位置
        int RootIndex=0;
        for(;RootIndex<len;RootIndex++){
            if(vin[RootIndex]==pre[0]){
                break;
            }
        }
        //由根节点将其分为左子树和右子树
        //左子树的前序遍历和中序遍历结果
        vector<int> frontPre(RootIndex);
        vector<int> frontVin(RootIndex);
        // 右子树的前序遍历和中序遍历结果
        vector<int> endPre(len-RootIndex-1);
        vector<int> endVin(len-RootIndex-1);
        for(int i=0;i<len;i++){
            if(i<RootIndex){
                frontPre[i]=pre[i+1];
                frontVin[i]=vin[i];
            }
            if(i>RootIndex){
                endPre[i-RootIndex-1]=pre[i];
                endVin[i-RootIndex-1]=vin[i];
            }
        }
        //用递归的方式来求得其左子树的根节点和右子树的根节点
        RootNode->left=reConstructBinaryTree(frontPre,frontVin);
        RootNode->right=reConstructBinaryTree(endPre,endVin);
        return RootNode;

    }
};

题目二

给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针

1、分析:根据该节点的位置大体上可以分为两种情况

(1)、该节点有右子树,则其下一个节点将是右子树的最左子树。

(2)、若该节点没有右子树,且其父节点不是空(即该节点不为根节点)。则此时需要不断的沿着父节点向上遍历,直到找到一个父节点的左子节点或者父节点为空时截至,此时的父节点即为该节点的下一个节点。即:该右节点所在的左子树的根节点或为空。

2、代码如下:

/*
struct TreeLinkNode {
    int val;
    struct TreeLinkNode *left;
    struct TreeLinkNode *right;
    struct TreeLinkNode *next;
    TreeLinkNode(int x) :val(x), left(NULL), right(NULL), next(NULL) {
        
    }
};
*/
class Solution {
public:
    TreeLinkNode* GetNext(TreeLinkNode* pNode)
    {
        //如果该节点为空
        if(pNode==nullptr)
            return nullptr;
        TreeLinkNode* pNext=nullptr;
        //如果该节点有右节点时
        if(pNode->right!=nullptr ){
            pNext=pNode->right;  //则其下一个节点就是右节点的最左节点
            // 当其左节点不为空时,继续求左节点
            while(pNext->left!=nullptr){
                pNext=pNext->left;
            }
        }
        //如果右节点为空,且父节点不为空。pNode->next为指向父节点的指针
        else if(pNode->right==nullptr && pNode->next!=nullptr){
            TreeLinkNode* pCurrent=pNode;
            TreeLinkNode* pParent=pNode->next;
            // 如果该节点的父节点不为空,且该节点为右节点,
            // 则会不断的沿着父节点向上遍历,直到找到其父节点的一个左子节点为止
            while(pParent!=nullptr && pCurrent==pParent->right){
                pCurrent=pParent;
                pParent=pParent->next;
            }
            pNext=pParent;
        }
        return pNext;
    }
};

题目三

输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

1、分析:

  • 首先判断B的根节点是否在A中,
  • 若在则递归的判断其接下来的左右节点

2、代码如下

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        bool result=false;
        if(pRoot1!=nullptr && pRoot2!=nullptr)
        {
            if(pRoot1->val==pRoot2->val)
            {
                result=SecondStep(pRoot1,pRoot2);
            }
            if(!result)
            {
                result=HasSubtree(pRoot1->left,pRoot2);
            }
            if(!result)
            {
                result=HasSubtree(pRoot1->right,pRoot2);
            }
        }
        return result;
    }
    bool SecondStep(TreeNode* pRoot1,TreeNode* pRoot2)
    {
        if(pRoot2==nullptr)
            return true;
        if(pRoot1==nullptr)
            return false;
        if(pRoot1->val!=pRoot2->val)
            return false;
        return SecondStep(pRoot1->left,pRoot2->left)&&SecondStep(pRoot1->right,pRoot2->right);
    }
    
};

题目四

操作给定的二叉树,将其变换为源二叉树的镜像

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    void Mirror(TreeNode *pRoot) {
        if(pRoot==nullptr)
            return;
        if(pRoot->left==nullptr && pRoot->right==nullptr)
            return;
        TreeNode* pTemp=pRoot->left;
        pRoot->left=pRoot->right;
        pRoot->right=pTemp;
        if(pRoot->left!=nullptr)
            Mirror(pRoot->left);
        if(pRoot->right)
            Mirror(pRoot->right);
    }
};

题目五:

从上往下打印出二叉树的每个节点,同层节点从左至右打印。

1、分析:

由于是逐层打印,可以通过队列(队列先进先出)来进行辅助。在打印一个节点时将其两个子节点(若存在)放入到容器中,这样按照先进先出的原则来实现分层打印。当容器中的元素个数为0时,则打印结束。

2、代码:

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    vector<int> PrintFromTopToBottom(TreeNode* root) {
        vector<int>res;
        if(!root)
            return res;
        
        // 尤其注意此处deque容器中存储的是指针类型的数据。
        deque<TreeNode*>deq;
        deq.push_back(root);
        while(deq.size()){
            TreeNode* proot=deq.front();
            res.push_back(proot->val);
            deq.pop_front();
            
            if(proot->left){
                deq.push_back(proot->left);
            }
            if(proot->right){
                deq.push_back(proot->right);
            }
        }
        return res;
    }
};

题目六

从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行

1、分析

将每一层打印成一行,这样需要两个变量来保存每行的元素个数,当改行打印完就进行换行。

  

2、代码

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
        vector<vector<int> > Print(TreeNode* pRoot) {
            vector<int>cur; // 临时存放每行的元素
            vector<vector<int> >res; //最终的结果
            if(pRoot==nullptr)
                return res;
            deque<TreeNode *>d;
            d.push_back(pRoot);
            int lev=0;
            int toBePrint=1;
            while(!d.empty())
            {
                TreeNode* p=d.front();
                cur.push_back(p->val);
                d.pop_front();
                
                if(p->left)
                {
                    d.push_back(p->left);
                    ++lev;
                }
                if(p->right)
                {
                    d.push_back(p->right);
                    ++lev;
                }
                --toBePrint; //打印一个元素就让元素的个数递减一个
                if(toBePrint==0) // 当改行元素打印完后,进行换行
                {
                    toBePrint=lev;
                    lev=0;
                    res.push_back(cur); // 将这一行的元素存放到结果中
                    cur.clear(); // 将临时存放一行元素的容器清空,接着来存放下一行元素
                }
            }
            return res;
        }
};

题目七

请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推

  

1、分析:

由于数据是先进后出,所以可以得出其使用的是两个栈来进行辅助。第2层和第3层在栈中的位置如下图

     

2、代码

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
    vector<vector<int> > Print(TreeNode* pRoot) {
        vector<int>cur;
        vector<vector<int> >res;
        if(pRoot==nullptr)
            return res;
        //定义两个栈分别用来保存树每层的数据
        stack<TreeNode *>s[2];
        int current=0;
        int next=1;
        s[current].push(pRoot);
        while(!s[current].empty()||!s[next].empty())
        {
            TreeNode *p=s[current].top();
            s[current].pop();
            cur.push_back(p->val);
            if(current==0)
            {
                if(p->left)
                    s[next].push(p->left);
                if(p->right)
                    s[next].push(p->right);
            }else
            {
                if(p->right)
                    s[next].push(p->right);
                if(p->left)
                    s[next].push(p->left);
            }
            if(s[current].empty())
            {
                res.push_back(cur);
                cur.clear();
                current=1-current;
                next=1-next;
            }
        }
        return res;
    }
};

题目八

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

1、分析

二叉搜索树的左子树都小于根节点,右子树都大于根节点,其后序遍历的最后一个元素就是根节点。左右子树都是二叉排序树。可以采用递归的方式来进行求解。

2、代码

class Solution {
public:
    bool VerifySquenceOfBST(vector<int> sequence) {
        int len=sequence.size();
        if(len<=0)
            return false;
        return Bst(sequence,0,len-1);
    }
    bool Bst(vector<int>seq,int begin,int end)
    {
        if(begin>end)
            return false;
        // 左子树的位置为[0,i-1],根节点为seq[end]
        int i=0;
        for(;i<end;++i)
        {
            if(seq[i]>seq[end])
                break;
        }
        // 右子树的位置为[i,end-1]
        int j=i;
        for(;j<end;++j)
        {
            if(seq[j]<seq[end])
                return false;
        }
        // 再递归的判断左子树和右子树是否为二叉排序树
        bool leftb=true,rightb=true;
        if(i>begin)
            leftb=Bst(seq,begin,i-1);
        if(i<end)
            rightb=Bst(seq,i,end-1);
        
        return (leftb&&rightb);
    }
};

题目九

输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径

1、分析:

从根节点出发,分别进行遍历,一直到叶节点。若这条路径上的所有节点的值相加之和,等于输入的值,则该条路径满足要求。所以可以使用前序遍历来进行。

2、代码

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    vector<vector<int> >res;
    vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
        vector<int>cur;
        if(root==nullptr)
            return res;
        FindPath(root,expectNumber,cur,0);
        return res;
    }
    void FindPath(TreeNode* pRoot,int resNum,vector<int>path,int sum)
    {
        sum+=pRoot->val;
        path.push_back(pRoot->val);
        bool isLeaf=(pRoot->left==nullptr)&&(pRoot->right==nullptr);
        if(sum==resNum && isLeaf)
        {
            res.push_back(path);
        }
        if(pRoot->left!=nullptr)
            FindPath(pRoot->left,resNum,path,sum);
        if(pRoot->right!=nullptr)
            FindPath(pRoot->right,resNum,path,sum);
        // 此处不需要恢复sum的值,因为在每次调用时就自动进行了恢复。
    }
};

题目十

1、分析:使用前序遍历的方式来对二叉树树进行序列化和反序列化,注意为了方便可以将char类型的字符串转换成string 类型的字符串

2、代码:

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
    char* Serialize(TreeNode *root) {    
        if(root==nullptr)
        {
            char *serial=new char[3];
            strcpy(serial,"#!");//将字符串“#!”拷贝到serial数组中,并返回指针serial
            return serial;
        }
        string str;
        Serialize(root,str);
        // 将string类型字符串转换成C语言形式的字符串char
        char *cstr=new char[str.length()+1];
        strcpy(cstr,str.c_str());
        return cstr;
    }
    
    TreeNode* Deserialize(char *str) {
        if(str==nullptr||*str=='\0')
        {
            return nullptr;
        }
        string cStr(str); //将char类型字符串转换成string类型字符串
        return Deserialize(cStr);
    }
    
private:
    void Serialize(TreeNode *root, string &str)
    {
        if(root==nullptr)
        {
            str+="#!";
            return;
        }
        str += (to_string(root->val)+"#!");
        Serialize(root->left,str);
        Serialize(root->right,str);
    }
    TreeNode *Deserialize(string &str)
    {
        if(str.empty())
        {
            return nullptr;
        }
        if(str[0]=='#')
        {
            str=str.substr(2);
            return nullptr;
        }
        TreeNode *root=new TreeNode(stoi(str));
        str=str.substr(str.find('!')+1);
        root->left=Deserialize(str);
        root->right=Deserialize(str);
        return root;
    }
};

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值