哈希表实现

1、哈希表的介绍和内存布局

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

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

要求: 不使用数据库,尽量节省内存,速度越快越好=>哈希表(散列)

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

2、哈希表的图解,

通过数组+链表的方式来快速访问。(java里的hashMap)
在这里插入图片描述
所以什么叫哈希表?

哈希表可以用来高效率解决元素不可重复这个问题,其本质就是:数组+链表+红黑树在这里插入图片描述

3、哈希表实现 案例

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

要求

  1. :不使用数据库,速度越快越好 => 哈希表(散列).
  2. 添加时,保证按照id从低到高插入, [课后思考:如果id不是从低到高插入,但要求各条链表仍是从低到高,怎么解决?]
  3. 使用链表来实现哈希表,该链表不带表头[即:链表的第一个结点就存放雇员信息]
  4. 思路分析并画出示意图

4、案例思路图解

使用哈希表来管理雇员信息
在这里插入图片描述

5、代码实现

  • 员工类
//员工  代表链表上的节点
class Emp {
    public int id;
    public String name;
    public Emp next; //next 默认为null

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

    @Override
    public String toString() {
        return "Emp{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}
  • EmpLinkedList
//创建EmpLinkedList 表示链表
class EmpLinkedList{
    //头指针,执行第一个Emp,因那次我们这个链表的head直接指向第一个Emp
    public Emp head; //默认为null

    //添加员工到链表
    public void addEmp(Emp emp){
        if(head == null){
            head = emp;
            return;
        }
        //不能添加已有id员工
        Emp findEmp = findEmp(emp.id);
        if(findEmp != null){
            System.out.println("已存在id= "+findEmp.toString()+"的员工,不能添加重复id员工"+emp.toString());
            return;
        }
        //不是第一个员工,定义一个辅助指针
        Emp curTemp = head;
        while(curTemp.next != null){
            curTemp = curTemp.next;//后移
        }
        //退出时将emp加入链表
        curTemp.next = emp;

    }

    //按id从小到大 添加员工到链表
    public void addEmpById(Emp emp){
        if(head == null){
            head = emp;
            return;
        }
        //不能添加已有id员工
        Emp findEmp = findEmp(emp.id);
        if(findEmp != null){
            System.out.println("已存在id= "+findEmp.toString()+"的员工,不能添加重复id员工"+emp.toString());
            return;
        }
        //如果新加结点比头节点小
        if(head.id > emp.id){
            Emp temp = null;
            emp.next = head;
            temp = head;
            head = emp;
            return;
        }
        
        //定义一个辅助指针
        Emp curTemp = head;
        while( true){
            //遍历到末尾还未找到,添加
            if(curTemp.next == null &&curTemp.id < emp.id){
                curTemp.next = emp;
                break;
            }
           
            if(curTemp.next.id > emp.id){
                emp.next = curTemp.next;
                curTemp.next = emp;
                break;
            }
            curTemp = curTemp.next;

        }

    }

    //遍历链表
    public void list(int index){
        if(head == null){
            System.out.println("第"+(index+1)+"链表为空。");
            return;
        }

        Emp curTemp = head; //辅助指针
        System.out.println("第"+(index+1)+"链表信息如下:");
        while(curTemp != null){
            System.out.println(curTemp.toString());
            curTemp = curTemp.next;
        }
    }

    //从链表删除员工
    //链表删除需要找到删除结点的前一个节点,因此分为第一个节点和第2-n结点判断
    public void delete( int id){
        if(head == null){
            System.out.println("当前链表为空。");
            return;
        }
        //头节点直接删除
        if(head.id == id){
            head = head.next;
            System.out.println("Id为" + id + "的员工已经被删除~~");
            return;
        }
        Emp curTemp = head; //辅助指针
        while (true){
            if(curTemp.next == null ){
                break;
            }

            if( curTemp.next.id == id){
                curTemp.next = curTemp.next.next;
                System.out.println("Id为" + id + "的员工已经被删除~~");
                return;//删除之后直接退出,否则还会向下执行
            }

            curTemp =curTemp.next;

        }
    }

    //修改员工信息
    public void update( Emp emp){
        if(head == null){
            System.out.println("当前链表为空。");
            return;
        }
        Emp findEmp = findEmp(emp.id);
        if(findEmp == null){
            System.out.println("未找到id = "+emp.id+"员工信息");
            return;
        }
        Emp curTemp = head; //辅助指针
        while (true){
          
            if(curTemp == null ){
                break;
            }
            if( curTemp.id == emp.id){
                curTemp.name = emp.name;
                return;//跟新之后直接退出,否则还会向下执行
            }
            curTemp =curTemp.next;


        }
    }

    //查找员工
    public Emp findEmp( int id){
        if(head == null){
            System.out.println("当前链表为空。");
            return null;
        }
        Emp curTemp = head; //辅助指针
       

        while (curTemp != null){
            if( curTemp.id == id){
                //System.out.println(curTemp.toString());
                return curTemp;
            }
            curTemp =curTemp.next;
        }

        return null;
    }
    //根据id查询雇员
    //如果查找到,就返回Emp,没有找到,就返回null
    public Emp findEmpId(int id){
        //判断链表是否为空
        if(head == null){
            System.out.println("链表为空.");
            return null;
        }
        //辅助指针
        Emp curEmp = head;
        while(true){
            if(curEmp.id == id){	//找到
                break;	//此时curEmp就指向要查找的雇员
            }
            //退出
            if(curEmp.next == null){	//未找到该雇员
                curEmp = null;
                break;
            }
            curEmp = curEmp.next;	//后移
        }
        return curEmp;
    }
}
  • HashTable
//创建HashTable 管理多条链表
class HashTable{
    private EmpLinkedList[] LinkedList;
    private int size ;//表示共有多少条链表

    public HashTable(int size){
        this.size = size;
        //初始化LinkedList
        LinkedList = new EmpLinkedList[size];
        //还未初始化 null , 分别为每个链表初始化
        for (int i = 0; i < size; i++) {
            LinkedList[i] = new EmpLinkedList();
        }
    }

    //添加雇员
    public void add(Emp emp){
        //根据员工id,找到员工应该在哪条链表下
        int LinkedListIndex = hashFun(emp.id);
        //将emp 添加到对应的链表中
		// LinkedList[LinkedListIndex].addEmp(emp);
		//按id大小进行排序
        LinkedList[LinkedListIndex].addEmpById(emp);
    }

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

    public void findEmpId(int id){
        //得到该员工id在hashtable上那个角标
        int index = hashFun(id);
        //进入该链表查找员工
        Emp emp = LinkedList[index].findEmp(id);
        if(emp == null){
            System.out.println("未找到");;//表示遍历完,还未找到该节点 返回null
        }else{
            System.out.println(emp.toString());
        }
    }
    public void delEmpId(int id){
        //得到该员工id在hashtable上那个角标
        int index = hashFun(id);
        //进入该链表查找员工
        LinkedList[index].delete(id);
    }

    public void updateEmp(Emp emp){
        int index = hashFun(emp.id);
        //进入该链表查找员工
        LinkedList[index].update(emp);
    }
    //编写一个散列函数,使用取模法
    public int hashFun(int id){
        return id % size;
    }
}
  • Demo测试
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("雇员管理系统:");
            System.out.println("add : 添加雇员    find: 查找雇员");
            System.out.println("list: 显示雇员    del : 删除雇员");
            System.out.println("update:跟新员工   exit: 退出系统");

            key = scanner.next();
            switch (key) {
                case "add":
                    System.out.print("输入id:");
                    int id = scanner.nextInt();
                    System.out.print("输入名字:");
                    String name = scanner.next();
                    //创建雇员
                    Emp emp = new Emp(id, name);
                    hashTable.add(emp);
                    break;
                case "list":
                    hashTable.list();
                    break;
                case "find":
                    System.out.print("请输入需要查找的id:");
                    id = scanner.nextInt();
                    hashTable.findEmpId(id);
                    break;
                case "del":
                    System.out.print("请输入雇员的id:");
                    id = scanner.nextInt();
                    hashTable.delEmpId(id);
                    break;
                case "update":
                    System.out.print("输入id:");
                    id = scanner.nextInt();
                    System.out.print("输入名字:");
                    name = scanner.next();
                    //创建雇员
                    emp = new Emp(id, name);
                    hashTable.updateEmp(emp);
                    break;
                case "exit":
                    scanner.close();
                    System.exit(0);
                default:
                    break;
            }
        }
    }
}
  • 代码汇总
