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

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. }
时间: 2024-10-14 19:48:54

数据结构与算法--二叉树(一)的相关文章

javascript数据结构与算法--二叉树(插入节点、生成二叉树)

javascript数据结构与算法-- 插入节点.生成二叉树 二叉树中,相对较小的值保存在左节点上,较大的值保存在右节点中 /* *二叉树中,相对较小的值保存在左节点上,较大的值保存在右节点中 * * * */ /*用来生成一个节点*/ function Node(data, left, right) { this.data = data;//节点存储的数据 this.left = left; this.right = right; this.show = show; } function sh

[数据结构与算法] 二叉树及其遍历方式

声明:原创作品,转载时请注明文章来自SAP师太技术博客:www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将追究法律责任!原文链接:http://www.cnblogs.com/jiangzhengjun/p/4289830.html 一.数据结构分类 (一)按逻辑结构 集合(无辑关系) 线性结构(线性表):数组.链表.栈.队列 非线性结构:树.图.多维数组 (二)按存储结构 顺序(数组)储结构.链式储结构.索引储结构.散列储结构 二.二叉树相关性质

数据结构与算法 —— 二叉树

二叉树 定义: 来自于百度百科. 在计算机科学中,二叉树是每个节点最多有两个子树的树结构.通常子树被称作"左子树"(left subtree)和"右子树"(right subtree).二叉树常被用于实现二叉查找树和二叉堆. 二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒.二叉树的第i层至多有2^{i-1}个结点:深度为k的二叉树至多有2^k-1个结点:对任何一棵二叉树T,如果其终端结点数为n_0,度为2的结点数为n_

小甲鱼数据结构和算法-----二叉树的构建和前序遍历

题目要求:建立二叉树并输出每个字符所在的层数.如下图要求输出 A 在第一层 B.C 在第二层 D.E在第三层 代码如下: #include <stdio.h> #include <stdlib.h> typedef struct BiTNode { char data; struct BiTNode *lchild,*rchild; }BiTNode,*BiTree; // 创建一棵二叉树,约定用户遵照前序遍历的方式输入数据 void CreateBiTree(BiTree *T)

数据结构和算法——二叉树

树1.树的优点有序数组: 查找很快,二分法实现的查找所需要的时间为O(logN),遍历也很快,但是在有序数组中插入,删除却需要先 找到位置, 在把数组部分元素后移,效率并不高. 链表: 链表的插入和删除都是很快速的,仅仅需要改变下引用值就行了,时间仅为O(1),但是在链表中查找数据却需要遍历所有的元素, 这个效率有些慢了.树的优点: 树结合了有序数组和链表的优点,可以实现快速的查找,也可以快速的删除,查找. 树的一些专用术语: 路径: 顺着连接节点的边从一个节点到另一个节点的,所经过的所有节点的

python数据结构与算法——二叉树结构与遍历方法

先序遍历,中序遍历,后序遍历 ,区别在于三条核心语句的位置 层序遍历  采用队列的遍历操作第一次访问根,在访问根的左孩子,接着访问根的有孩子,然后下一层 自左向右一一访问同层的结点 # 先序遍历 # 访问结点,遍历左子树,如果左子树为空,则遍历右子树, # 如果右子树为空,则向上走到一个可以向右走的结点,继续该过程 preorder(t):    if t:       print t.value       preorder t.L       preorder t.R # 中序遍历 # 从根

java数据结构和算法------二叉树基本操作

1 package iYou.neugle.tree; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 public class Binary_Tree<T> { 7 private Tree tree = new Tree(); 8 9 class Tree { 10 public T data; 11 public Tree left; 12 public Tree right; 13 } 14 15 public

js数据结构和算法---二叉树

原文: https://segmentfault.com/a/1190000000740261 //前序遍历 function preOrder(node) { if (node != null) { node.style.background = "black"; setTimeout(function () { preOrder(node.children[0]); },1500); setTimeout(function () { preOrder(node.children[1

js数据结构与算法——二叉树

function BinaryTree(){ var Node = function(key){ this.key = key; //值 this.left = null; //左箭头 this.right = null; //右箭头 } //根节点 var root = null; var insertNode = function(oldNode,newNode){ if(newNode.key < oldNode.key){ if(oldNode.left === null){ oldNo