剑指offer—树问题

面试题7:重建二叉树

面试题8:二叉树的下一个节点

面试题26:树的子结构

面试题27:二叉树的镜像

面试题28:对称的二叉树

面试题32:从上到下打印二叉树

                   按之字形打印二叉树

                   分行打印二叉树

面试题33:二叉树的后序遍历系列

面试题34:二叉树中和为某一值的路径

面试题36:二叉搜索树与双向链表

面试题37:序列化二叉树

面试题54:二叉搜索树的第K大字节

面试题55:二叉树的深度

                  平衡二叉树

 

1、面试题7:重建二叉树

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。

思路:用前序遍历确定根节点值(前序遍历的第一个数值),用中序遍历确定根节点值的位置。(先找到根结点,再划分四组依次展开递归调用

class Solution {
public:
    TreeNode* reConstructBinaryTree(vector<int> per,vector<int> vin) {
        if(per.size()<=0||vin.size()<=0)
            return NULL;
         
        int value=per[0];
        TreeNode* node=new TreeNode(value);
             
        vector<int>vin_left;vector<int>vin_right;
         
        vector<int>::iterator ite_t=vin.begin();   //迭代器
       
        //中序遍历拆分
        while(*ite_t!=value)
            {
            vin_left.push_back(*ite_t);
            ++ite_t;
            }
        ++ite_t;
        while(ite_t!=vin.end())
            {
            vin_right.push_back(*ite_t);
            ++ite_t;
            }
 
 
        vector<int>per_left;  vector<int>per_right;
        int i=1;
    
        //前序遍历拆分
        for(;i<=vin_left.size();++i)
            per_left.push_back(per[i]);
        for(;i<per.size();++i)
            per_right.push_back(per[i]);
         
        node->left=reConstructBinaryTree(per_left,vin_left);
        node->right=reConstructBinaryTree(per_right,vin_right);
         
        return node;
    }
};

2、面试题8:二叉树的下一个节点

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

思路:两种情况:1.节点有右子树,那么他的下一个节点就是其右子树中最左子节点。

2.节点不是根节点。如果该节点为其父节点的左子树,则返回父节点;否则继续向上遍历其父节点的父节点,重复之前的判断。

class Solution {
public:
    TreeLinkNode* GetNext(TreeLinkNode* pNode)
    {
       if(pNode==NULL)
           return NULL;

        if(pNode->right!=NULL)               //如果有右子树,则找右子树的最左节点
        {
             pNode=pNode->right;
            while(pNode->left!=NULL)
                pNode=pNode->left;
            return pNode;
        }

        while(pNode->next!=NULL)
        {
            TreeLinkNode* proot=pNode->next;  //如果没右子树,则找第一个当前节点是父节点左孩子的节点
            if(proot->left==pNode)
                return proot;
            pNode=pNode->next;
        }
        return NULL;
 
    }
};

3、面试题26:树的子结构

输入两棵二叉树A,B,判断B是不是A的子结构

思路:第一步,在树A中找到和树B的根节点值一样的节点R;第二步,判断树A中以R为节点的子树是否包含和树B一样的结构。

注:注意边界条件检查。

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

4、面试题27:二叉树的镜像

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

思路:交换根节点的两个子节点

class Solution {
public:
    void Mirror(TreeNode *pRoot) {
        if(pRoot == nullptr)
            return;
        if(pRoot->left == nullptr && pRoot->right == nullptr)
            return;
        TreeNode *pNode;
        pNode = pRoot->left;
        pRoot->left = pRoot->right;
        pRoot->right = pNode;
         
        if(pRoot->left != nullptr)
            Mirror(pRoot->left);
        if(pRoot->right != nullptr)
            Mirror(pRoot->right);
 
    }
};

5、面试题28:对称的二叉树

请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。

思路:应用前序遍历(根->左->右)和他的对称遍历算法(根->右->左)

class Solution {
public:
    bool isSymmetrical(TreeNode* pRoot)
    {
        return isSymmetrical(pRoot,pRoot);
    }
     
    bool isSymmetrical(TreeNode* pRoot1,TreeNode* pRoot2)
    {
        if(pRoot1 == nullptr && pRoot2 == nullptr)
            return true;
        if(pRoot1 == nullptr || pRoot2 == nullptr)
            return false;
         
        if(pRoot1->val != pRoot2->val)
            return false;
        return isSymmetrical(pRoot1->left,pRoot2->right) && isSymmetrical(pRoot1->right,pRoot2->left);
    }
 
};

6、面试题32:从上到下打印二叉树(不分行); 按之字形打印二叉树;分行打印二叉树

1)从上到下打印二叉树(不分行)

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

思路:建立一个数据容器队列,每次打印一个节点时,如果该节点有子节点,则把该节点的子节点放到队列末尾,然后重复操作。

class Solution {
public:
    vector<int> PrintFromTopToBottom(TreeNode* root) {
        vector<int> result;
        if(root == nullptr)
            return result;
             
        deque<TreeNode*> dequeroot;
        dequeroot.push_back(root);
        while(dequeroot.size())
        {
            TreeNode* pNode = dequeroot.front();
            result.push_back(pNode->val);
            dequeroot.pop_front();
             
            if(pNode->left)
                dequeroot.push_back(pNode->left);
            if(pNode->right)
                dequeroot.push_back(pNode->right);
        }
        return result;
 
    }
};

2)按之字形打印二叉树

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

思路:定义两个栈,打印某一节点时,将其子节点压入另一栈内。栈1存放奇数层节点(压栈顺序从右->左),栈2存放偶数层节点(压栈顺序从左->右)。

