【Java】二叉搜索树 TreeMap&TreeSet

上篇文章【Java】Map & Set_p_fly的博客-CSDN博客 介绍了Map和Set这两个接口,其中也提到了TreeMap和TreeSet,接下来就详细介绍一下这两个类。

TreeMap和TreeSet的底层是一个红黑树,红黑树是由AVL树的基础上做的升级,而二叉搜索树改变一下就变成了一个AVL树。所以了解一下二叉搜索树对于理解TreeMap和TreeSet有很大的帮助。

二叉搜索树

二叉搜索树又叫二叉排序树,它首先是一个二叉树,其次它的左子树比根小右子树比根大,左子树和右子树可以为空,最后在这颗树中,没有相同的值。它的中序遍历是有序的。

增加结点:

1.当树为空的时候,直接增加一个结点,让根的引用指向它即可。

2.当树不空的时候,增加的结点就是要到叶子结点的位置。增加结点的值和根结点的值比较,小了去左边,大了去右边。所以定义一个工作指针指向根节点,从根结点出发找合适的插入位置。

   注意:当找到合适的叶子节点位置后,此时如何插入结点,无法找到这个叶子结点的父亲                结点。所以就要再有一个指针一直指向工作结点的父亲结点。

 public boolean insert(int key) {
        if (root == null) {
            TreeNode node = new TreeNode(key);
            root = node;
        }
        else {
            TreeNode node = new TreeNode(key);
            TreeNode curPrev = null;
            TreeNode cur = root;
            while (cur != null) {
                curPrev = cur;
                if (key > cur.key) {
                    cur = cur.right;
                } else if (key < cur.key) {
                    cur = cur.left;
                } else {
                    return false;
                }
            }
            if (curPrev.key < key) {
                curPrev.right = node;
            } else {
                curPrev.left = node;
            }
        }
        return true;
    }

查找结点:

定义一个工作指针,从根节点出发,大了走左边,小了走后边,相等了就是找到了,走完了还没找到就是没有。

    public TreeNode search(int key) {
        if (root == null) {
            return null;
        }
        else {
            TreeNode cur = root;
            while (cur != null) {
                if (cur.key < key) {
                    cur = cur.right;
                } else if (cur.key > key) {
                    cur = cur.left;
                } else {
                    return cur;
                }
            }
        }
        return null;
    }

 结点删除:

删除整体分为三种情况。

一:当要删除的结点右子树为空

       ①当该结点为根结点时

       ②不是根结点。在要被删除结点的父亲结点的左边

       ③不是根结点。在要被删除结点的父亲结点的右边

       

二: 当要删除的结点的左子树为空

       ①当该结点为根节点时

       ②不是根结点。在要被删除结点的父亲结点的左边

       ③不是根结点。在要被删除结点的父亲结点的右边

三:当要删除的结点的左右子树都不为空时

       找到该结点左子树最右边的结点,交换值后删除最右边的那个结点,步骤同上。

或者找到该结点右子树最左边的结点,交换值后删除最左边的那个结点,步骤同上。

 public boolean remove(int key) {
        if (root == null) {
            return false;
        }
        else {
            TreeNode curPrev = null;
            TreeNode cur = root;
            while (cur != null && cur.key != key) {
                curPrev = cur;
                if (cur.key < key) {
                    cur = cur.right;
                } else if (cur.key > key) {
                    cur = cur.left;
                } else {
                    break;
                }
            }
            if (cur == null) {
                return false;
            }
            //此时找到了要删的值
            //一:当cur.left == null
            if (cur.left == null) {
                if (cur == root) {
                    root = root.right;
                } else if (cur == curPrev.left) {
                    curPrev.left = cur.right;
                } else {
                    curPrev.right = cur.right;
                }
            }
            //二:当cur.right == null
            else if (cur.right == null) {
                if (cur == root) {
                    root = root.left;
                } else if (cur == curPrev.left) {
                    curPrev.left = cur.left;
                } else {
                    curPrev.right = cur.left;
                }
            }
            //三:当cur.left != null && cur.right != null
            //替换法:找到左边中最后边的交换  或者  找到右边中最左边的交换
            //找到那个后,然后又是第一或第二中情况了
            else {
                TreeNode targetPrev = cur;
                TreeNode target = cur.left;
                //找左边的最右边
                while (target.right != null) {
                    targetPrev = target;
                    target = target.right;
                }
                cur.key = target.key;

                //一种是target没有走 就是target的右边没有
                if (targetPrev.left == target) {
                    targetPrev.left = target.left;
                }
                //一种是target走了
                else {
                    targetPrev.right = target.left;
                }
            }

        }
        return true;
    }

TreeMap

TreeMap是继承Map接口的,基本方法和性质与Set都一样。

增加的Key必须是可比较——实现Comparable接口或者Comparator接口

原因很容易想到,它的底层是一颗红黑树,树必须得能比较才可以建树。 

import java.util.TreeMap;

class Student {
    public int age;
    public String name;

    public Student(int age){
        this.age = age;
    }
}

public class TreeTest {
    public static void main(String[] args) {
        TreeMap<Student, Integer> treeMap1 = new TreeMap<>();
        Student student = new Student(20);
        treeMap1.put(student, 5);
    }
}

如果没有实现,那么就会报出以上错误。实现接口即可

import java.util.TreeMap;

class Student implements Comparable<Student> {
    public int age;
    public String name;

    public Student(int age){
        this.age = age;
    }

    @Override
    public int compareTo(Student o) {
        return this.age - o.age;
    }
}

public class TreeTest {
    public static void main(String[] args) {
        TreeMap<Student, Integer> treeMap1 = new TreeMap<>();
        Student student = new Student(20);
        treeMap1.put(student, 5);
    }
}

TreeMap的Key不可以增加null

HashMap可以。

        TreeMap<String, Integer> treeMap2 = new TreeMap<>();
        treeMap2.put(null, 5);

 TreeMap总结

TreeMap 底层结构
红黑树
插入 / 删除 / 查找时间
复杂度
log₂N
是否有序
关于 Key 有序
线程安全
不安全
应用场景需要Key有序

TreeSet

TreeSet的注意事项与MapSet一样。因为它是实现了Set接口的类,基本方法性质都与Set一致。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值