当前位置:   article > 正文

Java 数据结构之二叉搜索树(头歌平台,详细注释)_java n叉搜索树

java n叉搜索树

目录

第1关:二叉搜索树的介绍与构建

任务描述

相关知识

二叉搜索树

什么是二叉搜索树

二叉搜索树的构建

代码: 

第2关:二叉搜索树的删除

任务描述

相关知识

后继结点

二叉搜索树的删除

代码: 

第3关:二叉搜索树的查找

任务描述

相关知识

二叉搜索树的查找

代码: 


第1关:二叉搜索树的介绍与构建

任务描述

顾名思义,一棵二叉搜索树是以一棵二叉树来组织的,但是在组织的时候需要满足一些特定的性质。

本关任务:构建一棵二叉搜索树,向其中添加结点。

相关知识
二叉搜索树
什么是二叉搜索树

二叉搜索树(binary search tree),又称二叉排序树,它或者是空树,或者是满足如下性质的二叉树:

  1. 若它的左子树非空,则左子树上所有结点的值均小于根结点的值;
  2. 若它的右子树非空,则右子树上所有结点的值均大于根结点的值;
  3. 左、右子树本身又各是一棵二叉排序树。

简而言之,二叉搜索树中任一结点x,其左(右)子树中任一结点y(若存在)的关键字必小(大)于x的关键字。

下图是两棵二叉搜索树:

可以看到,对于树中的任何结点,其值大于左结点,小于右结点。 如果我们以中序遍历遍历一棵二叉搜索树,所得到的中序序列是一个递增有序序列。

上图二叉搜索树的中序遍历结果均为2 3 4 5 7 8

二叉搜索树的构建

构建一棵二叉搜索树,实际是构建一棵二叉树,但在添加结点时,需要满足二叉搜索树的性质。 以输入序列5 3 7 2 4 8为例,构建一棵二叉搜索树。如下图所示:

(1)二叉搜索树为空,5为根结点;

(2)输入值3比根结点5小,且此时根结点的左结点为空,所以3添加为根结点的左结点;

(3)输入值7比根结点5大,添加为根结点的右结点;

(4)输入值2比根结点5小,应添加至根结点的左结点,但此时根结点的左结点是323比较,小于,所以添加为3的左结点;

(5)输入值4比根结点5小,应添加至根结点的左结点,但此时根结点的左结点是343比较,大于,所以添加为3的右结点;

(6)输入值8比根结点5大,应添加至根结点的右结点,但此时根结点的左结点是787比较,大于,所以添加为7的右结点。

循环中小于当前需要置入结点放左边,大于当前置入结点放右边

代码: 
  1. package step1;
  2. /**
  3. * Created by zengpeng on 2018/3/3.
  4. */
  5. public class BSTree {
  6. private TreeNode root;//根结点
  7. public BSTree() {
  8. root = null;
  9. }
  10. /**
  11. * 向树root中插入key
  12. *
  13. * @param key 要插入的值
  14. */
  15. public void insert(int key) {
  16. /********** Begin *********/
  17. //二叉搜索树,严格要求左子树的值小于中间结点,右子树的值大于中间结点
  18. TreeNode x = root;//创建一个结点x,储存根结点root
  19. TreeNode p = null;//创建一个结点p,初始化为空
  20. while (x != null) {//如果x不为空则继续,找到需要插入的结点的前一个位置
  21. p = x;//用p储存x结点
  22. if (key < x.item) {//如果值小于当前结点的值,则进入左子树
  23. x = x.leftChild;//将左子树储存x
  24. } else {//如果值大于当前结点的值,则进入右子树
  25. x = x.rightChild;//将右子树储存x
  26. }
  27. }
  28. //找到位置后再进行判断,应该构建左还是右
  29. if (null == p) {//如果p为空,代表没有根结点
  30. root = new TreeNode(key);//根结点储存需要构建的当前结点
  31. } else if (key < p.item) {//如果值小于p结点的值
  32. p.leftChild = new TreeNode(key);//p的左子树构建当前结点
  33. } else {//如果值大于p结点的值
  34. p.rightChild = new TreeNode(key);//p的右子树构建当前结点
  35. }
  36. /********** End *********/
  37. }
  38. public void preOrder() {
  39. preOrder(root);
  40. }
  41. public void inOrder() {
  42. inOrder(root);
  43. }
  44. public void postOrder(){
  45. postOrder(root);
  46. }
  47. private void preOrder(TreeNode node) {
  48. if (node != null) {
  49. System.out.print(node.item + " ");
  50. preOrder(node.leftChild);
  51. preOrder(node.rightChild);
  52. }
  53. }
  54. private void inOrder(TreeNode node) {
  55. if (node != null) {
  56. inOrder(node.leftChild);
  57. System.out.print(node.item + " ");
  58. inOrder(node.rightChild);
  59. }
  60. }
  61. private void postOrder(TreeNode node) {
  62. if (node != null) {
  63. postOrder(node.leftChild);
  64. postOrder(node.rightChild);
  65. System.out.print(node.item + " ");
  66. }
  67. }
  68. public static class TreeNode {
  69. private TreeNode leftChild;
  70. private TreeNode rightChild;
  71. private int item;
  72. public TreeNode(int item) {
  73. this(null, null, item);
  74. }
  75. public TreeNode(TreeNode leftChild, TreeNode rightChild, int item) {
  76. this.leftChild = leftChild;
  77. this.rightChild = rightChild;
  78. this.item = item;
  79. }
  80. }
  81. }

