数据结构与算法--二叉树(一)


1 基于二叉链表的有序二叉树

1.1 问题

BST是Binary Search Tree的缩写,译为二叉搜索树,或有序二叉树,是二叉树的一种,它的定义如下:

1)或者是一棵空树;

2)或者是具有下列性质的二叉树:

I) 若左子树不空,则左子树上所有结点的值均小于它的根结点的值;

II) 若右子树不空,则右子树上所有结点的值均大于它的根结点的值;

III)左、右子树也分别为二叉排序树;

BST在查找一个结点或插入一个结点时,具有极大的优势,速度非常快。是一种基础性数据结构,广泛应用于更加抽象的集合、关联数组等数据结构。

1.2 方案

BST的基本操作包括:

1) 创建结点,新生成一个结点用于向BST中添加。

2) 增加结点,在BST中加入一个数据元素。

3) 删除结点,在BST中删除一个数据元素。

4) 查找结点,在BST中查找指定的数据是否存在,如果存在则得到其地址。

5) 修改结点,对BST中指定的数据进行修改。

6) 清除一棵树

1.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:定义BST结点结构

在C语言中:

1)定义一个变量data,用来模拟BST中的数据。

2)定义一个指针left,用于指向该结点的左孩子。

3)定义一个指针right,用于指向该结点的右孩子。

4)这三方面的信息共同描述一个BST结点,可将它们封装在一起。

代码如下:


    
    
  1. typedef int DataType;
  2. struct Node{
  3. DataType data;
  4. struct Node *left;
  5. struct Node *right;
  6. };

步骤二:创建结点

首先,向操作系统申请一块存储空间。

然后,存储数据域数据

最后,将左孩子和右孩子指针赋值为空。

代码如下所示:


    
    
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. typedef int DataType;
  4. struct Node{
  5. DataType data;
  6. struct Node *left;
  7. struct Node *right;
  8. };
  9. //创建一个节点
  10. struct Node* createNode(DataType d)
  11. {
  12. struct Node *pn = (struct Node*)malloc(sizeof(struct Node));
  13. pn->data = d;
  14. pn->left = pn->right = NULL;
  15. return pn;
  16. }

步骤三:增加结点

首先,判断要插入的结点是否存在。

然后,判断根结点是否为空。

1)若根结点不为空,则当要插入的结点数据大于根结点的数据时,插入右子树;否则插入左子树。

2)若根结点为空,则将新加入的结点添加到根结点处。

代码如下所示:


    
    
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. typedef int DataType;
  4. struct Node{
  5. DataType data;
  6. struct Node *left;
  7. struct Node *right;
  8. };
  9. //创建一个节点
  10. struct Node* createNode(DataType d)
  11. {
  12. struct Node *pn = (struct Node*)malloc(sizeof(struct Node));
  13. pn->data = d;
  14. pn->left = pn->right = NULL;
  15. return pn;
  16. }
  17. //讲一个节点插入到一个树中
  18. void insert(struct Node** root, struct Node* pn) {
  19. if (pn == NULL) return;
  20. //树为空
  21. if (*root == NULL) {*root = pn; return;}
  22. //树不为空
  23. if (pn->data > (*root)->data) {
  24. insert(&(*root)->right, pn);
  25. }else {
  26. insert(&(*root)->left, pn);
  27. }
  28. }

步骤四:删除结点

首先,查找要删除的结点,若不存在,则直接返回。

然后,若要被删除的结点没有左子树,则作如下处理:

1)要被删除的结点的双亲结点不存在时,则表明要删除根结点,此时只需将要删除结点的右孩子结点变成根结点即可。

2)要被删除的结点是其双亲结点的左孩子时,将要删除结点的右子树变成双亲结点的左孩子即可。

3) 要被删除的结点是其双亲结点的右孩子时,将要删除结点的右子树变成双亲结点的右孩子即可。

4)释放要被删除结点所占的存储空间。

最后,若要被删除的结点有左子树,则作如下处理:

