08 哈希表

2024 年 7 月 22 日 星期一

08 哈希表

哈希表详解

1. 引言

哈希表(Hash Table)是一种常用于快速查找的数据结构,它通过哈希函数将键映射到数组的特定位置,以实现快速的插入、删除和查找操作。

2. 哈希表原理

哈希表的原理基于哈希函数,这是一种能够将任意大小的数据映射到固定大小范围的函数。哈希表由一个数组和一个哈希函数组成。当插入一个键值对时,哈希函数计算键的哈希值,然后将该值映射到数组的特定位置。在查找时,哈希表再次使用哈希函数找到键的位置,从而实现快速的查找。

3. 解决冲突的方法

由于哈希函数的映射并非唯一,可能出现多个键映射到同一个位置的情况,称为冲突。解决冲突的常见方法有:

3.1 链地址法

每个哈希表位置维护一个链表,所有映射到该位置的键值对都存储在链表中。

3.2 开放地址法

当冲突发生时,通过线性探测、二次探测等方法在哈希表中寻找下一个可用位置。

4. 哈希表的应用场景

哈希表广泛应用于各种场景,包括:

  • 数据库索引:加速数据库中记录的查找。
  • 缓存实现:存储最近访问的数据,提高访问速度。
  • 字典数据结构:实现键值对的快速插入、删除和查找。

5. 哈希表的代码示例

class HashTable:
    def __init__(self, size):
        self.size = size
        self.table = [None] * size

    def _hash(self, key):
        return hash(key) % self.size

    def insert(self, key, value):
        index = self._hash(key)
        if self.table[index] is None:
            self.table[index] = [(key, value)]
        else:
            self.table[index].append((key, value))

    def search(self, key):
        index = self._hash(key)
        if self.table[index] is not None:
            for k, v in self.table[index]:
                if k == key:
                    return v
        return None

    def delete(self, key):
        index = self._hash(key)
        if self.table[index] is not None:
            for i, (k, v) in enumerate(self.table[index]):
                if k == key:
                    del self.table[index][i]
                    return
package org.example;  
  
import java.util.Scanner;  
  
public class HashTableDemo {  
    public static void main(String[] args) {  
        //创建一个哈希表  
        HashTable hashTable = new HashTable(7);  
        //写一个简单菜单  
        String key = "";  
        Scanner scanner = new Scanner(System.in);  
  
        while (true) {  
            System.out.println("add:添加雇员");  
            System.out.println("list:显示雇员");  
            System.out.println("find:查找雇员");  
            System.out.println("exit:退出系统");  
            key = scanner.next();  
  
            switch (key) {  
                case "add":  
                    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 "list":  
                    hashTable.list();  
                    break;  
                case "find":  
                    System.out.println("输入要查找的id");  
                    id = scanner.nextInt();  
                    hashTable.findEmpById(id);  
                    break;  
                case "exit":  
                    scanner.close();  
                    System.exit(0);  
                default:  
                    break;  
            }  
        }  
    }  
}  
  
//创建哈希表,用于管理多条链表  
class HashTable {  
    private EmpLinkedList[] empLinkedListArray;  
    private int size;//链表条数  
  
    public HashTable(int size) {  
        this.size = size;  
        // 初始化empLinkedListArray  
        empLinkedListArray = new EmpLinkedList[size];  
        //能不能直接用,不要忘了分别初始化每个链表  
        for (int i = 0; i < size; i++) {  
            empLinkedListArray[i] = new EmpLinkedList();  
        }  
    }  
  
    public void add(Emp emp) {  
        //根据员工id得到该员工应当加入到哪条链表  
        int empLinkedListNO = hashFun(emp.id);  
        //emp添加到对应的链表中  
        empLinkedListArray[empLinkedListNO].add(emp);  
    }  
  
    public void list() {  
        for (int i = 0; i < size; i++) {  
            empLinkedListArray[i].list(i);  
        }  
    }  
  
    //编写散列函数,使用取模法  
    public int hashFun(int id) {  
        return id % size;  
    }  
  
    //根据输入的id查找雇员  
    public void findEmpById(int id) {  
        int empLinkedListNO = hashFun(id);  
        Emp emp = empLinkedListArray[empLinkedListNO].findEmpById(id);  
        if (emp != null) {  
            System.out.printf("在第%d条链表中找到 雇员id = %d\n", empLinkedListNO + 1, id);  
        } else {  
            System.out.println("在表中未找到");  
        }  
    }  
}  
  
class Emp {  
    public int id;  
    public String name;  
    public Emp next;  
  
    public Emp(int id, String name) {  
        super();  
        this.id = id;  
        this.name = name;  
    }  
  
  
}  
  
class EmpLinkedList {  
    //表示一条链表、一条数据  
    //头指针 指向第一个emp,因此head有效,直接指向第一个雇员  
    private Emp head;  
  
    //添加员工到链表  
    public void add(Emp emp) {  
        //假定添加雇员时,id自增长  
        //因此将该雇员加入本链表的最后即可  
        if (head == null) {  
            head = emp;  
            return;  
        }  
        //如果不是第一个,定义辅助指针帮助定位到最后  
        Emp curEmp = head;  
        while (true) {  
            if (curEmp.next == null) {  
                break;  
            }  
            curEmp = curEmp.next;  
        }  
        curEmp.next = emp;  
    }  
  
    //    遍历雇员信息  
    public void list(int no) {  
        if (head == null) {  
            System.out.println("第" + (no + 1) + "链表信息为空");  
            return;  
        }  
        System.out.print("第" + (no + 1) + "链表信息为:");  
        Emp curEmp = head;  
        while (true) {  
            System.out.printf("=> id=%d name=%s\t", curEmp.id, curEmp.name);  
            if (curEmp.next == null) {  
                //说明是最后节点  
                break;  
            }  
            curEmp = curEmp.next;  
        }  
        System.out.println();  
    }  
  
    //根据id查找雇员  
    //找到返回emp,找不到返回空  
    public Emp findEmpById(int id) {  
        //判断链表是否为空  
        if (head == null) {  
            System.out.println("链表为空");  
            return null;  
        }  
        Emp curEmp = head;  
        while (true) {  
            if (curEmp.id == id) {  
                break;//找dao  
            }  
            if (curEmp.next == null) {  
                //遍历结束仍未找到  
                curEmp = null;  
                break;  
            }  
            curEmp = curEmp.next;  
        }  
        return curEmp;  
    }  
}

6. 总结

在选择哈希函数时,需要考虑均匀性和碰撞的处理方法,以确保哈希表的高效性。

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...