自学内容网 自学内容网

Map和Set,TreeMap和TreeSet,HashMap和HashSet

TreeSet和TreeMap

定义:

TreeSet是Java集合框架中的一种有序集合,它实现了Set接口,因此具有不允许重复元素的特性。与HashSet不同,TreeSet使用红黑树数据结构来存储元素,这使得元素在集合中保持有序

TreeMap是基于红黑树数据结构的键值对映射。它保证键的有序性,键按照其自然顺序(通过键的compareTo方法确定的顺序)进行排序。

联系:TreeSet的底层是TreeMap,TreeMap的底层是一颗红黑树,而红黑树是一颗近似平衡的二叉搜索树

二叉搜索树模拟TreeMAp

定义

是一颗特殊的二叉树,具有以下性质:

  1. 若左子树不为空,则左子树上的所有节点的值都小于根节点的值
  2. . 若右子树不为空,则右子树上的所有节点的值都大于根节点的值
  3. 左右子树也都是二叉搜索树
  4. 不存在键值相等的节点
    在这里插入图片描述
static class TreeNode{
        public int val;
        public TreeNode left;
        public TreeNode right;

        public TreeNode(int val) {
            this.val = val;
        }
    }
    private TreeNode root;

基本操作

插入

  1. 如果是空树,直接插入
  2. 如果不是空树,创建新节点,比较根节点的值
  3. 如果新节点的键小于根节点的键,则将新节点插入到根节点的左子树中。
  4. 如果新节点的键大于根节点的键,则将新节点插入到根节点的右子树中
//插入
    //定义一个parent
    public void insert(int key){
        if(root==null){
            root=new TreeNode(key);
            return ;
        }
        TreeNode parent=null;
        TreeNode cur=root;
        TreeNode node=new TreeNode(key);
        while(cur!=null){
            if(cur.val<key){
                //先让parent到cur的位置,后cur往后走
                parent=cur;
                cur=cur.right;
            }else if(cur.val==key){
                //不能插入两个相同的数
                return;
            }else {
                parent=cur;
                cur=cur.left;
            }
        }
        //走到这里说明要开始插入了,比较子树上根节点与要插入的值
        if(parent.val<key){
            parent.right=node;
        }else{
            parent.left=node;
        }
    }

查找

  1. 从根节点开始比较。
  2. 如果查找的键小于当前节点的键,则递归地在左子树中查找
  3. 如果查找的键大于当前节点的键,则递归地在右子树中查找
  4. 如果找到节点,则返回该节点
  5. 如果没有找到,则返回null。
public TreeNode search(int key) {
        TreeNode cur=root;
        while(cur!=null){
           if(cur.val<key){
               cur=cur.right;
           }else if(cur.val==key){
               return cur;
           }else{
               cur=cur.left;
           }
        }
        return null;
    }

删除(难点)

(1) cur.left==null

cur为root,则root=cur.right
在这里插入图片描述

cur不为root,cur是parent.left,则parent.left=cur.right
在这里插入图片描述

cur不为root,cur是parent.right,则parent.right=cur.right
在这里插入图片描述

(2) cur.right==null

cur为root,则root=cur.left
在这里插入图片描述

cur不为root,cur是parent.left,则parent.left=cur.left
在这里插入图片描述

cur不为root,cur是parent.right,则parent.right=cur.left
在这里插入图片描述

(3)cur的左右侧都不为空,需要进行替换法删除

定义tp=cur,target,右树的找到最小值,右树最左边的节点,最左边的节点说明左树为空

