哈希表

哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。哈希表基于数组。

优缺点
1.无论哈希表中多少数据,插入只需要接近常量的时间,时间复杂度为O(1)
2.基于数组的,数组创建后难以扩展,哈希表被填满后,性能下降
3.没有一种简便的方法可以以任意的顺序遍历数据项,所以适用于如果不需要有序遍历数据,并且可以预估数据量

已填入哈希表的数据项和表长的比率叫做装填因子

哈希表重要的是如何把关键字转换为数组下标,有时候会hash冲突(哈希化之后数组下标相同);解决hash冲突的方法
开放地址法(再寻找一个空位解决冲突)
    若数据不能直接存放通过hash函数计算的下标对应的单元,需要数组的其他位置来存放数据
1.线性探测
  通俗来说线性探测就是如果X是要插入的位置,它已经被占用,那就使用X+1,X+2...以此类推;直到找到空位
  线性探测会发生数据聚集(一连串的已填充序列越来越长);聚集越严重;探测长度越长,存取序列最后单元会非常耗时
2.二次探测
  二次探测是防止聚集产生的一种尝试,思路是探测相隔较远的单元,而不是相邻的位置
  线性探测中X+1,X+2,X+3..
  二次探测是步数的平方
  X+1,X+4,X+9,X+16,X+25...
  二次探测会发生二次聚集,原因同理
3.再哈希法
  使用不同的哈希函数再做一遍哈希,要求哈希结果不能为0(否则每次都在原地踏步)
  
链地址法(在每个单元设置一个链表)
链地址图示

Java代码(链地址法)

//模拟实现hash表
class StudentHashTable {
    //存放链表的数组
    private HashLinkedList[] stuLinkedList;
    //数组大小
    private int size;

    //初始化
    public StudentHashTable(int size) {
        this.size = size;
        stuLinkedList = new HashLinkedList[size];
        //链表初始化,否则执行变更操作会有空指针异常
        for (int i = 0; i < size; i++) {
            stuLinkedList[i] = new HashLinkedList();
        }
    }

    //加入一个元素
    public void add(Student student) {
        //首先判断新元素落在哪一个链表
        int stuLinkedListNO = hashFun(student.id);
        stuLinkedList[stuLinkedListNO].add(student);
    }

    //根据学生id查找
    public void findStudentById(int id) {
        //首先判断在哪一个链表
        int stuLinkedListNO = hashFun(id);
        Student student = stuLinkedList[stuLinkedListNO].findStudentById(id);
        if (student != null) {
            System.out.printf("该学生在第%d条链表 【id=%d,name=%s】", (stuLinkedListNO + 1), student.id, student.name
            );
        } else {
            System.out.println("查找-哈希表未找到该学生");
        }
    }

    //删除一个元素
    public void delStudentById(int id) {
        //首先判断在哪一个链表
        int stuLinkedListNO = hashFun(id);
        //根据返回结果判断是否删除 以及删除原因 -1代表没有找到
        int result = stuLinkedList[stuLinkedListNO].delStudentBuId(id);
        if (result == -1) {
            System.out.println("删除-哈希表未找到该学生");
        } else {
            System.out.println("删除成功");
        }
    }

    //遍历哈希表
    public void list() {
        for (int i = 0; i < size; i++) {
            stuLinkedList[i].list(i);
        }
    }

    //哈希函数采用简单的取模
    public int hashFun(int no) {
        return no % size;
    }
}

//创建一个没有头节点的单链表
class HashLinkedList {
    private Student head;//链表第一个元素
    private Student curStudent;//定位链表最后一个元素

    public void add(Student student) {
        if (head == null) {
            head = student;
            curStudent = student;
            return;
        }
        curStudent.next = student;
        curStudent = student;
    }

    public void list(int no) {
        if (head == null) {
            System.out.printf("第%d条的链表为空", (no + 1));
            System.out.println();
            return;
        }
        Student tmp = head;
        System.out.printf("第%d条的链表信息:", (no + 1));
        while (true) {
            System.out.printf("=>id=%d name=%s", tmp.id, tmp.name);
            if (tmp.next == null) {
                break;
            }
            tmp = tmp.next;
        }
        System.out.println();
    }

    //根据id删除一个
    public int delStudentBuId(int id) {
        //先判断有没有这个学生
        Student student = findStudentById(id);
        if (student == null) {
            return -1;
        }
        //判断该链表是否就一个元素且该元素就是要找的学生
        if (head.next == null) {
            head = null;
            return 0;
        }
        //上述都不是就遍历查找
        Student tmp = head;
        while (true) {
            if (tmp.next.id == id) {
                tmp.next = tmp.next.next;
                break;
            }
            tmp = tmp.next;
        }
        return 0;
    }

    public Student findStudentById(int id) {
        if (head == null) {
            return null;
        }
        Student tmp = head;
        while (true) {
            if (tmp.id == id) {
                break;
            }
            tmp = tmp.next;
        }
        return tmp;
    }
}

//学生节点
class Student {
    public int id;
    public String name;
    public Student next;

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值