package h_HashTable;

import java.util.LinkedList;
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("雇员管理系统:");
            System.out.println("add : 添加雇员    find: 查找雇员");
            System.out.println("list: 显示雇员    del : 删除雇员");
            System.out.println("update:跟新员工   exit: 退出系统");

            key = scanner.next();
            switch (key) {
                case "add":
                    System.out.print("输入id:");
                    int id = scanner.nextInt();
                    System.out.print("输入名字:");
                    String name = scanner.next();
                    //创建雇员
                    Emp emp = new Emp(id, name);
                    hashTable.add(emp);
                    break;
                case "list":
                    hashTable.list();
                    break;
                case "find":
                    System.out.print("请输入需要查找的id:");
                    id = scanner.nextInt();
                    hashTable.findEmpId(id);
                    break;
                case "del":
                    System.out.print("请输入雇员的id:");
                    id = scanner.nextInt();
                    hashTable.delEmpId(id);
                    break;
                case "update":
                    System.out.print("输入id:");
                    id = scanner.nextInt();
                    System.out.print("输入名字:");
                    name = scanner.next();
                    //创建雇员
                    emp = new Emp(id, name);
                    hashTable.updateEmp(emp);
                    break;
                case "exit":
                    scanner.close();
                    System.exit(0);
                default:
                    break;
            }
        }
    }
}

