什么是二叉搜索树?

二叉搜索树也叫做二叉排序树、二叉查找树,它有以下性质:

  1. 若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
  2. 若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
  3. 任意节点的左、右子树也分别为二叉查找树;
  4. 没有键值相等的节点。

二叉查找树相比于其他数据结构的优势在于查找、插入的时间复杂度较低,为O(log n)。

基本操作

1.定义BST对象

  1. public class BST<E extends Comparable<E>> {
  2. /**
  3. * 定义节点
  4. */
  5. private class Node {
  6. private E e;
  7. private Node left;
  8. private Node right;
  9. Node(E e) {
  10. this.e = e;
  11. }
  12. }
  13. private Node root;
  14. private int size;
  15. /**
  16. * 获取节点数
  17. *
  18. * @return
  19. */
  20. public int size() {
  21. return this.size;
  22. }
  23. }

2.添加节点

1.采用递归,终止条件是当前节点为null时,创建新的节点;

2.如果当前节点不为null,则通过比较插入值和当前节点值的大小,递归其左/右子节点。


  1. public void add(E element){
  2. root = add(root, element);
  3. }
  4. private void _add(Node node, E element){
  5. if(node == null){
  6. size++;
  7. return new Node(element);
  8. }
  9. if(element.compareTo(node.e) > 0){
  10. node.right = _add(node.right, element);
  11. }else if(element.compareTo(node.e) < 0){
  12. node.left = _add(node.left, element);
  13. }
  14. return node;
  15. }

3.查询节点

1.通过比较当前节点值的大小,递归查找左/右子节点。


  1. public boolean contains(E element){
  2. return _contains(root, element);
  3. }
  4. private boolean _contains(Node node,E element){
  5. if(node == null) return false;
  6. if(element.compareTo(node.e) == 0){
  7. return true;
  8. }else if(element.compareTo(node.e) > 0){
  9. return _contains(node.right, element);
  10. }else{
  11. return _contains(node.left, element);
  12. }
  13. return false;
  14. }

4.打印二叉树

1.通过递增树的深度dept,层级打印;

2.叶子节点通过##表示。


  1. public String toString(){
  2. StringBuilder builder = new StringBuilder();
  3. _toString(root, 0, builder);
  4. return builder.toString();
  5. }
  6. private void _toString(Node node, int dept, StringBuilder builder){
  7. if(int i=0; i<dept; i++){
  8. builder.append("--");
  9. }
  10. if(node == null){
  11. builder.append("##\n");
  12. return;
  13. }
  14. builder.append(node.e).append("\n");
  15. _toString(node.left, dept+1, builder);
  16. _toString(right, dept+1, builder);
  17. }

测试运行


  1. public static void main(String[] args) {
  2. BST<Integer> bst = new BST<>();
  3. bst.add(30);
  4. bst.add(10);
  5. bst.add(22);
  6. bst.add(50);
  7. bst.add(18);
  8. bst.add(34);
  9. bst.add(5);
  10. System.out.println(bst);
  11. }

输出结果为:

  1. 30
  2. --10
  3. ----5
  4. ------##
  5. ------##
  6. ----22
  7. ------18
  8. --------##
  9. --------##
  10. ------##
  11. --50
  12. ----34
  13. ------##
  14. ------##
  15. ----##

遍历操作

二叉树的遍历分为:前序遍历、中序遍历、后序遍历,

这里的前中后指的是父节点的顺序,所以三种遍历的顺序是:

  • 前序遍历(父-左-右)
  • 中序遍历 (左-父-右)
  • 后序遍历 (左-右-父)

1.前序遍历

1.递归遍历,终止条件是当前节点为null;

2.先遍历输出父节点,再遍历左、右子节点


  1. public void preOrder(){
  2. StringBuilder builer = new StringBuilder();
  3. _preOrder(root, builer);
  4. System.out.println(builer.toString);
  5. }
  6. private void _preOrder(Node node,StringBuilder builder){
  7. if(node == null) return;
  8. builder.append(node.e).append(" ");
  9. _preOrder(node.left, builder);
  10. _preOrder(node.right, builder);
  11. }

2.前序遍历2(非递归)

1.借助栈,首先将节点放入栈中,开始遍历栈;

2.遍历取出节点后再将其右子节点和左子节点放入压入栈中;


  1. public void preOrder2(){
  2. StringBuilder builder = new StringBuilder();
  3. Stack<Node> stack = new Stack();
  4. stack.push(root);
  5. while(!stack.isEmpty()){
  6. Node node = stack.pop();
  7. if(node == null) continue;
  8. builder.append(node.e).append(" ");
  9. stack.push(node.right);
  10. stack.push(node.left);
  11. }
  12. System.out.println(builder.toString());
  13. }

