哈希表介绍

本文详细介绍了哈希表的概念,包括其快速的插入和查找操作,以及基于链表和数组的数据结构。讨论了哈希表在缓存层中的应用,以谷歌面试题为例,展示了如何使用哈希表构建员工信息管理系统。此外,还提供了哈希表的Java实现,包括员工类、链表类和哈希表类,并给出了操作菜单的测试案例。

一、什么是哈希表

1、哈希表也叫散列表,哈希表是一种数据结构,它提供了快速的插入操作和查找操作,无论哈希表总中有多少条数据,插入和查找的时间复杂度都是为O(1),因为哈希表的查找速度非常快,所以在很多程序中都有使用哈希表,例如拼音检查器。

2、哈希表也有自己的缺点,哈希表是基于数组的,我们知道数组创建后扩容成本比较高,所以当哈希表被填满时,性能下降的比较严重。

3、哈希表是由链表和数组组成的:

        链表:增删的效率极高,但是查询的效率极低。

        数组:查询效率极高,增删效率低

        所以哈希表中继承了链表和数组的优缺点,不仅查询效率高,增删效率也高

4、哈希表通过键值对的方式存储数据,【key,value】,哈希表会通过散列函数/哈希函数将key转换成对应的数组下标。【采用 % 的方式】

二、哈希表的应用


    通常数据都是放在数据库中的,但是有一些数据不需要存放在数据库中 ,太麻烦,那么就需要我们使用缓存层
        缓存层一般有俩种:

            缓存产品或者自己写一个缓存层(也就是哈希表)

通过一个列子自己构建出哈希表:

看一个实际需求,google公司的一个上机题:

有一个公司,当有新的员工来报道时,要求将该员工的信息加入(id,性别,年龄,住址..),当输入该员工的id时,要求查找到该员工的 所有信息.

要求: 不使用数据库,尽量节省内存,速度越快越好

思路:

        1、首先要求不使用数据库,那么是能用一些缓存产品用来存储数据,所以需要构建一个哈希表

        2、哈希表有数组,链表组成。在链表中保存员工数据信息,数组中保存链表

  代码实现:

        1、创建员工类:定义员工的属性

//创建员工类
class Emp {
    int id ; // id
    String name; // 姓名
    String sex ; // 性别

    Emp next; //指向下一节点

    //构造器
    public Emp(int id, String name, String sex) {
        this.id = id;
        this.name = name;
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Emp{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                '}';
    }
}

 2、创建链表【如何创建之前有写】

//创建链表
class LinkList {
    //头结点
    Emp head = new Emp(0, "", "");

    //增加员工信息
    public void add(Emp emp) {
        Emp temp = head;
        while (true) {
            if (temp.next == null) {
                break;
            }
            temp = temp.next;
        }
        temp.next = emp;
    }

    //根据ID查询员工信息
    public Emp findByID(int id) {
        if (head.next == null) {
            System.out.println("空链表");
            return null;
        }
        Emp temp = head;
        while (true) {
            if (temp.id == id) {
                break;
            }
            temp = temp.next;
        }
        return temp;
    }

    //遍历员工信息
    public void show() {
        if (head.next == null) {
            System.out.println("该链表没有增加员工信息");
            return;
        }
        Emp temp = head.next;
        while (true) {
            System.out.println("ID = " + temp.id + ", name = " + temp.name + ", 性别 = " + temp.sex);
            if (temp.next == null) {
                break;
            }
            temp = temp.next;
        }
    }

