• 二叉搜索树


    二叉搜索树可以按照关键字的顺序保存结点,查找和其他操作可以使用二叉搜索原理:当在树(或者寻找新的结点的地方)中查找节点是,它从根接点遍历叶结点,与每个结点的关键字进行比较,然后基于比较结果,决定在左子树或右子树中进行搜索。

    每次比较将跳过树的大约一半的元素,这使得每次查找,插入或删除一个结点所花费的时间与树的结点个树的对数(树的高度)成正比。

    定义

    二叉搜索树又叫二叉查找树或者二叉排序树。它支持快速的查找,插入删除一个数据。它的每个结点是一个对象,包括 key,卫星数据,还不快一些为了维护树结构所需要的信息:left、right、parent,分别指向左孩子、右孩子、父结点。如果孩子节点或者父结点不存在时,用NULL表示。

    二叉搜索树是一颗二叉树,具有搜索树的特征:

    • 每个结点都比它的左节点打,比右节点小
    • 每个结点的左右子树都是一课二叉搜索树
    • 对一颗二叉搜索树进行中序遍历的结果是从小到大排序

    操作逻辑

    二叉查找树中的查找逻辑:

    先取根结点,如果它等于我们要查找的数据,就返回;

    如果要查找的数据比根结点小,就在左子树中递归查找;

    如果要查找的数据比根结点大,就在右子树中递归查找

    二叉查找树中的插入操作:

    从根结点开始,一次比较要插入的数据和结点的大小关系;

    如果要插入的数据比结点数据大,并且右子树为空,就将新数据直接插到右子树的位置;如果不为空,再递归遍历右子树,查找插入的位置

    二叉查找树中的删除操作:

    如果要删除的结点没有子结点,只需要将被删除结点的父结点只想被删除结点的指针位置为null;

    如果要删除的结点只有一个子结点(左结点或右结点),只需要更新父结点中指向要删除结点的指针,让他指向要删除结点的子结点;

    如果要删除结点有两个子结点,我们需要找到这个结点的右子树中的最小结点,把它替换到要删除的结点上,然后在删除这个最小结点,因为最小结点肯定没有左子节点(如果有左子结点,就不是最小结点了)

    时间复杂度

    • 最好的情况:二叉搜索树是一课满二叉树,这是根结点到所有叶子结点的长度都为lgN,对应的树的高度也是lgN,此时的查找、插入、删除都可以在O(lgN)时间内完成
    • 最坏的情况:二叉搜索树的每个结点只有一个孩子,几乎是链表的形状,对应树的高度是N,此时查找,插入,删除的时间复杂度是O(N)

    二叉搜索树的中序遍历

    给定一串数字,这串数字按照二叉搜索树的规则插入之后,这个二叉数的中序遍历是从小到大的排序。例如给定的数字是:8,3,10,1,6,14,4,7,13

    其二叉搜索树是:

    求出中序遍历是:1,3,4,6,7,8,10,13,14 。

    代码实现

    • 初始化二叉搜索树

    定义一个结构体作为结点,并且初始化

    1. //二叉搜索树的节点结构体
    2. struct node {
    3. struct node* left;
    4. struct node* right;
    5. int data;
    6. }*root;
    7. void init(int data) {
    8. root = (struct node*)malloc(sizeof(struct node));
    9. root->data = data;
    10. root->left = NULL;
    11. root->right = NULL;
    12. }
    • 插入元素

    只要左子树为空,就把小于父节点的数插入作为左子树

    只要右子树为空,就把大于父节点的数插入作为右子树

    如果不为空,就一直往下去搜索,直到找到合适的插入位置

    1. //插入元素
    2. void insert(int key) {
    3. struct node* temp=root;
    4. struct node* prev=NULL;
    5. //先循环遍历,找到符合要求的叶子结点
    6. while (temp != NULL) {
    7. prev = temp;
    8. if (key < temp->data) {
    9. temp = temp->left;
    10. }
    11. else if (key > temp->data) {
    12. temp = temp->right;
    13. }
    14. else {
    15. return;
    16. }
    17. }
    18. //根据key值与当前叶子结点的比较,决定将元素存放在右结点或者是左结点
    19. //当元素与当前叶子节点是重复的情况,就不用插入
    20. if (key < prev->data) {
    21. prev->left = (struct node*)malloc(sizeof(struct node));
    22. prev->left->data = key;
    23. prev->left->left = NULL;
    24. prev->left->right = NULL;
    25. }
    26. else if (key > prev->data) {
    27. prev->right = (struct node*)malloc(sizeof(struct node));
    28. prev->right->data = key;
    29. prev->right->left = NULL;
    30. prev->right->right = NULL;
    31. }
    32. }
    • 输出二叉搜索树中的元素(中序遍历)
    1. //对二叉搜索树进行中序遍历,可以得到从小到大的排序
    2. void show(struct node *root) {
    3. if (root == NULL) {
    4. return;
    5. }
    6. show(root->left);
    7. printf("%d ", root->data);
    8. show(root->right);
    9. }
    • 判断二叉树中是否含有某一元素
    1. //判断该元素是否存在
    2. bool search(int key) {
    3. struct node* temp=root;
    4. struct node* prev;
    5. while (temp != NULL) {
    6. prev = root;
    7. if (key < temp->data) {
    8. temp = temp->left;
    9. }
    10. else if (key > temp->data) {
    11. temp = temp->right;
    12. }
    13. else
    14. return true;
    15. }
    16. return false;
    17. }
    • 删除元素

    分为三种情况

    第一种:删掉的结点没有子树

    可以直接从二叉查找树中删除,不会影响其他结点

    第二种:删掉的结点只有左子树

    把要删除结点的左孩子,连接到要删除结点的父亲结点(父结点的左子树),然后删除D结点

    第三种:删掉的结点只有右子树

    将要删除结点的右孩子,连接到要删除结点D的父亲结点(父结点的右子树),然后删除D结点。

    第四种:删掉的结点有左子树和右子树

    删除这个结点之后,它的位置怎么替补呢?

    首先是要选择和它相邻的结点(一个是左子树的“最右边”,另一个是右子树的“最左边”),可以根据中序遍历来理解。

    然后用找到的这个相邻的结点来替换被删除的结点。

    1. //删除结点
    2. node* deleteNode(node* root, int key) {
    3. if (root == NULL) {
    4. return root;
    5. }
    6. if (key < root->data) {
    7. root->left = deleteNode(root->left, key);
    8. }
    9. else if (key > root->data) {
    10. root->right = deleteNode(root->right, key);
    11. }
    12. else {
    13. if (root->left == NULL) {
    14. node* temp = root->right;
    15. free(root);
    16. return temp;
    17. }
    18. else if (root->right == NULL) {
    19. node* temp = root->left;
    20. free(root);
    21. return temp;
    22. }
    23. node* temp = root->right;
    24. while (temp->left != NULL) {
    25. temp = temp->left;
    26. }
    27. root->data = temp->data;
    28. root->right = deleteNode(root->right, temp->data);
    29. }
    30. return root;
    31. }

    总代码

    1. #include<stdio.h>
    2. #include<string.h>
    3. #include<stdlib.h>
    4. struct node {
    5. struct node* left;
    6. struct node* right;
    7. int data;
    8. }*root;
    9. //初始化
    10. void init(int key) {
    11. root = (struct node*)malloc(sizeof(struct node));
    12. root->data = key;
    13. root->left = NULL;
    14. root->right = NULL;
    15. }
    16. //插入元素
    17. void insert(int key) {
    18. struct node* temp = root;
    19. struct node* prev = NULL;
    20. while (temp != NULL) {
    21. prev = temp;
    22. if (key < temp->data) {
    23. temp = temp->left;
    24. }
    25. else if (key > temp->data) {
    26. temp = temp->right;
    27. }
    28. else {
    29. return;
    30. }
    31. }
    32. if (key < prev->data) {
    33. prev->left = (struct node*)malloc(sizeof(struct node));
    34. prev->left->data = key;
    35. prev->left->left = NULL;
    36. prev->left->right = NULL;
    37. }
    38. else if (key > prev->data) {
    39. prev->right = (struct node*)malloc(sizeof(struct node));
    40. prev->right->data = key;
    41. prev->right->left = NULL;
    42. prev->right->right = NULL;
    43. }
    44. }
    45. //对二叉搜索树进行中序遍历,可以得到从小到大的排序
    46. void show(struct node* root) {
    47. if (root == NULL) {
    48. return;
    49. }
    50. show(root->left);
    51. printf("%d ", root->data);
    52. show(root->right);
    53. }
    54. //判断该元素是否存在
    55. bool search(int key) {
    56. struct node* temp = root;
    57. struct node* prev;
    58. while (temp != NULL) {
    59. prev = root;
    60. if (key < temp->data) {
    61. temp = temp->left;
    62. }
    63. else if (key > temp->data) {
    64. temp = temp->right;
    65. }
    66. else
    67. return true;
    68. }
    69. return false;
    70. }
    71. node* deleteNode(node* root, int key) {
    72. if (root == NULL) {
    73. return root;
    74. }
    75. if (key < root->data) {
    76. root->left = deleteNode(root->left, key);
    77. }
    78. else if (key > root->data) {
    79. root->right = deleteNode(root->right, key);
    80. }
    81. else {
    82. if (root->left == NULL) {
    83. node* temp = root->right;
    84. free(root);
    85. return temp;
    86. }
    87. else if (root->right == NULL) {
    88. node* temp = root->left;
    89. free(root);
    90. return temp;
    91. }
    92. node* temp = root->right;
    93. while (temp->left != NULL) {
    94. temp = temp->left;
    95. }
    96. root->data = temp->data;
    97. root->right = deleteNode(root->right, temp->data);
    98. }
    99. return root;
    100. }
    101. int main() {
    102. //定义一个结构体二叉搜索树
    103. init(8);
    104. //插入元素
    105. insert(3);
    106. insert(10);
    107. insert(1);
    108. insert(6);
    109. insert(14);
    110. insert(4);
    111. insert(7);
    112. insert(13);
    113. //查找是否含有元素6
    114. if (search(6))
    115. printf("存在元素——6\n");
    116. else {
    117. printf("不存在元素——6\n");
    118. }
    119. //查找是否含有元素9
    120. if (search(9))
    121. printf("存在元素——9\n");
    122. else {
    123. printf("不存在元素——9\n");
    124. }
    125. //二叉搜索树中的元素(中序遍历)
    126. printf("该二叉搜索树中的元素为:");
    127. show(root);
    128. printf("\n");
    129. //删除元素
    130. root=deleteNode(root, 8);
    131. printf("该二叉搜索树中的元素为:");
    132. show(root);
    133. printf("\n");
    134. return 0;
    135. }

  • 相关阅读:
    Ubuntu ssh 配置
    开放词汇视觉定位 OV-VG: A Benchmark for Open-Vocabulary Visual Grounding 论文笔记
    DIY 一个汽车方向盘游戏外设(MMOS OSW DIY)
    SpringBoot 多种优雅的线程池配置与使用(异步执行函数,反射机制,动态识别参数,有返回值)
    计算机毕业设计ssm+vue基本微信小程序的智能图书管理系统
    Unity UGUI 绘制优雅的线段
    一、几种常用的设计模式
    复盘归因,提高交付质量的秘诀
    2024年06月在线IDE流行度最新排名
    定时器的理论和使用
  • 原文地址:https://blog.csdn.net/m0_73172034/article/details/130879239