3.中序遍历

1.同前序遍历,只是输出的顺序不同


  1. public void inOrder(){
  2. StringBuilder builer = new StringBuilder();
  3. _inOrder(root, builer);
  4. System.out.println(builer.toString);
  5. }
  6. private void _inOrder(Node node,StringBuilder builder){
  7. if(node == null) return;
  8. _inOrder(node.left, builder);
  9. builder.append(node.e).append(" ");
  10. _inOrder(node.right, builder);
  11. }

输入你会发现,二叉搜索树的中序遍历的结果是有序的(从小到大)。

4.后序遍历

1.同前序遍历,只是输出的顺序不同


  1. public void postOrder(){
  2. StringBuilder builer = new StringBuilder();
  3. _postOrder(root, builer);
  4. System.out.println(builer.toString);
  5. }
  6. private void _postOrder(Node node,StringBuilder builder){
  7. if(node == null) return;
  8. _postOrder(node.left, builder);
  9. _postOrder(node.right, builder);
  10. builder.append(node.e).append(" ");
  11. }

5.层序遍历

层序遍历指的是从上到下,从左到右一层层遍历,其遍历方法和上面的前序遍历类似,只是这里借助的是队列;

1.需要借助队列,首选将根节点入队,开始遍历队列;

2.如果当前节点不为null,则将其左子节点和右子节点入队,继续遍历;


  1. public void levelOrder() {
  2. StringBuilder builder = new StringBuilder();
  3. Queue<Node> queue = new LinkedList<>();
  4. queue.offer(root);
  5. while (!queue.isEmpty()) {
  6. Node node = queue.poll();
  7. if (node == null) continue;
  8. builder.append(node.e).append(" ");
  9. queue.offer(node.left);
  10. queue.offer(node.right);
  11. }
  12. System.out.println(builder.toString());
  13. }

删除

1.删除最小/最大节点

以删除最小为例,最小节点一定是树的最左边的那个:

1.因为删除的节点可能是遍历的当前节点,所以需要通过return的形式返回节点;

2.判断左子节点是否为null:

如果不为null则递归遍历左子节点;

如果为null则要删除的是当前节点,要把它的右节点返回;

  1. public Node removeMinimum() {
  2. root = _removeMinimun(root);
  3. return root;
  4. }
  5. private Node _removeMinimun(Node node) {
  6. if (node == null) return null;
  7. if (node.left == null) {
  8. //如果左子节点为null,则把右子节点返回
  9. Node tmp = node.right;
  10. node.right = null;
  11. size--;
  12. return tmp;
  13. }
  14. node.left = _removeMinimun(node.left);
  15. return node;
  16. }

2.删除任意节点

分三种情况:

  • 如果该节点没有左子节点:返回其右子节点;
  • 如果该节点没有右子节点:返回其左子节点;
  • 如果该节点同时拥有左右子节点:

节点的后继节点是大于它并最接近它的那个节点,也就是该节点的右子树中最小的节点,就是其右子树中最左边的那个,如下图中15的后继节点是17。

1.需要找到其后继节点;

2.将后继节点删除;

3.将后继节点的左子树等于该节点的左子树;

4.将后继节点的右子树等于该节点的右子树删除后继节点返回的树。


  1. /**
  2. * 删除任意节点,并返回删除后的根节点
  3. *
  4. * @param e
  5. * @return
  6. */
  7. public void removeNode(E e) {
  8. root = _removeNode(root, e);
  9. }
  10. private Node _removeNode(Node node, E e) {
  11. if (node == null) return null;
  12. if (e.compareTo(node.e) < 0) {
  13. node.left = _removeNode(node.left, e);
  14. } else if (e.compareTo(node.e) > 0) {
  15. node.right = _removeNode(node.right, e);
  16. } else {
  17. //1.查找到对应的节点
  18. //2.左子树为空的情况,直接把右子树返回
  19. if (node.left == null) {
  20. Node right = node.right;
  21. node.right = null;
  22. return right;
  23. }
  24. //3.右子树为空的情况,直接把左子树放回
  25. if (node.right == null) {
  26. Node left = node.left;
  27. node.left = null;
  28. return left;
  29. }
  30. //4.找到后继节点(右子树中最小的节点)
  31. Node successor = _mininum(node.right);
  32. //5.将右子树中最小的节点删除
  33. successor.right = _removeMinimun(node.right);
  34. //6.将节点的左子树等于后继节点的左子树
  35. successor.left = node.left;
  36. node.left = node.right = null;
  37. //7.返回后继节点
  38. return successor;
  39. }
  40. return node;
  41. }
  42. /**
  43. * 查找最小的节点
  44. *
  45. * @param node
  46. * @return
  47. */
  48. private Node _mininum(Node node) {
  49. if (node == null) return null;
  50. if (node.left != null) {
  51. return _mininum(node.left);
  52. }
  53. return node;
  54. }

