当前位置: 首页 > news >正文

数据结构与算法——哈希表,数组加强哈希表,双链表加强哈希表

文章目录

  • 哈希表
    • 1. 数组实现hash表
    • 2. 双链表实现hash表

哈希表

key是唯一的,value可以重复

哈希表和我们常说的 Map(键值映射)不是同一个东西。

Map】是一个 Java 接口,仅声明了若干个方法,并没有给出方法的具体实现;HashMap 这种数据结构根据自身特点实现了这些操作。

可以说hashmap的get、put、remove等方法复杂度为O(1),但是map接口的复杂度不一定,需要看他底层数据结构的实现方法。

哈希函数】的作用是把任意长度的输入(key)转化成固定长度的输出(索引),类比数组。增删查改时都会用到哈希函数,所以哈希函数的算法设计与复杂度紧紧相关。

任意 Java 对象都有一个 int hashCode() 方法,在实现自定义的类时,如果不重写这个方法,返回值可以认为是该对象的内存地址。一个对象的内存地址显然是全局唯一的一个整数。

所以调用 keyhashCode() 方法就相当于把 key 转化成了一个整数,且这个整数是全局唯一的。

哈希冲突】两个不同的key通过哈希函数得到了相同的索引。因为 hash 函数相当于把一个无穷大的空间映射到了一个有限的索引空间,所以必然会有不同的 key 映射到同一个索引上。

拉链法和开放寻址法】用于解决哈希冲突。

负载因子】= size/table.length,需要避免负载因子过大,会导致哈希表性能下降。理论上拉链法负载因子能达到无穷大,开放寻址法负载因子不超过1。

扩容】当哈希表内元素达到负载因子时,哈希表会扩容。


1. 数组实现hash表

数组keys存储所有键,indexMap存储键在数组中的索引,valueMap存储键值对,增删查改时实现时间复杂度为O(1)

编写randomKey(),在hash表中实现等概率随机生成一个键,解决了hash表中,因为哈希冲突产生的“空洞”导致不能等概率读取的问题。

class RandomizedHashTable {
    constructor() {
        this.keys = []; //存储所有键
        this.indexMap = new Map();  //键:索引,存储键在数组中的索引

        this.valueMap = new Map();  //键:值,存储键值对
    }

    //添加元素
    mySet(key, value) {
        //如果键不存在,则新建
        if (!this.valueMap.has(key)) {
            this.indexMap.set(key, this.keys.length);
            this.keys.push(key);
        }
        //如果键已经存在,直接修改值value
        this.valueMap.set(key, value);
    }

    //获取元素
    myGet(key) {
        return this.valueMap.get(key);
    }

    //删除元素
    myDelete(key) {
        //如果key不存在
        if (! this.valueMap.has(key)) { return false;}

        //如果key存在
        //通过键:索引map获取要删除元素的索引
        const index = this.indexMap.get(key);
        //获取最后一个元素的键
        const lastKey = this.keys[this.keys.length - 1];

        //将最后一个元素移动到当前删除位置
        this.keys[index] = lastKey;
        this.indexMap.set(lastKey, index);

        //删除最后一个元素
        this.keys.pop();
        this.indexMap.delete(key);
        this.valueMap.delete(key);

        return true;
    }

    //均匀随机生成一个键
    randomKey() {
        if (this.keys.length === 0) { return false;}
        const randomIndex = Math.floor(Math.random() * this.keys.length);
        return this.keys[randomIndex];
    }
}

const example =new RandomizedHashTable();
example.mySet(1, 'a');
example.mySet(2, 'b');
example.mySet(3, 'c');
console.log(example.randomKey());

2. 双链表实现hash表

算法实现:

注意:hash表与hash链表同步操作

class ListNode {
    constructor(key, value) {
        this.key = key;
        this.value = value;
        this.prev = null;
        this.next = null;
    }
}

class HashLinkedList {
    constructor(maxsize = 10) {
        this.maxsize = maxsize;
        this.hashMap = new Map();
        this.head = new ListNode(0, 0);
        this.tail = new ListNode(0, 0);
        this.head.next = this.tail;
        this.tail.prev = this.head;
    }

