王道数据结构课后题 | P121

28 篇文章 0 订阅
3 篇文章 1 订阅

7、判断一个二叉树是否为完全 二叉树。

一开始是想着利用递归,判断有左孩子而无右孩子的节点的个数,并且要求左孩子为叶子节点。
判断的条件很复杂,最后发现这种方法实际上是有问题的。
如果同时碰到两个节点同时为有左叶子节点而右子树为空,这种情况按照判断应当返回false
但是这种方法并不能正确返回
1
/ \
2 3
/ /
4 5

正确的方法应当是利用层次遍
不同于一般的层次遍历的地方在于无论node->left ,right 是否为空全部推入。
如果碰到第一个为空的node,设置flag=1;在以后如果遇到任意一个不为空的节点,必然是false的

道理也比较清晰明了

class Solution{
public:
    //是否为完全二叉树 
    bool isFull(TreeNode* root){
        int flag=0;
        queue<TreeNode*> Q;
        Q.push(root);
        while(!Q.empty()){
            TreeNode* node=Q.front();
            Q.pop();

            if(flag && node) return false;

            if(!node) flag=1;
            else{
                Q.push(node->left);
                Q.push(node->right);    
            }
        }
        return true;
    }

    //遇到多个 有左孩子而无右孩子 的情况将无能为力 
    //正确的方法应当是使用队列,如果出现了空节点,那么之后出现的节点不再可能!=NULL 

    //利用flag表示父亲节点是否是有lchild而无rchild
//  //flag>1,要求这个节点不能再有叶子节点,否则必错 
//      return isFull(root,0)==0;
//  int isFull(TreeNode* root,int flag){
//      if(!root) return 0;
//      //父节点只有left,子节点必须是孩子节点 
//      if(flag>0 && (root->left || root->right)) return 1;
//      //单纯的叶子节点,pass 
//      if(!root->left && !root->right) return 0; 
//      //没有左孩子而有右孩子,必然不是满二叉树 
//      if(!root->left && root->right)  return 1;
//      
//      //只剩下,只有左孩子和 左右孩子均有的情况 
//      if(root->left && !root->right) flag=1;
//      else flag=0;
//      
//      //递归判断孩子节点 
//      int left=isFull(root->left,flag);
//      int right=isFull(root->right,flag);
//      
//      return left+right;
//  }
}; 

8、判断孩子节点数量为2【度为2】的节点数
自顶向下递归即可解决~

class Solution{
public:
    //判断双孩子节点个数 
    int doubleChild(TreeNode* root){
        if(!root) return 0;
        if(!root->left && !root->right) return 0;//叶子节点为0 

        int plus=0;
        if(root->left && root->right) plus++;

        return doubleChild(root->left)+doubleChild(root->right)+plus;
    }
};

10、求先序遍历中第k个节点的值
使用栈的话很容易解决。
如果用递归的话,也值需要外设一个变量。然后利用先序遍历的思想,在相应的位置对这个变量进行操作就好了。

class Solution{
public:
    //result结果 k总数 count计数 
    int result=-1,k,count=0;
    //获得先序遍历第k个元素 
    int get_kth(TreeNode* root,int k){
        int count=0;
        this->k=k;

        get_kth(root);
        return result;

        //递归版 
//      if(!root || k<=0) return 0;
//      if(k==1){
//          return root->val;
//      }
//      else if(k<1) return 0;
//      else k--;
//      
//      return get_kth(root->left,k)+get_kth(root->right,k);
//      
//      非递归版 
//      stack<TreeNode*> Q;
//      Q.push(root);
//      int count=0,num=root->val;
//      while(!Q.empty()){
//          TreeNode* node=Q.top();
//          Q.pop();
//          if(++count==k)
//          return node->val;
//          
//          if(node->right) Q.push(node->right);
//          if(node->left) Q.push(node->left);
//      }
//      return -1;
    }

    //先定义后实现
//  void get_kth(TreeNode* root);
    //直接在class内部实现
    void get_kth(TreeNode* root) {
        if(!root) return;
        count++;
        if(k==count){
            result=root->val;
            return;
        }

        get_kth(root->left);
        get_kth(root->right);
    }
};

//void Solution::get_kth(TreeNode* root) {
//  if(!root) return;
//  count++;
//  if(k==count){
//      result=root->val;
//      return;
//  }
//  
//  get_kth(root->left);
//  get_kth(root->right);
//}

12、求x的祖先节点
思路是使用一个栈,从根节点开始递归,存放递归到当前步骤时候有哪些节点。
并使用一个数组(引用传递)当找到x的时候,取出当前栈内所有元素,返回。

class Solution{
public:
    void getXancestor(TreeNode* root,int x,vector<int>& result,vector<int>& cur){
        getX(root,x,result,cur);
    }

