Map和Set以及搜索二叉树的介绍

前言

本章介绍二叉搜索树的实现,Map和Set的区别,TreeMap,TreeSet,HashMap.HashSet的区别

一.二叉搜索树

1.概念

二叉搜索树(Binary Search Tree, BST)是一种常见的数据结构,它或是一棵空树,或是具有以下性质的二叉树:

  • 若它的左子树不为空,则左子树上所有结点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有结点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树

下图就是一个二叉搜索树

⬇️⬇️⬇️

二叉搜索树 的图像结果

2.查找

因为二叉搜索树独特的特性,让我们查找元素变得很容易,按照其值与节点的值所比较,如果cur.val < val就说明在比此节点的值大,进而推断出所查找的节点在其右子树,相反若cur.val > val就说明比此节点的值小,进而推断出所查找的节点在其左子树,直到找到达成cur.val == val条件为止返回true,否则返回false

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

3.插入

如果是空树的话,直接把新插入的节点赋给root,否则就按照查找的逻辑,找到该节点所应该插入的位置,要是树中原本就有该值,则插入失败,返回false

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

4.删除

删除操作相对复杂一些,因为要考虑被删除节点子树的情况,一般分为三种情况:

(1)如果被删除节点是叶子节点,直接删除它即可。

(2)如果被删除节点有一个叶子节点,那么将被删除节点替换成其子节点即可。

(3)如果被删除节点有两个子节点,那么需要使用替换法,即找到该节点的中序后继节点(即比删除节点大的最小节点),将中序后继节点的值替换到待删除结点的值,然后再删除中序后继节点。

public void remove(int val) {
        TreeNode cur = root;
        TreeNode parent = null;
        while (cur != null) {
            parent = cur;
            if (cur.val < val) {
                cur = cur.right;
            } else if (cur.val > val) {
                cur = cur.left;
            } else {
                //删除的逻辑
                removeNode(parent, cur);
                return;
            }
        }
    }

    private void removeNode(TreeNode parent, TreeNode cur) {
        if (cur.left == null) {
            if (cur == root) {
                root = cur.right;
            }
            if (cur == parent.left) {
                parent.left = cur.right;
            } else if (cur == parent.right) {
                parent.right = cur.right;
            }
        } else if (cur.right == null) {
            if (cur == root) {
                root = root.left;
            }
            if (cur == parent.left) {
                parent.left = cur.left;
            } else if (cur == parent.right) {
                parent.right = cur.left;
            }
        } else {
            TreeNode targetParent = cur;
            TreeNode target = cur.right;
            //找到中序后继节点
            while (target.left != null) {
                targetParent = target;
                target = target.left;
            }
            //把待删除结点的值替换成中序后继节点的值
            cur.val = target.val;
            
            if (target == targetParent.left) {
                targetParent.left = target.right;
            } else {
                targetParent.right = target.right;
            }
        }
    }

二.Map和Set介绍

1.概念和场景

MapSet是一种专门用来进行搜索的容器或数据结构,也可以用于存储和操作数据集合,其搜索的效率与其具体的实例化子类有关.

静态查找:一般不会对区间进行插入和删除操作,像以前的搜索方式为:直接遍历(O(n)),二分查找(O(log2n)).

动态查找:那就与静态查找相反,在查找时会涉及到插入和删除的操作.即Map和Set所适用的查找.

2.模型

我们一般把搜索的数据称为关键字(Key),和关键字对应的称为值(Value),将其称之为Key-value的键值对,所以对应的模型会有两种:

  1. 纯Key模型,比如:

    快速查找一个单词是否在词典中

  2. Key-Value模型,比如:

    统计文件中的每个单词出现的次数,统计结果为每个单词与之对应的次数:<单词,出现的次数>

Map中储存的是Key-Value的键值对模型,Set中储存的是纯Key模型

三.Map的使用

1.Map.Entry<K,V>的说明

Map.Entry<K,V>是Map内部用来存放<key,value>键值对映射关系的内部类,该内部类主要提供了<key,value>的获取,value的设置及key的比较方法.

注意:Map.Entry<K,V>并没有提供设置Key的方法

2.Map的说明

  1. Map是一个接口,不能直接实例化对象,如果要实例化对象只能实例化其实现类TreeMap或者HashMap
  2. Map中存放的键值对Key是唯一的,value是可以重复的
  3. TreeMap中插入键值对时,key不能为空,否则就会抛NullPointerException异常,value可以为空.但是HashMap的key和value都可以为空
  4. Map中的Key可以全部分离出来,存储到Set中来进行访问(Key不能重复)
  5. Map中的value可以全部分离出来,储存在Collection的任何一个子类集合中(Value可以有重复)
  6. Map中键值对的Key不能直接修改,value可以修改,如果要修改Key,只能将key先删除掉,然后再重新插入
Map底层结构TreeMapHashMap
底层结构红黑树哈希桶
插入/删除/查找时间复杂度O(log2n)O(1)
是否有序关于Key有序无序
线程安全不安全不安全
插入/删除查找区别需要进行元素比较通过哈希函数计算哈希地址
比较与覆写key必须能够比较,否则会抛出ClassCastException异常自定义类型需要覆写equls和hashCode方法
应用场景需要key有序场景下key是否有序不关心,需要更高的时间性能

3.Set的说明

  1. Set是继承自Collection的一个接口类
  2. Set中只存储了key,并且要求key唯一
  3. TreeSet的底层是使用Map来实现的,其实用key与Object的一个默认对象作为键值对插入到Map中的
  4. Set会对集合中的元素进行去重
  5. 实现Set接口的常用类有TreeSet和HashSet,还有一个LinkedHashSet,LinkedHashSet是在HashSet的基础上维护了一个双向链表来记录元素的插入次序
  6. Set中的Key同样不能修改,如果要修改,现将原来的删除掉,然后再重新插入
  7. TreeSet在插入时,key不能为空,HashSet可以
Set底层结构TreeSetHashSet
底层结构红黑树哈希桶
插入/删除/查找时间复杂度O(log2n)O(1)
是否有序关于Key有序不一定有序
线程安全不安全不安全
插入/删除查找区别按照红黑树的特性来进行插入和删除通过哈希函数计算哈希地址
比较与覆写key必须能够比较,否则会抛出ClassCastException异常自定义类型需要覆写equls和hashCode方法
应用场景需要key有序场景下key是否有序不关心,需要更高的时间性能
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值