[一篇详解哈希表]数据结构之哈希表(散列表)

✅作者简介:大家好,我是Philosophy7?让我们一起共同进步吧! 个人主页:Philosophy7的csdn博客
系列专栏: 哲学语录: 承认自己的无知,乃是开启智慧的大门
如果觉得博主的文章还不错的话,请点赞+收藏⭐️+留言支持一下博>主哦

在这里插入图片描述

文章目录

    • 散列表
      • 简介:
      • 为什么需要散列表?
      • 哈希函数的实现
      • 哈希表的代码

散列表

简介:

散列表又可以叫做哈希表,那什么是散列表呢? 现在就让我们深入研究一下吧
是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,或者叫哈希函数,存放记录的数组叫做散列表

为什么需要散列表?

通过上面的描述,相信大家都比较不理解,现在让我们来举一个例子让大家更好的理解散列表。

相信大家会经常用到某某词典,而每次去搜索一个单词,如果都去查询数据库,那么将会大大影响到这个查询效率,而如果我们去查找缓存的话那么就会大大的提升了我们查找的效率。

[一篇详解哈希表]数据结构之哈希表(散列表)_第1张图片

哈希函数的实现

以Java语言为例,这里以常用集合的HashMap,来看一看哈希函数在Java中的实现
我们都知道Java当中以及很多的面向对象语言,每一个对象都有属于自己的HashCode(哈希值),这个哈希值就是用于区分不同对象的重要标识。无论对象自身的类型是什么,哈希值都是一个整数型。所以我们采用取模法实现

索引 = 哈希值 % 数组的长度 = index = HashCode % Array.length

不过JDK(Java Development Kit Java语言的软件开发工具包)中的哈希函数是采用位运算的方式实现,没有直接使用取模法。

通过哈希函数,我们可以把字符串或其他类型的Key,转化成数组的下标

例: 给定一个长度为8的数组 当key = 001121时

index = HashCode(“001121”,“张三”) % Array.length = 1420036703 % 8 = 7

哈希表的代码

哈希表=数组+链表

图解:
[一篇详解哈希表]数据结构之哈希表(散列表)_第2张图片

package com.philosophy7.hashtable;

import java.util.Scanner;

public class HashTableDemo {
    public static void main(String[] args) {
        //1.创建一个哈希表
        HashTable hashTable = new HashTable(7);

        //菜单
        String key = "";
        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.println("1." + "添加员工");
            System.out.println("2." + "显示员工");
            System.out.println("3." + "查找员工");
            System.out.println("0." + "退出系统");

            key = scanner.nextLine();
            switch (key){
                case "1":
                    System.out.println("输入id");
                    int id = scanner.nextInt();
                    System.out.println("请输入名字:");
                    String name = scanner.next();
                    //创建员工
                    Emp emp = new Emp(id,name);
                    hashTable.add(emp);
                    break;
                case "2":
                    hashTable.list();
                    break;
                case "3":
                    System.out.println("请输入要查找的id");
                    id = scanner.nextInt();
                    hashTable.findEmpById(id);
                    break;
                case "0":
                    scanner.close();
                    System.exit(0);
                default:
                    break;
            }
        }
    }

}
//员工类
class Emp{
    private int id;
    private String name;
    public Emp next; //为了更好的理解指针的操作 在这里我们用public修饰

    public Emp(int id, String name) {
        super();
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Emp{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}
//创建EmpLinkedList链表
class EmpLinkedList{
    private Emp head; //头指针 指向的是第一个Emp 因此链表的头 是直接指向第一个Emp

    //添加员工到列表 id是自增长
    //也就是将员工直接添加到链表的尾部
    public void add(Emp emp){
        //若链表为空
        if(head == null){
            head = emp;
            return;
        }
        //若链表不为空 -- 创建一个Emp节点(临时节点) 作用:更好的帮助我们进行插入操作
        Emp curEmp = head;
        while (curEmp.next != null){
            curEmp = curEmp.next;
        }
        //直到不进入循环 就证明该结点 == null 也就是说到了链表的最后一个结点
        curEmp.next = emp; //于是将结点信息插入到尾部
    }

    //遍历链表
    public void list(int no){
        if (head == null){
            System.out.println("第" +(no+1)+"条链表空");
            return;
        }
        //不为空从头部开始遍历
        Emp temp = this.head;
        System.out.print("第" +(no+1) +"条链表信息");
        while (temp != null){
            System.out.printf("=> id=%d name=%s\t",temp.getId(),temp.getName());
            temp = temp.next; //指针往下移
        }
    }
    //根据id查找雇员
    //如果查找到 返回Emp 没有找到返回null
    public Emp findEmpById(int id){
        if (head == null){
            System.out.println("链表为空");
            return null;
        }
        //辅助指针
        Emp temp = head;
        while (true){
            if (temp.getId() == id){ //找到
                break; //temp指向要查找到的员工
            }
            //退出 遍历到最后一个结点指向为Null 就表示啥也没找到
            if (temp.next == null){
                System.out.println("在哈希表中,没找到该员工");
                temp = null;
                break; //退出 不退出的话会报空指针异常
            }
            temp = temp.next; //往下找
        }
        return temp;
    }
}
//创建哈希表 用于管理多条链表
class HashTable{
    //数组 用于存放EmpLinkedList链表
    private EmpLinkedList[] empLinkedListArray;
    private int size; //统计链表的条数

    public HashTable(int size){
        //初始化数组的容量
        this.size = size;
        //1.数组当中存放的是对象 所以默认初始化数据里面都是null 会造成空指针异常
        empLinkedListArray = new EmpLinkedList[size]; //仅仅是创建了数组
        //2.坑 这个时候需要初始化每条链表 否则将会报空指针异常
        for (int i = 0; i < size; i++){
            empLinkedListArray[i] =  new EmpLinkedList();
        }
    }

    public void add(Emp emp){
        //根据员工的id得到对应的信息,添加到哪条链表
        int empLinkedListNo =  hashFunction(emp.getId());
        //将emp添加到对应的链表中
        empLinkedListArray[empLinkedListNo].add(emp);
    }

    //遍历所有链表
    public void list(){
        for (int i = 0; i < size; i++){
            empLinkedListArray[i].list(i);
        }
    }

    //哈希函数 --- 采用取模法 当然不是魔法啦
    public int hashFunction(int id){
        return id % size;
    }

    //根据输入的id查找员工
    public void findEmpById(int id){
        //使用哈希函数确定到哪条链表
        int  empLinkedNo = hashFunction(id);
        Emp emp = empLinkedListArray[empLinkedNo].findEmpById(id);
        if (emp != null){
            System.out.println("在第"+(id+1)+"条链表找到了");
        }else{
            System.out.println("在哈希表没有找到该员工");
        }
    }


}

你可能感兴趣的