public boolean remove(int key){
        TreeNode cur=root;
        TreeNode parent=null;
        //parent要记录下cur的前一个节点,要不然删不了
        while(cur!=null){
            if(cur.val<key){
                parent=cur;
                cur=cur.right;
            }else if(cur.val>key){
                parent=cur;
                cur=cur.left;
            }else {
                removeNode(parent,cur);
                return true;
            }
        }
        return false;
       }

    private void removeNode(TreeNode parent,TreeNode cur) {
        if(cur.left==null){
            if(cur==root){
                root=cur.right;
            }else if(parent.left==cur){
                parent.left=cur.right;
            }else {
                parent.right=cur.right;
            }
        }else if(cur.right==null){
            if(root==cur){
                root=cur.left;
            }else if(parent.left==cur){
                parent.left=cur.left;
            }else {
                parent.right=cur.left;
            }
        }else{//cur的左右都不为空,替换删除
            TreeNode targetParent=cur;
            TreeNode target=cur.right;
            while(target.left!=null){
                targetParent=target;
                target=target.left;
            }
            cur.val=target.val;
            if(targetParent.left==target){
                targetParent.left=target.right;
            }else{
                targetParent.right=target.right;
            }
        }
    }

遍历

二叉搜索树的遍历操作与普通二叉树相同,可以使用前序、中序和后序遍历。

前序遍历举例

  public void prevOreder(TreeNode root){
        if (root==null){
            return;
        }
        prevOreder(root.left);
        System.out.print(root.key+" ");
        prevOreder(root.right);
    }

注意:

  1. 如果TreeMap实现排序,那么存放的对象如果是引用类型那么就必须要实现Comparable或者Comparator接口如果没有实现就会报错,因为无法比较两个对象的大小.

  2. 在TreeMap插入键值对时,key不能为null,否则或抛出NullPointerException异常,value可以为空

性能分析

插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。
对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。

最优情况下,二叉搜索树为完全二叉树,其平均比较次数为:log以2为底N
最差情况下,二叉搜索树退化为单支树,其平均比较次数为:N/2

应用场景

TreeMap的应用场景
在实际应用中,如果你需要一个有序的映射表,并且不允许键重复,那么TreeMap是一个很好的选择。它既满足了有序性的需求,又提供了高效的操作性能

  • 需要保持键的有序性且需要根据键快速查找值
  • 常用于需要按照特定顺序处理键值对的情况。

TreeSet的应用场景
适用于需要保持元素有序并且去除重复元素的场景。由于其基于红黑树实现,可以高效地支持元素的查找、插入和删除操作。因此,在需要有序集合且不允许重复元素的情况下,TreeSet是一个十分实用的选择

  • 当需要保持元素的有序性且不允许重复
  • 常用于需要按照特定顺序处理元素的情况。

Map&&Set

Map和set是一种专门用来进行搜索的容器或者数据结构,其搜索的效率与其具体的实例化子类有关

常见的搜索方式:
直接遍历,时间复杂度为O(N),元素如果比较多效率会非常慢

二分查找,时间复杂度为O(logN) ,但搜索前必须要求序列是有序的

上述排序比较适合静态类型的查找,即一般不会对区间进行插入和删除操作了,而现实中的查找比如:

  1. 根据姓名查询考试成绩
  2. 通讯录,即根据姓名查询联系方式
  3. 不重复集合,即需要先搜索关键字是否已经在集合中

可能在查找时进行一些插入和删除的操作,即动态查找,那上述两种方式就不太适合了,下面介绍的Map和Set是一种适合动态查找的集合容器

模型

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

  1. 纯 key 模型,比如:
    有一个英文词典,快速查找一个单词是否在词典中,这个单词就是Key
  2. Key-Value 模型,比如:
    统计文件中每个单词出现的次数,统计结果是每个单词都有与其对应的次数:<单词,单词出现的次数>

Map中存储的就是key-value的键值对
Set中只存储了Key。

在这里插入图片描述

Map是一个接口类,该类没有继承自Collection,该类中存储的是<K,V>结构的键值对,并且K一定是唯一的,不
能重复。

注意:Map是一个接口,不能直接实例化对象,如果要实例化对象只能实例化其实现类TreeMap或者HashMap

