哈希表之拉鏈法
前段時間理解了一下所謂的哈希表,一直以來在小博印象中哈希表是深奧的,是高大上的,但是接觸原理以及看了一份demo之後我就覺得哈希表也就那樣吧,接下來我把小博自己的理解盡量用最直白的語句來解釋下~~~
---------------------------------------------------------我是分界線,沒錯,很醜------------------------------------------------------------------
首先什麽是哈希表???
散列表(Hash table,也叫哈希表),是根據關鍵碼值(Key value)而直接進行訪問的數據結構。也就是說,它通過把關鍵碼值映射到表中一個位置來訪問記錄,以加快查找的速度。這個映射函數叫做散列函數,存放記錄的數組叫做散列表。
以上是一段在百度百科中的解釋,如果還是不能理解,那麽我就抽象的比喻一下。
先看(是根據關鍵碼值(Key value)而直接進行訪問的數據結構),這裏的關鍵碼值以及數據結構可以用具體的物體代替一下,這裏小博將關鍵碼值用學號代替,數據結構用學生代替,學生與學號都是唯一的,可以根據學號直接找到學生。(說了一堆廢話~~~~接下去才是重點)。
場景演示一下,當我們需要將數據存入哈希表中的時候,就好像是一堆學生在辦理入學,那麽這時候有個地中海的大肚子男老師過來了,他對每個上前辦理的學生說:你是XXX號,你所在的班級是YYY班,記住咯。然後這名學生就屁顛屁顛的去YYY班了。那麽怎麽查找呢??想必在學校中被校長點名過的同學應該都想到了,比如XXX號犯錯了,他所在的班級是YYY一班,那麽校長會說,在YYY一班的XXX號,你出來一下咱們好好喝喝茶~~這樣校長就不用為了找XXX號同學而找遍每一個學生了,相對來說就是提升了查找效率。這是哈希表其中的一個特性————查找方便
還有一個比較重要的特性,小博暫時沒想到怎麽怎麽形象的比喻(原諒小博的見識淺薄~~囧)。另一個特性是空間高利用率,各位大老爺可能有幾個一下子不能理解這個,請容小博詳細說明,都知道需要存數據是需要容器的,數組,結構體等都是容器的一種,那麽只要是容器都需要各自的空間(其實不僅僅是容器需要,其他也是一樣的,但是相比之下其他占用的比較少罷了,繼續正文),比如數組,當申請的時候需要一塊連續的空間,而數據結構也是如此,申請的時候需要一塊整個數據結構大小的空間,而且每次申請空間的時候,系統內部所為你劃分的空間並不是一塊挨著一塊申請的,因此肯定會有少部分空間無法申請導致浪費,采用數據結構可以靈活的利用這些空間,采用鏈表將每個結構體聯系起來,那麽就形成了一個最簡單的哈希表。直接上代碼理解一下吧~~~~(寫這些的時候小博已經神遊,斷片了,原諒小博的才疏學淺。)
#include <stdio.h>
#include <string>
#include <time.h>
typedef struct ITEM
{
int val;
int index;
struct ITEM *next;
}Item;
typedef struct LIST
{
Item *head;
Item *end;
}List;
#define HASH_VAL 10
void insertItem(List* list, Item* item)
{
int key = (unsigned int)item->val%HASH_VAL;
if (!list[key].head)
{
list[key].head = item;
list[key].end = item;
}
else
{
list[key].end->next = item;
list[key].end = item;
}
}
void showList(List* list)
{
Item* p = NULL;
for (int i = 0; i < 10; i++)
{
printf("%d:", i);
p = list[i].head;
if (p)
{
do
{
printf("%d(下標:%d) ", p->val, p->index);
if (p->next)
p = p->next;
else
break;
} while (1);
}
printf("\n");
}
}
bool aaa(int* a, int val)
{
for (int i = 0; i < 10; i++)
{
if (a[i] == val)
return true;
}
return false;
}
bool serchItem(List* list, int val)
{
int key = (unsigned int)val%HASH_VAL;
Item* p = list[key].head;
while (p)
{
if (p->val == val)
return true;
p = p->next;
}
return false;
}
int main(int argc, char* argv[])
{
List list[10];
Item item[10];
memset(list, 0, sizeof(List)* 10);
memset(item, 0, sizeof(Item)* 10);
int a[10] = {21,11,1,51,5,6,7,8,9,0};
for (int i = 0; i < 10; i++)
{
item[i].index = i;
item[i].val = a[i];
insertItem(list, &item[i]);
}
showList(list);
system("pause");
return 0;
}
小博演示的拉鏈法是將數組的特點(方便查找,不易刪除)以及鏈表的特點(方便刪除,不易查找)取長補短的一種折中方法。
----------------------------------------------分界線,又出現了-----------------------------------------------------------------
文中好像很多廢話,希望沒有把各位大老爺給繞暈了,新人小白發表,不足之處請見諒,歡迎大牛指導,其他同道也可交流
如果不理解小博的思路,可以參考更詳細的原理:http://www.cnblogs.com/tuhooo/p/7092288.html#3934840
哈希表之拉鏈法