完整代码BST.java

  1. import java.util.LinkedList;
  2. import java.util.Queue;
  3. import java.util.Stack;
  4. /**
  5. * Binary Search Tree
  6. *
  7. * @param <E>
  8. */
  9. public class BST<E extends Comparable<E>> {
  10. /**
  11. * 定义节点
  12. */
  13. private class Node {
  14. private E e;
  15. private Node left;
  16. private Node right;
  17. Node(E e) {
  18. this.e = e;
  19. }
  20. }
  21. private Node root;
  22. private int size;
  23. /**
  24. * 获取节点数
  25. *
  26. * @return
  27. */
  28. public int size() {
  29. return this.size;
  30. }
  31. /**
  32. * 添加节点
  33. *
  34. * @param element
  35. */
  36. public void add(E element) {
  37. root = add(root, element);
  38. }
  39. private Node add(Node node, E element) {
  40. if (node == null) {
  41. size++;
  42. return new Node(element);
  43. }
  44. if (element.compareTo(node.e) > 0) {
  45. node.right = add(node.right, element);
  46. } else if (element.compareTo(node.e) < 0) {
  47. node.left = add(node.left, element);
  48. }
  49. return node;
  50. }
  51. /**
  52. * 是否含有节点
  53. *
  54. * @param element
  55. * @return
  56. */
  57. public boolean contains(E element) {
  58. return contains(root, element);
  59. }
  60. private boolean contains(Node node, E element) {
  61. if (node == null) return false;
  62. if (element.equals(node.e)) return true;
  63. if (element.compareTo(node.e) < 0) {
  64. return contains(node.left, element);
  65. } else {
  66. return contains(node.right, element);
  67. }
  68. }
  69. @Override
  70. public String toString() {
  71. StringBuilder builder = new StringBuilder();
  72. toString(root, 0, builder);
  73. return builder.toString();
  74. }
  75. private void toString(Node node, int dept, StringBuilder builder) {
  76. for (int i = 0; i < dept; i++) {
  77. builder.append("--");
  78. }
  79. if (node == null) {
  80. builder.append("##\n");
  81. return;
  82. }
  83. builder.append(node.e);
  84. builder.append("\n");
  85. toString(node.left, dept + 1, builder);
  86. toString(node.right, dept + 1, builder);
  87. }
  88. /**
  89. * 前序遍历
  90. */
  91. public void preOrder() {
  92. StringBuilder builder = new StringBuilder();
  93. preOrder(root, builder);
  94. System.out.println(builder.toString());
  95. }
  96. private void preOrder(Node root, StringBuilder builder) {
  97. if (root == null) return;
  98. builder.append(root.e).append(" ");
  99. preOrder(root.left, builder);
  100. preOrder(root.right, builder);
  101. }
  102. /**
  103. * 前序遍历(非递归)
  104. */
  105. public void preOrderTraverse2() {
  106. StringBuilder builder = new StringBuilder();
  107. Stack<Node> stack = new Stack<>();
  108. stack.push(root);
  109. while (!stack.isEmpty()) {
  110. Node node = stack.pop();
  111. if (node == null) continue;
  112. builder.append(node.e).append(" ");
  113. stack.push(node.right);
  114. stack.push(node.left);
  115. }
  116. System.out.println(builder.toString());
  117. }
  118. /**
  119. * 中序遍历
  120. */
  121. public void inOrder() {
  122. StringBuilder builder = new StringBuilder();
  123. inOrder(root, builder);
  124. System.out.println(builder.toString());
  125. }
  126. private void inOrder(Node root, StringBuilder builder) {
  127. if (root == null) return;
  128. inOrder(root.left, builder);
  129. builder.append(root.e).append(" ");
  130. inOrder(root.right, builder);
  131. }
  132. /**
  133. * 后序遍历
  134. */
  135. public void postOrder() {
  136. StringBuilder builder = new StringBuilder();
  137. postOrder(root, builder);
  138. System.out.println(builder.toString());
  139. }
  140. private void postOrder(Node root, StringBuilder builder) {
  141. if (root == null) return;
  142. postOrder(root.left, builder);
  143. postOrder(root.right, builder);
  144. builder.append(root.e).append(" ");
  145. }
  146. /**
  147. * 层序遍历
  148. */
  149. public void levelOrder() {
  150. StringBuilder builder = new StringBuilder();
  151. Queue<Node> queue = new LinkedList<>();
  152. queue.offer(root);
  153. while (!queue.isEmpty()) {
  154. Node node = queue.poll();
  155. if (node == null) continue;
  156. builder.append(node.e).append(" ");
  157. queue.offer(node.left);
  158. queue.offer(node.right);
  159. }
  160. System.out.println(builder.toString());
  161. }
  162. /**
  163. * 删除最小节点,返回删除后的根节点
  164. */
  165. public Node removeMinimum() {
  166. root = _removeMinimun(root);
  167. return root;
  168. }
  169. private Node _removeMinimun(Node node) {
  170. if (node == null) return null;
  171. if (node.left == null) {
  172. //如果左子节点为null,则把右子节点返回
  173. Node tmp = node.right;
  174. node.right = null;
  175. size--;
  176. return tmp;
  177. }
  178. node.left = _removeMinimun(node.left);
  179. return node;
  180. }
  181. /**
  182. * 删除最大节点,,返回删除后的根节点
  183. *
  184. * @return
  185. */
  186. public Node removeMaximum() {
  187. root = _removeMaxinum(root);
  188. return root;
  189. }
  190. private Node _removeMaxinum(Node node) {
  191. if (node == null) return null;
  192. if (node.right == null) {
  193. Node tmp = node.left;
  194. node.left = null;
  195. size--;
  196. return tmp;
  197. }
  198. node.right = _removeMaxinum(node.right);
  199. return node;
  200. }
  201. /**
  202. * 删除任意节点,并返回删除后的根节点
  203. *
  204. * @param e
  205. * @return
  206. */
  207. public void removeNode(E e) {
  208. root = _removeNode(root, e);
  209. }
  210. private Node _removeNode(Node node, E e) {
  211. if (node == null) return null;
  212. if (e.compareTo(node.e) < 0) {
  213. node.left = _removeNode(node.left, e);
  214. } else if (e.compareTo(node.e) > 0) {
  215. node.right = _removeNode(node.right, e);
  216. } else {
  217. //1.查找到对应的节点
  218. //2.左子树为空的情况,直接把右子树返回
  219. if (node.left == null) {
  220. Node right = node.right;
  221. node.right = null;
  222. return right;
  223. }
  224. //3.右子树为空的情况,直接把左子树放回
  225. if (node.right == null) {
  226. Node left = node.left;
  227. node.left = null;
  228. return left;
  229. }
  230. //4.找到后继节点(右子树中最小的节点)
  231. Node successor = _mininum(node.right);
  232. //5.将右子树中最小的节点删除
  233. successor.right = _removeMinimun(node.right);
  234. //6.将节点的左子树等于后继节点的左子树
  235. successor.left = node.left;
  236. node.left = node.right = null;
  237. //7.返回后继节点
  238. return successor;
  239. }
  240. return node;
  241. }
  242. /**
  243. * 查找最小的节点
  244. *
  245. * @param node
  246. * @return
  247. */
  248. private Node _mininum(Node node) {
  249. if (node == null) return null;
  250. if (node.left != null) {
  251. return _mininum(node.left);
  252. }
  253. return node;
  254. }
  255. }