Set是继承自Collection的接口类,Set中只存储了Key

实现 Set 接口的常用类有 TreeSet 和 HashSet ,还有一个 LinkedHashSet

LinkedHashSet 是在 HashSet 的基础上维护了一个双向链表来记录元素的插入次序,本质上还是HashMap

区别:map带有伴随数据,set没有伴随数据并且能去重

HashMap

常用方法

在这里插入图片描述
关于Entry<K,V>说明:

查看源码可知,Entry是Map接口的一个内部接口
在这里插入图片描述

HashMap实现了Map接口,必然也实现了Entry接口
在这里插入图片描述

可以看到在HashMap的内部类中,提供了获取Key,value的方法以及设置value的方法,没有设置Key的方法
在这里插入图片描述

注意

  1. HashMap存放键值对的Key是唯一的,value是可以重复的

  2. HashMap中的key可以全部分离出来,存储到Set中来访问(因为key不能重复),即keySet()方法.

在这里插入图片描述
在这里插入图片描述

  1. HashMap中的value也可以全部分离出来,存储在Collection中(value可能有重复),即 values()方法.
    在这里插入图片描述
    在这里插入图片描述

  2. entrySet()方法的使用:

    entrySet()方法就是把HashMap中的每个键值对打包成一个整体,然后放入到一个集合中
    在这里插入图片描述
    在这里插入图片描述

HashSet

常用方法

在这里插入图片描述
注意

  1. HashSet中只存储了key,并且key不会重复

  2. 每个构造方法都会初始化一个HashMap
    在这里插入图片描述

  3. HashSet底层HashMap的每个value都是一个空的Object类型的对象
    在这里插入图片描述

  4. HashSet中的key不能修改,如果要修改,需要先删除再重新插入.

  5. HashMap和HashSet的key和value都可以存放null.

在这里插入图片描述
在这里插入图片描述

HashMap和HashSet区别

  1. HashSet是继承自Collection的接口类,Set中只存储key。

  2. HashMap是继承于Map接口,存储的是key-value键值对。

数据结构不同

HashSet: HashSet 内部使用哈希表(或哈希集合)来存储元素。哈希表是一个无序的数据结构,元素之间没有特定的顺序。

HashMap: HashMap 内部也使用哈希表,但它存储键值对,其中键和值之间有关联关系。HashMap 具有键的集合和值的集合,键是唯一的,值可以重复。

元素类型不同

HashSet: HashSet 存储的是单一的元素类型,如整数、字符串等。它用于存储不重复的对象,通过元素的哈希码来判断重复性。

HashMap: HashMap 存储键值对,键和值可以是不同类型的对象。键用于检索值,每个键都必须是唯一的,值可以重复。

方法不同

HashSet: HashSet 提供了添加、删除、查找元素的方法,例如 add(), remove(), contains() 等。它没有提供根据键查找值的方法。

HashMap: HashMap 提供了添加键值对、删除键值对、根据键查找值的方法,例如 put(), remove(), get() 等。它可以根据键来查找对应的值。

使用场景不同

HashSet 的适用场景
数据去重:当你需要存储一组数据,但不关心顺序和关联信息,只关心数据是否重复时,使用 HashSet 是合适的。
例如,存储一组唯一的用户名或标签。

集合运算:HashSet 适合用于集合运算
比如求交集、并集、差集等。

HashMap 的适用场景
键值存储:当你需要将数据与关联的键一起存储时,使用 HashMap 是合适的。
例如,存储学生的成绩,其中学生名是键,成绩是值。

数据索引:HashMap 适合用于构建索引,提供快速的查找能力。
例如,建立一个电话簿,根据姓名查找电话号码。

需要键值对的功能:如果你需要存储关联数据,并且需要使用键来查找值、替换值或遍历键值对,那么 HashMap 是最好的选择。


原文地址:https://blog.csdn.net/2302_80455544/article/details/142514004

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!