    //移动到头部
    moveToHead(node) {
        //先断开原本连接
        node.prev.next = node.next;
        node.next.prev = node.prev;

        //插入到头部
        node.prev = this.head;
        node.next = this.head.next;
        this.head.next.prev = node;
        this.head.next = node;
    }

    // 增加到头部操作
    addToHead(node) {
        node.prev = this.head;
        node.next = this.head.next;
        this.head.next.prev = node;
        this.head.next = node;
    }


    //删除尾部元素
    removeTail() {
        const lastNode = this.tail.prev;
        //从hash表中删除
        this.hashMap.delete(lastNode.key);
        //从hash链表中删除
        lastNode.prev.next = this.tail;
        this.tail.prev = lastNode.prev;
        return lastNode;
    }


     //查询操作
     myGet(key) {
        if(!this.hashMap.has(key)) { return -1;}

        const node = this.hashMap.get(key);
        this.moveToHead(node);
        return node.value;
    }


    //插入/更新
    myPut(key, value) {
        //如果key已经存在则更新value
        if(this.hashMap.has(key)) {
            const node = this.hashMap.get(key);
            node.value = value;
            //移动到头节点处
            this.moveToHead(node);
            return;
        }

        //如果key不存在则添加新key和value
        const newnode = new ListNode(key, value);
        //注意是添加一个节点对象newnode
        this.hashMap.set(key,newnode);
        this.addToHead(newnode);

        //如果超过容量则删除尾部元素
        if(this.hashMap.size > this.maxsize) {
            this.removeTail();
        }
    }

    //删除操作
    myDelete(key) {
        if(!this.hashMap.has(key)) { return false;}

        const node = this.hashMap.get(key); 
        //从链表删除
        node.prev.next = node.next;
        node.next.prev = node.prev;
        //从hash表删除
        this.hashMap.delete(key);
        return true;
    }
}

const example = new HashLinkedList(2);
example.myPut(1, 'a');
example.myPut(2, 'b');
console.log(example.myGet(1));

相关文章:

  • 【算法day9】回文数-给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。
  • Python和FastAPI框架开发和容器化部署AWS上支持多种LLM和向量数据库的微服务API
  • Mysql的utf8mb4_general_ci 与 utf8mb4_bin 的具体区别是什么?中文适合哪个?
  • 如何使用 ONLYOFFICE 宏对 PDF 表单中的特定字段执行计算
  • Gemini Robotics:Google DeepMind 让 AI 机器人真正“动”起来!
  • DeepSeek模型本地化部署方案及Python实现
  • Linux笔记---文件系统硬件部分
  • 大语言模型学习及复习笔记(1)语言模型的发展历程
  • TTL肖特基触发器
  • 睡不着营养补充贴士
  • 特种作业高压电工考试练习题库
  • 每日一题----------set接口及其内容(未)
  • 嵌入式人工智能应用- 第八章 车牌识别
  • AI应用加速落地丨MaxKB正在被政府、公共事业、教育和医疗行业用户广泛采纳
  • 如何学习VBA_3.2.20:DTP与Datepicker实现日期的输入
  • SpringBoot当中当主线程使用异步处理其他流程的时候需要获取上下文会出现什么情况详解
  • 蓝桥杯备赛-入门训练题 day1
  • 当今前沿技术:人工智能与区块链的未来发展
  • 每天五分钟深度学习框架PyTorch:算法模型的保存和加载(CPU和GPU)
  • Android Media3 ExoPlayer 开发全攻略:从基础集成到高级功能实战
  • 传媒湃︱《金陵晚报》副刊“雨花石”5月起改为免费刊登
  • 政治局会议:持续稳定和活跃资本市场
  • 经济日报:美离间国际关系注定徒劳无功
  • 上海开展2025年“人民城市 文明风采”群众性主题活动
  • 著名诗人、中国城市发展研究院原常务副院长吕贵品逝世
  • 漫游者秦龙,一生为经典画插图