1)在要删除结点的左子树中查找最右下的结点。

2)如果最右下结点的双亲结点与被删除结点是同一结点,则将最右下结点的双亲结点的左孩子变为最右下结点的左孩子。

3)如果最右下结点的双亲结点与被删除结点不是同一结点,则将最右下结点的双亲结点的右孩子变为最右下结点的左孩子。

4)将最右下结点的数据复制到要删除结点内。

5)释放最右下结点所占的存储空间。

代码如下:


    
    
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. typedef int DataType;
  4. struct Node{
  5. DataType data;
  6. struct Node *left;
  7. struct Node *right;
  8. };
  9. //创建一个节点
  10. struct Node* createNode(DataType d)
  11. {
  12. struct Node *pn = (struct Node*)malloc(sizeof(struct Node));
  13. pn->data = d;
  14. pn->left = pn->right = NULL;
  15. return pn;
  16. }
  17. //讲一个节点插入到一个树中
  18. void insert(struct Node** root, struct Node* pn) {
  19. if (pn == NULL) return;
  20. //树为空
  21. if (*root == NULL) {*root = pn; return;}
  22. //树不为空
  23. if (pn->data > (*root)->data) {
  24. insert(&(*root)->right, pn);
  25. }else {
  26. insert(&(*root)->left, pn);
  27. }
  28. }
  29. //在二叉排序树t中删去关键字为k的结点
  30. void delete(struct Node** root, DataType k)
  31. {
  32. struct Node* p, *f, *s, *q;
  33. p=*root;
  34. f=NULL;
  35. while(p)//查找关键字为k的待删结点p
  36. {
  37. if(p->data==k ) break; //找到则跳出循环
  38. f=p; //f指向p结点的双亲结点
  39. if(p->data>k)
  40. p=p->left;
  41. else
  42. p=p->right;
  43. }
  44. if(p==NULL) return; //若找不到,返回原来的二叉排序树
  45. if(p->left==NULL) //p无左子树
  46. {
  47. if(f==NULL)
  48. *root=p->right; //p是原二叉排序树的根
  49. else if(f->left==p) //p是f的左孩子
  50. f->left=p->right; //将p的右子树链到f的左链上
  51. else //p是f的右孩子
  52. f->right=p->right; //将p的右子树链到f的右链上
  53. free(p);
  54. }
  55. else //p有左子树
  56. {
  57. q=p;
  58. s=p->left;
  59. while(s->right) //在p的左子树中查找最右下结点
  60. {
  61. q=s;
  62. s=s->right;
  63. }
  64. if(q==p)
  65. q->left=s->left; //将s的左子树链到q上
  66. else
  67. q->right=s->left;
  68. p->data=s->data; //将s的值赋给p
  69. free(s);
  70. }
  71. }

上述代码中,以下代码:


    
    
  1. while(p)//查找关键字为k的待删结点p
  2. {
  3. if(p->data==k ) break; //找到则跳出循环
  4. f=p; //f指向p结点的双亲结点
  5. if(p->data>k)
  6. p=p->left;
  7. else
  8. p=p->right;
  9. }
  10. if(p==NULL) return; //若找不到,返回原来的二叉排序树

设置循环查找要删除的结点,若不存在,则直接返回。

上述代码中,以下代码:


    
    
  1. if(p->left==NULL) //p无左子树
  2. {
  3. if(f==NULL)
  4. *root=p->right; //p是原二叉排序树的根
  5. else if(f->left==p) //p是f的左孩子
  6. f->left=p->right; //将p的右子树链到f的左链上
  7. else //p是f的右孩子
  8. f->right=p->right; //将p的右子树链到f的右链上
  9. free(p);
  10. }

是若要被删除的结点没有左子树时的情形,其中指针p指向要被删除的结点,指针f指向被删除的结点的双亲结点。


    
    
  1. if(f==NULL)
  2. *root=p->right; //p是原二叉排序树的根

