数据结构_map、set、哈希表(概念、方法、冲突、代码实现)

一、Map

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

1、Map的相关说明

常用方法
 1. V get(Object key)  [返回 key 对应的 value]
 2. V put(K key, V value)  [设置 key 对应的 value]
 3. V remove(Object key)  [删除 key 对应的映射关系]
 4. boolean containsKey(Object key) [判断是否包含 key]
注意事项:
 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删除掉,然后再来进行
重新插入

2、TreeMap和HashMap

在这里插入图片描述

二、Set

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

1. Set相关说明

常用方法
  1. boolean add(E e)  [添加元素,但重复元素不会被添加成功]
  2. boolean contains(Object o)  [判断 o 是否在集合中]
  3. boolean remove(Object o)   [删除集合中的 o]
  4. Object[] toArray()   [将set中的元素转换为数组返回]
注意事项
  1. Set是继承自Collection的一个接口类
  2. Set中只存储了key,并且要求key一定要唯一
  3. TreeSet的底层是使用Map来实现的,其使用key与Object的一个默认对象作为键值对插入到Map中的
  4. Set最大的功能就是对集合中的元素进行去重
  5. 实现Set接口的常用类有TreeSet和HashSet
  6. Set中的Key不能修改,如果要修改,先将原来的删除掉,然后再重新插入
  7. TreeSet中不能插入null的key,HashSet可以

2.TreeSet和HashSet

在这里插入图片描述

三、哈希表

如果构造一种存储结构,通过某种函数(hashFunc)使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么在查找时通过该函数可以很快找到该元素.当向该结构中:
插入元素
  根据待插入元素的关键码,以此函数计算出该元素的存储位置并按此位置进行存放
搜索元素
 对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置取元素比较,若关键码相等,则搜索成功
该方式即为哈希(散列)方法,哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称为哈希表(Hash
Table)(或者称散列表)
**

哈希函数设置:
  哈希函数设置为:hash(key) = key % capacity; capacity为存储元素底层空间总的大小

1. Hash冲突

  不同关键字通过相同哈希哈数计算出相同的哈希地址,该种现象称为哈希冲突或哈希碰撞

2. 冲突-避免-负载因子调节

散列表的负载因子定义:
  负载因子 = 填入表中的元素个数 / 散列表长度
在JAVA中负载因子设为 0.75,超过此值将对散列表长度进行扩容。已知哈希表中已有的关键字个数是不可变的,那能调整的就只有哈希表中的数组的大小。

3. 冲突-解决

1). 线性探测

  从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止,插入新元素。

2).二次探测

重设Hash函数
H(i) = (H(0) (±) i^2) % length;

3). 哈希桶

  开散列法又叫链地址法(开链法),首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中
在这里插入图片描述

代码实现

package Demo;

/**
 * @author ZhangXu
 * @date 2024/3/2417:50
 * @Description:
 */
public class HashBuck {

    //key-Value 模型
    static  class  Node{
        private   int key;
        private   int value;
        public  Node next;

        public Node(int key, int value) {
            this.key = key;
            this.value = value;
        }
    }

    private Node[] array;
    private  int usedSize;  //当前数据的个数
    private  static  final double LOAD_FACTOR = 0.75;
    public HashBuck(){
        array = new Node[10];
    }

    public void put(int key,int value){
        int index = key %array.length;
        Node cur = array[index];
        //遍历index下标的链表,是否存在key, 若存在更新value;若不存在,则采用头插法插入数据
        while (cur != null){
            if (cur.key == key){
                //更新value
                cur.value = value;
                return;
            }
            cur = cur.next;
        }
        //cur  = null ,链表遍历完成,没有找到key
        Node node = new Node(key,value);
        node.next = array[index];
        array[index] = node;
        usedSize++;

        if (loadFactor() >= LOAD_FACTOR){
            resize();
        }
    }

    private void resize() {
        Node[] Newarray = new Node[2*array.length];

        //遍历原来的数组
        for (int i = 0; i < array.length; i++) {
            Node cur = array[i];
            //遍历每个人数组元素的链表
            while (cur != null){
                Node tmp = cur.next;
                int NewIndex = cur.key % Newarray.length; //新的数组下标
                //采用头插法,插入到新数组的NewIndex下标
                cur.next = Newarray[NewIndex];
                Newarray[NewIndex] = cur;
                cur =  tmp;
            }
        }
        array = Newarray;
    }

    public int get(int key){
        int index = key % array.length;
        Node cur = array[index];
        while (cur != null){
            if (cur.key == key){
                return cur.value;
            }
            cur = cur.next;
        }
        return -1;
    }
    /**
     * 计算负载因子
     * @return
     */
    private double loadFactor(){
        return usedSize*1.0/array.length;
    }
}
  • 25
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
unordered_set和unordered_map的底层结构是哈希表哈希表是一种根据关键字值直接进行访问的数据结构,通过把关键字映射到中的一个位置来实现快速查找。在C++中,unordered_set和unordered_map都是使用哈希表实现的。 哈希表实现包括以下几个关键部分: 1. 哈希函数:用于将关键字映射到哈希表中的位置。哈希函数可以根据关键字的类型进行特化,比如整形和字符串类型。 2. 哈希桶:哈希表由一系列桶组成,每个桶可以存储一个或多个元素。哈希函数将关键字映射到具体的桶中。 3. 冲突解决方法:由于不同的关键字可能映射到同一个桶中,需要解决冲突问题。常见的解决方法有链地址法和开放地址法。链地址法使用链冲突的元素链接在一起,开放地址法则在冲突时重新计算哈希值,找到下一个空闲的位置。 4. 插入和查找:插入操作将元素插入到哈希表中的合适位置,查找操作根据关键字找到对应的元素。 可以看出,unordered_set和unordered_map在底层结构上非常相似,只是unordered_set存储的是唯一的关键字,而unordered_map存储的是键值对(key,value)。哈希表的特性使得它们在插入和查找操作上具有非常高的效率。 <span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [unordered_map和unordered_set的模拟实现](https://download.csdn.net/download/weixin_38629362/14886751)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [简单了解unordered_set和unordered_map底层](https://blog.csdn.net/weixin_57023347/article/details/120217804)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值