    void getX(TreeNode* root,int x,vector<int> &result,vector<int>& cur) {
        if(!root) return;

        //推入数据 
        result.push_back(root->val);
        if(root->val==x){
            for(int i=0;i<result.size();i++)
            cur.push_back(result[i]);
            return;
        }

        getX(root->left,x,result,cur);
        getX(root->right,x,result,cur);

        //递归推出 
        result.pop_back();
    }
};

13、寻找两个节点的公共祖先
一开始我所想是自底向上,传一个bool值,判断有无找到和所需要值相对应的节点。
如果left==true && right==true 说明是root,另一种情况是left||right 并且root是所需要找寻的节点。

其实我所采取的做法的分情况讨论是没有必要的。
如果找到一个节点root,就应当返回。这样再判断左右节点,如果两节点分布在两棵子树上,则返回root。
而之前所担心的,一个节点是另一个节点的祖先
这种方式会直接将这个节点一直返回到root节点而无法遍历到另一节点。
但是,这个节点就是祖先节点
1
/
2
/
3
例如一路左子树往下,任取两个节点,先遍历到的那个节点就是祖先。

class Solution{
public:
    TreeNode* getAncestor(TreeNode* root,int x,int y){
        if(!root) return NULL;
        //这样岂不是在找到一个节点之后就没法继续找?
        //实际上是没有影响的。
        //如果一个节点是另一个节点的祖先,并且走到这里被回退了
        //那么全程只找到这一个节点,也是正确的。因为目的节点就是它 
        if(root->val==x || root->val==y) return root;

        TreeNode* left=getAncestor(root->left,x,y);
        TreeNode* right=getAncestor(root->right,x,y);

        if(left && right) return root;
        if(left) return left;
        if(right) return right;
        return NULL;

    }

    bool getAncestor(TreeNode* root,int x,int y,TreeNode* &target){
        if(!root) return false;

        //要求从底向上,第一个同时位于两个节点左右孩子节点的点
        //或者一个点是另一个点的parent
        bool left=getAncestor(root->left,x,y,target);
        bool right=getAncestor(root->right,x,y,target);

        //左右节点分布在两个子树内,那这个节点就是 最近祖先节点 
        if(left && right){
            target=root;
            return true;
        }

        //如果孩子节点仅有一个是true,但是当前节点是目的节点
        //故二者公共祖先为当前节点
        if((left || right)&&(root->val == x || root->val==y)){
            target=root;
            return true;
        }

        //当前节点是需要找寻的节点 
        if(root->val==x || root->val==y)
        return true; 

        //返回两个子节点的或 
        return left || right;       
    }
}; 

13PLUS、如果我们把二叉树看成一个图,父子节点之间的连线看成是双向的,我们姑且定义”距离”为两节点之间边的个数。写一个程序求一棵二叉树中相距最远的两个节点之间的距离。
这题据说有使用DP计算经过某一节点的方法。
参考地址:http://www.cnblogs.com/flyinghearts/archive/2011/03/22/1991991.html

实际上可以归结于求左右子树和的最大值的问题。

int max=INT_MIN;
int getMaxDepth(TreeNode* root){
     if(!root) return 0;
     int left=getMaxDepth(root->left);
     int right=getMaxDepth(root->right);
     int depth=left+right;
     if(depth>max) max=depth;
     return max(left,right)+1;
}

15、对于一个满二叉树,将前序转化为后序。
满二叉树的左右子树数量相等。
故可在拿走一个节点之后,确定剩下的一半是左子树的节点,一半是右子树的。
对于一个顺序排列的ABCDEFG满二叉树
ABDECFG
DEBFGCA
post[end]=pre[start],接着将剩下的节点进行二分。
half=(start-end)/2;
分别对两部分递归求取。
注意在这时候,不能算上s1和e1所占的位置,所以位置要适当微调。

class Solution{
public:
    void pre2post(vector<int> &pre,int s1,int e1,vector<int>& post,int s2,int e2){
        if(e1>=s1){
            post[e2]=pre[s1];
            int half=(e1-s1)/2;
            pre2post(pre,s1+1,s1+half,post,s2,s2+half-1);
            pre2post(pre,s1+half+1,e1,post,s2+half,e2-1);
        }
    }
}; 

17、判断两棵二叉树是相似的
这题其实就是判断两棵二叉树具有相同的树形结构
可以使用递归完成

if(!root1 && !root2) return true;
if(!root1 || !root2) return false;
//要求左右子树均相似
return isSimilar(root1->left,root2->left) && isSimilar(root1->right,root2->right);

19、求带权路径长度之和
每个节点的带权路径长度 = 节点值*节点深度
难度倒还好,传入一个deep深度随着递归增加即可

注意可以不必采取先加后减deep的操作,而是可以在传值的时候直接加1即可

int getWPL(TreeNode* root){
    return WPL(root,1);
}

int WPL(TreeNode* root,int deep){
    if(!root) return 0;
    //可以在传值的时候将deep的值+1而无需在前后进行操作和复原
    //deep++;
    int left=WPL(root->left,deep+1);
    int right=WPL(root->right,deep+1);
    //deep--;
    return left+right+root->val*deep;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值