• 树之二叉排序树(二叉搜索树)


    什么是排序树

    说一下普通二叉树可不是左小右大的 

    插入的新节点是以叶子形式进行插入的

    二叉排序树的中序遍历结果是一个升序的序列

    下面是两个典型的二叉排序树 

     

     

     二叉排序树的操作

     构造树的过程即是对无序序列进行排序的过程

     存储结构

     通常采用二叉链表作为存储结构 

     插入算法

     

     查找算法

     

     删除算法

      

     

    第三种情况:你删除的结点下面就是说还有左右子树,那么这个时候,我们就要去找到这棵树中序遍历结果之后的直接前驱或者直接后继,然后把这个前驱或者后继给按到删除结点这个位置上,将它下面的树移到被替换结点的位置

    删除操作的具体讲解

    重点讲解一下删除节点的核心分析

    这里在补一张中序遍历的递归调用图

      直接上代码

    在上代码之前,先来说一下,二叉搜索树很多方法都利用了递归的思想,许多说明我都放到代码注释里面了,可以结合下面的这张图进行思维分析

    先来看c语言代码(algorithm/bst/bst1.c)

    1. #include
    2. #include
    3. typedef int key_type;
    4. typedef struct _node
    5. {
    6. key_type key;
    7. struct _node *left;
    8. struct _node *right;
    9. }node, *pnode;
    10. void insert_bst(pnode *root, key_type key)
    11. {
    12. //初始化插入结点
    13. pnode p = (pnode)malloc(sizeof(node));
    14. if (p != NULL)
    15. {
    16. p->key = key;//把值给放进去
    17. p->left = p->right = NULL;
    18. }
    19. //空树的时候,直接作为根结点
    20. if (*root == NULL)
    21. {
    22. *root = p;
    23. return;
    24. }
    25. //插入到当前结点的左孩子
    26. if ((*root)->left == NULL && (*root)->key > key)
    27. {
    28. (*root)->left = p;//直接在堆上面指就可以了
    29. return;
    30. }
    31. //插入到当前结点的右孩子
    32. if ((*root)->right == NULL && (*root)->key < key)
    33. {
    34. (*root)->right = p;
    35. return;
    36. }
    37. //上面都没有进入,说明结点就要往下继续存放
    38. //需要先把我们分配的结点内存给释放掉
    39. free(p);
    40. //左子树递归
    41. if ((*root)->key > key)
    42. {
    43. insert_bst(&(*root)->left, key);
    44. }
    45. //右子树递归
    46. else if((*root)->key < key)
    47. {
    48. insert_bst(&(*root)->right, key);
    49. }
    50. }
    51. //根据关键字删除某个结点,成功返回1,失败返回0
    52. int delete_bst(pnode *root, key_type key)
    53. {
    54. if (*root == NULL)
    55. {
    56. return 0;//这是一棵空树
    57. }
    58. if ((*root)->key == key)
    59. {
    60. pnode pbak1, pmove;
    61. //判断右子树是否为空,为空,只需要重接左子树
    62. if ((*root)->right == NULL)
    63. {
    64. //把当前结点的左子树接上去就可以了
    65. pbak1 = *root;//当前结点备份等会释放
    66. //改变在栈上面一级指针的指向
    67. *root = (*root)->left;
    68. //删除
    69. free(pbak1);
    70. }
    71. //左子树为空的情况下,只需要重接右子树就行了
    72. else if ((*root)->left == NULL)
    73. {
    74. //删除结点的空间备份
    75. pbak1 = *root;
    76. *root = (*root)->right;//改变栈上结点的指向
    77. free(pbak1);
    78. }
    79. //左右子树都不为空
    80. else
    81. {
    82. //我们要找到直接前驱或者一个直接后继
    83. //前驱就是当前结点下一个结点左边结点的右边(尽头),所以先把root指向了左结点
    84. pbak1 = *root;//删除结点的一个备份
    85. pmove = (*root)->left;//左边等会要接接上
    86. //再来循环右边
    87. //注意的问题是我们需要指向一个直接前驱的父结点
    88. //以便于用来更改当前的子树结点,也就是被删除结点的下一个结点要连接上去
    89. while (pmove->right)
    90. {
    91. pbak1 = pmove;//前驱结点的父节点
    92. pmove = pmove->right;//这个是指向了我们需要的前驱结点
    93. }
    94. //s指向了前驱结点,将s放到root结点上面
    95. (*root)->key = pmove->key;//改变了值,不是地址,等会吧pmove给释放掉
    96. //重接一下下面结点的子树
    97. //如果pbak1没有移动过,那么pbak1->left = pmove ->left;
    98. if (pbak1 == *root)
    99. {
    100. pbak1->left = pmove->left;
    101. }
    102. else
    103. {
    104. //如果移动过,那么pbak1->right就要改变
    105. pbak1->right = pmove->left;
    106. }
    107. //释放掉pmove这个结点
    108. free(pmove);
    109. }
    110. return 1;
    111. }
    112. //没有找到的情况下,我们需要遍历树
    113. else if (key < (*root)->key)
    114. {
    115. //直接走左子树
    116. //这里必须return ,不然找到了也会false
    117. return delete_bst(&(*root)->left, key);
    118. }
    119. else if (key > (*root)->key)
    120. {
    121. //大于当前结点就直接走右子树
    122. return delete_bst(&(*root)->right, key);
    123. }
    124. return 0;
    125. }
    126. //查找元素,找到返回结点指针,没找到返回NULL
    127. //找结点,传入一个一级指针就好了
    128. pnode search_bst(pnode root, key_type key)
    129. {
    130. if (root == NULL)
    131. {
    132. return NULL;
    133. }
    134. //查找右子树
    135. if (key > root->key)
    136. {
    137. return search_bst(root->right, key);
    138. }
    139. //查找左子树
    140. else if (key < root->key)
    141. {
    142. return search_bst(root->left, key);
    143. }
    144. else
    145. {
    146. return root;
    147. }
    148. }
    149. //查找最小的关键字,空树时返回NULL
    150. pnode search_min_bst(pnode root)
    151. {
    152. if (root == NULL)
    153. {
    154. return NULL;
    155. }
    156. //最小的话应该就是最左边孩子
    157. if (root->left == NULL)
    158. {
    159. return root;//叶子结点下面都是NULL
    160. }
    161. else
    162. {
    163. return search_min_bst(root->left);
    164. }
    165. }
    166. //查找最大关键字,空树时返回NULL
    167. pnode search_max_bst(pnode root)
    168. {
    169. if (root == NULL)
    170. {
    171. return NULL;
    172. }
    173. //找到最后的孩子
    174. if (root->right == NULL)
    175. {
    176. return root;
    177. }
    178. else
    179. {
    180. //一直往右边找,直到没有有孩子结点
    181. return search_max_bst(root->right);
    182. }
    183. }
    184. //中序遍历二叉树
    185. void inorder_traverse_bst(pnode root)
    186. {
    187. if (root != NULL)
    188. {
    189. //遍历左子树
    190. //先走到最左边,依次调用结束,返回打印
    191. inorder_traverse_bst(root->left);
    192. //走到最后一个结束,打印,中间根结点也会打印
    193. printf("%d ", root->key);
    194. //然后走右边开始打印
    195. inorder_traverse_bst(root->right);
    196. }
    197. }
    198. int main()
    199. {
    200. //创建一棵二叉树
    201. pnode root = NULL;
    202. insert_bst(&root, 3);
    203. insert_bst(&root, 8);
    204. insert_bst(&root, 2);
    205. insert_bst(&root, 5);
    206. insert_bst(&root, 4);
    207. insert_bst(&root, 9);
    208. insert_bst(&root, 11);
    209. //中序遍历二叉树
    210. inorder_traverse_bst(root);
    211. delete_bst(&root, 2);
    212. printf("\n---------------------\n");
    213. inorder_traverse_bst(root);
    214. return 0;
    215. }

    再来看java的运行代码(algorithm/bst1)

    1. package com.pxx.tree.bst1;
    2. class Node {
    3. int key;
    4. Node left, right;
    5. //这里就是在new的时候可以出初始化一个头结点
    6. public Node(int key) {
    7. this.key = key;
    8. this.left = this.right = null;
    9. }
    10. }
    11. class BstTree {
    12. //插入结点
    13. public Node insertBst(Node root, int key) {
    14. if (root == null) {
    15. //直接返回这个新结点
    16. //指到最后可添加位置,也是直接指向这个新节点
    17. return new Node(key);
    18. }
    19. if (key < root.key) {
    20. //往左边走
    21. root.left = insertBst(root.left, key);
    22. } else if(key > root.key) {
    23. //往右边走
    24. root.right = insertBst(root.right, key);
    25. }
    26. return root;//这里返回root的意思也就是中间的结点必须连上
    27. }
    28. //删除结点
    29. public Node deleteBST(Node root, int key) {
    30. if (root == null) {
    31. return root;
    32. }
    33. if (key < root.key) {
    34. root.left = deleteBST(root.left, key);
    35. } else if (key > root.key) {
    36. root.right = deleteBST(root.right, key);
    37. } else {
    38. //找到了这个结点
    39. if (root.left == null) {
    40. //直接返回这个结点的右结点给上一个节点
    41. return root.right;
    42. } else if (root.right == null) {
    43. return root.left;
    44. }
    45. //上面都没有进入,说明有左右子树,需要结点上一移动
    46. //先改变查找到结点的值,我们需要用它的直接后继来替换
    47. //也就是找到它右边的结点,然后不停的左边,一直到尽头
    48. root.key = minValue(root.right);
    49. //改变结点之间的连接
    50. root.right = deleteBST(root.right, root.key);
    51. }
    52. return root;
    53. }
    54. // 寻找最小值
    55. //从某个结点一直找到最左边就是最小值
    56. public int minValue(Node root) {
    57. while (root != null && root.left != null) {
    58. root = root.left;
    59. }
    60. return root.key;
    61. }
    62. //中序遍历这个结点
    63. public void inorderTraverseBst(Node root) {
    64. if (root != null) {
    65. //先打印左边
    66. inorderTraverseBst(root.left);
    67. System.out.print(root.key + " ");
    68. inorderTraverseBst(root.right);
    69. }
    70. }
    71. //查找某一个元素
    72. public Node searchBST(Node root, int key) {
    73. if (root == null || root.key == key) {
    74. return root;
    75. }
    76. if (key < root.key) {
    77. return searchBST(root.left, key);
    78. }
    79. return searchBST(root.right, key);
    80. }
    81. }
    82. public class Solution {
    83. public static void main(String[] args) {
    84. BstTree bstTree = new BstTree();
    85. Node root = null;
    86. //root在堆上就已经建立空间
    87. root = bstTree.insertBst(root, 3);
    88. bstTree.insertBst(root, 8);
    89. bstTree.insertBst(root,2);
    90. bstTree.insertBst(root,5);
    91. bstTree.insertBst(root,4);
    92. bstTree.insertBst(root,9);
    93. bstTree.insertBst(root,1);
    94. //中序遍历这片空间
    95. bstTree.inorderTraverseBst(root);
    96. System.out.println("-----------------");
    97. bstTree.deleteBST(root,2);
    98. bstTree.deleteBST(root,8);
    99. bstTree.inorderTraverseBst(root);
    100. }
    101. }

    好了,说到这。

  • 相关阅读:
    从零开始搭建React+TypeScript+webpack开发环境-性能优化
    WPF+ASP.NET SignalR实现后台通知
    小熊家务帮day8-day9 客户管理模块2 (用户定位,地址簿,实名认证,银行卡信息上传等功能)
    Leetcode算法入门与数组丨3. 数组基础
    锁机制之 Condition 接口
    mmcv常用API介绍
    单机版k8s搭建
    PMP考试通关宝典-敏捷专题
    火星车初级java面向对象
    LeetCode刷题(python版)——Topic65.有效数字
  • 原文地址:https://blog.csdn.net/Pxx520Tangtian/article/details/131573104