以下是测试样例:

测试输入:

  1. 3 2 1 0 21

预期输出:

  1. 前序遍历: 3 2 1 0 21
  2. 中序遍历: 0 1 2 3 21
  3. 后序遍历: 0 1 2 21 3

第2关:二叉搜索树的删除

任务描述

在上一关,我们构建了一棵二叉搜索树,并且可以向其添加结点数据,本关将实现在二叉搜索树的删除功能。

本关任务是:实现二叉搜索树的删除功能。

相关知识
后继结点

给定一棵二叉搜索树中的一个结点,有时候需要按中序遍历的次序查找它的后继。如果所有的关键字互不相同,则一个结点x的后继结点是大于x的最小关键字的结点。

上图中,结点66的后继结点为6820的后继结点为66

二叉搜索树的删除

从一棵二叉搜索树T中删除一个结点z的整个策略可分为三个基本情况:

  • 如果z没有孩子结点,那么只是简单地将它删除,并修改它的父结点;
  • 如果z只有一个孩子结点,那么将这个孩子提升到树中z的位置上,并修改z的父结点,使其指向z的孩子结点;
  • 如果z有两个孩子,那么找z的后继结点y,并让y占据树中z的位置。z的原来右子树部分成为y的新的右子树,并且z的左子树成为y的新的左子树。(根据二叉搜索树的性质,后继结点y一定在z的右子树中)。

下列是几种情况的图示: (1)删除没有子结点的结点;

因为结点150没有孩子结点,所以直接把其删掉。

(2)删除有一个子结点的结点;

结点50有一个孩子结点,我们只需把其孩子结点提升到结点50的位置即可。

(3)删除有两个子结点的结点;

对于有两个子结点的结点50,在其右子树中找到其后继结点66,使后继结点相应的替换结点50

删除的方法是如果有左子树,以左子树中最大值作为新的树根,否则,以右子树最小值作为树根。

