哈希表
看一個實際需求,google公司的一個上機題:
有一個公司,當有新的員工來報道時,要求將該員工的信息加入(id,性別,年齡,住址…),當輸入該員工的id時,要求查找到該員工的 所有信息.
要求: 不使用數據庫,儘量節省內存,速度越快越好=>哈希表(散列)
- 散列表(Hashtable,也叫哈希表),是根據關鍵碼值(Keyvalue)而直接進行訪問的數據結構。也就是說,它通過把關鍵碼值映射到表中一個位置來訪問記錄,以加快查找的速度。這個映射函數叫做散列函數,存放記錄的數組叫做散列表。
-
上述圖例爲韓順平老師所制。
-
題目分析:題目要求其實就是實現對象的增刪改查,無論你是用queue還是鏈表都是可以實現的,但是此處要求就是越快越好,面試的情況下這種題目說最簡單的當然不是最優解,那麼哈希表(散列表)就能在鏈表的基礎上更加提升性能,減少了遍歷的時間,數據量越大分的鏈表越多那麼相對減少的時間是十分可觀的。
-
實現:一個數組中放多個鏈表,放入哪個由id的hash得到,此處用取模得到位置。
-
過程:員工對象 -> 鏈表 -> hash表 -> 實現
-
鏈表類
class LinkedListEmp{
private Emp head;//頭結點,每個鏈表需要頭結點來定位
//添加員工
public void add(Emp nemp){
if (head == null){//爲空直接添加
head = nemp;
return;
}
Emp temp = head;//一個臨時變量
while (true){
if (temp.next==null){//成立時說明遍歷到鏈表最後了
break;
}
temp = temp.next;//沒到最後就一直向後遍歷
}
temp.next = nemp;//新員工添加到最後
}
//展示所有的員工
public void show(int n){
if (head == null){
System.out.println("鏈表"+(n+1)+"爲空;");
return;
}
Emp temp = head;
System.out.print("鏈表"+(n+1)+":");
while (true){
System.out.printf("->> %d :%s\t" , temp.id , temp.name);
if (temp.next == null){
break;
}
temp=temp.next;
}
System.out.println();
}
//根據id找員工
public Emp findEmpByID(int id){
if (head == null){
System.out.println("此鏈表爲空!");
}
Emp temp = head;
while (true){
if (temp.id == id){//找到員工號就退出
break;
}
temp = temp.next;
if (temp.next==null){
return null;//沒找到返回null
}
}
return temp;//返回這個員工
}
public void deleteEmpById(int id){//刪除員工
if (head == null){
System.out.println("此鏈表爲空!");
}
Emp temp = head;
while (true){//此處與普通單鏈表有區別,如果頭結點就是要找的員工,那麼直接換頭結點
if (head.id == id){
head = temp.next;
System.out.println("刪除員工:"+id);
break;
}else if (temp.next.id == id){//如果找到將next域指向下下一個對象,那麼中間的對象就拋棄了,也就刪除了
temp.next = temp.next.next;
System.out.println("刪除員工:"+id);
break;
}
if (temp.next == null){
System.out.println("沒有找到此員工!!");
break;
}
temp = temp.next;
}
}
}
- 員工類
//員工類
class Emp{
int id;
String name ;
Emp next = null;
public Emp(int id, String name) {
this.id = id;
this.name = name;
}
}
- 哈希表
//哈希表
class HashEmpTable{
int size;
LinkedListEmp[] hashEmpTable;//定義一個以鏈表爲數據類型的數組
public HashEmpTable(int size) {//初始化數組和確認空間大小
this.size=size;
hashEmpTable = new LinkedListEmp[size];
for (int i = 0; i < size; i++) {
hashEmpTable[i] = new LinkedListEmp();
}
}
//添加
public void add(Emp emp){//直接利用方法取到這個id號的員工所在的鏈表,用那個鏈表調用。
int funId = hashFunId(emp.id);
hashEmpTable[funId].add(emp);
}
//展示
public void show(){
for (int i = 0; i < size; i++) {
hashEmpTable[i].show(i);
}
}
//刪除
public void deleteEmpById(int id){//同樣,找到鏈表再刪除對象
int funId = hashFunId(id);
hashEmpTable[funId].deleteEmpById(id);
}
//find員工,
public void findEmpById(int id){
int funId = hashFunId(id);
Emp emp = hashEmpTable[funId].findEmpByID(id);
if (emp != null){
System.out.printf("在第 %d 條鏈表找到員工-> %d,姓名是-> %s" , funId , emp.id , emp.name);
}
}
//利用取模實現哈希尋找鏈表號
public int hashFunId(int id){
int hashId = id % size;
return hashId;
}
}
//員工類
class Emp{
int id;
String name ;
Emp next = null;
public Emp(int id, String name) {
this.id = id;
this.name = name;
}
}
- 實現類
public class HashExercise {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
HashEmpTable hashEmpTable = new HashEmpTable(7);
String oper = "";
while (true){
System.out.println("add");
System.out.println("show");
System.out.println("find");
System.out.println("delete");
System.out.println("exit");
oper = sc.next();
switch (oper){
case "add":
System.out.println("請輸入員工id");
int id = sc.nextInt();
System.out.println("請輸入員工姓名");
String name = sc.next();
Emp emp = new Emp(id, name);
hashEmpTable.add(emp);
break;
case "show":
hashEmpTable.show();
break;
case "find":
System.out.println("請輸入需要查找的員工ID");
int findId = sc.nextInt();
hashEmpTable.findEmpById(findId);
break;
case "delete":
System.out.println("請輸入需要刪除的員工ID");
int deleteID = sc.nextInt();
hashEmpTable.deleteEmpById(deleteID);
break;
case "exit":
return;
}
}
}
}
將此例結合會建立對hash表的一個很好的入門思想。