    //根据ID删除员工信息
    public void del(int id) {
        if (head.next == null) {
            System.out.println("空链表");
            return;
        }
        boolean flag = false;
        Emp temp = head;
        while (true) {
            if (temp.next.id == id) { //找到删除节点
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if (flag) {
            temp.next = temp.next.next;
            System.out.println("删除成功");
        } else {
            System.out.println("没有找到删除节点");
        }
    }
}

3、创建哈希表

//创建哈希表
class HashTable {
    //链表的个数
    int size;
    //arr[0]:第一个链表 arr[1]:第二个链表
    LinkList[] arr;

    public HashTable(int size) {
        this.size = size;
        arr = new LinkList[size];

        //初始化数组中每个链表
        for (int i = 0; i < arr.length; i++) {
            arr[i] = new LinkList();
        }
    }

    //散列函数--用来指明员工信息保存在哪个链表中
    public int function(int id) {
        return id % size;
    }

    //增加
    public void add(Emp emp) {
        //增加的emp在 第 i 个链表中
        int i = function(emp.id);
        //增加到对应的链表中
        arr[i].add(emp);
    }

    //遍历
    public void show() {
        for (int i = 0; i < size; i++) {
            arr[i].show();
        }
    }

    //通过id查询信息
    public Emp findByID(int id) {
        int i = function(id);
        return arr[i].findByID(id);
    }

    //删除
    public void del(int id) {
        int i = function(id);
        arr[i].del(id);
    }
}

    public int function(int id) {
        return id % size;
    }

这是一个简单的散列函数,哈希表通过这样的一个函数将关键值转换为对应的数组下标,并将数据存入链表中

4、测试

提供一个操作菜单:

     HashTable hashTable = new HashTable(7);

        //写一个简单的菜单
        String key = "";//接受用户输入
        Scanner scanner = new Scanner(System.in);
        boolean flag = true;
        while (flag) {
            System.out.println("输入:a 表示增加员工信息");
            System.out.println("输入:l 表示遍历员工信息");
            System.out.println("输入:e 表示退出程序");
            System.out.println("输入:f 表示 通过ID获取员工信息");
            System.out.println("输入:d 表示 通过ID删除员工信息");
            System.out.print("请输入关键字:");
            key = scanner.next();
            switch (key) {
                case "a":
                    System.out.println("请填写增加的数据:");
                    System.out.print("请输入ID:");
                    int id = scanner.nextInt();
                    System.out.print("请输入姓名:");
                    String name = scanner.next();
                    System.out.print("请输入性别:");
                    String sex = scanner.next();

                    Emp emp = new Emp(id, name, sex);
                    hashTable.add(emp);
                    break;
                case "l":
                    hashTable.show();
                    break;
                case "f":
                    System.out.print("请输入要查找员工的ID:");
                    int id1 = scanner.nextInt();
                    Emp emp1 = hashTable.findByID(id1);
                    System.out.println(emp1);
                    break;
                case "d":
                    System.out.print("请输入删除员工的ID:");
                    int id2 = scanner.nextInt();
                    hashTable.del(id2);
                    break;
                case "e":
                    scanner.close();
                    flag = false;
                    break;
                default:
                    break;
            }
        }
        System.out.println("您已经退出程序!");

测试结果

输入:a 表示增加员工信息
输入:l 表示遍历员工信息
输入:e 表示退出程序
输入:f 表示 通过ID获取员工信息
输入:d 表示 通过ID删除员工信息
请输入关键字:a
请填写增加的数据:
请输入ID:1
请输入姓名:张三
请输入性别:男

输入:a 表示增加员工信息
输入:l 表示遍历员工信息
输入:e 表示退出程序
输入:f 表示 通过ID获取员工信息
输入:d 表示 通过ID删除员工信息
请输入关键字:a
请填写增加的数据:
请输入ID:2
请输入姓名:李四
请输入性别:女

输入:a 表示增加员工信息
输入:l 表示遍历员工信息
输入:e 表示退出程序
输入:f 表示 通过ID获取员工信息
输入:d 表示 通过ID删除员工信息
请输入关键字:l
该链表没有增加员工信息
ID = 1, name = 张三, 性别 = 男
ID = 2, name = 李四, 性别 = 女

该链表没有增加员工信息
该链表没有增加员工信息
该链表没有增加员工信息
该链表没有增加员工信息
输入:a 表示增加员工信息
输入:l 表示遍历员工信息
输入:e 表示退出程序
输入:f 表示 通过ID获取员工信息
输入:d 表示 通过ID删除员工信息
请输入关键字:f
请输入要查找员工的ID:2
Emp{id=2, name='李四', sex='女'}

输入:a 表示增加员工信息
输入:l 表示遍历员工信息
输入:e 表示退出程序
输入:f 表示 通过ID获取员工信息
输入:d 表示 通过ID删除员工信息
请输入关键字:d
请输入删除员工的ID:1
删除成功

输入:a 表示增加员工信息
输入:l 表示遍历员工信息
输入:e 表示退出程序
输入:f 表示 通过ID获取员工信息
输入:d 表示 通过ID删除员工信息
请输入关键字:l
该链表没有增加员工信息
该链表没有增加员工信息
ID = 2, name = 李四, 性别 = 女
该链表没有增加员工信息
该链表没有增加员工信息
该链表没有增加员工信息
该链表没有增加员工信息
输入:a 表示增加员工信息
输入:l 表示遍历员工信息
输入:e 表示退出程序
输入:f 表示 通过ID获取员工信息
输入:d 表示 通过ID删除员工信息
请输入关键字:e
您已经退出程序!

在 C 语言中,哈希表的实现通常依赖于数组和哈希函数。哈希表通过哈希函数将键(key)映射到数组的某个索引上,从而实现快速的插入和查找操作。由于哈希冲突的存在(即不同的键可能映射到相同的索引),需要解决冲突的方法,例如开放寻址法或链地址法。 ### 哈希表的基本实现 以下是一个使用开放寻址法实现的简单哈希表示例: ```c #include <stdio.h> #include <stdbool.h> #define MAXSIZE 20 bool Insert(int hash[MAXSIZE], int e, int length) { if (length < MAXSIZE) { int j = e % MAXSIZE; while (hash[j] != 0) { j = (++j % MAXSIZE); } hash[j] = e; length++; return true; } else { return false; } } void Print(int hash[MAXSIZE]) { printf("元素:"); for (int i = 0; i < MAXSIZE; i++) { printf("%d ", hash[i]); } } int Find(int hash[MAXSIZE], int key) { int j = key % MAXSIZE; int temp = j; do { if (hash[temp] == key) { return temp; } temp = (++temp % MAXSIZE); } while (hash[temp] != key && temp != j); return -1; } int main() { int hash[MAXSIZE] = {0}; int length = 0; int e, key, get; while (length < MAXSIZE) { printf("请输入插入元素(结束请插入0):"); scanf("%d", &e); if (e == 0) { break; } else { Insert(hash, e, length); } } Print(hash); while (true) { printf("\n请输入要查找的元素(0结束):"); scanf("%d", &key); if (key == 0) { printf("查找结束"); break; } else { get = Find(hash, key); if (get == -1) { printf("不存在"); } else { printf("%d", get); } } } } ``` ### 哈希表的使用方法 1. **初始化哈希表**:定义一个固定大小的数组,初始值为 `0` 或 `NULL`。 2. **插入元素**:使用哈希函数将键值映射到数组的索引位置。如果该位置已被占用,则根据冲突解决策略(如开放寻址法)寻找下一个可用位置。 3. **查找元素**:通过哈希函数计算键值的索引位置,并检查该位置是否存储了目标键值。如果未找到,则根据冲突解决策略继续搜索。 4. **删除元素**:查找目标键值的索引位置,并将其标记为可用或删除状态。 ### 使用 C 语言库函数实现哈希表 除了手动实现哈希表外,还可以使用 C 语言的一些库函数,例如 `uthash`,它提供了一种方便的方式来管理哈希表。以下是一个使用 `uthash` 的示例: ```c #include <stdio.h> #include <stdlib.h> #include "uthash.h" typedef struct MyHash { int key; int value; UT_hash_handle hh; } MyHash; MyHash *myhs = NULL; void insert(int key1, int value1) { MyHash *tmp1 = NULL; HASH_FIND_INT(myhs, &key1, tmp1); if (tmp1 == NULL) { tmp1 = (MyHash *)malloc(sizeof(MyHash)); tmp1->key = key1; tmp1->value = value1; HASH_ADD_INT(myhs, key, tmp1); } else { tmp1->value = value1; } } MyHash *find(int key1) { MyHash *tmp1 = NULL; HASH_FIND_INT(myhs, &key1, tmp1); return tmp1; } int main() { insert(1, 10); insert(2, 20); insert(3, 30); MyHash *result = find(2); if (result != NULL) { printf("找到键 %d,值为 %d\n", result->key, result->value); } else { printf("未找到键\n"); } return 0; } ``` ### 哈希表的优点 - **查找速度快**:理想情况下,哈希表可以在常数时间内完成插入和查找操作。 - **适合大数据量**:对于需要频繁查找和插入的场景,哈希表表现出色。 ### 哈希表的缺点 - **扩展困难**:由于哈希表基于数组实现,因此在数据量超出初始容量时,扩展较为困难。 - **不利于遍历**:哈希表不适合进行顺序遍历操作。 ### 总结 哈希表是一种高效的数据结构,适用于需要快速查找和插入的场景。在 C 语言中,可以通过数组和哈希函数手动实现哈希表,也可以使用第三方库(如 `uthash`)来简化实现过程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鲨瓜2号

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值