1. 程式人生 > >雜湊表(模板,參考用)

雜湊表(模板,參考用)

雜湊表又叫做散列表,關鍵值通過雜湊函式對映到陣列上,查詢時通過關鍵值直接訪問陣列

雜湊函式指的是關鍵值和儲存位置建立的對應關係,查詢時只要根據這個關係就可以找到目標位置

雜湊表裡,可能存在關鍵字不同但是雜湊地址相同的情況,會產生衝突,一般情況下,衝突是不可避免的,因為關鍵字集合往往比雜湊地址集合大很多

雜湊表的構造方法:

(1)直接定址法:即取關鍵字的指或者關鍵字的某個函式變換值,線性對映到儲存地址上

(2)取餘數法:我們將關鍵字對整數p取的餘數值直接作為儲存地址,整數p一般取小於等於雜湊表長度size的最大質數,如果關鍵字不是整數,比如是一個字串,我們先將其做個轉換,可以先將其做個轉換,然後再對p取餘

下面例舉一個hash變換函式:

int hash(string& value) {   //雜湊函式
		int code = 0;
		for (size_t i = 0; i < value.length(); ++i) {
			code = (code * 256 + value[i] + 128) % size;   //產生雜湊編碼
		}
		return code;   
}

我們知道有字元範圍是從-128到127,也就是一共有256種。為了減少衝突,

我們可以使用上述構造方法,為了防止出現負數,我們對value[i]加上128,為了防止資料溢位,我們再對其進行取餘操作

衝突的解決

開放地址法:如果發生衝突,那麼就使用某種策略尋找下一儲存地址,直到找到一個不衝突的地址或者找到關鍵字,否則一直按照這種策略繼續尋找。如果衝突次數達到了上限則終止程式,表示關鍵字不在雜湊表裡

1.線性探測法,如果當前的衝突地址為d,那麼接下來幾個探測地址為d+1,d+2,d+3等,

2.線性補償探測法: d+m, d+2*m, d+3*m(m和表長size互質;

3.隨機探測法

4.二次探測法:形成的探測地址為 d + 1平方 d - 1平方 d + 2平方 d - 2平方演算法

演算法的具體實現策略:

(1)用雜湊函式找到字串S的初始位置,初始化衝突次數

(2)從當前位置從後面進行查詢,找到第一個未發生衝突的位置K(當前位置上如果儲存的字串不是S則視為發生衝突) 查詢過程中記錄發生衝突的次數T,如果T大於等於表長,則結束演算法,表示查詢失敗

(3)如果K上的元素就是查詢的字串,則查詢成功,否則查詢失敗

	bool search(string& value, int& pos, int& times) {
		times = 0;   //查詢次數歸零
		pos = hash(value);  //構建雜湊值
		while (elem[pos] != "#" && elem[pos] != value) {  //如果沒找到,則繼續進行查詢
			times++;
			if (times < size) {
				pos = (pos + 1) % size;   //向下一個方向查詢
			}
			else {
				return false;   //需要擴容
			}
		}

		if (elem[pos] == value) {
			return true;
		}
		else {
			return false;
		}
	}

雜湊表的擴容操作:

程式執行時,如果當前元素已經存在於雜湊表中了,就直接返回一個值結束這次插入操作。

當衝突次數小於表長的一半時,我們就可以把字串插入到雜湊表中,如果大於一半,則需要進行重建雜湊表(擴容,防止發生堆聚現象)

雜湊表重建操作的演算法:

  1. 開闢一段和當前雜湊表等大的臨時空間
  2. 將原雜湊表中的關鍵字一一複製到臨時陣列中
  3. 申請一個原空間大小兩倍的新空間,釋放原空間
  4. 將新空間裡的儲存地址初始化
  5. 將關鍵字從臨時陣列複製到新的空間,釋放臨時空間
void recreate() {  //擴容操作
		string* temp_elem = new string[size];
		for (int i = 0; i < size; ++i) {
			temp_elem[i] = elem[i];
		}
		
		int copy_size = size;  //保留原有的大小
		size = size * 2;       //擴大兩倍
		
		delete[] elem;
		elem = new string[size]; //擴容
		for (int i = 0; i < size; ++i) {  //初始化
			elem[i] = "#";
		}
		//插入到表中
		for (int i = 0; i < copy_size; ++i) {
			if (temp_elem[i] != "#") {
				insert(temp_elem[i]);
			}
		}
		delete[] temp_elem;
	}
};

整體程式碼如下:

#include <iostream>
#include <string>
using std::cin;
using std::cout;
using std::endl;
using std::string;
class HashTable {
private:
    string *elem;
    int size;
public:
    HashTable() {
        size = 2000;
        elem = new string[size];
        for (int i = 0; i < size; i++) {
            elem[i] = "#";
        }
    }
    ~HashTable() {
        delete[] elem;
    }
    int hash(string &value) {
        int code = 0;
        for (size_t i = 0; i < value.length(); i++) {
            code = (code * 256 + value[i] + 128) % size;
        }
        return code;
    }
    bool search(string &value, int &pos, int &times) {
        pos = hash(value);
        times = 0;
        while (elem[pos] != "#" && elem[pos] != value) {
            times++;
            if (times < size) {
                pos = (pos + 1) % size;
            } else {
                return false;
            }
        }
        if (elem[pos] == value) {
            return true;
        } else {
            return false;
        }
    }
    int insert(string &value) {
        int pos, times;
        if (search(value, pos, times)) {
            return 2;
        } else if (times < size / 2) {
            elem[pos] = value;
            return 1;
        } else {
            recreate();
            insert(value);
            return 0;
        }
    }
    // 請在下面實現重建方法 recreate
    void recreate(){
        string* temp_elem;
        temp_elem = new string[size];
        for(int i = 0; i < size; ++i){
            temp_elem[i] = elem[i];
        }
            int copy_size = size;
            size = size*2;
            delete[] elem;
            elem = new string[size];
        for(int i = 0; i < size; ++i){
            elem[i] = "#";
        }
        for(int i = 0; i < copy_size;i++){
             if(temp_elem[i] != "#"){
                 insert(temp_elem[i]);
             }
         }
        delete[] temp_elem;
    }
    
};

int main() {
    HashTable hashtable;
    string buffer;
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> buffer;
        int ans = hashtable.insert(buffer);
        if (ans == 0) {
            cout << "recreate while insert!" << endl;
        } else if (ans == 1) {
            cout << "insert success!" << endl;
        } else if (ans == 2) {
            cout << "It already exists!" << endl;
        }
    }
    int temp_pos, temp_times;
    cin >> buffer;
    if (hashtable.search(buffer, temp_pos, temp_times)) {
        cout << "search success!" << endl;
    } else {
        cout << "search failed!" << endl;
    }
    return 0;
}