二叉搜索树(BST)基本操作的更多相关文章

  1. C++版 - 剑指offer 面试题24:二叉搜索树BST的后序遍历序列(的判断) 题解

    剑指offer 面试题24:二叉搜索树的后序遍历序列(的判断) 题目:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是则返回true.否则返回false.假设输入的数组的任意两个 ...

  2. 数据结构-二叉搜索树(BST binary search tree)

    本文由@呆代待殆原创,转载请注明出处:http://www.cnblogs.com/coffeeSS/ 二叉搜索树简介 顾名思义,二叉搜索树是以一棵二叉树来组织的,这样的一棵树可以用一个链表数据结构来 ...

  3. 萌新笔记之二叉搜索树(BST)

    前言,以前搞过线段树,二叉树觉得也就那样= =.然后数据结构的课也没怎么听过,然后下周期中考... 本来以为今天英语考完可以好好搞ACM了,然后这个数据结构期中考感觉会丢人,还是好好学习一波. 二叉搜 ...

  4. 给定一个二叉搜索树(BST),找到树中第 K 小的节点

    问题:给定一个二叉搜索树(BST),找到树中第 K 小的节点. 出题人:阿里巴巴出题专家:文景/阿里云 CDN 资深技术专家. 考察点: 1. 基础数据结构的理解和编码能力 2.  递归使用 参考答案 ...

  5. 二叉搜索树(BST)学习笔记

    BST调了一天,最后遍历参数错了,没药救了-- 本文所有代码均使用数组+结构体,不使用指针! 前言--BFS是啥 BST 二叉搜索树是基于二叉树的一种树,一种特殊的二叉树. 二叉搜索树要么是一颗空树, ...

  6. 看动画学算法之:二叉搜索树BST

    目录 简介 BST的基本性质 BST的构建 BST的搜索 BST的插入 BST的删除 简介 树是类似于链表的数据结构,和链表的线性结构不同的是,树是具有层次结构的非线性的数据结构. 树是由很多个节点组 ...

  7. 在二叉搜索树(BST)中查找第K个大的结点之非递归实现

    一个被广泛使用的面试题: 给定一个二叉搜索树,请找出其中的第K个大的结点. PS:我第一次在面试的时候被问到这个问题而且让我直接在白纸上写的时候,直接蒙圈了,因为没有刷题准备,所以就会有伤害.(面完的 ...

  8. 二叉搜索树 (BST) 的创建以及遍历

    二叉搜索树(Binary Search Tree) : 属于二叉树,其中每个节点都含有一个可以比较的键(如需要可以在键上关联值), 且每个节点的键都大于其左子树中的任意节点而小于右子树的任意节点的键. ...

  9. [LeetCode] Convert BST to Greater Tree 将二叉搜索树BST转为较大树

    Given a Binary Search Tree (BST), convert it to a Greater Tree such that every key of the original B ...

  10. 二叉搜索树(BST)

    (第一段日常扯蛋,大家不要看)这几天就要回家了,osgearth暂时也不想弄了,毕竟不是几天就能弄出来的,所以打算过完年回来再弄.这几天闲着也是闲着,就掏出了之前买的算法导论看了看,把二叉搜索树实现了 ...

随机推荐

  1. SQL Server 数据库本地备份文件通过OSS工具上阿里云(恢复还原数据库)

    SQL Server数据库上云,通过备份文件上传进行恢复. 1.通过OSS工具上传备份文件. 相关知识和操作步骤请参考: https://blog.csdn.net/weixin_35773751/a ...

  2. Python—系统模块(os和sys)

    os模块 https://www.cnblogs.com/feifeifeisir/p/9519282.html sys模块 获取Python版本信息 import sys a = sys.versi ...

  3. Ubuntu18.04 安装TensorFlow 和 Keras

    TensorFlow和Keras是当前两款主流的深度学习框架,Keras被采纳为TensorFlow的高级API,平时做深度学习任务,可以使用Keras作为深度学习框架,并用TensorFlow作为后 ...

  4. canopy聚类算法的MATLAB程序

    canopy聚类算法的MATLAB程序 凯鲁嘎吉 - 博客园 http://www.cnblogs.com/kailugaji/ 1. canopy聚类算法简介 Canopy聚类算法是一个将对象分组到 ...

  5. react界面跳转,滚动到顶部

    在使用react-router-dom时,我们经常会遇到路由切换时滚动到浏览器顶部的问题. 滚动到顶部 Scroll to top 很多时候我们需要的是滚动到顶部“Scroll to top”,因为发 ...

  6. jmeter从上一个请求使用正则表达式抓取Set-Cookie值,在下一个请求中运用

    工作中遇到的问题,登录请求,返回的Response Headers中有个参数Set-Cookie,需要抓取这个参数,运用到下一个请求中,见下图: 通过正则表达式抓取Set-Cookie的值,由于该值存 ...

  7. [C10] 异常检测(Anomaly Detection)

    异常检测(Anomaly Detection) 问题的动机 (Problem Motivation) 异常检测(Anomaly detection)问题是机器学习算法中的一个常见应用.这种算法的有趣之 ...

  8. es6 的类 class

    1.ES6提供了更接近传统语言的写法,引入了Class(类)这个概念,作为对象的模板.通过class关键字,可以定义类. 2. //定义类 class Point { constructor(x, y ...

  9. Educational Codeforces Round 63 (Rated for Div. 2) E 带模高斯消元

    https://codeforces.com/contest/1155/problem/E 题意 \(f(x)=a_0+a_1x+a_2x^2+...+a_kx^k,k \leq 10,0 \leq ...

  10. 【2019.7.24 NOIP模拟赛 T1】道路建设(road)(水题)

    原题与此题 原题是一道神仙不可做题,两者区别在于,原题不能有重边和自环. 然而,这题可以有重边... 于是这题就变成了一道大水题. 此题的解法 考虑如何构造. 对于\(n\le10^4\)的情况: 对 ...