代码: 
  1. package step2;
  2. /**
  3. * Created by zengpeng on 2018/3/14.
  4. */
  5. public class BSTree {
  6. private TreeNode root;//根结点
  7. public BSTree() {
  8. root = null;
  9. }
  10. /**
  11. * 向树root中插入a
  12. *
  13. * @param key 要插入的值
  14. */
  15. public void insert(int key) {
  16. TreeNode x = root;
  17. TreeNode p = null;//始终指向x的父结点
  18. while (x != null) {
  19. p = x;
  20. if (key < x.item) {
  21. x = x.leftChild;
  22. } else {
  23. x = x.rightChild;
  24. }
  25. }
  26. if (null == p) {//空树
  27. root = new TreeNode(key);
  28. } else if (key < p.item) {
  29. p.leftChild = new TreeNode(key);
  30. } else {
  31. p.rightChild = new TreeNode(key);
  32. }
  33. }
  34. /**
  35. * 在树root中删除结点key
  36. *
  37. * @param key
  38. * @return
  39. */
  40. public void delete(int key) {
  41. root = delete(root, key);
  42. }
  43. private TreeNode delete(TreeNode x, int key) {
  44. /********** Begin *********/
  45. if (x == null)//x为根结点,如果根结点为空
  46. return null;//返回空
  47. //递归实现
  48. if (key < x.item) {//如果删除的值小于当前结点
  49. x.leftChild = delete(x.leftChild, key);//再次进入delete方法,往左子树继续搜寻需要删除的结点
  50. } else if (key > x.item) {
  51. x.rightChild = delete(x.rightChild, key);//再次进入delete方法,往右子树继续搜寻需要删除的结点
  52. } else {//如果等于,由于是二叉搜索树需要严格满足条件
  53. if (x.leftChild == null) return x.rightChild;//如果左子树为空,返回右子树的结点
  54. if (x.rightChild == null) return x.leftChild;//如果右子树为空,返回左子树的结点
  55. TreeNode t = x;//用t储存x结点
  56. x = min(t.rightChild);//寻找t右子树的最小值结点,并用x储存,需要用最小结点代替要被删除的结点的位置
  57. x.rightChild = deleteMin(t.rightChild);//删除找到的最小结点
  58. x.leftChild = t.leftChild;//让最小结点的左子树变为要删除的结点的左子树
  59. }
  60. return x;//返回
  61. /********** End *********/
  62. }
  63. /**
  64. * 删除树x中的最小结点
  65. *
  66. * @param x
  67. * @return
  68. */
  69. private TreeNode deleteMin(TreeNode x) {
  70. if (x.leftChild == null) return x.rightChild;
  71. x.leftChild = deleteMin(x.leftChild);
  72. return x;
  73. }
  74. /**
  75. * 查找树x中的最小结点
  76. *
  77. * @param x
  78. * @return
  79. */
  80. private TreeNode min(TreeNode x) {
  81. TreeNode p = x;
  82. while (p.leftChild != null) {
  83. p = p.leftChild;
  84. }
  85. return p;
  86. }
  87. public void preOrder() {
  88. preOrder(root);
  89. }
  90. private void preOrder(TreeNode node) {
  91. if (node != null) {
  92. System.out.print(node.item + " ");
  93. preOrder(node.leftChild);
  94. preOrder(node.rightChild);
  95. }
  96. }
  97. public void inOrder() {
  98. inOrder(root);
  99. }
  100. private void inOrder(TreeNode node) {
  101. if (node != null) {
  102. inOrder(node.leftChild);
  103. System.out.print(node.item + " ");
  104. inOrder(node.rightChild);
  105. }
  106. }
  107. public void postOrder() {
  108. postOrder(root);
  109. }
  110. private void postOrder(TreeNode node) {
  111. if (node != null) {
  112. postOrder(node.leftChild);
  113. postOrder(node.rightChild);
  114. System.out.print(node.item + " ");
  115. }
  116. }
  117. public static class TreeNode {
  118. private TreeNode leftChild;
  119. private TreeNode rightChild;
  120. private int item;
  121. public TreeNode(int item) {
  122. this(null, null, item);
  123. }
  124. public TreeNode(TreeNode leftChild, TreeNode rightChild, int item) {
  125. this.leftChild = leftChild;
  126. this.rightChild = rightChild;
  127. this.item = item;
  128. }
  129. }
  130. }

以下是测试样例:

测试输入:3 2 1 0 21 预期输出:

  1. 前序遍历: 3 2 1 0 21
  2. 中序遍历: 0 1 2 3 21
  3. 后序遍历: 0 1 2 21 3
  4. 前序遍历: 21 2 1 0
  5. 中序遍历: 0 1 2 21
  6. 后序遍历: 0 1 2 21

第3关:二叉搜索树的查找

任务描述

我们经常需要查找一个存储在二叉搜索树中的关键字,本关卡将讨论并实现查找功能。

本关任务:实现二叉搜索树的查找功能,如果指定的元素x存在树中则返回true,否则返回false

相关知识
二叉搜索树的查找

给定一个关键字key,我们要判断key是否在指定的二叉搜索树T中,如果存在则返回true,不存在则返回false

这个查找过程从树根结点开始,并沿着这棵树中的一条简单路径向下进行,如下图所示。对于遇到的每个结点x,比较keyx。如果两个关键字相等,查找终止。如果key小于x,查找在x的左子树中继续,因为二叉搜索树的性质意味着key不可能被存储在x的右子树中。对称地,如果key大于x,查找在右子树中继续。

为了查找上图中的关键字为13的结点,从树根开始沿着15->6->7->13路径进行查找。