class Solution {
public:
    vector<vector<int> > Print(TreeNode* pRoot) {
        vector<vector<int> > result;
        if(pRoot == nullptr)
            return result;
         
        stack<TreeNode*> stack1,stack2;
         
        stack1.push(pRoot);
         
        while(!stack1.empty() || !stack2.empty())
        {
            vector<int> data;
            if(!stack1.empty())
            {
                while(!stack1.empty())
                {
                    TreeNode* pNode = stack1.top();
                    data.push_back(pNode->val);
                    stack1.pop();
                    if(pNode->left)
                        stack2.push(pNode->left);
                    if(pNode->right)
                        stack2.push(pNode->right);
                }
                result.push_back(data);
            }
           else if(!stack2.empty())
            {
                while(!stack2.empty())
                {
                    TreeNode* pNode = stack2.top();
                    data.push_back(pNode->val);
                    stack2.pop();
                    if(pNode->right)
                        stack1.push(pNode->right);
                    if(pNode->left)
                        stack1.push(pNode->left);
                }
                result.push_back(data);
            }
        }
        return result;
    }
};

3)分行打印二叉树:

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

思路:定义一个队列,再定义一计数器记录每层节点数。

class Solution {
public:
        vector<vector<int> > Print(TreeNode* pRoot) {
        vector<vector<int> > vec;
            if(pRoot == NULL) return vec;
  
            queue<TreeNode*> q;
            q.push(pRoot);
  
            while(!q.empty())
            {
                int count = 0, hi = q.size();
                vector<int> data;
                while(count++ < hi)
                {
                    TreeNode *tmp = q.front();
                    q.pop();
                    data.push_back(tmp->val);
                    if(tmp ->left) q.push(tmp->left);
                    if(tmp ->right) q.push(tmp->right);
                }
                vec.push_back(data);
            }
            return vec;
        }
     
};

7、面试题33:二叉搜索树的后序遍历系列

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

思路:后序遍历得到的序列,最后一个数为树根节点数值。数组可分为两部分,第一部分是左子树值,其值皆小于根节点值;第二部分是右子树值,其值皆大于根节点值。采用递归

class Solution {
public:
    bool VerifySquenceOfBST(vector<int> sequence) {
        int length = sequence.size();
        if(length <= 0)
            return false;
        int i=0,j;
        int root = sequence[length-1];
        vector<int> preseq,nextseq;
          
        bool pre = true,next = true;
          
        while(root > sequence[i])
        {
            preseq.push_back(sequence[i]);
            ++i;
        }
        j=i;
        while(root < sequence[j])
        {
            nextseq.push_back(sequence[j]);
            ++j;
        }
        if(j < length-2)
            return false;
        if(i>0)
            pre = VerifySquenceOfBST(preseq);
        if(j<length-1)
            next = VerifySquenceOfBST(nextseq);
          
        return pre && next;
 
    }
};

8、面试题34:二叉树中和为某一值的路径

输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)

思路:

class Solution {
public:
    vector<vector<int> > buffer;
    vector<int> tmp;
    vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
        if(root==NULL)
            return buffer;
        tmp.push_back(root->val);
        if((expectNumber-root->val)==0 && root->left==NULL && root->right==NULL)
            {
            buffer.push_back(tmp);
        }
        FindPath(root->left,expectNumber-root->val);
        FindPath(root->right,expectNumber-root->val);
        if(tmp.size()!=0)
            tmp.pop_back();
        return buffer;
    }
};

9、面试题36:二叉搜索树与双向链表

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

思路:中序遍历,

class Solution {
public:
    TreeNode* Convert(TreeNode* pRootOfTree)
    {
        if(pRootOfTree == nullptr)
            return nullptr;
         
        TreeNode* TreeList = nullptr;
         
        ConvertNode(pRootOfTree,TreeList);
         
        TreeNode* TreeHead = TreeList;
        while(TreeHead->left != nullptr && TreeHead != nullptr) //回到链表表头
            TreeHead = TreeHead->left;
        return TreeHead;
    }
     
    void ConvertNode(TreeNode* pNode,TreeNode*& TreeList)
        //这里的 TreeList 一定要引用,因为观察递归流程可以发现,后面 TreeList 的改变要返回去给前一次的调用
    {
        if(pNode == nullptr)
            return;
         
        if(pNode->left != nullptr)
            ConvertNode(pNode->left,TreeList);
         
        pNode->left = TreeList;
        if(TreeList != nullptr)
            TreeList->right = pNode;
        TreeList = pNode;
         
        if(pNode->right != nullptr)
            ConvertNode(pNode->right,TreeList);
    }
};

10、面试题37:序列化二叉树

请实现两个函数,分别用来序列化和反序列化二叉树

思路:前序遍历

class Solution {
public:
    char* Serialize(TreeNode *root) {   
        if(!root) return "#";
        string r = to_string(root->val);
        r.push_back(',');
        char *left = Serialize(root->left);
        char *right = Serialize(root->right);
        char *ret = new char[strlen(left) + strlen(right) + r.size()];
        strcpy(ret, r.c_str());   // c_str()是指返回C兼容的字符串,尾部肯定有'\0'
        strcat(ret, left);   //字符串连接
        strcat(ret, right);
        return ret;
    }
     
    TreeNode* Deserialize(char *&str) {
        if(*str=='#'){
            str++;
            return NULL;
        }
        int num = 0;
        while(*str != ',')
            num = num*10 + (*(str++)-'0');  //将数字字符转化为数字
        str++;
        TreeNode *root = new TreeNode(num);
        root->left = Deserialize(str);
        root->right = Deserialize(str);
        return root;
    }
     
};

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值