表示要被删除的结点的双亲结点不存在时,要删除根结点。


    
    
  1. else if(f->left==p) //p是f的左孩子
  2. f->left=p->right; //将p的右子树链到f的左链上

表示要被删除的结点是其双亲结点的左孩子时,将要删除结点的右子树变成双亲结点的左孩子即可。


    
    
  1. else //p是f的右孩子
  2. f->right=p->right; //将p的右子树链到f的右链上

表示要被删除的结点是其双亲结点的右孩子时,将要删除结点的右子树变成双亲结点的右孩子即可。

上述代码中,以下代码:


    
    
  1. else //p有左子树
  2. {
  3. q=p;
  4. s=p->left;
  5. while(s->right) //在p的左子树中查找最右下结点
  6. {
  7. q=s;
  8. s=s->right;
  9. }
  10. if(q==p)
  11. q->left=s->left; //将s的左子树链到q上
  12. else
  13. q->right=s->left;
  14. p->data=s->data; //将s的值赋给p
  15. free(s);
  16. }

是若要被删除的结点有左子树时的情形,其中指针p指向要被删除的结点,指针s指向最右下结点,指针q指向最右下结点的双亲结点。


    
    
  1. s=p->left;
  2. while(s->right) //在p的左子树中查找最右下结点
  3. {
  4. q=s;
  5. s=s->right;
  6. }

是在要删除结点的左子树中查找最右下的结点。


    
    
  1. if(q==p)
  2. q->left=s->left; //将s的左子树链到q上

表示如果最右下结点的双亲结点q与被删除结点p是同一结点,则将最右下结点的双亲结点q的左孩子q->left变为最右下结点s的左孩子s->left。


    
    
  1. else
  2. q->right=s->left;

表示如果最右下结点的双亲结点q与被删除结点p不是同一结点,则将最右下结点的双亲结点q的右孩子q->right变为最右下结点s的左孩子s->left。

步骤五:查找结点

首先,判断根结点是否为空,若是则直接返回NULL。

然后,在根结点不为空的情况下,若要查找的数据大于根结点数据,遍历右子树查找。

下一步,在根结点不为空的情况下,若要查找的数据小于根结点数据,遍历左子树查找。

最后,在根结点不为空的情况下,若要查找的数据等于根结点数据,则返回根结点。

代码如下:


    
    
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. typedef int DataType;
  4. struct Node{
  5. DataType data;
  6. struct Node *left;
  7. struct Node *right;
  8. };
  9. //创建一个节点
  10. struct Node* createNode(DataType d)
  11. {
  12. struct Node *pn = (struct Node*)malloc(sizeof(struct Node));
  13. pn->data = d;
  14. pn->left = pn->right = NULL;
  15. return pn;
  16. }
  17. //讲一个节点插入到一个树中
  18. void insert(struct Node** root, struct Node* pn) {
  19. if (pn == NULL) return;
  20. //树为空
  21. if (*root == NULL) {*root = pn; return;}
  22. //树不为空
  23. if (pn->data > (*root)->data) {
  24. insert(&(*root)->right, pn);
  25. }else {
  26. insert(&(*root)->left, pn);
  27. }
  28. }
  29. //在二叉排序树t中删去关键字为k的结点
  30. void delete(struct Node** root, DataType k)
  31. {
  32. struct Node* p, *f, *s, *q;
  33. p=*root;
  34. f=NULL;
  35. while(p)//查找关键字为k的待删结点p
  36. {
  37. if(p->data==k ) break; //找到则跳出循环
  38. f=p; //f指向p结点的双亲结点
  39. if(p->data>k)
  40. p=p->left;
  41. else
  42. p=p->right;
  43. }
  44. if(p==NULL) return; //若找不到,返回原来的二叉排序树
  45. if(p->left==NULL) //p无左子树
  46. {
  47. if(f==NULL)
  48. *root=p->right; //p是原二叉排序树的根
  49. else if(f->left==p) //p是f的左孩子
  50. f->left=p->right; //将p的右子树链到f的左链上
  51. else //p是f的右孩子
  52. f->right=p->right; //将p的右子树链到f的右链上
  53. free(p);
  54. }
  55. else //p有左子树
  56. {
  57. q=p;
  58. s=p->left;
  59. while(s->right) //在p的左子树中查找最右下结点
  60. {
  61. q=s;
  62. s=s->right;
  63. }
  64. if(q==p)
  65. q->left=s->left; //将s的左子树链到q上
  66. else
  67. q->right=s->left;
  68. p->data=s->data; //将s的值赋给p
  69. free(s);
  70. }
  71. }
  72. //查找一个树
  73. struct Node* find(struct Node* root, DataType d){
  74. if(root == NULL)
  75. return NULL;
  76. if(d > root->data) //查找右子树
  77. return find(root->right,d);
  78. else if(d < root->data) //查找左子树
  79. return find(root->left,d);
  80. else
  81. return root;
  82. }

