数据结构--数组+链表实现哈希表

哈希表

哈希表是一种数据结构

不同于往常的数据结构的是,往常的数据结构我们存储的数据比较单一,存储的标识比较简单,不能像数据库中的一条信息一样包含多个属性,为此,哈希表就可以简单实现这些要求

基本介绍

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

哈希表的结构

在这里插入图片描述

数据不一定跟我们下面的例子有关,这里我是再网上找了一张图来表示

那这个就像是一个数组+链表组成的,当然还有例如数组+二叉树的这种数据结构的哈希表。

怎么手写哈希表呢?怎么实现呢?

题目

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

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

思路

我们采用的是数组+链表的方式来实现

那么就需要有这个模型结构,用老师的图

在这里插入图片描述

蓝色的框框就是哈希表

那么雇员emp有些什么呢?有的就是我们的属性字段

class Emp{
    
    
    id;
    name;
    address;
}

那我们的链表有些什么呢?

class EmpLinkedList{
    
    
    Emp head = null;//头指针指向当前链表的第一个雇员
}

那我们的哈希表有什么

class HashTab{
    
    
    EmpLinkedList[] empLinkedListArr;//是一个数组,管理我们的许多个链表
}
//我们的增删改查也会在hashTab中进行操作
//其中散列函数也在这个hashTab中,管理那个id对应哪个链表

代码

雇员类

//雇员类
class Emp{
    
    
	public int id;
	public String name;
	public Emp next;//next  默认为空
	public Emp(int id, String name) {
    
    
		super();
		this.id = id;
		this.name = name;
	}
}

链表怎么写

//创建EmpLinkedList,表示链表
class EmpLinkedList{
    
    
	//头指针,指向我们第一个有效的雇员,head是直接指向有效数据的
	private Emp head;//默认为空
	
	//添加雇员列表
	//1.假设我们添加雇员时,id是自增长的,即id分配从小到大
	//2.因此我们将该雇员直接加入到本链表的最后一个即可
	public void add(Emp emp){
    
    
		//如果是添加第一个雇员
		if(head == null){
    
    
			head = emp;
			return;
		}
		//如果不是第一个雇员,则使用辅助指针帮助我们定位到链表最后
		Emp curEmp = head;
		while(true){
    
    
			if(curEmp.next == null){
    
    
				//说明到链表最后了
				break;
			}
			curEmp = curEmp.next;
		}
		//退出时直接将emp加入链表
		curEmp.next = emp;
	}
	
	//遍历链表雇员信息
	public void show(int no){
    
    
		if(head == null){
    
    
			System.out.println("第"+(no+1)+"链表为空");
			return;
		}
		System.out.print("第"+(no+1)+"链表的信息为");
		Emp curEmp = head;//辅助指针,帮助我们循环遍历
		while(true){
    
    
			System.out.println("id="+curEmp.id+curEmp.name);
			if(curEmp.next == null){
    
    
				//已经是最后节点
				break;
			}
			curEmp = curEmp.next;
		}	
	}
	//根据id查找雇员
	public Emp findEmpById(int id){
    
    
		//判断链表是否为空
		if(head ==null){
    
    
			System.out.println("链表为空");
			return null;
		}
		//辅助指针
		Emp curEmp = head;
		while(true){
    
    
			if(curEmp.id == id){
    
    
				//查找到了
				break;
			}
			if(curEmp.next == null){
    
    
				//链表没有这个id的雇员
				curEmp = null;
				break;//一定要写,不然找不到的话就会抛出空指针错误
			}
			curEmp = curEmp.next;//没有找到后移
		}
		return curEmp;
	}
	//根据id删除链表
		//找到要删除链表的前一个
		public Emp deleteEmp(int no,int id) {
    
    
			if(head==null) {
    
    
				return null;
			}
			Emp curEmp=head;//辅助指针
			if(head.id==id) {
    
    
				head=null;
				System.out.printf("第%d个链表中的id=%d的节点将被删除\n",no,id);
				return curEmp;
			}
			while(curEmp.next!=null) {
    
    
				if(curEmp.next.id==id) {
    
    
					System.out.printf("第%d个链表中的id=%d的节点将被删除\n",no,id);
					Emp res=curEmp.next;
					curEmp=curEmp.next.next;
					return res;
				}
			}
			System.out.printf("链表中没有id=%d的雇员\n",id);
			return null;
		}

再然后是我们的hashtab

//写出我们的hashtab,管理我们的多条链表
class HashTab{
    
    
	private EmpLinkedList[] empLinkedListArray;//链表数组
	public int size;//数组大小,数组中有多少条链表
	//构造器
	/**
	 * 
	 * @param size	数组大小,也就是有几条链表
	 */
	public HashTab(int size) {
    
    
		//初始化我们的链表
		this.size = size;
		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);
		
	}
	//遍历我们所有的链表,遍历hashtab哈希表,就是遍历数组+链表
	public void list(){
    
    
		for (int i = 0; i < size; i++) {
    
    
			empLinkedListArray[i].show(i);
		}
	}
	public void findEmpById(int id){
    
    
		//使用散列函数确定在哪条链表中查找
		int empLinkedListNo = hashFun(id);
		Emp emp = empLinkedListArray[empLinkedListNo].findEmpById(id);
		if(emp != null){
    
    
			//找到了
			System.out.println("在第"+(empLinkedListNo+1)+"条链表中找到了雇员id = "+id);
		}
		if(emp == null){
    
    
			System.out.println("在哈希表中,你没有找到该雇员");
		}
	}

