JAVA 數據結構與算法(四)—— 哈希算法

一、哈希算法

1、哈希表概述

(1)哈希表介紹

  • 哈希表(Hash table,也叫散列表),是根據關鍵碼值(Key value)而直接進行訪問的數據結構。也就是說,它通過把關鍵碼值映射到表中一個位置來訪問記錄,以加快查找的速度。這個映射函數叫做散列函數,存放記錄的數組叫做散列表。
  • 哈希表hashtable(key,value) 就是把Key通過一個固定的算法函數既所謂的哈希函數轉換成一個整型數字,然後就將該數字對數組長度進行取餘,取餘結果就當作數組的下標,將value存儲在以該數字爲下標的數組空間裏。而當使用哈希表進行查詢的時候,就是再次使用哈希函數將key轉換爲對應的數組下標,並定位到該空間獲取value,如此一來,就可以充分利用到數組的定位性能進行數據定位。

(2)哈希表結構
哈希表結構圖

圖中左邊很明顯是個數組,數組的每個成員包括一個指針,指向一個鏈表的頭,當然這個鏈表可能爲空,也可能元素很多。我們根據元素的一些特徵把元素分配到不同的鏈表中去,也是根據這些特徵,找到正確的鏈表,再從鏈表中找出這個元素。

2、哈希算法示例

(1)創建一個Student類

/*Student類,代表一個Student*/
public class Student {
    public int id;
    public  String name;
    /*指向下一個節點的引用,默認爲空*/
    public Student nextStudent;

