漫谈二叉搜索树的基本算法(三种思路实现查询操作)
来源:互联网 发布:侠义精神 知乎 编辑:程序博客网 时间:2024/05/22 00:38
前面我们说了二叉树前序中序后序遍历的递归非递归算法的实现,下面我们再来说说二叉搜索树~
二叉排序树分为静态查找(find)和动态查找(insert、delete)
二叉搜索树:一棵二叉树,可以为空;如果不为空,满足下列性质:
1.非空左子树的所有键值小于其根结点的键值。
2.非空右子树的所有键值大于其根结点的键值
3.左右子树都是二叉搜索树!!
2.以上是二叉搜索树(也叫二叉排序树)的一些基本操作,此处我们先说一下二叉树的结点定义··
代码中判断当前结点位置情况的辅助方法以及简单的 get 方法都在常数时间内可以完成,实现也相应非常简单。下面主要讨论 updateHeight ()、 updateSize ()、 sever()、setLChild(lc)、 getRChild(rc)的实现与时间复杂度。
1)
// 更新当前结点及其祖先的高度
public void updateHeight() {
int newH = 0;// 新高度初始化为 0,高度等于左右子树高度加 1 中的大者
if (hasLChild())
newH = Math.max(newH, 1 + getLChild().getHeight());//返回两者中较大的一个
if (hasRChild())
newH = Math.max(newH, 1 + getRChild().getHeight());
if (newH == height)
return; // 高度没有发生变化则直接返回
height = newH; // 否则更新高度
if (hasParent())
getParent().updateHeight(); // 递归更新祖先的高度
}
updateHeight ():若当前结点 v 的孩子发生变化,就需要使用 updateHeight ()方法更新当前结点及其祖先结点的高度。请注意,由于一个结点的高度发生变化,会影响到其祖先结点的高度,在这里我们允许直接对任何结点执行这一操作。因为在二叉树中任何一个结点的高度,都等于其左右子树的高度中大者加 1,而左右子树的高度只需要获取该结点左右孩子的高度,只需要Θ (1)时间。继而从 v 出发沿parent 引用逆行向上,依次更新各祖先结点的高度即可。如果在上述过程中,发现某个结点的高度没有发生变化,算法可以直接终止。综上所述,当对一个结点 v 调用 updateHeight ()方法时,若 v 的层数为 level(v),则最多只需要更新 level(v)+1 个结点的高度,因此算法的时间复杂度 T(n) = Ο (level(v))。
2)
// 更新当前结点及其祖先的子孙数
public void updateSize() {
size = 1; // 初始化为 1,结点本身
if (hasLChild())
size += getLChild().getSize(); // 加上左子树规模
if (hasRChild())
size += getRChild().getSize(); // 加上右子树规模
if (hasParent())
getParent().updateSize(); // 递归更新祖先的规模
}
updateSize ():同样如果结点 v 的孩子发生变化,应该更新当前结点以及其祖先的规模。因为在二叉树中任何一个结点的规模,都等于其左右子树的规模之和加上结点自身,而左右子树的规模只需要获取该结点左右孩子的规模即可获得,只需要Θ (1)时间。因此算法的时间复杂度 T(n) = Ο (level(v))。
3)
3)
// 断开与父亲的关系
public void sever() {
if (!hasParent())
return;
if (isLChild())
parent.lChild = null;
else
parent.rChild = null;
parent.updateHeight(); // 更新父结点及其祖先高度
parent.updateSize(); // 更新父结点及其祖先规模
parent = null;
}
sever():切断结点 v 与父结点 p 之间的关系。该算法需要修改 v 与 p 的指针域,需要常数时间。除此之外由于 p 结点的孩子发生了变化,因此需要调用 updateHeight ()和updateSize ()来更新父结点 p 及其祖先的高度与规模。其时间复杂度 T(n) = Ο (level(v))。
4)
4)
// 设置当前结点的左孩子,返回原左孩子
public BinTreeNode setLChild(BinTreeNode lc) {
BinTreeNode oldLC = this.lChild;
if (hasLChild()) {
lChild.sever();
} // 断开当前左孩子与结点的关系
if (lc != null) {
lc.sever(); // 断开 lc 与其父结点的关系
this.lChild = lc; // 确定父子关系
lc.parent = this;
this.updateHeight(); // 更新当前结点及其祖先高度
this.updateSize(); // 更新当前结点及其祖先规模
}
return oldLC; // 返回原左孩子
}
// 取右孩子
public BinTreeNode getRChild() {
return rChild;
}
// 设置当前结点的右孩子,返回原右孩子
public BinTreeNode setRChild(BinTreeNode rc) {
BinTreeNode oldRC = this.rChild;
if (hasRChild()) {
rChild.sever();
} // 断开当前右孩子与结点的关系
if (rc != null) {
rc.sever(); // 断开 lc 与其父结点的关系
this.rChild = rc; // 确定父子关系
rc.parent = this;
this.updateHeight(); // 更新当前结点及其祖先高度
this.updateSize(); // 更新当前结点及其祖先规模
}
return oldRC; // 返回原右孩子
}
setLChild(lc)、 getRChild(rc):两个算法的功能相对,一个是设置结点 v 的左孩子,一个是设置结点 v 的右孩子。两个算法的实现是类似的,以 setLChild()为例说明。首先,如果 v 有左孩子 oldLC,则应当调用 oldLC. sever()断开 v 与其左孩子的关系。 其次, 调用 lc. sever()断开其与父结点的关系。最后,建立 v 与 lc 之间的父子关系,并调用 v. updateSize ()与 v.updateHeight ()更新 v 及其祖先的规模与高度。
下面是用代码实现的树高度和查询树叶子结点的操作。public class BinSearchTreeTest01 {public Point root;//访问结点public static void visitKey(Point p){System.out.println(p.getKey() + " ");}//构造树public static Point Tree(){Point a = new Point('A', null, null);Point b = new Point('B', null, a);Point c = new Point('C', null, null);Point d = new Point('D', b, c);Point e = new Point('E', null, null);Point f = new Point('F', e, null);Point g = new Point('G', null, f);Point h = new Point('H', d, g);return h;// root}//求二叉树的高度 height = max(HL , HR) + 1public static int height(Point p){int HL , HR , MaxH;if(p != null){HL = height(p.getLeftChild());HR = height(p.getRightChild());MaxH = Math.max(HL, HR);return MaxH + 1;}return 0;}//求二叉树的叶子结点public static void OrderLeaves(Point p){if(p != null){if(p.getLeftChild() == null && p.getRightChild() == null)visitKey(p);OrderLeaves(p.getLeftChild());OrderLeaves(p.getRightChild());}}public static void main(String[] args) {System.out.println("二叉树的高度为:" + height(Tree()));System.out.print("二叉树的叶子结点为:");OrderLeaves(Tree());}}class Point{private char key;private Point LeftChild , RightChild;public Point(char key , Point LeftChild , Point RightChild){this.key = key;this.LeftChild = LeftChild;this.RightChild = RightChild;}public Point(char key){this(key , null ,null);}public char getKey() {return key;}public void setKey(char key) {this.key = key;}public Point getLeftChild() {return LeftChild;}public void setLeftChild(Point leftChild) {LeftChild = leftChild;}public Point getRightChild() {return RightChild;}public void setRightChild(Point rightChild) {RightChild = rightChild;}}3.下面开始今天的正题,二叉搜索树的查询,插入和删除操作
1)Find
·查找从根结点开始,如果树为空,返回NULL
·若搜索树非空,则根结点关键字和X进行比较,并进行不同处理:
1)若X小于根结点键值,只需要在左子树进行搜索;
2)若X大于根结点键值,在右子树进行搜索;
3)若两者比较结果相同,搜索完成,返回此结点。
import java.util.Scanner;/** * 二叉搜索树的操作集锦 * * @author Administrator * */class BinSearchTreeTest02 {public Point1 root;// 访问结点public static void visitKey(Point1 p) {System.out.println(p.getKey() + " ");}// 构造树public static Point1 Tree() {Point1 m = new Point1(4, null, null);Point1 a = new Point1(6, m, null);Point1 b = new Point1(5, null, a);Point1 c = new Point1(14, null, null);Point1 d = new Point1(10, b, c);Point1 e = new Point1(24, null, null);Point1 f = new Point1(25, e, null);Point1 g = new Point1(20, null, f);Point1 h = new Point1(15, d, g);return h;// root}// 构造空树public static Point1 EmptyTree(int t) {Point1 a = new Point1(t, null, null);return a;}// *********************************查找操作****************************/** * 二叉搜索树查找操作方法1 * * @param t * @param node * @return */public static boolean contains(int t, Point1 node) {if (node == null)return false;// 结点为空,查找失败String st = Integer.toString(t);// 把要查找的结点转化为String类型int result = compareTo(st, node);if (result == 1) {return true;} else {return false;}}public static int compareTo(String a, Point1 p) {// String b = Integer.toString(p.getKey());// if(a.equals(b))和下面这行是等价的if (a.equals(p.getKey() + ""))return 1;int i = 0, j = 0;if (p.getLeftChild() != null) {i = compareTo(a, p.getLeftChild());}if (p.getRightChild() != null) {j = compareTo(a, p.getRightChild());}if (i == 1) {return i;} else if (j == 1) {return j;} else {return -1;}}/** * 二叉搜索树查找操作方法2(递归算法) 尾递归的方式 * * @param t * @param node * @return */public static Point1 contains2(int t, Point1 node) {if (node == null)return null;// 结点为空,查找失败if (t > node.getKey())return contains2(t, node.getRightChild());// 查找右子树else if (t < node.getKey())return contains2(t, node.getLeftChild());// 查找左子树elsereturn node;}/** * 二叉搜索树查找的搜索方法2的变形,非递归算法的效率更高 将尾递归改为迭代函数 * * @param args */public static Point1 contains3(int t, Point1 node) {while (node != null) {if (t > node.getKey())node = node.getRightChild();else if (t < node.getKey())node = node.getLeftChild();elsereturn node;}return null;}/** * 二叉搜索树的最大元素 根据其性质可以得出二叉搜索树的最大元一定位于右子树的最右端,最小元则相反 * * @param args */public static int findMax(Point1 point) {if (point != null) {while (point.getRightChild() != null) {point = point.getRightChild();}}return point.getKey();}public static int findMin(Point1 point) {if (point == null)return 0;// 先判断树是否为空else if (point.getLeftChild() == null)return point.getKey();// 在判断左子树是否为空elsereturn findMin(point.getLeftChild());}public static void main(String[] args, Point1 p) {System.out.println("此二叉树的最大结点为:" + findMax(Tree()));System.out.println("此二叉树的最小结点为:" + findMin(Tree()));@SuppressWarnings("resource")Scanner scanner = new Scanner(System.in);System.out.println("输入一个结点,将判断是否是此二叉树的结点");int a = scanner.nextInt();if (contains2(a, Tree()) != null) System.out.println(a + " 是此二叉树的结点"); else System.out.println(a + " 不是此二叉树的结点"); }}class Point1 {private int key;private Point1 LeftChild, RightChild;public Point1(int key, Point1 LeftChild, Point1 RightChild) {this.key = key;this.LeftChild = LeftChild;this.RightChild = RightChild;}public Point1(int key) {this(key, null, null);}public int getKey() {return key;}public void setKey(char key) {this.key = key;}public Point1 getLeftChild() {return LeftChild;}public void setLeftChild(Point1 leftChild) {LeftChild = leftChild;}public Point1 getRightChild() {return RightChild;}public void setRightChild(Point1 rightChild) {RightChild = rightChild;}}
(给出了三种不同的查找的思路,不过确实要比另外两种好实现一些,希望可以有所借鉴,插入和删除晚些时候放上)
1 0
- 漫谈二叉搜索树的基本算法(三种思路实现查询操作)
- 漫谈二叉树之递归遍历算法(两种不同的思路)
- 二叉搜索树的基本操作(C代码实现)
- 【二叉搜索树】二叉搜索树的基本操作
- 二叉搜索树(BST)的基本操作
- 二叉搜索树的一些基本操作
- 二叉搜索树的基本操作
- c++搜索二叉树的基本操作
- 二叉搜索树的一些基本操作
- 二叉搜索树的基本操作
- 搜索二叉树的基本操作
- 二叉搜索树的基本操作
- 二叉搜索树(BST)的基本操作
- 二叉搜索树的基本操作
- 数据结构 — 二叉树的基本操作实现(递归算法)
- 二叉搜索树(二叉排序数)的基本操作实现
- 二叉搜索树的基本java实现
- 二叉搜索树基本操作
- Oracle中的MD5加密
- Kafka深度解析
- nginx_upstream_jvm_route 实验
- Android中Uri的使用
- 有关像素深度bpp的概念
- 漫谈二叉搜索树的基本算法(三种思路实现查询操作)
- 【最小割】POJ-2914 Minimum Cut
- 视图
- 你不知道的SpringMVC——@RequestParam必须要加上参数名
- I/O端口、I/O内存
- Maximum Depth of Binary Tree
- C语言及程序设计提高例程-28 字符串、字符数组、字符指针
- C语言及程序设计提高例程-27 编写查找和排序函数
- C语言及程序设计提高例程-26 实现线性表基本操作的函数