//员工  代表链表上的节点
class Emp {
    public int id;
    public String name;
    public Emp next; //next 默认为null

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

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

//创建HashTable 管理多条链表
class HashTable{
    private EmpLinkedList[] LinkedList;
    private int size ;//表示共有多少条链表

    public HashTable(int size){
        this.size = size;
        //初始化LinkedList
        LinkedList = new EmpLinkedList[size];
        //还未初始化 null , 分别为每个链表初始化
        for (int i = 0; i < size; i++) {
            LinkedList[i] = new EmpLinkedList();
        }
    }

    //添加雇员
    public void add(Emp emp){
        //根据员工id,找到员工应该在哪条链表下
        int LinkedListIndex = hashFun(emp.id);
        //将emp 添加到对应的链表中
//        LinkedList[LinkedListIndex].addEmp(emp);
		//按id大小进行排序
        LinkedList[LinkedListIndex].addEmpById(emp);
    }

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

    public void findEmpId(int id){
        //得到该员工id在hashtable上那个角标
        int index = hashFun(id);
        //进入该链表查找员工
        Emp emp = LinkedList[index].findEmp(id);
        if(emp == null){
            System.out.println("未找到");;//表示遍历完,还未找到该节点 返回null
        }else{
            System.out.println(emp.toString());
        }
    }
    public void delEmpId(int id){
        //得到该员工id在hashtable上那个角标
        int index = hashFun(id);
        //进入该链表查找员工
        LinkedList[index].delete(id);
    }

    public void updateEmp(Emp emp){
        int index = hashFun(emp.id);
        //进入该链表查找员工
        LinkedList[index].update(emp);
    }
    //编写一个散列函数,使用取模法
    public int hashFun(int id){
        return id % size;
    }
}

//创建EmpLinkedList 表示链表
class EmpLinkedList{
    //头指针,执行第一个Emp,因那次我们这个链表的head直接指向第一个Emp
    public Emp head; //默认为null

