赞
踩
目录
任务描述
顾名思义,一棵二叉搜索树是以一棵二叉树来组织的,但是在组织的时候需要满足一些特定的性质。
本关任务:构建一棵二叉搜索树,向其中添加结点。
相关知识
二叉搜索树
什么是二叉搜索树
二叉搜索树
(binary search tree)
,又称二叉排序树,它或者是空树,或者是满足如下性质的二叉树:
- 若它的左子树非空,则左子树上所有结点的值均小于根结点的值;
- 若它的右子树非空,则右子树上所有结点的值均大于根结点的值;
- 左、右子树本身又各是一棵二叉排序树。
简而言之,二叉搜索树中任一结点
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
小,应添加至根结点的左结点,但此时根结点的左结点是3
,2
与3
比较,小于,所以添加为3
的左结点;
(5)
输入值4
比根结点5
小,应添加至根结点的左结点,但此时根结点的左结点是3
,4
与3
比较,大于,所以添加为3
的右结点;
(6)
输入值8
比根结点5
大,应添加至根结点的右结点,但此时根结点的左结点是7
,8
与7
比较,大于,所以添加为7
的右结点。
循环中小于当前需要置入结点放左边,大于当前置入结点放右边
- package step1;
-
- /**
- * Created by zengpeng on 2018/3/3.
- */
- public class BSTree {
-
- private TreeNode root;//根结点
-
- public BSTree() {
- root = null;
- }
-
- /**
- * 向树root中插入key
- *
- * @param key 要插入的值
- */
- public void insert(int key) {
- /********** Begin *********/
- //二叉搜索树,严格要求左子树的值小于中间结点,右子树的值大于中间结点
- TreeNode x = root;//创建一个结点x,储存根结点root
- TreeNode p = null;//创建一个结点p,初始化为空
- while (x != null) {//如果x不为空则继续,找到需要插入的结点的前一个位置
- p = x;//用p储存x结点
- if (key < x.item) {//如果值小于当前结点的值,则进入左子树
- x = x.leftChild;//将左子树储存x
- } else {//如果值大于当前结点的值,则进入右子树
- x = x.rightChild;//将右子树储存x
- }
- }
- //找到位置后再进行判断,应该构建左还是右
- if (null == p) {//如果p为空,代表没有根结点
- root = new TreeNode(key);//根结点储存需要构建的当前结点
- } else if (key < p.item) {//如果值小于p结点的值
- p.leftChild = new TreeNode(key);//p的左子树构建当前结点
- } else {//如果值大于p结点的值
- p.rightChild = new TreeNode(key);//p的右子树构建当前结点
- }
- /********** End *********/
- }
-
- public void preOrder() {
- preOrder(root);
- }
-
- public void inOrder() {
- inOrder(root);
- }
-
- public void postOrder(){
- postOrder(root);
- }
-
- private void preOrder(TreeNode node) {
- if (node != null) {
- System.out.print(node.item + " ");
- preOrder(node.leftChild);
- preOrder(node.rightChild);
- }
- }
-
- private void inOrder(TreeNode node) {
- if (node != null) {
- inOrder(node.leftChild);
- System.out.print(node.item + " ");
- inOrder(node.rightChild);
- }
- }
-
- private void postOrder(TreeNode node) {
- if (node != null) {
- postOrder(node.leftChild);
- postOrder(node.rightChild);
- System.out.print(node.item + " ");
- }
- }
-
-
- public static class TreeNode {
- private TreeNode leftChild;
- private TreeNode rightChild;
- private int item;
-
- public TreeNode(int item) {
- this(null, null, item);
- }
-
- public TreeNode(TreeNode leftChild, TreeNode rightChild, int item) {
- this.leftChild = leftChild;
- this.rightChild = rightChild;
- this.item = item;
- }
- }
- }
以下是测试样例:
测试输入:
3 2 1 0 21
预期输出:
前序遍历: 3 2 1 0 21
中序遍历: 0 1 2 3 21
后序遍历: 0 1 2 21 3
任务描述
在上一关,我们构建了一棵二叉搜索树,并且可以向其添加结点数据,本关将实现在二叉搜索树的删除功能。
本关任务是:实现二叉搜索树的删除功能。
相关知识
后继结点
给定一棵二叉搜索树中的一个结点,有时候需要按中序遍历的次序查找它的后继。如果所有的关键字互不相同,则一个结点
x
的后继结点是大于x
的最小关键字的结点。上图中,结点
66
的后继结点为68
。20
的后继结点为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
。
删除的方法是如果有左子树,以左子树中最大值作为新的树根,否则,以右子树最小值作为树根。
- package step2;
-
- /**
- * Created by zengpeng on 2018/3/14.
- */
- public class BSTree {
- private TreeNode root;//根结点
-
- public BSTree() {
- root = null;
- }
-
- /**
- * 向树root中插入a
- *
- * @param key 要插入的值
- */
- public void insert(int key) {
- TreeNode x = root;
- TreeNode p = null;//始终指向x的父结点
- while (x != null) {
- p = x;
- if (key < x.item) {
- x = x.leftChild;
- } else {
- x = x.rightChild;
- }
- }
- if (null == p) {//空树
- root = new TreeNode(key);
- } else if (key < p.item) {
- p.leftChild = new TreeNode(key);
- } else {
- p.rightChild = new TreeNode(key);
- }
- }
-
- /**
- * 在树root中删除结点key
- *
- * @param key
- * @return
- */
- public void delete(int key) {
- root = delete(root, key);
- }
-
- private TreeNode delete(TreeNode x, int key) {
- /********** Begin *********/
- if (x == null)//x为根结点,如果根结点为空
- return null;//返回空
- //递归实现
- if (key < x.item) {//如果删除的值小于当前结点
- x.leftChild = delete(x.leftChild, key);//再次进入delete方法,往左子树继续搜寻需要删除的结点
- } else if (key > x.item) {
- x.rightChild = delete(x.rightChild, key);//再次进入delete方法,往右子树继续搜寻需要删除的结点
- } else {//如果等于,由于是二叉搜索树需要严格满足条件
- if (x.leftChild == null) return x.rightChild;//如果左子树为空,返回右子树的结点
- if (x.rightChild == null) return x.leftChild;//如果右子树为空,返回左子树的结点
- TreeNode t = x;//用t储存x结点
- x = min(t.rightChild);//寻找t右子树的最小值结点,并用x储存,需要用最小结点代替要被删除的结点的位置
- x.rightChild = deleteMin(t.rightChild);//删除找到的最小结点
- x.leftChild = t.leftChild;//让最小结点的左子树变为要删除的结点的左子树
- }
- return x;//返回
-
-
- /********** End *********/
- }
-
- /**
- * 删除树x中的最小结点
- *
- * @param x
- * @return
- */
- private TreeNode deleteMin(TreeNode x) {
- if (x.leftChild == null) return x.rightChild;
- x.leftChild = deleteMin(x.leftChild);
- return x;
- }
-
- /**
- * 查找树x中的最小结点
- *
- * @param x
- * @return
- */
- private TreeNode min(TreeNode x) {
- TreeNode p = x;
- while (p.leftChild != null) {
- p = p.leftChild;
- }
- return p;
- }
-
- public void preOrder() {
- preOrder(root);
- }
-
- private void preOrder(TreeNode node) {
- if (node != null) {
- System.out.print(node.item + " ");
- preOrder(node.leftChild);
- preOrder(node.rightChild);
- }
- }
-
- public void inOrder() {
- inOrder(root);
- }
-
- private void inOrder(TreeNode node) {
- if (node != null) {
- inOrder(node.leftChild);
- System.out.print(node.item + " ");
- inOrder(node.rightChild);
- }
- }
-
- public void postOrder() {
- postOrder(root);
- }
-
- private void postOrder(TreeNode node) {
- if (node != null) {
- postOrder(node.leftChild);
- postOrder(node.rightChild);
- System.out.print(node.item + " ");
- }
- }
-
-
- public static class TreeNode {
- private TreeNode leftChild;
- private TreeNode rightChild;
- private int item;
-
- public TreeNode(int item) {
- this(null, null, item);
- }
-
- public TreeNode(TreeNode leftChild, TreeNode rightChild, int item) {
- this.leftChild = leftChild;
- this.rightChild = rightChild;
- this.item = item;
- }
- }
- }
以下是测试样例:
测试输入:
3 2 1 0 21
预期输出:
前序遍历: 3 2 1 0 21
中序遍历: 0 1 2 3 21
后序遍历: 0 1 2 21 3
前序遍历: 21 2 1 0
中序遍历: 0 1 2 21
后序遍历: 0 1 2 21
任务描述
我们经常需要查找一个存储在二叉搜索树中的关键字,本关卡将讨论并实现查找功能。
本关任务:实现二叉搜索树的查找功能,如果指定的元素
x
存在树中则返回true
,否则返回false
。相关知识
二叉搜索树的查找
给定一个关键字
key
,我们要判断key
是否在指定的二叉搜索树T
中,如果存在则返回true
,不存在则返回false
。这个查找过程从树根结点开始,并沿着这棵树中的一条简单路径向下进行,如下图所示。对于遇到的每个结点
x
,比较key
和x
。如果两个关键字相等,查找终止。如果key
小于x
,查找在x
的左子树中继续,因为二叉搜索树的性质意味着key
不可能被存储在x
的右子树中。对称地,如果key
大于x
,查找在右子树中继续。为了查找上图中的关键字为
13
的结点,从树根开始沿着15->6->7->13
路径进行查找。
- package step3;
-
- /**
- * Created by zengpeng on 2018/3/14.
- */
- public class BSTree {
- private TreeNode root;//根结点
-
- public BSTree() {
- root = null;
- }
-
- /**
- * 向树root中插入a
- *
- * @param key 要插入的值
- */
- public void insert(int key) {
- TreeNode x = root;
- TreeNode p = null;//始终指向x的父结点
- while (x != null) {
- p = x;
- if (key < x.item) {
- x = x.leftChild;
- } else {
- x = x.rightChild;
- }
- }
- if (null == p) {//空树
- root = new TreeNode(key);
- } else if (key < p.item) {
- p.leftChild = new TreeNode(key);
- } else {
- p.rightChild = new TreeNode(key);
- }
- }
-
- /**
- * 判断树root中是否包含key,包含则返回true,不包含返回false
- *
- * @param key
- * @return
- */
- public boolean search(int key) {
- /********** Begin *********/
- TreeNode p=root;//用p结点储存根结点
- while(p!=null&&key!=p.item)//如果p不为空,并且要查找的值不为p结点的值
- {
- if(key<p.item)//如果要查找的值小于p结点的值
- {
- p=p.leftChild;//让p储存为p的左子树,因为小于p结点值的,结点都在左边
- }
- else//如果要查找的值大于p结点的值
- {
- p=p.rightChild;//让p储存为p的大子树,因为大于p结点值的,结点都在右边
- }
- }
- if(p==null)//如果p为空则表示没找到
- return false;//返回false
- else//找到了
- return true;//返回true
- /********** End *********/
- }
-
-
- /**
- * 在树root中删除结点key
- *
- * @param key
- * @return
- */
- public void delete(int key) {
- root = delete(root, key);
- }
-
- private TreeNode delete(TreeNode x, int key) {
- if (x == null) {
- return null;
- }
-
- if (key < x.item) {
- x.leftChild = delete(x.leftChild, key);
- } else if (key > x.item) {
- x.rightChild = delete(x.rightChild, key);
- } else {
- if (x.leftChild == null) return x.rightChild;
- if (x.rightChild == null) return x.leftChild;
- TreeNode t = x;
- x = min(t.rightChild);
- x.rightChild = deleteMin(t.rightChild);
- x.leftChild = t.leftChild;
- }
-
- return x;
- }
-
- /**
- * 删除树x中的最小结点
- * @param x
- * @return
- */
- private TreeNode deleteMin(TreeNode x) {
- if (x.leftChild == null) return x.rightChild;
- x.leftChild = deleteMin(x.leftChild);
- return x;
- }
-
- /**
- * 查找树x中的最小结点
- *
- * @param x
- * @return
- */
- private TreeNode min(TreeNode x) {
- TreeNode p = x;
- while (p.leftChild != null) {
- p = p.leftChild;
- }
- return p;
- }
-
- public void preOrder() {
- preOrder(root);
- }
-
- public void inOrder() {
- inOrder(root);
- }
-
- public void postOrder() {
- postOrder(root);
- }
-
- private void preOrder(TreeNode node) {
- if (node != null) {
- System.out.print(node.item + " ");
- preOrder(node.leftChild);
- preOrder(node.rightChild);
- }
- }
-
- private void inOrder(TreeNode node) {
- if (node != null) {
- inOrder(node.leftChild);
- System.out.print(node.item + " ");
- inOrder(node.rightChild);
- }
- }
-
- private void postOrder(TreeNode node) {
- if (node != null) {
- postOrder(node.leftChild);
- postOrder(node.rightChild);
- System.out.print(node.item + " ");
- }
- }
-
-
- public static class TreeNode {
- private TreeNode leftChild;
- private TreeNode rightChild;
- private int item;
-
- public TreeNode(int item) {
- this(null, null, item);
- }
-
- public TreeNode(TreeNode leftChild, TreeNode rightChild, int item) {
- this.leftChild = leftChild;
- this.rightChild = rightChild;
- this.item = item;
- }
- }
- }
以下是测试样例:
测试输入:
1 5 4 3 2 6
预期输出:
删除前树中的结点:1 5 4 3 2 6
删除后树中的结点:5 4 3 2
false true true true true false
注意:二叉搜索树中,如果存在左右子树,则左子树上的结点都小于根结点,右结点子树上的结点都大于根结点,严格要求左子树小于根结点,右子树大于根结点
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。