【HashTab初学】哈希表

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

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

1、散列表

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

左侧是一条长为16的数组,每条数组又对应一条链表,这里的散列函数就是 X对16取余,余几,就把该数据放到对应的链表中。

这里有一个通俗的理解:0-15像我们英文字典的a-z,496、896等这些数据相当于a字母对应的单词。

2、用哈希表实现

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

要求: 不使用数据库,速度越快越好=>哈希表(散列) 添加时,保证按照id从低到高插入

(a)使用链表来实现哈希表, 该链表不带表头 [即: 链表的第一个结点就存放雇员信息]

(b)思路分析并画出示意图

(c)代码实现[增删改查(显示所有员工,按id查询)]

(1)创建员工类

class Emp{
	public int id;              //id
	public String name;         //姓名
	public Emp next;            //指向下一个员工,默认为空
	
	//构造方法
	public Emp(int id, String name) {
		super();
		this.id = id;
		this.name = name;
	}
}

(2)创建EmpLinkedList类,表示链表

class EmpLinkedList{
	//头指针,指向第一个Emp,因此我们这个链表的head,是直接指向第一个Emp
	private Emp head;        //默认为空
	
	//添加员工到链表
	//说明
	//1、假定,当添加雇员时,id是自增长的,即id的分配总是从小到大
	//因此我们将员工直接加入到本链表最后即可
	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的尾部
		
		/*
		while(curEmp.next!=null){
			curEmp = curEmp.next;   
		}
		*/
		
		curEmp.next = emp;	
	}
	
	
	//2、遍历链表的员工信息
	public void list(int no){
		if(head == null){    //说明链表为空
			System.out.println("第"+(no+1)+"链表为空......");
			return ;
		}
		System.out.print("第"+(no+1)+"链表的信息为");
		Emp curEmp = head;   //辅助指针
		
		while(curEmp!=null){
			System.out.printf("=> id=%d,name=%s\t",curEmp.id,curEmp.name);
			curEmp = curEmp.next;     //移动指针
		}
		System.out.println();
	}
	
	//3、根据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;
			}
			
			if(curEmp.next == null){   //curEmp就是最后一个
				//查找到最后一个了,还没有找到,说明没有该员工
				curEmp = null;
				break;
			}
			
			curEmp = curEmp.next;      //移动指针
		}
		//出了上面这个while循环,curEmp要么为空,要么为所找到的员工
		return curEmp;
		
	}
	
	
	//4、根据id删除员工信息
	public void delEmpById(int id){
		//判断是否为空
		if(head == null){
			System.out.println("没有该员工......");
			return;
		}
			
		//如果是头结点
		if(head.id == id){
			System.out.printf("找到,已删除,其id=%d,name=%s\n",head.id,head.name);
			head = head.next;
			return;
		}
		
		//如果不是头结点
		//辅助指针
		Emp curEmp = head.next;      //当前指针,即要删除的节点指针
		Emp preEmp = head;           //删除节点的前一个指针
		
		while(true){
			//找到
			if(curEmp.id ==id){
				preEmp.next = curEmp.next;    //挂新链,即删除
				System.out.printf("找到,已删除,其id=%d,name=%s\n",curEmp.id,curEmp.name);
				break;                        //跳出
			}			
			//到尾部了
			if(curEmp==null){
				System.out.println("没有找到......");
			}
						
			//移动两个指针
			preEmp = preEmp.next;
			curEmp = curEmp.next;			
		}	
	}
	
	//5、根据id修改员工信息【思路是传入Emp,比较no,相同就覆盖name】
	public void updateEmpById(Emp emp){
		//判断为空
		if(head == null){
			System.out.println("链表为空......");
			return ;
		}
		
		//辅助变量
		Emp curEmp = head;
		while(true){
			//找到
			if(curEmp.id == emp.id){
				curEmp.name = emp.name;    //修改name
				System.out.println("修改成功.....");
				return;
			}
			
			if(curEmp.next == null){
				//最后位置
				System.out.println("没有找到该id");
				return;
			}
			curEmp = curEmp.next;    //移动指针	
		}		
	}	
}

这个类是对Emp对象进行操作,包括了增删改查遍历。

(3)创建HashTab,管理多条链表

class hashTab{
	//empLinkedListArray是一个数组,里面放的数据是EmpLinkedList类型
	private EmpLinkedList[] empLinkedListArray;
	private int size;      //表示链表数量
	
	//1、构造函数
	public hashTab(int size){
		this.size = size;
		//初始化empLinkedListArray
		empLinkedListArray = new EmpLinkedList[size];
		//?不要忘了分别初始化每一条链表
		for(int i=0;i<size;i++){
			empLinkedListArray[i] = new EmpLinkedList();
		}
	}
	
	//3、添加员工
	public void add(Emp emp){
		//根据员工的id,得到该员工应该添加到哪条链表
		int empLinkedListNo = hashFun(emp.id);
		
		//将emp加入到对应的链表中
		empLinkedListArray[empLinkedListNo].add(emp);	
	}
	
	//4、遍历所有的链表,遍历哈希表
	public void list(){
		for(int i=0;i<size;i++){
			empLinkedListArray[i].list(i);
		}
	}
	
	//5、根据输入的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,name=%s\n",empLinkedListNo+1,emp.id,emp.name);
		}else{
			//没有找到
			System.out.println("没有找到该员工......");
		}	
	}
	
	//6、根据输入的id删除员工信息
	public void delEmpById(int id){
		int empLinkedListNo = hashFun(id);
		empLinkedListArray[empLinkedListNo].delEmpById(id);
	}
	
	//7、根据输入的id修改员工信息
	public void updateEmpById(Emp emp){
		int empLinkedListNo = hashFun(emp.id);
		empLinkedListArray[empLinkedListNo].updateEmpById(emp);
	}
	
	
	//2、散列函数,使用取模法
	public int hashFun(int id) {
		return id % size;
	}
}

这里的方法可以和EmpLinkedList类中的方法对应逐步写,方便调试。

(4)测试

	public static void main(String[] args) {
		//测试
		//创建哈希表,7条链表
		hashTab hashtab = new hashTab(7);
		
		//写一个简单菜单
		String key = "";
		Scanner scanner= new Scanner(System.in);
		while(true){
			System.out.println("a:添加员工");
			System.out.println("l:显示员工");
			System.out.println("f:查找员工");
			System.out.println("d:删除员工");
			System.out.println("u:修改员工");
			System.out.println("e:退出系统");

			key = scanner.next();
			char ch = key.charAt(0); // 字符串转字符
			switch (ch) {
			case 'a':
				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 'l':
				hashtab.list();
				break;
				
			case 'f':
				System.out.println("输入要查找的id:");
				id = scanner.nextInt();
				hashtab.findEmpById(id);
				break;
				
			case 'd':
				System.out.println("输入要删除的id:");
				id = scanner.nextInt();
				hashtab.delEmpById(id);
				break;
			
			case 'u':
				System.out.println("输入要修改的id:");
				id = scanner.nextInt();
				System.out.println("请输入要修改输入名字:");
				name = scanner.next();
				emp = new Emp(id,name);
				hashtab.updateEmpById(emp);
				break;
					
			case 'e':
				scanner.close();
				System.exit(0);
				
			default:
				break;
				
			}
		}
	}
}

哈希表还是很难理解的。

参考:https://blog.csdn.net/sinat_33921105/article/details/103344078?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160267365819724813243269%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=160267365819724813243269&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_v2~rank_v28-2-103344078.pc_first_rank_v2_rank_v28&utm_term=%E5%93%88%E5%B8%8C%E8%A1%A8&spm=1018.2118.3001.4187

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值