    public Student(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

(2)創建SchoolLinkedList類,代表一條鏈表,鏈表中存放Student對象

/*SchoolLinkedList類,表示鏈表*/
public class SchoolLinkedList {
    /*頭指針,執行第一個Student,所以直接指向Student*/
    private Student headStudent;

    /*添加student到School:假定id時自增長,
        所以添加的時候會添加到鏈表的末尾*/
    public void addStudent(Student student){
        /*如果要添加的是第一個student*/
        if(headStudent == null){
            headStudent = student;
            return;
        }
        /*如果要添加的不是第一個Student,則定位到鏈表的末尾,
            將student添加到末尾位置*/
        Student tempStudent = headStudent;
        while (true){
            /*判斷鏈表是否到最後*/
            if(tempStudent.nextStudent == null){
                break;
            }
            /*沒有到鏈表的最後就繼續向後定位*/
            tempStudent = tempStudent.nextStudent;
        }
        /*將student添加到鏈表最後*/
        tempStudent.nextStudent = student;
    }
    /* 遍歷鏈表中的信息*/
    public void show(int num){
        if(headStudent == null ){
            System.out.println("第"+ num +"鏈表的信息爲空");
            return;
        }

        System.out.println("第"+ num +"鏈表中的信息爲:");
        Student student = headStudent;
        while(true){
            System.out.println("        id = "+ student.id +"  name = " + student.name);
            if(student.nextStudent == null){
                break;
            }
            student = student.nextStudent;
        }
    }

    /*查找student*/
    public Student search(int id){
        if(headStudent == null){
            return null;
        }

        Student tempStudent = headStudent;
        while(true){
            /*鏈表爲空直接返回null*/
            if(tempStudent.id == id){
                break;
            }
            /*沒有查找到就退出*/
            if(tempStudent.nextStudent == null){
                tempStudent = null;
                break;
            }
            tempStudent = tempStudent.nextStudent;
        }
        return tempStudent;
    }
}

(3)創建哈希表,用於管理多條鏈表

/*哈希表,用於管理多條鏈表*/
public class HashTable {
    private SchoolLinkedList[]  schoolLinkedListArray;
    /*size表示共有多少鏈表*/
    private int size;

    /*構造器*/
    public HashTable(int size) {
        this.size = size;
        /*初始化*/
        schoolLinkedListArray = new SchoolLinkedList[size];
        /*初始化每一個鏈表*/
        for (int i = 0; i < size; i++){
            schoolLinkedListArray[i] = new SchoolLinkedList();
        }
    }

    /*添加student*/
    public void add(Student student){
        /*根據student的id判斷該student應該添加到那個鏈表*/
        int schoolLinkedListNum = (student.id) % size;
        /*添加到對應鏈表*/
        schoolLinkedListArray[schoolLinkedListNum].addStudent(student);
    }

    /*遍歷所有的鏈表即哈希表*/
    public void showHashTable(){
        for (int i = 0; i < size; i++) {
            schoolLinkedListArray[i].show(i);
        }
    }

    /*查找student*/
    public void searchById(int id){
        int schoolLinkedListNum = id % size;
        Student student = schoolLinkedListArray[schoolLinkedListNum].search(id);
        if(student != null){
            System.out.println("在第" + schoolLinkedListNum + "條鏈表中找到id爲" + id + "的student");
        }else{
            System.out.println("沒有找到該student");
        }
    }
}

(4)測試類

public class HashTableTest {
    public static void main(String[] args) {
        /*創建哈希表*/
        HashTable hashTable = new HashTable(7);

        /**/
        int num ;
        Scanner scanner = new Scanner(System.in);
        while(true){
            System.out.println("\n請選擇相應的操作: 1:添加student  2:顯示student  3:查找student  4:退出系統");

            num = scanner.nextInt();
            switch (num){
                case 1:
                    /*輸入要添加的student信息*/
                    System.out.print("請輸入id:");
                    int id = scanner.nextInt();
                    System.out.print("請輸入名字:");
                    String name = scanner.next();

                    /*添加student*/
                    Student student = new Student(id, name);
                    hashTable.add(student);
                    break;
                case 2:
                    hashTable.showHashTable();
                    break;
                case 3:
                    System.out.println("請輸入要查找的student的id:");
                    id = scanner.nextInt();
                    hashTable.searchById(id);
                    break;
                case 4:
                    scanner.close();
                    System.out.println("系統退出");
                    System.exit(0);
                    break;
                default:
                    break;
            }
        }
    }
}

(5)測試結果

請選擇相應的操作: 1:添加student  2:顯示student  3:查找student  4:退出系統
1
請輸入id:0
請輸入名字:張三

請選擇相應的操作: 1:添加student  2:顯示student  3:查找student  4:退出系統
1
請輸入id:5
請輸入名字:李四

請選擇相應的操作: 1:添加student  2:顯示student  3:查找student  4:退出系統
1
請輸入id:12
請輸入名字:王五

請選擇相應的操作: 1:添加student  2:顯示student  3:查找student  4:退出系統
20鏈表中的信息爲:
        id = 0  name = 張三
第1鏈表的信息爲空
第2鏈表的信息爲空
第3鏈表的信息爲空
第4鏈表的信息爲空
第5鏈表中的信息爲:
        id = 5  name = 李四
        id = 12  name = 王五
第6鏈表的信息爲空

請選擇相應的操作: 1:添加student  2:顯示student  3:查找student  4:退出系統
3
請輸入要查找的student的id:
5
在第5條鏈表中找到id爲5的student

請選擇相應的操作: 1:添加student  2:顯示student  3:查找student  4:退出系統
3
請輸入要查找的student的id:
10
沒有找到該student


請選擇相應的操作: 1:添加student  2:顯示student  3:查找student  4:退出系統
4
系統退出

3、哈希算法的應用場景

(1)全加密
日常用戶密碼加密通常使用的都是 md5、sha等哈希函數,因爲不可逆,而且微小的區別加密之後的結果差距很大,所以安全性更好。

(2)唯一標識
比如 URL 字段或者圖片字段要求不能重複,這個時候就可以通過對相應字段值做 md5 處理,將數據統一爲 32 位長度從數據庫索引構建和查詢角度效果更好,此外,還可以對文件之類的二進制數據做 md5 處理,作爲唯一標識,這樣判定重複文件的時候更快捷。

(3)數據校驗
比如從網上下載的很多文件(尤其是P2P站點資源),都會包含一個 MD5 值,用於校驗下載數據的完整性,避免數據在中途被劫持篡改。

(4)散列函數
前面已經提到,PHP 中的 md5、sha1、hash 等函數都是基於哈希算法計算散列值

(5)負載均衡
對於同一個客戶端上的請求,尤其是已登錄用戶的請求,需要將其會話請求都路由到同一臺機器,以保證數據的一致性,這可以藉助哈希算法來實現,通過用戶 ID 尾號對總機器數取模(取多少位可以根據機器數定),將結果值作爲機器編號。

(6)分佈式緩存
分佈式緩存和其他機器或數據庫的分佈式不一樣,因爲每臺機器存放的緩存數據不一致,每當緩存機器擴容時,需要對緩存存放機器進行重新索引(或者部分重新索引),這裏應用到的也是哈希算法的思想。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章