iT邦幫忙

2021 iThome 鐵人賽

DAY 21
0
Software Development

程式菜鳥自學C++資料結構演算法系列 第 21

[Day21]程式菜鳥自學C++資料結構演算法 – 雜湊搜尋法實作

前言:昨天講解完了雜湊法的定義和,今天要來把它實際創建出來,這次用到的雜湊法要用之前學過的鏈結串列來實現,如果忘記相關技術的可以回去看看喔!

廢話不多說,開始實作!!!

先建立雜湊函式的整體架構函式。
https://ithelp.ithome.com.tw/upload/images/20211005/201401878WXScrj8dJ.png

接著建立鏈結串列的節點。
https://ithelp.ithome.com.tw/upload/images/20211005/20140187fh6jQJyEaL.png

第三步要撰寫雜湊表,查詢、插入、修改、刪除都寫在這裡面,所以這一整段比較複雜。
https://ithelp.ithome.com.tw/upload/images/20211005/20140187a1dDzH6vGi.png

首先是第一個查詢的部分。
https://ithelp.ithome.com.tw/upload/images/20211005/20140187HrsF9y1qc4.png

插入和修改比較類似,所以都寫在put裡面。
https://ithelp.ithome.com.tw/upload/images/20211005/20140187wKaWk2wsk8.png

最後一個就是刪除。
https://ithelp.ithome.com.tw/upload/images/20211005/20140187cQjfxzpmc5.png

整體的架構完成得差不多了,可以先執行看看結果跑不跑得出來。
https://ithelp.ithome.com.tw/upload/images/20211005/20140187BTpcL5VKm0.png

將五筆資料存入後,先用程式碼顯示第二三筆資料,然後把第四筆資料刪除後就無法顯示出來,所以剛剛撰寫的基本功能都可以正常運作。
https://ithelp.ithome.com.tw/upload/images/20211005/20140187GldyE3yHu8.png

接著來嘗試做做看學生雜湊表。

這樣的做法可以比較統一資料儲存的格式,先寫好輸出的模板也可以少寫東西在主程式碼中。
https://ithelp.ithome.com.tw/upload/images/20211005/20140187fY0d1277XR.png

接下來簡單測試一下!
https://ithelp.ithome.com.tw/upload/images/20211005/20140187vr6qZHVWdt.png
儲存三筆學生的資料,先顯示二號林同學的資料,將它修改成蔡同學程式也能執行。
https://ithelp.ithome.com.tw/upload/images/20211005/20140187j8gYsAjM80.png

本日小結:今天的實作內容先到這邊為止,雖然只有一個基本的概念,但是就有不少程式碼,當初在研究的時候也花了很多時間。其實在第一次接觸到雜湊的時後,我覺得是資料結構和演算法中比較令我頭痛的,很多複雜的名詞和概念讓我在學習時吃了不少苦頭,經過這兩篇文章的整理,確實有對雜湊更了解一些。在實作雜湊法的時候,想想要怎麼利用雜湊函式得到雜湊值,並尋找出我們要的value,以這樣的方式思考會必較好喔٩(✿∂‿∂✿)۶

template<typename K>
struct KeyHash {
	KeyHash(const unsigned long table_size) //建立雜湊函式的構造函數
		:table_size(table_size) {}
	unsigned long operator()(const K& key) const {
		return static_cast<unsigned long>(key) % table_size; //將key強制轉換成unsigned long型別
	}                                                        //並取除以table_size的餘數
private:
	unsigned long table_size; //unsigned long資料型別的一種,類似於int
};

template<typename K,typename V>
class HashNode { //建立鏈結串列的節點
public:
	K key; //宣告雜湊值
	V value; //宣告資料
	HashNode *next; //宣告下一個節點的指針
	HashNode(const K &key, const V &value) :
	key(key), value(value), next(nullptr) {
	}
};