步骤六:修改结点

首先,使用步骤四的方法查找到要修改的结点。

然后,将要修改的结点数据修改为指定数据。

代码如下:


    
    
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. typedef int DataType;
  4. struct Node{
  5. DataType data;
  6. struct Node *left;
  7. struct Node *right;
  8. };
  9. //创建一个节点
  10. struct Node* createNode(DataType d)
  11. {
  12. struct Node *pn = (struct Node*)malloc(sizeof(struct Node));
  13. pn->data = d;
  14. pn->left = pn->right = NULL;
  15. return pn;
  16. }
  17. //讲一个节点插入到一个树中
  18. void insert(struct Node** root, struct Node* pn) {
  19. if (pn == NULL) return;
  20. //树为空
  21. if (*root == NULL) {*root = pn; return;}
  22. //树不为空
  23. if (pn->data > (*root)->data) {
  24. insert(&(*root)->right, pn);
  25. }else {
  26. insert(&(*root)->left, pn);
  27. }
  28. }
  29. //在二叉排序树t中删去关键字为k的结点
  30. void delete(struct Node** root, DataType k)
  31. {
  32. struct Node* p, *f, *s, *q;
  33. p=*root;
  34. f=NULL;
  35. while(p)//查找关键字为k的待删结点p
  36. {
  37. if(p->data==k ) break; //找到则跳出循环
  38. f=p; //f指向p结点的双亲结点
  39. if(p->data>k)
  40. p=p->left;
  41. else
  42. p=p->right;
  43. }
  44. if(p==NULL) return; //若找不到,返回原来的二叉排序树
  45. if(p->left==NULL) //p无左子树
  46. {
  47. if(f==NULL)
  48. *root=p->right; //p是原二叉排序树的根
  49. else if(f->left==p) //p是f的左孩子
  50. f->left=p->right; //将p的右子树链到f的左链上
  51. else //p是f的右孩子
  52. f->right=p->right; //将p的右子树链到f的右链上
  53. free(p);
  54. }
  55. else //p有左子树
  56. {
  57. q=p;
  58. s=p->left;
  59. while(s->right) //在p的左子树中查找最右下结点
  60. {
  61. q=s;
  62. s=s->right;
  63. }
  64. if(q==p)
  65. q->left=s->left; //将s的左子树链到q上
  66. else
  67. q->right=s->left;
  68. p->data=s->data; //将s的值赋给p
  69. free(s);
  70. }
  71. }
  72. //查找一个树
  73. struct Node* find(struct Node* root, DataType d){
  74. if(root == NULL)
  75. return NULL;
  76. if(d > root->data) //查找右子树
  77. return find(root->right,d);
  78. else if(d < root->data) //查找左子树
  79. return find(root->left,d);
  80. else
  81. return root;
  82. }
  83. //修改树中结点的值
  84. void modify(struct Node* root, DataType oldData, DataType newData)
  85. {
  86. struct Node* p;
  87. p = find(root, oldData);
  88. p->data = newData;
  89. }

1.4 完整代码

