在树之前的笔记中讲到,树这种结构对比数组,链表,增删改查都很快。

二叉排序树简介

二叉排序树:BST,对于二叉排序树的任何一个非叶子节点要求左子节点的值比当前节点的值小,右子节点的值比当前节点的值大。如果有相同的值,可以放在左右子节点都可以,但是要满足前面的条件。
二叉排序数插入图解

思路

添加

  1. 传入一个节点,这个节点得值和当前节点的值进行比较
  2. 如果传入节点的值比当前节点得值小
    1. 如果当前节点的左子节点为null,就把传入节点赋值给当前节点的左子节点
    2. 否则就向当前节点的左子节点递归添加
  3. 第2步否则(就是传入的节点的值大于等于当前节点的值)

    1. 如果当前节点的右子节点为null,就把传入节点赋值给当前节点的右子节点
    2. 否则就向当前节点的右子节点递归添加

      删除

      删除节点有三种情况
  4. 删除叶子节点

    1. 找到要删除节点target和它的父节点parent
    2. 确定target是parent的左右哪个子节点
    3. 根据前面的情况parent.left=null;或者parent.right=null;
  5. 删除只有一棵子树的节点

    1. 找到要删除节点target和它的父节点parent
    2. 确认target是parent的左右子节点
    3. 确定target的子树是它的左右子节点
    4. 然后target的对应子树的指针,赋值给parent对应taeget的哪个指针
  6. 删除有两棵子树的节点

    1. 找到要删除节点target和它的父节点parent
    2. 从target的右子树找到最小的节点(从左子树找,就要找到左子树最大的值,下面步骤同理)
    3. 用一个临时变量,将这个最小节点的值保存
    4. 删除该最小节点
    5. 这个临时变量赋值给target的值

代码

二叉排序树中序遍历出来就是拍好的顺序

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. namespace ConsoleApp1
  5. {
  6. public class BST
  7. {
  8. public Node root;
  9. //添加
  10. public void add (Node node)
  11. {
  12. if (root == null)
  13. root = node;
  14. else
  15. root.add(node);
  16. }
  17. //中序遍历
  18. public void infixOrder()
  19. {
  20. if (root == null)
  21. {
  22. Console.WriteLine("二叉树为空");
  23. return;
  24. }
  25. root.infixOrder();
  26. }
  27. //查找要删除的节点
  28. public Node search(int val)
  29. {
  30. if (root == null)
  31. return null;
  32. else
  33. return root.search(val);
  34. }
  35. //查找要删除节点的父节点
  36. public Node searchParent(int val)
  37. {
  38. if (root == null)
  39. return null;
  40. else
  41. return root.searchParent(val);
  42. }
  43. //删除右边节点的最小的一个节点
  44. //返回以node为根节点的二叉排序树的最小节点的值,并且删除这个节点
  45. public int delRightTreeMin(Node node)
  46. {
  47. Node target = node;
  48. while (target.left != null)
  49. {
  50. target = target.left;
  51. }
  52. //这是target就指向了最小的节点
  53. //删除这个最小节点,并且返回这个节点的值
  54. delNode(target.id);
  55. return target.id;
  56. }
  57. //删除节点
  58. public void delNode(int val)
  59. {
  60. if (root == null)
  61. return;
  62. else
  63. {
  64. //找要删除的节点target和它的父节点parent
  65. Node target = search(val);
  66. if (target == null)//都没有找到要删除的节点,下面的代码也不用执行了
  67. return;
  68. //如果这个二叉树只有一个节点
  69. if (root.left == null && root.right == null)
  70. {
  71. root = null;
  72. return;
  73. }
  74. Node parent = searchParent(val);
  75. //要删除的节点是叶子节点
  76. if (target.left == null && target.right == null)
  77. {
  78. if (parent.left == target)
  79. parent.left = null;
  80. if (parent.right == target)
  81. parent.right = null;
  82. }
  83. //如果要删除的节点有两棵子树
  84. else if (target.left != null && target.right != null)
  85. {
  86. int minval = delRightTreeMin(target.right);
  87. target.id = minval;
  88. }
  89. //要删除的节点只有一棵子树
  90. else
  91. {
  92. if(target.left!=null)//要删除的节点有左子树
  93. {
  94. if (parent != null)
  95. {
  96. if (parent.left == target)
  97. parent.left = target.left;
  98. if (parent.right == target)
  99. parent.right = target.left;
  100. }
  101. else
  102. root = target.left;
  103. }
  104. else//要删除的节点有右子树
  105. {
  106. if (parent != null)
  107. {
  108. if (parent.left == target)
  109. parent.left = target.right;
  110. if (parent.right == target)
  111. parent.right = target.right;
  112. }
  113. else
  114. root = target.right;
  115. }
  116. }
  117. }
  118. }
  119. }
  120. public class Node
  121. {
  122. public int id;
  123. public Node left;
  124. public Node right;
  125. public Node(int id)
  126. {
  127. this.id = id;
  128. }
  129. //二叉排序树添加
  130. public void add(Node node)
  131. {
  132. if (node == null)
  133. return;
  134. //判断传入的节点的值,和当前子树的根节点的关系
  135. if (node.id < this.id)
  136. {
  137. //如果当前的左子节点为null
  138. if (this.left == null)
  139. this.left = node;
  140. else
  141. this.left.add(node);//递归向左子节点添加
  142. }
  143. else
  144. {
  145. if (this.right == null)
  146. this.right = node;
  147. else
  148. this.right.add(node);//递归向右节点添加
  149. }
  150. }
  151. //中序遍历
  152. public void infixOrder()
  153. {
  154. //1.如果当前节点的左子节点不为空,则递归继续中序遍历
  155. if (this.left != null)
  156. this.left.infixOrder();
  157. //2.输出当前节点
  158. Console.WriteLine(this.id);
  159. //3.如果当前节点的右子节点不为空,则递归继续中序遍历
  160. if (this.right != null)
  161. this.right.infixOrder();
  162. }
  163. //查找节点
  164. public Node search(int val)
  165. {
  166. if (val == this.id)
  167. return this;
  168. else if (val < this.id)
  169. {
  170. if (this.left == null)
  171. return null;
  172. return this.left.search(val);
  173. }
  174. else
  175. {
  176. if (this.right == null)
  177. return null;
  178. return this.right.search(val);
  179. }
  180. }
  181. //查找要删除节点的父节点
  182. public Node searchParent(int val)
  183. {
  184. //如果当前节点就是要删除的节点的父节点,就返回
  185. if (this.left != null && this.left.id == val || this.right != null && this.right.id == val)
  186. return this;
  187. else
  188. {
  189. if (val < this.id && this.left != null)//左递归
  190. return this.left.searchParent(val);
  191. else if (val >= this.id && this.right != null)//右递归
  192. return this.right.searchParent(val);
  193. else//没有父节点
  194. return null;
  195. }
  196. }
  197. }
  198. }