代码: 
  1. package step3;
  2. /**
  3. * Created by zengpeng on 2018/3/14.
  4. */
  5. public class BSTree {
  6. private TreeNode root;//根结点
  7. public BSTree() {
  8. root = null;
  9. }
  10. /**
  11. * 向树root中插入a
  12. *
  13. * @param key 要插入的值
  14. */
  15. public void insert(int key) {
  16. TreeNode x = root;
  17. TreeNode p = null;//始终指向x的父结点
  18. while (x != null) {
  19. p = x;
  20. if (key < x.item) {
  21. x = x.leftChild;
  22. } else {
  23. x = x.rightChild;
  24. }
  25. }
  26. if (null == p) {//空树
  27. root = new TreeNode(key);
  28. } else if (key < p.item) {
  29. p.leftChild = new TreeNode(key);
  30. } else {
  31. p.rightChild = new TreeNode(key);
  32. }
  33. }
  34. /**
  35. * 判断树root中是否包含key,包含则返回true,不包含返回false
  36. *
  37. * @param key
  38. * @return
  39. */
  40. public boolean search(int key) {
  41. /********** Begin *********/
  42. TreeNode p=root;//用p结点储存根结点
  43. while(p!=null&&key!=p.item)//如果p不为空,并且要查找的值不为p结点的值
  44. {
  45. if(key<p.item)//如果要查找的值小于p结点的值
  46. {
  47. p=p.leftChild;//让p储存为p的左子树,因为小于p结点值的,结点都在左边
  48. }
  49. else//如果要查找的值大于p结点的值
  50. {
  51. p=p.rightChild;//让p储存为p的大子树,因为大于p结点值的,结点都在右边
  52. }
  53. }
  54. if(p==null)//如果p为空则表示没找到
  55. return false;//返回false
  56. else//找到了
  57. return true;//返回true
  58. /********** End *********/
  59. }
  60. /**
  61. * 在树root中删除结点key
  62. *
  63. * @param key
  64. * @return
  65. */
  66. public void delete(int key) {
  67. root = delete(root, key);
  68. }
  69. private TreeNode delete(TreeNode x, int key) {
  70. if (x == null) {
  71. return null;
  72. }
  73. if (key < x.item) {
  74. x.leftChild = delete(x.leftChild, key);
  75. } else if (key > x.item) {
  76. x.rightChild = delete(x.rightChild, key);
  77. } else {
  78. if (x.leftChild == null) return x.rightChild;
  79. if (x.rightChild == null) return x.leftChild;
  80. TreeNode t = x;
  81. x = min(t.rightChild);
  82. x.rightChild = deleteMin(t.rightChild);
  83. x.leftChild = t.leftChild;
  84. }
  85. return x;
  86. }
  87. /**
  88. * 删除树x中的最小结点
  89. * @param x
  90. * @return
  91. */
  92. private TreeNode deleteMin(TreeNode x) {
  93. if (x.leftChild == null) return x.rightChild;
  94. x.leftChild = deleteMin(x.leftChild);
  95. return x;
  96. }
  97. /**
  98. * 查找树x中的最小结点
  99. *
  100. * @param x
  101. * @return
  102. */
  103. private TreeNode min(TreeNode x) {
  104. TreeNode p = x;
  105. while (p.leftChild != null) {
  106. p = p.leftChild;
  107. }
  108. return p;
  109. }
  110. public void preOrder() {
  111. preOrder(root);
  112. }
  113. public void inOrder() {
  114. inOrder(root);
  115. }
  116. public void postOrder() {
  117. postOrder(root);
  118. }
  119. private void preOrder(TreeNode node) {
  120. if (node != null) {
  121. System.out.print(node.item + " ");
  122. preOrder(node.leftChild);
  123. preOrder(node.rightChild);
  124. }
  125. }
  126. private void inOrder(TreeNode node) {
  127. if (node != null) {
  128. inOrder(node.leftChild);
  129. System.out.print(node.item + " ");
  130. inOrder(node.rightChild);
  131. }
  132. }
  133. private void postOrder(TreeNode node) {
  134. if (node != null) {
  135. postOrder(node.leftChild);
  136. postOrder(node.rightChild);
  137. System.out.print(node.item + " ");
  138. }
  139. }
  140. public static class TreeNode {
  141. private TreeNode leftChild;
  142. private TreeNode rightChild;
  143. private int item;
  144. public TreeNode(int item) {
  145. this(null, null, item);
  146. }
  147. public TreeNode(TreeNode leftChild, TreeNode rightChild, int item) {
  148. this.leftChild = leftChild;
  149. this.rightChild = rightChild;
  150. this.item = item;
  151. }
  152. }
  153. }

以下是测试样例:

测试输入:1 5 4 3 2 6 预期输出:

  1. 删除前树中的结点:1 5 4 3 2 6
  2. 删除后树中的结点:5 4 3 2
  3. false true true true true false

注意:二叉搜索树中,如果存在左右子树,则左子树上的结点都小于根结点,右结点子树上的结点都大于根结点,严格要求左子树小于根结点,右子树大于根结点

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/669854
推荐阅读
相关标签
  

闽ICP备14008679号