本案例的完整代码如下所示:


    
    
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. typedef int DataType;
  4. struct Node{
  5. DataType data;
  6. struct Node *left;
  7. struct Node *right;
  8. };
  9. //创建一个节点
  10. struct Node* createNode(DataType d)
  11. {
  12. struct Node *pn = (struct Node*)malloc(sizeof(struct Node));
  13. pn->data = d;
  14. pn->left = pn->right = NULL;
  15. return pn;
  16. }
  17. //讲一个节点插入到一个树中
  18. void insert(struct Node** root, struct Node* pn) {
  19. if (pn == NULL) return;
  20. //树为空
  21. if (*root == NULL) {*root = pn; return;}
  22. //树不为空
  23. if (pn->data > (*root)->data) {
  24. insert(&(*root)->right, pn);
  25. }else {
  26. insert(&(*root)->left, pn);
  27. }
  28. }
  29. //在二叉排序树t中删去关键字为k的结点
  30. void delete(struct Node** root, DataType k)
  31. {
  32. struct Node* p, *f, *s, *q;
  33. p=*root;
  34. f=NULL;
  35. while(p)//查找关键字为k的待删结点p
  36. {
  37. if(p->data==k ) break; //找到则跳出循环
  38. f=p; //f指向p结点的双亲结点
  39. if(p->data>k)
  40. p=p->left;
  41. else
  42. p=p->right;
  43. }
  44. if(p==NULL) return; //若找不到,返回原来的二叉排序树
  45. if(p->left==NULL) //p无左子树
  46. {
  47. if(f==NULL)
  48. *root=p->right; //p是原二叉排序树的根
  49. else if(f->left==p) //p是f的左孩子
  50. f->left=p->right; //将p的右子树链到f的左链上
  51. else //p是f的右孩子
  52. f->right=p->right; //将p的右子树链到f的右链上
  53. free(p);
  54. }
  55. else //p有左子树
  56. {
  57. q=p;
  58. s=p->left;
  59. while(s->right) //在p的左子树中查找最右下结点
  60. {
  61. q=s;
  62. s=s->right;
  63. }
  64. if(q==p)
  65. q->left=s->left; //将s的左子树链到q上
  66. else
  67. q->right=s->left;
  68. p->data=s->data; //将s的值赋给p
  69. free(s);
  70. }
  71. }
  72. //查找一个树
  73. struct Node* find(struct Node* root, DataType d){
  74. if(root == NULL)
  75. return NULL;
  76. if(d > root->data) //查找右子树
  77. return find(root->right,d);
  78. else if(d < root->data) //查找左子树
  79. return find(root->left,d);
  80. else
  81. return root;
  82. }
  83. //修改树中结点的值
  84. void modify(struct Node* root, DataType oldData, DataType newData)
  85. {
  86. struct Node* p;
  87. p = find(root, oldData);
  88. p->data = newData;
  89. }
  90. //清除一颗树
  91. void clears(struct Node **root) {
  92. if (*root == NULL) return;
  93. clears(&(*root)->left);
  94. clears(&(*root)->right);
  95. free(*root);
  96. *root = NULL;
  97. }
  98. //打印一颗树
  99. void print(struct Node* root) {
  100. if (root == NULL) return;
  101. printf("%d ", root->data);
  102. print(root->left);
  103. print(root->right);
  104. }
  105. int main() {
  106. struct Node* root = NULL;
  107. insert(&root, createNode(10));
  108. insert(&root, createNode(5));
  109. insert(&root, createNode(3));
  110. insert(&root, createNode(2));
  111. insert(&root, createNode(1));
  112. insert(&root, createNode(4));
  113. insert(&root, createNode(7));
  114. insert(&root, createNode(6));
  115. insert(&root, createNode(9));
  116. insert(&root, createNode(8));
  117. insert(&root, createNode(15));
  118. print(root);
  119. printf("\n");
  120. printf("%d\n", find(root, 5)->data);
  121. delete(&root, 10);
  122. print(root);
  123. return 0;
  124. }


















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值