	public void deleteEmp(int id) {
    
    
		//使用散列函数确定删除哪条链表
		int empLinkedListNo=hashFun(id);
		Emp emp=empLinkedListArray[empLinkedListNo].deleteEmp(empLinkedListNo+1,id);
		if(emp!=null) {
    
    //找到
//			System.out.printf("在第%d条链表中找到该雇员 id=%d\n",empLinkedListNo+1,id);
		}
		else {
    
    
			System.out.println("在哈希表中没有找到该雇员~");
		}
	}

	
	
	//编写散列函数,使用简单的取模法
	public int hashFun(int id){
    
    
		return id % size;
	}
	
}

完整版代码

package hashTab数据结构;
//哈希表的实现   数组+链表
//@author  王
//2021年1月23日22:34:46
import java.util.Scanner;

public class HashTabDemo {
    
    

	public static void main(String[] args) {
    
    
		// TODO Auto-generated method stub
		//1.创建hashtab哈希表
		HashTab hashTab = new HashTab(7);
		
		//写一个简单的菜单
		String key = "";
		Scanner scanner = new Scanner(System.in);
		while(true){
    
    
			System.out.println("add:添加雇员");		
			System.out.println("show:显示雇员");
			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);
					hashTab.add(emp);
					break;
				}	
				case "show":{
    
    
					hashTab.list();
					break;
				}
				case "find":{
    
    
					System.out.println("请输入你要查找的id");
					int id = scanner.nextInt();
					hashTab.findEmpById(id);
					break;
				}
				case "delete":{
    
    
					System.out.println("请输入你要删除的id");
					int id = scanner.nextInt();
					hashTab.deleteEmp(id);
					break;
				}
				case "exit":{
    
    
					scanner.close();
					System.exit(0);
				}
				default:
					break;
			}
		}
	}

}
//写出我们的hashtab,管理我们的多条链表
class HashTab{
    
    
	private EmpLinkedList[] empLinkedListArray;//链表数组
	public int size;//数组大小,数组中有多少条链表
	//构造器
	/**
	 * 
	 * @param size	数组大小,也就是有几条链表
	 */
	public HashTab(int size) {
    
    
		//初始化我们的链表
		this.size = size;
		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);
		
	}
	//遍历我们所有的链表,遍历hashtab哈希表,就是遍历数组+链表
	public void list(){
    
    
		for (int i = 0; i < size; i++) {
    
    
			empLinkedListArray[i].show(i);
		}
	}
	public void findEmpById(int id){
    
    
		//使用散列函数确定在哪条链表中查找
		int empLinkedListNo = hashFun(id);
		Emp emp = empLinkedListArray[empLinkedListNo].findEmpById(id);
		if(emp != null){
    
    
			//找到了
			System.out.println("在第"+(empLinkedListNo+1)+"条链表中找到了雇员id = "+id);
		}
		if(emp == null){
    
    
			System.out.println("在哈希表中,你没有找到该雇员");
		}
	}

	public void deleteEmp(int id) {
    
    
		//使用散列函数确定删除哪条链表
		int empLinkedListNo=hashFun(id);
		Emp emp=empLinkedListArray[empLinkedListNo].deleteEmp(empLinkedListNo+1,id);
		if(emp!=null) {
    
    //找到
//			System.out.printf("在第%d条链表中找到该雇员 id=%d\n",empLinkedListNo+1,id);
		}
		else {
    
    
			System.out.println("在哈希表中没有找到该雇员~");
		}
	}

	
	
	//编写散列函数,使用简单的取模法
	public int hashFun(int id){
    
    
		return id % size;
	}
	
}





