二叉树问题汇总(2)—常见问题

在上一篇二叉树问题汇总(1)中总结了下二叉树的一些基本问题,主要是针对二叉排序树。这篇文章主要汇总二叉树的一些常见的但是难度稍大一点的问题。

1、判定二叉树是否存在和为给定值的路径

问题:

给定一个值,判定二叉树是否存在从根结点到叶结点的路径,其结点的数据之和为该值。

比如二叉树如下所示,给定的和为27:

则二叉树存在的根结点到叶结点的路径有:

path 1: 5 4 11 7
path 2: 5 4 11 2
path 3: 5 8 13
path 4: 5 8 4 1

则该二叉树存在和为27的路径,因为5+4+11+7 = 27.若是给定和为40,则不存在这样的路径。

解答:

主要思路还是递归,通过用和值减去根结点的值,然后判断左右子树是否存在和为sum-root.data的路径。代码如下:

int hasPathSum(struct node* root, int sum)
{
    if (root == NULL)  return sum == 0;  //基本条件:根结点为NULL,则判断此时剩下的和值是否为0,为0则表示存在,否则不存在
    return hasPathSum(root->left, sum-root->data) || hasPathSum(root->right, sum-root->data); //递归判断左右子树
}

2、打印二叉树的所有路径(从根结点到叶结点)

问题:

如1中所示,打印二叉树的所有从根结点到叶结点的路径,如上例中打印输出4条路径。

解答:

//打印路径主函数
void printPaths(struct node* root)
{
    int path[1000];
    printPathsRecur(root, path, 0);
}

//递归调用打印路径
void printPathsRecur(struct node* root, int path[], int pathLen)
{
    if (root == NULL) return;

    path[pathLen++] = root->data;  //将根结点加入到路径数组中,路径长度加1
    if (root->left==NULL && root->right==NULL) //到了叶子节点,所以打印输出路径。
        printArray(path, pathLen);
    else {
        printPathsRecur(root->left, path, pathLen); //递归打印左子树
        printPathsRecur(root->right, path, pathLen); //递归打印右子树
    }
}

//该函数打印输出路径。路径存储在数组path中,pathLen为路径结点个数。
void printArray(int path[], int pathLen)
{ 
   int i;
   for (i=0; i<pathLen; i++) { 
       printf("%d ", path[i]); 
   } 
   printf("\n");
}


3、二叉树镜像

问题:

二叉树镜像问题即是将二叉树所有结点的左右孩子结点指针互换。如下图所示,左边的二叉树的镜像为右边所示。

解答:

还是递归。从底向上,先交换完根结点的左右子树,再交换根结点的左右孩子结点。自顶向下也是可以的,即先交换根结点的左右孩子,再递归交换左右子树。

递归主要要理解函数的意义,mirror函数的意义就是得到该二叉树的镜像,二叉树镜像时根结点不变。则mirror(node->left)将左子树镜像,mirror(node->right)将右子树镜像,此时,左右子树的根结点即node->left和node->right还没有交换,所以接下来交换它们,这样就完成了整棵树的镜像过程。

//自底向上,即先交换左右子树,再交换左右孩子节点。
void mirror(struct node* node) 
{
    if (node==NULL) {
        return;
    } else {
        struct node* temp;
        // 子树完成镜像
        mirror(node->left); 
        mirror(node->right);
        // 交换node的左右孩子节点
        temp = node->left;
        node->left = node->right;
        node->right = temp;
    }
}

//自顶向下完成镜像
void mirror2(struct node* root)
{
    if (root == NULL) return;
    struct node* tmp = root->left;
    root->left = root->right;
    root->right = tmp;
    mirror2(root->left);
    mirror2(root->right);
}

4、二叉搜索树复制

问题:

复制二叉搜索树的各个结点,并插入其中,使得新的二叉树还是一棵二叉搜索树。

例如原来的二叉搜索树为:

    2

 /      \

1     3

复制后变成:   

解答:

复制源结点,然后将其作为左孩子插入到源结点中。先复制完左右子树,然后将本结点复制插入到源结点的左孩子中。

void doubleTree(struct node* root)
{
    struct node* oldLeft;
    if (root == NULL) return;
    doubleTree(root->left);  //复制左子树
    doubleTree(root->right); //复制右子树
    oldLeft = root->left;
    root->left = newNode(root->data); //复制根结点并将其作为新的左孩子,如图中所示,结点2复制一个作为原来根结点2的左孩子。
    root->left->left = oldLeft;      //复制的根结点的左孩子为原来的根结点的左孩子。如图中所示,复制的结点2的左孩子为原来根结点的左孩子结点1.
}

5、判定两棵二叉树结构是否相同

问题:

给定两棵二叉树,判定其结构是否相同,即对应的所有结点值是否一样。

如下面(1)(2)两棵二叉树结构相同,但是(1)(3), (2)(3)不相同。

  2                      2                             2

 /  \                    /   \                          /    \

1  3                 1    3                      3     1

(1)                    (2)                           (3)

解答:

先判定根结点的data域是否相同,如果相同则继续判断左右子树。一定是根结点和左右子树都相同才能判定两棵二叉树结构相同。

特殊情况处理:如果两棵树都为NULL,则相同。其他一棵为NULL,一棵不为NULL则不同。

int sameTree(struct node* a, struct node* b)
{
    if (a==NULL && b==NULL)  //两棵树都为空,返回true
        return true;
    else if (a!=NULL && b!=NULL) { //两棵树都不为空,则比较根结点和递归比较左右子树
        return a->data==b->data &&
            sameTree(a->left, b->left) &&
            sameTree(a->right, b->right);
    } else                 //其他情况,一棵树为空,一棵不为空,返回false
        return false;
}

6、二叉搜索树的数目

问题:

给定结点数目num,结点的data值为1,2,3...num。给出这些结点能够构造的二叉搜索树数目。
比如结点数目为3时,结点的data依次为1,2,3,则能够构造5棵二叉搜索树:
  1                      1                    2                    3                     3
    \                       \                  /   \                   /                       /
     2                      3              1   3                1                      2

        \                     /                                        \                     /

          3                 2                                        2                 1

解答:

考虑到每个结点都可能是根结点root,递归计算左右子树,总的数目就是左右子树数目的乘积之和。当结点数目为3时,则当root为1时,则左子树结点为0,只有右子树结点数目为2。当root为2时,则左右子树结点数目为1,1。root为3时,左右子树结点数目为2,0。

int countTrees(int numKeys) 
{ 
  if (numKeys <=1) { 
    return(1); 
  } 
  else { 
    int sum = 0; 
    int left, right, root; 
    for (root=1; root<=numKeys; root++) { 
      left = countTrees(root - 1); 
      right = countTrees(numKeys - root); 
      // left*right为当前root的二叉搜索树数目
      sum += left*right; 
    } 
    return(sum); 
  } 




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值