散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
数组是链表数组,数组元素是链表。
google公司的一个上机题:
有一个公司,当有新的员工来报道时,要求将该员工的信息加入(id,性别,年龄,名字,住址..),当输入该员工的id时,要求查找到该员工的所有信息.
要求:
1.不使用数据库,速度越快越好=>哈希表(散列)
2.添加时,保证按照id从低到高插入 [课后思考:如果id不是从 低到高插入,但要求各条链表仍是从低到高,怎么解决?]
3.使用链表来实现哈希表,该链表不带表头[即:链表的第一个结点就存放雇员信息]
4.思路分析并画出示意图
代码实现:
import java.util.Scanner; public class HashTabDemo { public static void main(String[] args) { //创建哈希表 HashTab hashTab=new HashTab(7); //写一个简单的菜单 String key=""; Scanner scanner=new Scanner(System.in); while (true){ System.out.println("add:添加雇员"); System.out.println("list:显示雇员"); System.out.println("find:查找雇员"); System.out.println("delete:删除雇员"); 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 "list": hashTab.list(); break; case "find": System.out.println("请输入要查找的id"); id=scanner.nextInt(); hashTab.findEmpById(id); break; case "delete": System.out.println("请输入要删除的id"); id=scanner.nextInt(); hashTab.delete(id); break; case "exit": scanner.close(); System.exit(0); default: break; } } } } //创建HashTab class HashTab{ private EmpLinkedList[] empLinkedLists; private int maxSize;//表示有多少条链表 public HashTab(int maxSize) { empLinkedLists = new EmpLinkedList[maxSize]; this.maxSize = maxSize; for (int i = 0; i < maxSize; i++) { empLinkedLists[i]=new EmpLinkedList(); } } //添加雇员 public void add(Emp emp){ //根据员工的id,得到该员工应当添加到哪条链表 int empLinkedListNo = hashFun(emp.getId()); //将emp添加到对应的链表中 empLinkedLists[empLinkedListNo].add(emp); } //遍历链表 public void list(){ for (int i = 0; i < maxSize; i++) { empLinkedLists[i].list(i); } } //根据输入的id,查找雇员 public void findEmpById(int id){ int empLinkedListNo = hashFun(id); Emp emp = empLinkedLists[empLinkedListNo].findEmpById(id); if (emp!=null){ System.out.printf("在第%d条链表中找到雇员id=%d,name=%s\n",(empLinkedListNo+1),id,emp.getName()); }else { System.out.println("在哈希表中,没有找到该雇员~"); } } //根据输入的id,删除雇员 public void delete(int id){ int empLinkedListNo = hashFun(id); empLinkedLists[empLinkedListNo].delete(id); } //编写散列函数 public int hashFun(int id){ return id % maxSize; } } //创建Emp 表示一个雇员 class Emp{ private int id; private String name; private Emp next; public Emp(int id, String name) { this.id = id; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Emp getNext() { return next; } public void setNext(Emp next) { this.next = next; } } //创建EmpLinkedList 表示链表 class EmpLinkedList{ //头指针,执行第一个Emp,因此我们这个链表的head是直接指向第一个Emp private Emp head; //添加雇员到链表 public void add(Emp emp){ //如果是添加第一个雇员 if (head==null){ head=emp; return; } //如果不是第一个雇员,则使用一个辅助的指针,帮助定位到最后 Emp curEmp=head; while (true){ if (curEmp.getNext()==null){//说明到链表最后 break; } curEmp=curEmp.getNext();//后移 } //退出时直接将emp加入链表 curEmp.setNext(emp); } //遍历链表的雇员信息 public void list(int no){ //判断链表是否为空 if (head==null){ System.out.println("第"+(no+1)+"条链表为空!"); return; } System.out.print("第"+(no+1)+"链表的信息为"); Emp curEmp=head; while (true){ System.out.printf("=>id=%d name=%s\t",curEmp.getId(),curEmp.getName()); if (curEmp.getNext()==null){ break; } curEmp=curEmp.getNext(); } System.out.println(); } //根据id查找雇员 //如果查找到,就返回Emp,如果没有找到,就返回null public Emp findEmpById(int id){ //判断链表是否为空 if (head==null){ return null; } Emp curEmp=head; while (true){ if (curEmp.getId()==id){//这时curEmp就指向要查找的雇员 break; } if (curEmp.getNext()==null){//说明遍历当前链表没有找到该雇员 curEmp=null; break; } curEmp=curEmp.getNext(); } return curEmp; } //根据id 删除雇员 public void delete(int id){ //判断链表是否为空 if (head==null){ System.out.println("链表为空!"); return; } if (head.getId()==id){ head=head.getNext(); return; } Emp curEmp=head; boolean flag=false; while (true){ if (curEmp.getNext()==null){ break; } if (curEmp.getNext().getId()==id){ flag=true; break; } curEmp=curEmp.getNext(); } if (flag){ curEmp.setNext(curEmp.getNext().getNext()); }else { System.out.printf("要删除的%d 节点不存在\n",id); } } }
1.总结:哈希表(HashTable)是一个数组,数组中的元素是链表,而员工则相当于链表的节点,被存储在链表中,如图,HashTable数组的规定长度为7,也就是说明,数组中能存放7条链表,而每一个链表中可以存放若干的员工信息 这比较于以前一条链表来存储员工信息,效率提高了7倍,
2.我们在创建HashTable的时候,要先创建Emp员工节点类,来存储员工信息,然后要创建员工链表类,作为员工信息节点的链表,最后创建HashTab类,用来创建存放链表的数组,和完成相应的功能。
3.我们在创建哈希表时,要制定一种哈希函数,确定员工节点存入哈希表的规律(取模是其中较为简单的一种),这样将来我们能知道对应的员工存在哈希表的哪条链中,方便操作。