树--04---二叉树--01---简介二叉搜索树(BST)实现
Posted 高高for 循环
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树--04---二叉树--01---简介二叉搜索树(BST)实现相关的知识,希望对你有一定的参考价值。
文章目录
二叉树
基本定义:
二叉树就是度不超过2的树(每个结点最多有两个子结点)
满二叉树:
- 一个二叉树,如果每一个层的结点树都达到最大值,则这个二叉树就是满二叉树。
完全二叉树:
- 叶节点只能出现在最下层和次下层,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树
二叉搜索树(BST)—Binary Search Tree
定义:
- 二叉排序树(Binary Sort Tree),又称二叉查找树(Binary Search Tree),亦称二叉搜索树。是数据结构中的一类。在一般情况下,查询效率比链表结构要高。
特点:
一棵空树,或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
(4)没有键值相等的结点。
BST树的搜索:
从根结点开始,如果查询的关键字与结点的关键字相等,那么就命中;否则,如果查询关键字比结点关键字小,就进入左儿子;如果比结点关键字大,就进入右儿子;如果左儿子或右儿子的指针为空,则报告找不到相应的关键字.
如果BST树的所有非叶子结点的左右子树的结点数目均保持差不多(平衡),那么B树的搜索性能逼近二分查找;但它比连续内存空间的二分查找的优点是,改变BST树结构(插入与删除结点)不需要移动大段的内存数据,甚至通常是常数开销;
如果BST树的所有非叶子结点的左右子树的结点数目均保持差不多(平衡),那么B树的搜索性能逼近二分查找;
但BST树在经过多次插入与删除后,有可能导致不同的结构:
右边也是一个BST树,但它的搜索性能已经是线性的了;同样的关键字集合有可能导致不同的树结构索引;所以,使用BST树还要考虑尽可能让BST树保持左图的结构,和避免右图的结构,也就是所谓的“平衡”问题;
二叉搜索树(BST)实现 方法一
1.二叉树的结点类
- 根据对图的观察,我们发现二叉树其实就是由一个一个的结点及其之间的关系组成的,按照面向对象的思想,我们设计一个结点类来描述结点这个事物。
结点类API设计:
代码实现:
private class Node {
//存储键
public Key key;
//存储值
private Value value;
//记录左子结点
public Node left;
//记录右子结点
public Node right;
public Node(Key key, Value value, Node left, Node right) {
this.key = key;
this.value = value;
this.left = left;
this.right = right;
}
}
2.二叉查找树API设计
2.1 插入方法put实现思想:
- 如果当前树中没有任何一个结点,则直接把新结点当做根结点使用
- 如果当前树不为空,则从根结点开始:
- 如果新结点的key小于当前结点的key,则继续找当前结点的左子结点;
- 如果新结点的key大于当前结点的key,则继续找当前结点的右子结点;
- 如果新结点的key等于当前结点的key,则树中已经存在这样的结点,替换该结点的value值即可。
//向树中添加元素key-value
public void put(Key key, Value value) {
root = put(root, key, value);
}
//向指定的树x中添加key-value,并返回添加元素后新的树
private Node put(Node x, Key key, Value value) {
//如果x子树为空,
if (x==null){
N++;
return new Node(key,value, null,null);
}
//如果x子树不为空
//比较x结点的键和key的大小:
int cmp = key.compareTo(x.key);
if (cmp>0){
//如果key大于x结点的键,则继续找x结点的右子树
x.right = put(x.right,key,value);
}else if(cmp<0){
//如果key小于x结点的键,则继续找x结点的左子树
x.left = put(x.left,key,value);
}else{
//如果key等于x结点的键,则替换x结点的值为value即可
x.value = value;
}
return x;
}
2.2 查询方法get实现思想:
从根节点开始:
- 如果要查询的key小于当前结点的key,则继续找当前结点的左子结点;
- 如果要查询的key大于当前结点的key,则继续找当前结点的右子结点;
- 如果要查询的key等于当前结点的key,则树中返回当前结点的value。
//查询树中指定key对应的value
public Value get(Key key) {
return get(root,key);
}
//从指定的树x中,查找key对应的值
public Value get(Node x, Key key) {
//x树为null
if (x==null){
return null;
}
//x树不为null
//比较key和x结点的键的大小
int cmp = key.compareTo(x.key);
if (cmp>0){
//如果key大于x结点的键,则继续找x结点的右子树
return get(x.right,key);
}else if(cmp<0){
//如果key小于x结点的键,则继续找x结点的左子树
return get(x.left,key);
}else{
//如果key等于x结点的键,就找到了键为key的结点,只需要返回x结点的值即可
return x.value;
}
}
2.3 删除方法delete实现思想:
- 找到被删除结点;
- 找到被删除结点右子树中的最小结点minNode
- 删除右子树中的最小结点
- 让被删除结点的左子树称为最小结点minNode的左子树,让被删除结点的右子树称为最小结点minNode的右子树
- 让被删除结点的父节点指向最小结点minNode
下面代码:删除右子树中最小的结点,代码有bug,应该要判断最小节点有没有右子树是不是空 n.left=null; //n.left=n.left.right;
//删除树中key对应的value
public void delete(Key key) {
delete(root, key);
}
//删除指定树x中的key对应的value,并返回删除后的新树
public Node delete(Node x, Key key) {
//x树为null
if (x==null){
return null;
}
//x树不为null
int cmp = key.compareTo(x.key);
if (cmp>0){
//如果key大于x结点的键,则继续找x结点的右子树
x.right = delete(x.right,key);
}else if(cmp<0){
//如果key小于x结点的键,则继续找x结点的左子树
x.left = delete(x.left,key);
}else{
//如果key等于x结点的键,完成真正的删除结点动作,要删除的结点就是x;
//让元素个数-1
N--;
//得找到右子树中最小的结点
if (x.right==null){
return x.left;
}
if (x.left==null){
return x.right;
}
Node minNode = x.right;
while(minNode.left!=null){
minNode = minNode.left;
}
//删除右子树中最小的结点
Node n = x.right;
while(n.left!=null){
if (n.left.left==null){
n.left=null; //bug!!! n.left=n.left.right;
}else{
//变换n结点即可
n = n.left;
}
}
//让x结点的左子树成为minNode的左子树
minNode.left = x.left;
//让x结点的右子树成为minNode的右子树
minNode.right = x.right;
//让x结点的父结点指向minNode
x = minNode;
}
return x;
}
2.4 查找二叉树中最小的键 和 最大的键
最小值
- 在某些情况下,我们需要查找出树中存储所有元素的键的最小值,比如我们的树中存储的是学生的排名和姓名数据,那么需要查找出排名最低是多少名?这里我们设计如下两个方法来完成:
最大值
- 在某些情况下,我们需要查找出树中存储所有元素的键的最大值,比如比如我们的树中存储的是学生的成绩和学生的姓名,那么需要查找出最高的分数是多少?这里我们同样设计两个方法来完成:
//查找整个树中最小的键
public Key min(){
return min(root).key;
}
//在指定树x中找出最小键所在的结点
private Node min(Node x){
//需要判断x还有没有左子结点,如果有,则继续向左找,如果没有,则x就是最小键所在的结点
if (x.left!=null){
return min(x.left);
}else{
return x;
}
}
//在整个树中找到最大的键
public Key max(){
return max(root).key;
}
//在指定的树x中,找到最大的键所在的结点
public Node max(Node x){
//判断x还有没有右子结点,如果有,则继续向右查找,如果没有,则x就是最大键所在的结点
if (x.right!=null){
return max(x.right);
}else{
return x;
}
}
总代码
import java.util.Queue;
public class BinaryTree<Key extends Comparable<Key>, Value> {
//记录根结点
private Node root;
//记录树中元素的个数
private int N;
private class Node {
//存储键
public Key key;
//存储值
private Value value;
//记录左子结点
public Node left;
//记录右子结点
public Node right;
public Node(Key key, Value value, Node left, Node right) {
this.key = key;
this.value = value;
this.left = left;
this.right = right;
}
}
//获取树中元素的个数
public int size() {
return N;
}
//向树中添加元素key-value
public void put(Key key, Value value) {
root = put(root, key, value);
}
//向指定的树x中添加key-value,并返回添加元素后新的树
private Node put(Node x, Key key, Value value) {
//如果x子树为空,
if (x==null){
N++;
return new Node(key,value, null,null);
}
//如果x子树不为空
//比较x结点的键和key的大小:
int cmp = key.compareTo(x.key);
if (cmp>0){
//如果key大于x结点的键,则继续找x结点的右子树
x.right = put(x.right,key,value);
}else if(cmp<0){
//如果key小于x结点的键,则继续找x结点的左子树
x.left = put(x.left,key,value);
}else{
//如果key等于x结点的键,则替换x结点的值为value即可
x.value = value;
}
return x;
}
//查询树中指定key对应的value
public Value get(Key key) {
return get(root,key);
}
//从指定的树x中,查找key对应的值
public Value get(Node x, Key key) {
//x树为null
if (x==null){
return null;
}
//x树不为null
//比较key和x结点的键的大小
int cmp = key.compareTo(x.key);
if (cmp>0){
//如果key大于x结点的键,则继续找x结点的右子树
return get(x.right,key);
}else if(cmp<0){
//如果key小于x结点的键,则继续找x结点的左子树
return get(x.left,key);
}else{
//如果key等于x结点的键,就找到了键为key的结点,只需要返回x结点的值即可
return x.value;
}
}
//删除树中key对应的value
public void delete(Key key) {
delete(root, key);
}
//删除指定树x中的key对应的value,并返回删除后的新树
public Node delete(Node x, Key key) {
//x树为null
if (x==null){
return null;
}
//x树不为null
int cmp = key.compareTo(x.key);
if (cmp>0){
//如果key大于x结点的键,则继续找x结点的右子树
x.right = delete(x.right,key);
}else if(cmp<0){
//如果key小于x结点的键,则继续找x结点的左子树
x.left = delete(x.left,key);
}else{
//如果key等于x结点的键,完成真正的删除结点动作,要删除的结点就是x;
//让元素个数-1
N--;
//得找到右子树中最小的结点
if (x.right==null){
return x.left;
}
if (x.left==null){
return x.right;
}
Node minNode = x.right;
while(minNode.left!=null){
minNode = minNode.left;
}
//删除右子树中最小的结点
Node n = x.right;
while(n.left!=null){
if (n.left.left==null){
n.left=null; //bug!!! n.left=n.left.right;
}else{
//变换n结点即可
n = n.left;
}
}
//让x结点的左子树成为minNode的左子树
minNode.left = x.left;
//让x结点的右子树成为minNode的右子树
minNode.right = x.right;
//让x结点的父结点指向minNode
x = minNode;
}
return x;
}
//查找整个树中最小的键
public Key min(){
return min(root).key;
}
//在指定树x中找出最小键所在的结点
private Node min(Node x){
//需要判断x还有没有左子结点,如果有,则继续向左找,如果没有,则x就是最小键所在的结点
if (x.left!=null){
return min(x.left);
}else{
return x;
}
}
//在整个树中找到最大的键
public Key max(){
return max(root).key;
}
//在指定的树x中,找到最大的键所在的结点
public Node max(Node x){
//判断x还有没有右子结点,如果有,则继续向右查找,如果没有,则x就是最大键所在的结点
if (x.right!=null){
return max(x.right);
}else{
return x;
}
}
}
测试:
public class BinaryTreeTest {
public static void main(String[] args) {
//创建二叉查找树对象
BinaryTree<Integer, String> tree = new BinaryTree<>();
//测试插入
tree.put(1,"张三");
tree.put(2,"李四");
tree.put(3,"王五");
System.out.println("插入完毕后元素的个数:"+tree.size());
//测试获取
System.out.println("键2对应的元素是:"+tree.以上是关于树--04---二叉树--01---简介二叉搜索树(BST)实现的主要内容,如果未能解决你的问题,请参考以下文章
⭐算法入门⭐《二叉树 - 二叉搜索树》简单04 —— LeetCode 1008. 前序遍历构造二叉搜索树