哈希表的原理与实现

哈希表也称散列表,是由数组+单链表构成的一种数据结构,它是根据关键值(key-value)进行对数据的访问的,它通过把关键码值映射在表中的一个位置来访问记录,以加快查找速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。

其底层如图:

 所以,由此可见我们的哈希表实质就是一个链表的数组即linkedList[] hashtable

对于创建哈希表,我们首先需要创建一个单链表,并且创建一个类,作为链表中添加的数据。

这里我就创建了一个Emp类,属性有id,name,next。作为链表中的添加元素。

代码如下:

  //创建添加元素
    static class Emp {
        int id;
        String name;
        Emp next;

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

        public Emp() {
        }

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

然后创建一个单链表,实现CRUD操作,若对创建单链表的朋友可以看我以前的文章,有对创建单链表的介绍与实现。

//创建一个链表
    static class LinkedList {
        Emp head;

        /**
         * 添加emp到链表中
         * @param emp
         */
        public void addEmp(Emp emp) {
            if (isEmpty()) {
                //如果为空,直接将雇员添加到链表中
                head = emp;
                System.out.println("添加成功");
            } else {
                //不为空,根据no值进行插入
                Emp temp = head;//借助辅助结点
                while(temp.next!=null&&temp.next.id<emp.id){
                    //当下一个结点不为空而且下一个结点的id值小于传入的emp的id值,则继续循环
                    temp=temp.next;
                }
                //当循环结束,要么temp.next为空,要么temp.next.id>emp.id
                if(temp.next==null){
                    //直接插入在后面
                    temp.next=emp;
                    System.out.println("添加成功");
                }else{
                    //此时为temp.next.id>emp.id,即找到了插入数据的前一个结点
                    emp.next=temp.next;
                    temp.next=emp;
                    System.out.println("添加成功");
                }
            }
        }

        /**
         * 删除数据
         * @param id 要删除的数据的id值
         */
        public void deleteEmp(int id){
            if (isEmpty()){
                //如果链表为空,无法删除
                System.out.println("链表为空,无法删除");
            }else{
                if(head.id==id){//要删除的结点为头结点
                    //将后面的结点前移
                    head=head.next;
                    System.out.println("删除成功");
                    return;//结束方法
                }
                Emp temp = head;//借助辅助结点
                while (temp.next!=null&&temp.next.id!=id){
                    //当下一个结点不为空而且下一个结点的id值不等于传入的emp的id值,则继续循环
                    temp=temp.next;
                }
                //当循环结束,要么temp.next为空,要么temp.next.id==emp.id
                if(temp.next==null){
                    //遍历结束,没有对应的员工
                    System.out.println("没有该员工,无法删除");
                }else{
                    //此时为temp.next.id==emp.id,即找到了要被删除结点的前一个结点
                    temp.next=temp.next.next;
                    System.out.println("删除成功");
                }
            }
        }

        /**
         * 更改结点
         * @param emp  要更改的结点
         */
        public void update(Emp emp){
            if (isEmpty()){
                //如果链表为空,无法删除
                System.out.println("链表为空,没有数据更改");
            }else{
                Emp temp = head;//借助辅助结点
                if(head.id==emp.id){
                    //更改的是头结点
                    emp.next=head.next;
                    head=emp;
                    System.out.println("更改成功");
                    return;
                }
                while (temp.next!=null&&temp.next.id!=emp.id){
                    //当下一个结点不为空而且下一个结点的id值不等于传入的emp的id值,则继续循环
                    temp=temp.next;
                }
                //当循环结束,要么temp.next为空,要么temp.next.id==emp.id
                if(temp.next==null){
                    //遍历结束,没有对应的员工
                    System.out.println("没有该员工,无法更新");
                }else{
                    //此时为temp.next.id==emp.id,即找到了要被更新结点的前一个结点
                    emp.next=temp.next.next;
                    temp.next=emp;
                    System.out.println("更改成功");
                }
            }

        }

        /**
         * 查询员工
         * @param id  员工的id
         * @return  返回员工
         */
        public Emp selectEmp(int id){
            if (isEmpty()){
                //如果链表为空,无法删除
                throw new RuntimeException("链表为空,没有数据");
            }else{
                Emp temp = head;//借助辅助结点
                while (temp!=null&&temp.id!=id){
                    //当当前结点不为空而且当前结点的id值不等于传入的emp的id值,则继续循环
                    temp=temp.next;
                }
                //当循环结束,要么temp.next为空,要么temp.next.id==emp.id
                if(temp.next==null){
                    //遍历结束,没有对应的员工
                    throw new RuntimeException("没有该数据");
                }else{
                    //此时为找到该员工,返回该员工
                    return temp;
                }
            }
        }

        /**
         * 遍历链表的信息
         */
        public void show(){
            if (isEmpty()){
                System.out.println("链表为空");
            }else {
                Emp temp = head;
                while(temp!=null){
                    System.out.println(temp);
                    temp=temp.next;
                }
            }
        }

        /**
         * 判断链表是否为空
         *
         * @return 如果为空,返回true。否则返回false
         */
        public boolean isEmpty() {
            return head == null;
        }
    }

 这些前提准备完成后,我们就进入创建哈希表的操作,我们刚才指出,哈希表就是一个链表的集合,那么要操作这些链表,我们就得需要创建一个链表数组。即linkedList[] hashtable

然后还有一个要点:就是我们加入一个数据时,应该添加到哪一个链表中呢?这里我就通过一个对加入数据的id值的一个简单的取模,就可以判断出该数据应该添加到什么位置。

比如说:我们传入的Emp的id属性为8,我们的哈希表的链表数组长度为8,id/size=8/8=0,这时就把该数据加入到hashtable[0]中,这就成功添加数据了。

大家可以尝试着实现,我把完整代码附上,里面注释解释的较为清楚:

package com.liu.hashtable;



/**
 * @author liuweixin
 * @create 2021-09-12 15:02
 */
//哈希表的实现
public class HashTab {
    //因为哈希表实质为一个单链表数组
    private LinkedList[] lists;
    private int maxSize;

    public HashTab(int maxSize) {
        this.maxSize = maxSize;
        lists=new LinkedList[this.maxSize];
        for (int i = 0; i < maxSize; i++) {
            //初始化lists单链表数组
            lists[i]=new LinkedList();
        }
    }

    public static void main(String[] args) {
        HashTab hashTab = new HashTab(10);
        hashTab.add(new Emp(1,"a"));
        hashTab.add(new Emp(2,"b"));
        hashTab.add(new Emp(4,"c"));
        hashTab.add(new Emp(3,"d"));
        hashTab.add(new Emp(11,"e"));
        hashTab.show();
        hashTab.delete(3);
        hashTab.show();
        hashTab.update(new Emp(6,"sb"));
        hashTab.show();
        System.out.println(hashTab.select(4));
    }

    /**
     * 判断传进来的员工id应该放在那个单链表中
     * @param id  传进来的员工id
     * @return
     */
    public int check(int id){
        //来判断其应该放在哪个链表
        return id%maxSize;
    }

    /**
     * 添加员工
     * @param emp  添加的员工的信息
     */
    public void add(Emp emp){
        //判断应该放置在哪个链表中
        int check = check(emp.id);
        //添加员工
        lists[check].addEmp(emp);
    }

    /**
     * 删除员工
     * @param id  删除的员工的id
     */
    public void delete(int id){
        //判断应该放置在哪个链表中
        int check = check(id);
        //添加员工
        lists[check].deleteEmp(id);
    }

    /**
     * 更改员工
     * @param emp  更改的员工信息
     */
    public void update(Emp emp){
        //判断应该放置在哪个链表中
        int check = check(emp.id);
        //删除员工
        lists[check].update(emp);
    }

    /**
     * 查找员工
     * @param id  员工的id
     * @return  返回查询到的员工
     */
    public Emp select(int id){
        //判断应该放置在哪个链表中
        int check = check(id);
        //找寻员工
        return lists[check].selectEmp(id);
    }

    /**
     * 遍历链表中的信息
     */
    public void show(){
        for (int i = 0; i < lists.length; i++) {
            if(!lists[i].isEmpty()){
                System.out.print("第"+i+"个链表的信息为:");
                lists[i].show();
            }
        }
    }
    //创建一个链表
    static class LinkedList {
        Emp head;

        /**
         * 添加emp到链表中
         * @param emp
         */
        public void addEmp(Emp emp) {
            if (isEmpty()) {
                //如果为空,直接将雇员添加到链表中
                head = emp;
                System.out.println("添加成功");
            } else {
                //不为空,根据no值进行插入
                Emp temp = head;//借助辅助结点
                while(temp.next!=null&&temp.next.id<emp.id){
                    //当下一个结点不为空而且下一个结点的id值小于传入的emp的id值,则继续循环
                    temp=temp.next;
                }
                //当循环结束,要么temp.next为空,要么temp.next.id>emp.id
                if(temp.next==null){
                    //直接插入在后面
                    temp.next=emp;
                    System.out.println("添加成功");
                }else{
                    //此时为temp.next.id>emp.id,即找到了插入数据的前一个结点
                    emp.next=temp.next;
                    temp.next=emp;
                    System.out.println("添加成功");
                }
            }
        }

        /**
         * 删除数据
         * @param id 要删除的数据的id值
         */
        public void deleteEmp(int id){
            if (isEmpty()){
                //如果链表为空,无法删除
                System.out.println("链表为空,无法删除");
            }else{
                if(head.id==id){//要删除的结点为头结点
                    //将后面的结点前移
                    head=head.next;
                    System.out.println("删除成功");
                    return;//结束方法
                }
                Emp temp = head;//借助辅助结点
                while (temp.next!=null&&temp.next.id!=id){
                    //当下一个结点不为空而且下一个结点的id值不等于传入的emp的id值,则继续循环
                    temp=temp.next;
                }
                //当循环结束,要么temp.next为空,要么temp.next.id==emp.id
                if(temp.next==null){
                    //遍历结束,没有对应的员工
                    System.out.println("没有该员工,无法删除");
                }else{
                    //此时为temp.next.id==emp.id,即找到了要被删除结点的前一个结点
                    temp.next=temp.next.next;
                    System.out.println("删除成功");
                }
            }
        }

        /**
         * 更改结点
         * @param emp  要更改的结点
         */
        public void update(Emp emp){
            if (isEmpty()){
                //如果链表为空,无法删除
                System.out.println("链表为空,没有数据更改");
            }else{
                Emp temp = head;//借助辅助结点
                if(head.id==emp.id){
                    //更改的是头结点
                    emp.next=head.next;
                    head=emp;
                    System.out.println("更改成功");
                    return;
                }
                while (temp.next!=null&&temp.next.id!=emp.id){
                    //当下一个结点不为空而且下一个结点的id值不等于传入的emp的id值,则继续循环
                    temp=temp.next;
                }
                //当循环结束,要么temp.next为空,要么temp.next.id==emp.id
                if(temp.next==null){
                    //遍历结束,没有对应的员工
                    System.out.println("没有该员工,无法更新");
                }else{
                    //此时为temp.next.id==emp.id,即找到了要被更新结点的前一个结点
                    emp.next=temp.next.next;
                    temp.next=emp;
                    System.out.println("更改成功");
                }
            }

        }

        /**
         * 查询员工
         * @param id  员工的id
         * @return  返回员工
         */
        public Emp selectEmp(int id){
            if (isEmpty()){
                //如果链表为空,无法删除
                throw new RuntimeException("链表为空,没有数据");
            }else{
                Emp temp = head;//借助辅助结点
                while (temp!=null&&temp.id!=id){
                    //当当前结点不为空而且当前结点的id值不等于传入的emp的id值,则继续循环
                    temp=temp.next;
                }
                //当循环结束,要么temp.next为空,要么temp.next.id==emp.id
                if(temp.next==null){
                    //遍历结束,没有对应的员工
                    throw new RuntimeException("没有该数据");
                }else{
                    //此时为找到该员工,返回该员工
                    return temp;
                }
            }
        }

        /**
         * 遍历链表的信息
         */
        public void show(){
            if (isEmpty()){
                System.out.println("链表为空");
            }else {
                Emp temp = head;
                while(temp!=null){
                    System.out.println(temp);
                    temp=temp.next;
                }
            }
        }

        /**
         * 判断链表是否为空
         *
         * @return 如果为空,返回true。否则返回false
         */
        public boolean isEmpty() {
            return head == null;
        }
    }

    //创建添加元素
    static class Emp {
        int id;
        String name;
        Emp next;

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

        public Emp() {
        }

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


 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值