剑指offer(树的算法题)

本文包含以下算法题:

1.重建二叉树(面试题6)

2.树的子结构(面试题18)

3.二叉树的镜像(面试题19)---先序遍历

4.从上往下打印二叉树(面试题23)---层序遍历

5.二叉搜索树的后序遍历序列(面试题24)

6.二叉树中和为某一值的路径(面试题25)---DFS

7.二叉搜索树与双向链表(面试题27)---中序遍历

8.二叉树的深度(面试题39)

9.平衡二叉树(与上题深度相关)

10.二叉树的下一个结点(面试题58)

11.对称的二叉树(面试题59)---先序遍历

12.把二叉树打印成多行(面试题60)---层序遍历

13.按之字顺序打印二叉树(面试题61)---层序遍历

14.序列化二叉树面试题(面试题62)---先序遍历

15.二叉搜索树的第k个结点(面试题63)---中序遍历


1.输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。(先找到根结点,再划分四组依次展开递归调用)

/**
 * 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) {
      if(pre.size()==0||vin.size()==0)
      return NULL;
        vector<int>left_in,left_pre,right_in,right_pre;
        TreeNode* head=new TreeNode(pre[0]);     //创建根节点,根节点肯定是前序遍历的第一个数
        int gen=0;
        for(int i=0;i<vin.size();i++)            //找到中序遍历根节点所在位置,存放于变量gen中
        {
            if(vin[i]==pre[0])
            {
                gen=i;
                break;
            }
        }
        for(int i=0;i<gen;i++)                   //根据gen进行划分左右子树
        {
            left_in.push_back(vin[i]);  
            left_pre.push_back(pre[i+1]);        //前序第一个为根节点,所以要加一
        }
        for(int i=gen+1;i<vin.size();i++)
        {
            right_in.push_back(vin[i]);
            right_pre.push_back(pre[i]);
        }
        head->left=reConstructBinaryTree(left_pre,left_in);      //递归处理左右子树
        head->right=reConstructBinaryTree(right_pre,right_in);
        return head;
    }
};

2.输入两棵二叉树A,B,判断B是不是A的子结构(采用两层递归,先判断两棵树的根是否相等,调用判断函数;否则从左右结点出发递归调用本函数,然后再调用判断函数是否相等)

class Solution {
public:

   bool doesTree1HaveTree2(TreeNode* node1, TreeNode* node2) {
        //如果Tree2已经遍历完了都能对应的上,返回true
        if (node2 == NULL) {
            return true;
        }
        //如果Tree2还没有遍历完,Tree1却遍历完了。返回false
        if (node1 == NULL) {
            return false;
        }
        //如果其中有一个点没有对应上,返回false
        if (node1->val != node2->val) {  
                return false;
        }
         
        //如果根节点对应的上,那么就分别去子节点里面匹配
        return doesTree1HaveTree2(node1->left,node2->left) && doesTree1HaveTree2(node1->right,node2->right);
    }

    bool HasSubtree(TreeNode* root1, TreeNode* root2)           //先找到相同的根节点,再以根结点展开来判断
    {
 
      bool result = false;
         //当Tree1和Tree2都不为零的时候,才进行比较。否则直接返回false
        if (root2 != NULL && root1 != NULL) {
            //如果找到了对应Tree2的根节点的点
            if(root1->val == root2->val){
                //以这个根节点为为起点判断是否包含Tree2
                result = doesTree1HaveTree2(root1,root2);
            }
            //如果找不到,那么就再去root的左儿子当作起点,去判断时候包含Tree2
            if (!result) {
                result = HasSubtree(root1->left,root2);
            }
             
            //如果还找不到,那么就再去root的右儿子当作起点,去判断时候包含Tree2
            if (!result) {
                result = HasSubtree(root1->right,root2);
               }
            }
            //返回结果
        return result;
    }
};
3. 操作给定的二叉树,将其变换为源二叉树的镜像。(先序遍历的思想)
class Solution {
public:
    void Mirror(TreeNode *pRoot) {
      if(pRoot==NULL)
          return;
        if(pRoot->left==NULL && pRoot->right==NULL)
            return;
        TreeNode *temp=pRoot->left;    //先序遍历的思想(交换左右子结点即可)
        pRoot->left=pRoot->right;
        pRoot->right=temp;
        Mirror(pRoot->left);     //如果加上if判断pRoot->left是否为NULL,可以减少一层递归的时间
        Mirror(pRoot->right);

    }
};

4.从上往下打印出二叉树的每个节点,同层节点从左至右打印。(层序遍历的思想)

class Solution {
public:
    vector<int> PrintFromTopToBottom(TreeNode* root) {    //层序遍历的思想
        vector<int> v;
        if(root==NULL)
            return v;
        queue<TreeNode*> q;
        q.push(root);
        while(!q.empty())
        {
            TreeNode* Node=q.front();
            q.pop();
            v.push_back(Node->val);
            if(Node->left)
                q.push(Node->left);
            if(Node->right)
                q.push(Node->right);
        }
        return v;
    }
};

5.输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果(最后一个为根结点,以根结点为标准划分左右子树,然后再递归调用判断左右子树即可)

class Solution {
public:
    bool VerifySquenceOfBST(vector<int> sequence) {
       vector<int> leftTree,rightTree;
        int root; // 根结点
        if(sequence.empty()) return false;
        int index = 0; // 标记左右子树界限
        int len = sequence.size();
        root = sequence[len-1];
        int i=0;
        for(;i<len-1;++i)
        {
            if(sequence[i]>root) break; // 找到第一个大于根结点的位置,则左边为左子树,右边为右子树
        }
        for(int j=i;j<len-1;++j) // 循环时去除root,因此为len-1
        {
            if(sequence[j]<root) return false; // 有一个小于root,则返回false
        }
         
        if(i!=0)
        {
            // 即有左子树
            for(int m=0;m<i;++m)
            {
                leftTree.push_back(sequence[m]);
            }
        }
        if(i!=len-2)
        {
            for(int j=i;j<len-1;++j)
            {
                rightTree.push_back(sequence[j]);
            }
        }
         
        bool left = true,right = true; // 看左右子树是否是二叉搜索树
        if(leftTree.size()>1) VerifySquenceOfBST(leftTree);
        if(rightTree.size()>1) VerifySquenceOfBST(rightTree);
         
        return (left&&right);
    }
};
6. 输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(深度搜索优先思想,先判断是否为0,如果是0,且是叶子结点,则是符合的一条路径;如果不为0,且不是叶子结点,继续递归搜索,最后记得返回父节点时,要将原先压入的结点弹出!)
class Solution {
public:
    void DFSFind(TreeNode* root,int rest,vector<vector<int>> &path,vector<int> &ret)
    {
        rest-=root->val;             // 减去当前结点的值
        ret.push_back(root->val);
        
        if(rest==0)                  // 如果此时路径和为0,即(等于exceptNumber),再判断是否为叶子结点,如果是则保留该路径
        {
          if(root->left==NULL && root->right==NULL)
             path.push_back(ret);
        }
        else                         //  如果此时路径和不为0,则递归进入左右子树(注:若rest==0,则删除该结点后返回)
        {
          if(root->left!=NULL)
            DFSFind(root->left,rest,path,ret);
          if(root->right!=NULL)
            DFSFind(root->right,rest,path,ret);
        }
        ret.pop_back();              //返回到父结点,都要删除弹出
        
    }
    vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
         vector<vector<int>> path;
         vector<int>ret ;
         if(root!=NULL)
           DFSFind(root,expectNumber,path,ret);
        
          return path;
    }
};
7. 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。(中序遍历)
class Solution {
public:
    TreeNode* Convert(TreeNode* root)     //中序遍历的思想(非递归)
    {  
        if(root==NULL)
            return NULL;
        stack<TreeNode*> s;
        TreeNode* p=root;
        TreeNode* pre=NULL;
        bool IsFirst=true;
        while(p!=NULL||!s.empty())
        {
            while(p!=NULL)
            {
                s.push(p);
                p=p->left;
            }
            p=s.top();
            s.pop();
            if(IsFirst)                 //就是将原中序遍历输出改成if else这段,
            {                           //因为这里的root代表树最左端的结点,也就是链表最左端的结点(不是原先树的根结点所以只能在这里赋值)
                root=p;
                pre=root;
                IsFirst=false;
            }
           else                        //将前一个结点的右指针指向当前结点,将当前结点的左指针指向前一个结点
           {
                pre->right=p;
                p->left=pre;
                pre=p;
           }
            p=p->right;
        }
        return root;
        
    }
};

8.输入一棵二叉树,求该树的深度。

class Solution {
public:
    int TreeDepth(TreeNode* pRoot)
    {
       if(pRoot==NULL)
           return 0;
        int left=TreeDepth(pRoot->left);
        int right=TreeDepth(pRoot->right);
        
        return (left>right)?(left+1):(right+1);
    }
};

9.输入一棵二叉树,判断该二叉树是否是平衡二叉树。(与上题有关联,左右变量用于实时记录左右深度,并比较)

class Solution {
public:
    bool IsBalance(TreeNode* pRoot,int *depth)
    {
        if(pRoot==NULL)
        {
            *depth=0;
            return true;
        }
        int left,right;
        if(IsBalance(pRoot->left,&left) && IsBalance(pRoot->right,&right))
        {
            int dif=left-right;
            if(dif>=-1&&dif<=1)
            {
                *depth=(left>right)?(left+1):(right+1);
                return true;
            }
        }
        return false;
    }
    bool IsBalanced_Solution(TreeNode* pRoot) {
      int pdepth=0;
      return IsBalance(pRoot,&pdepth); 

    }
};

10.给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。(分两种情况: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;

    }
};

11.判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。(先序遍历思想,分为两种先左后右和先右后左两种顺序遍历)

class Solution {
public:
//    bool Symmetrical(TreeNode* p1,TreeNode* p2)         //递归版本
//    {
//        if(p1==NULL&&p2==NULL)
//            return true;
//        if(p1==NULL||p2==NULL)
//            return false;
//        if(p1->val!=p2->val)
//            return false;
//        return Symmetrical(p1->left,p2->right)&&Symmetrical(p1->right,p2->left);  
//    }
//    bool isSymmetrical(TreeNode* pRoot)
//    {
//       return Symmetrical(pRoot,pRoot);
//    }
   bool isSymmetrical(TreeNode* pRoot)                   //非递归版本
    {
       stack<TreeNode*> s1,s2;                          //先序遍历分为两种先左后右和先右后左两种顺序遍历
       TreeNode *p1,*p2;
       p1=p2=pRoot;
       while((!s1.empty()&&!s2.empty())||(p1!=NULL&&p2!=NULL))
       {
           while(p1!=NULL&&p2!=NULL)
           {
            s1.push(p1);
            s2.push(p2);
            p1=p1->left;
            p2=p2->right;
           }
           p1=s1.top();
           s1.pop();
           p2=s2.top();
           s2.pop();
            if(p1->val!=p2->val)
                return false;
           p1=p1->right;
           p2=p2->left;
           
       }
       if(!s1.empty()||!s2.empty())
           return false;
       if(p1!=NULL||p2!=NULL)
           return false;
       return true;
       
    }

};

12.请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。(层序遍历思想,也可以用两个栈)

class Solution {
public:
    vector<vector<int> > Print(TreeNode* pRoot) {
        vector<vector<int>> res;                       //层序遍历思想
        if(pRoot == NULL)
            return res;
        queue<TreeNode*> que;
        que.push(pRoot);
        bool even = false;
        while(!que.empty()){
            vector<int> vec;
            const int size = que.size();
            for(int i=0; i<size; ++i){
                TreeNode* tmp = que.front();
                que.pop();
                vec.push_back(tmp->val);
                if(tmp->left != NULL)
                    que.push(tmp->left);
                if(tmp->right != NULL)
                    que.push(tmp->right);
            }
            if(even)                                     //判断奇偶层后翻转
                std::reverse(vec.begin(), vec.end());
            res.push_back(vec);
            even = !even;
        }
        return res;
    }
    
};

13.从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。(层序遍历的思想)

class Solution {
public:
        vector<vector<int> > Print(TreeNode* pRoot) {
         vector<vector<int>> res;
            if(pRoot==NULL)
                return res;
         queue<TreeNode*> que;
         que.push(pRoot);
         while(!que.empty())
         {
             vector<int> vec;
             int size=que.size();
             for(int i=0;i<size;i++)
             {
                 TreeNode* p=que.front();
                 que.pop();
                 vec.push_back(p->val);
                 if(p->left!=NULL)
                    que.push(p->left);
                 if(p->right!=NULL)
                     que.push(p->right);
             }
             res.push_back(vec);
         }
            return res;
        }
    
};

14. 请实现两个函数,分别用来序列化和反序列化二叉树(1. 对于序列化:使用前序遍历,递归的将二叉树的值转化为字符,并且在每次二叉树的结点不为空时,在转化val所得的字符之后添加一个' , '作为分割。对于空节点则以 '#' 代替。 2. 对于反序列化:按照前序顺序,递归的使用字符串中的字符创建一个二叉树(特别注意:在递归时,递归函数的参数一定要是char ** ,这样才能保证每次递归后指向字符串的指针会随着递归的进行而移动!!!)

class Solution {
public:
char* Serialize(TreeNode *root) {
       if(root == NULL)
           return NULL;
        string str;
        Serialize(root, str);
        char *ret = new char[str.length() + 1];
        int i;
        for(i = 0; i < str.length(); i++){
            ret[i] = str[i];
        }
        ret[i] = '\0';
        return ret;
    }
    void Serialize(TreeNode *root, string& str){
        if(root == NULL){
            str += '#';
            return ;
        }
        string r = to_string(root->val);
        str += r;
        str += ',';
        Serialize(root->left, str);
        Serialize(root->right, str);
    }
     
    TreeNode* Deserialize(char *str) {
        if(str == NULL)
            return NULL;
        TreeNode *ret = Deserialize(&str);
 
        return ret;
    }
    TreeNode* Deserialize(char **str){//由于递归时,会不断的向后读取字符串
        if(**str == '#'){  //所以一定要用**str,
            ++(*str);         //以保证得到递归后指针str指向未被读取的字符
            return NULL;
        }
        int num = 0;
        while(**str != '\0' && **str != ','){
            num = num*10 + ((**str) - '0');
            ++(*str);
        }
        TreeNode *root = new TreeNode(num);
        if(**str == '\0')
            return root;
        else
            (*str)++;
        root->left = Deserialize(str);
        root->right = Deserialize(str);
        return root;
    }
};

15.给定一颗二叉搜索树,请找出其中的第k大的结点.。(中序遍历的思想)

class Solution {
public:
//    void InOrder(TreeNode* pRoot,vector<TreeNode*>& vec)    //中序遍历递归版本
//    {
//        if(pRoot==NULL)
//            return ;
//        InOrder(pRoot->left,vec);
//        vec.push_back(pRoot);
//        InOrder(pRoot->right,vec);
//    }
//    TreeNode* KthNode(TreeNode* pRoot, int k)
//    {
//       if(pRoot==NULL||k<=0)
//           return NULL;
 //       vector<TreeNode*> vec;
 //       InOrder(pRoot,vec);
//        if(k>vec.size())
//            return NULL;
//        return vec[k-1];
//    }
     TreeNode* KthNode(TreeNode* pRoot, int k)        //中序遍历非递归版本
     {  
         if(pRoot==NULL||k<=0)
           return NULL;
         stack<TreeNode*> s;
         TreeNode* pNode=pRoot;
        
         while(!s.empty()||pNode!=NULL)
         {
             while(pNode!=NULL)
             {
                 s.push(pNode);
                 pNode=pNode->left;
             }
             pNode=s.top();
             s.pop();
             k--;
             if(k==0)
               return pNode;
             pNode=pNode->right;
         }
         return NULL;
     }

    
};

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值