//雇员类
class Emp{
    
    
	public int id;
	public String name;
	public Emp next;//next  默认为空
	public Emp(int id, String name) {
    
    
		super();
		this.id = id;
		this.name = name;
	}
}

//创建EmpLinkedList,表示链表
class EmpLinkedList{
    
    
	//头指针,指向我们第一个有效的雇员,head是直接指向有效数据的
	private Emp head;//默认为空
	
	//添加雇员列表
	//1.假设我们添加雇员时,id是自增长的,即id分配从小到大
	//2.因此我们将该雇员直接加入到本链表的最后一个即可
	public void add(Emp emp){
    
    
		//如果是添加第一个雇员
		if(head == null){
    
    
			head = emp;
			return;
		}
		//如果不是第一个雇员,则使用辅助指针帮助我们定位到链表最后
		Emp curEmp = head;
		while(true){
    
    
			if(curEmp.next == null){
    
    
				//说明到链表最后了
				break;
			}
			curEmp = curEmp.next;
		}
		//退出时直接将emp加入链表
		curEmp.next = emp;
	}
	
	//遍历链表雇员信息
	public void show(int no){
    
    
		if(head == null){
    
    
			System.out.println("第"+(no+1)+"链表为空");
			return;
		}
		System.out.print("第"+(no+1)+"链表的信息为");
		Emp curEmp = head;//辅助指针,帮助我们循环遍历
		while(true){
    
    
			System.out.println("id="+curEmp.id+curEmp.name);
			if(curEmp.next == null){
    
    
				//已经是最后节点
				break;
			}
			curEmp = curEmp.next;
		}	
	}
	//根据id查找雇员
	public Emp findEmpById(int id){
    
    
		//判断链表是否为空
		if(head ==null){
    
    
			System.out.println("链表为空");
			return null;
		}
		//辅助指针
		Emp curEmp = head;
		while(true){
    
    
			if(curEmp.id == id){
    
    
				//查找到了
				break;
			}
			if(curEmp.next == null){
    
    
				//链表没有这个id的雇员
				curEmp = null;
				break;//一定要写,不然找不到的话就会抛出空指针错误
			}
			curEmp = curEmp.next;//没有找到后移
		}
		return curEmp;
	}
	//根据id删除链表
		//找到要删除链表的前一个
		public Emp deleteEmp(int no,int id) {
    
    
			if(head==null) {
    
    
				return null;
			}
			Emp curEmp=head;//辅助指针
			if(head.id==id) {
    
    
				head=null;
				System.out.printf("第%d个链表中的id=%d的节点将被删除\n",no,id);
				return curEmp;
			}
			while(curEmp.next!=null) {
    
    
				if(curEmp.next.id==id) {
    
    
					System.out.printf("第%d个链表中的id=%d的节点将被删除\n",no,id);
					Emp res=curEmp.next;
					curEmp=curEmp.next.next;
					return res;
				}
			}
			System.out.printf("链表中没有id=%d的雇员\n",id);
			return null;
		}
}

猜你喜欢

转载自blog.csdn.net/qq_22155255/article/details/113063712