2、idea 啟動專案JDK、JRE報錯:Class JavaLaunchHelper ...One of the two will be used. Which one is undefined.
一、雜湊表概述
散列表(Hash table, 也叫雜湊表),是根據關鍵碼 - 值(Key - value)而直接進行訪問的資料結構。 也就是說, 它通過把關鍵碼 - 值對映到表中一個位置來訪問記錄, 以加快查詢的速度。這個對映的函式叫做雜湊函式,存放記錄的陣列叫做散列表。
其實把雜湊表看做是字典來理解雜湊表就很容易明白了,我們通過關鍵碼即可快速定位關鍵值。顯而易見雜湊表有一個很大的優點就是查詢資料的速度快。
如下所示就是一個典型的雜湊表:
雜湊表是陣列和連結串列的結合體,上圖的雜湊表左邊是一個數組,右邊是連結串列,即陣列中的每個元素都是一個連結串列。一個新的結點具體新增到哪個連結串列中是由對映關係來決定的。同樣,我們如果想要查詢某個結點,只需要通對結點的關鍵碼進行對映關係運算,計算出在陣列的第幾個元素(即哪個連結串列),然後遍歷連結串列比對關鍵碼即可得出結果。
從典型的雜湊表結構中,我們可以得出這樣的結論:本質上雜湊表是一個元素為連結串列的陣列。
二、雜湊表的程式碼實現
下面以 google 的一個上機題為例來做程式碼實現。
【案例描述】
有一個公司,當有新的員工來報道時,要求將該員工的資訊加入(id、名字、…),當輸入該員工的 id 時,要求查詢到該員工的所有資訊。要求不使用資料庫,速度越快越好。
【思路分析】
要求不使用資料庫,而且是通過員工 id 來查詢員工的所有資訊,很明顯可以使用雜湊表來解決。
在本例中,員工的 id 是關鍵碼,我們可以對員工的 id 取餘來作為到連結串列的對映。假設陣列的長度是 5,比如 id 取餘 5 等於 0 的員工都存放到第一個連結串列中(如 5、15…)、再比如 id 取餘5 等於 2 的都存放到第 3 個連結串列中(如 2、7、12…)。其實現程式碼如下:
// 模擬獲取雜湊值,該值決定結點要存放到陣列的第幾個連結串列
public int hashFun(int id){
return id % size;
}
本案例中要實現一個雜湊表,需要三個類:一個類作為結點類存放僱員的個人資訊、一個類作為連結串列類,用於記錄相同餘數的僱員結點、一個類作為雜湊表類。通過雜湊表類可以實現對任意結點(也即員工)進行增刪改查,而增刪改查的具體操作也就是對結點的具體操作,則是要通過連結串列類來實現。
【程式碼實現】
本案例完整的雜湊表程式碼實現如下:
public class No1_HashTable {
public static void main (String[] args) {
Scanner input = new Scanner(System.in);
MyHashTable hashTable = new MyHashTable(6); // 建立雜湊表
while (true){
System.out.println();
System.out.println("(add)新增僱員");
System.out.println("(ser)查詢僱員");
System.out.println("(lis)顯示僱員");
System.out.println("(del)刪除僱員");
System.out.println("(ext)退出");
System.out.print("請輸入選項:");
String choice = input.nextLine();
switch (choice){
case "add": // 新增
System.out.print("請輸入ID:");
int id = Integer.parseInt(input.nextLine());
System.out.print("請輸入姓名:");
String name = input.nextLine();
hashTable.addEmp(new Employee(id, name));
break;
case "ser": // 查詢
System.out.print("請輸入要查詢ID:");
hashTable.findById(Integer.parseInt(input.nextLine()));
break;
case "lis": // 顯示
hashTable.list();
break;
case "del": // 刪除
System.out.print("請輸入要刪除ID:");
hashTable.deleteById(Integer.parseInt(input.nextLine()));
break;
case "ext": // 退出
break;
default:
System.out.println("輸入有誤!");
}
}
}
}
/**
* 定義一個雜湊表
*/
class MyHashTable{
private int size;
private EmployeeLinkedList[] empList;
public MyHashTable(int maxSize){
size = maxSize;
empList = new EmployeeLinkedList[maxSize];
for (int i=0; i<maxSize; i++){ // 陣列中存放的不是基本型別時,必須初始化陣列
empList[i] = new EmployeeLinkedList();
}
}
// 新增一個結點到雜湊表中
public void addEmp(Employee emp){
int index = hashFun(emp.id);
empList[index].addEmp(emp);
}
// 顯示所有結點
public void list(){
for (int i=0; i<size; i++){
System.out.printf("第 %d 個連結串列為:",i);
empList[i].list();
System.out.println();
}
}
// 根據 id 查詢某個結點
public void findById(int id){
int index = hashFun(id);
empList[index].findById(id);
}
// 根據 id 刪除某個結點
public void deleteById(int id){
int index = hashFun(id);
empList[index].deleteById(id);
}
// 模擬獲取雜湊值,該值決定結點要存放到陣列的第幾個連結串列
public int hashFun(int id){
return id % size;
}
}
/**
* 定義一個僱員連結串列
*/
class EmployeeLinkedList{
private Employee head = null; // 這裡的頭結點是有意義的,是一個真實的僱員
// 新增結點
public void addEmp(Employee employee){
if (head == null){ // 判斷連結串列是否為空
head = employee;
return;
}
Employee cur = head; // 輔助指標
while(cur.next != null){ // 找到最後一個結點
cur = cur.next;
}
cur.next = employee;
employee.next = null;
}
// 通過 id 查詢結點
public void findById(int id){
if (head == null){ // 判斷連結串列是否為空
System.out.printf("連結串列為空, ID 為 %d 的結點不存在!", id);
return;
}
Employee cur = head; // 第一個結點不能動,要用輔助指標
while (cur != null){
if (cur.id == id){
System.out.printf("找到了!該結點資訊為:ID = %d,NAME = %s", id, head.name);
return;
}
cur = cur.next;
}
System.out.println("該結點不存在!");
}
// 通過 id 刪除結點
public void deleteById(int id){
if (head == null){
System.out.println("連結串列為空,無法刪除!");
return;
}
// 因為 head 結點也是一個有效結點,他沒有前驅,所以要單獨判斷
if (head.id == id){ // 如果第一個結點是目標結點
head = head.next;
return;
}
// head 之後的節點都是有前驅結點的結點
Employee cur = head; // 輔助指標
while (true){
if (cur.next == null){ // 如果判斷到了最後一個結點還是沒有找到
System.out.println("該結點不存在!");
return;
}
if (cur.next.id == id){
cur.next = cur.next.next;
return;
}
cur = cur.next;
}
}
// 顯示所有結點
public void list(){
if (head == null){
System.out.print("連結串列為空!");
return;
}
Employee cur = head; // 輔助指標
while (cur != null){
System.out.printf(" =>ID:%d,NAME:%s", cur.id, cur.name);
cur = cur.next;
}
}
}
/**
* 定義一個僱員結點
*/
class Employee{
public int id;
public String name;
public Employee next;
public Employee(int id, String name){
this.id = id;
this.name = name;
}
}