    //添加员工到链表
    public void addEmp(Emp emp){
        if(head == null){
            head = emp;
            return;
        }
        //不能添加已有id员工
        Emp findEmp = findEmp(emp.id);
        if(findEmp != null){
            System.out.println("已存在id= "+findEmp.toString()+"的员工,不能添加重复id员工"+emp.toString());
            return;
        }
        //不是第一个员工,定义一个辅助指针
        Emp curTemp = head;
        while(curTemp.next != null){
            curTemp = curTemp.next;//后移
        }
        //退出时将emp加入链表
        curTemp.next = emp;

    }

    //按id从小到大 添加员工到链表
    public void addEmpById(Emp emp){
        if(head == null){
            head = emp;
            return;
        }
        //不能添加已有id员工
        Emp findEmp = findEmp(emp.id);
        if(findEmp != null){
            System.out.println("已存在id= "+findEmp.toString()+"的员工,不能添加重复id员工"+emp.toString());
            return;
        }
        //如果新加结点比头节点小
        if(head.id > emp.id){
            Emp temp = null;
            emp.next = head;
            temp = head;
            head = emp;
            return;
        }
        
        //定义一个辅助指针
        Emp curTemp = head;
        while( true){
            //遍历到末尾还未找到,添加
            if(curTemp.next == null &&curTemp.id < emp.id){
                curTemp.next = emp;
                break;
            }
           
            if(curTemp.next.id > emp.id){
                emp.next = curTemp.next;
                curTemp.next = emp;
                break;
            }
            curTemp = curTemp.next;

        }

    }

    //遍历链表
    public void list(int index){
        if(head == null){
            System.out.println("第"+(index+1)+"链表为空。");
            return;
        }

        Emp curTemp = head; //辅助指针
        System.out.println("第"+(index+1)+"链表信息如下:");
        while(curTemp != null){
            System.out.println(curTemp.toString());
            curTemp = curTemp.next;
        }
    }

    //从链表删除员工
    //链表删除需要找到删除结点的前一个节点,因此分为第一个节点和第2-n结点判断
    public void delete( int id){
        if(head == null){
            System.out.println("当前链表为空。");
            return;
        }
        //头节点直接删除
        if(head.id == id){
            head = head.next;
            System.out.println("Id为" + id + "的员工已经被删除~~");
            return;
        }
        Emp curTemp = head; //辅助指针
        while (true){
            if(curTemp.next == null ){
                break;
            }

            if( curTemp.next.id == id){
                curTemp.next = curTemp.next.next;
                System.out.println("Id为" + id + "的员工已经被删除~~");
                return;//删除之后直接退出,否则还会向下执行
            }

            curTemp =curTemp.next;

        }
    }

    //修改员工信息
    public void update( Emp emp){
        if(head == null){
            System.out.println("当前链表为空。");
            return;
        }
        Emp findEmp = findEmp(emp.id);
        if(findEmp == null){
            System.out.println("未找到id = "+emp.id+"员工信息");
            return;
        }
        Emp curTemp = head; //辅助指针
        while (true){
          
            if(curTemp == null ){
                break;
            }
            if( curTemp.id == emp.id){
                curTemp.name = emp.name;
                return;//跟新之后直接退出,否则还会向下执行
            }
            curTemp =curTemp.next;


        }
    }

    //查找员工
    public Emp findEmp( int id){
        if(head == null){
            System.out.println("当前链表为空。");
            return null;
        }
        Emp curTemp = head; //辅助指针
       

        while (curTemp != null){
            if( curTemp.id == id){
                //System.out.println(curTemp.toString());
                return curTemp;
            }
            curTemp =curTemp.next;
        }

        return null;
    }
    //根据id查询雇员
    //如果查找到,就返回Emp,没有找到,就返回null
    public Emp findEmpId(int id){
        //判断链表是否为空
        if(head == null){
            System.out.println("链表为空.");
            return null;
        }
        //辅助指针
        Emp curEmp = head;
        while(true){
            if(curEmp.id == id){	//找到
                break;	//此时curEmp就指向要查找的雇员
            }
            //退出
            if(curEmp.next == null){	//未找到该雇员
                curEmp = null;
                break;
            }
            curEmp = curEmp.next;	//后移
        }
        return curEmp;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值