template<typename K, typename V,typename F=KeyHash<K>> //傳遞雜湊函數的類型typename F=KeyHash<K>
class HashMap { //建立雜湊表
	int table_size;
	HashNode<K, V> * *table; //*table指向HashNode<K, V>這種類型的變量
	F hashFunc;
public:
	HashMap(F hashF, const int table_size = 10) : //建立HashMap的構造函式
		//宣告雜湊函數的類型和table_size的大小
		table_size(table_size), hashFunc(hashF) { //傳回各自的值
		table = new HashNode<K, V> * [table_size](); //創建動態數組
		for (int i = 0; i < table_size; i++)
			table[i] = 0; //初始化table
	}
	~HashMap() { //刪除每個鏈結串列table[i]中的所有節點
		for (int i = 0; i < table_size;i++) {
			HashNode<K, V>* entry = table[i];
			while (entry) {
				HashNode<K, V>* p = entry;
				entry = entry->next;
				delete p;
			}
			table[i] = 0;
		}
		delete[] table; //刪除table數組
	}
	bool get(const K& key, V& value) { //編寫查詢方法,給定一個key,查詢一個value
		unsigned long hashValue = hashFunc(key); //將hashFunc裡面的雜湊值傳入hashValue,可以想成雜湊表的地址
		HashNode<K, V>* entry = table[hashValue]; //資照雜湊表的下標table[hashValue],取代成第一個節點的指針*entry 

		while (entry) { //當entry不是空指針
			if (entry->key == key) { //如果key是我們要找的
				value = entry->value; //則將value傳回entry所指向的value
				return true;
			}
	    entry = entry->next; //如果沒有找到,則把entry往下一個移,繼續此步驟
		}
		return false;
	}
	void put(const K& key, const V& value) { //編寫插入或修改的 函式
		unsigned long hashValue = hashFunc(key);
		HashNode<K, V>* prev = nullptr; //將prev指向空指針
		HashNode<K, V>* entry = table[hashValue]; //將entry指向table[hashValue]
		while (entry && entry->key != key) { //如果entry所指向的key不是我們要找的值
			prev = entry; //在entry指向下一個節點前先讓prev = entry,也就是讓prev保持在entry的前一個節點
			entry = entry->next;
		}
		if (!entry) { //如果都沒有找到
			entry = new HashNode<K, V>(key, value); //則將entry做一個新節點的插入
			if (!prev) { //如果prev為空的,代表一開始是空的表
				table[hashValue] = entry; //把entry放到之雜湊表內
			}
			else {
				prev->next = entry;
			}
		}
		else {
			entry->value = value; //更新其他value
		}
	}
	void remove(const K&key) { //編寫刪除雜湊值的函式
		unsigned long hashValue = hashFunc(key);
		HashNode<K, V>* prev = 0;
		HashNode<K, V>* entry = table[hashValue];
		while (entry && entry->key != key) {
			prev = entry;
			entry = entry->next;
		}
		if (!entry) //當entry是空指針,則說明沒找到
			return;
		else {
			if (!prev) //如果prev是空指針,代表耀珊的節點是第一個節點
				table[hashValue] = entry->next; //把entry的下一個節點修改成table[hashValue]
			else
				prev->next = entry->next;
			delete entry;
		}
		
	}
};

#include <iostream>
#include <string>
using namespace std;
struct student { //定義學生的數據類型
	string name, phone;
	unsigned int age;
	student(string name = "no", string phone = "000", unsigned age = 0) :
		name(name), phone(phone), age(age) {}
};
ostream& operator<<(ostream& o, const student& stu) { //可以輸出學生類型的資料
	o << stu.name << "," << stu.phone << "," << stu.age << endl;
	return o;
 }

int main() {
	const int table_size = 10;
	HashMap<int, string, KeyHash<int>>hamp(KeyHash<int>(table_size), table_size); 
	//創建一個hamp為HashMap的構造對象,並存入HashMap的模板參數
	hamp.put(1, "Allen");
	hamp.put(2, "Hank");
	hamp.put(3, "Rudy");
	hamp.put(4, "Nick");
	hamp.put(5, "Edward");
	string value;
	hamp.get(2, value);
	cout << value << endl;
	bool res = hamp.get(3, value);
	if(res)
		cout << value << endl;
	hamp.remove(4);
	res = hamp.get(4, value);
	if (res)
		cout << value << endl;


	HashMap<int, student, KeyHash<int>>hamp2(KeyHash<int>(table_size), table_size);
	hamp2.put(1, student("Chen", "0989267", 20));
	hamp2.put(2, student("Lin", "0929403", 22));
	hamp2.put(3, student("Wang", "0962771", 18));
	student stu;
	hamp2.get(2, stu);
	cout << stu;
	hamp2.put(2, student("Tsai", "0917364", 24));
	hamp2.get(2, stu);
	cout << stu << endl;
}

上一篇
[Day20]程式菜鳥自學C++資料結構演算法 – 雜湊法(Hash)
下一篇
[Day22]程式菜鳥自學C++資料結構演算法 – 氣泡排序法(Bubble Sort)
系列文
程式菜鳥自學C++資料結構演算法30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言