您的位置:首页 > 其它

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

2012-07-19 21:43 288 查看
在上一篇二叉树问题汇总(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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  path struct null 存储