演算法導論 11.2-4 散列表內未佔用槽連結成自由鏈表

來源:互聯網
上載者:User
一、題目

說明在散列表內部,如何通過將所有未佔用的槽位鏈成一個自由表,來分配和去配元素的儲存空間。假定一個槽位可以儲存一個標誌、一個元素加上一個或兩個指標。所有的字典操作和自由鏈表操作應具有O(1)的期望已耗用時間。該自由鏈表是雙鏈表嗎?或者,是不是單鏈表就足夠了?

二、思考

已知(1)所有未佔用的槽位鏈成一個自由鏈表(2)槽位即slot(3)Hash(x)返回x所屬於的slot

一個slot儲存以下內容,佔用和未佔用時表示的含義不同

struct node{int key;bool flag;//0:free,1:usedint pre;int next;};

當這個slot未佔用時,取值如下:

struct node{int key;//沒有意義,初始化為-1bool flag;//0:freeint pre;//自由鏈表中上一個空閑slot,沒有則為-1int next;//自由鏈表中下一個空閑slot,沒有則為-1};

當這個slot被佔用時,取值如下:

struct node{int key;//關鍵字bool flag;//1:usedint pre;//具有相同Hash值的下一個結點int next;//具有相同Hash值的上一個結點};

 

插入操作時,從自由鏈表中取出一個空閑slot,填入關鍵字x,修改指標,鏈表相應的隊列中,具體可以分為以下幾種情況:

(1)x所屬的slot未被佔用,則

step1:把這個slot從自由鏈表中移出

step2:填入關鍵字x

step3:修改指標,在這種情況下其next和pre都置為-1

(2)x所屬的slot已經被佔用,令佔用這個slot的關鍵是y,y也屬於這個slot,則

step1:從自由鏈表中取出一個閒置slot,這個slot肯定不是x所屬的slot,只是拿過來用

step2:填入關鍵字x

step3:修改指標,把slot鏈表入到“以x所屬的slot為頭結點的隊列”中

(3)x所屬的slot已經被佔用,令佔用這個slot的關鍵是y,y不屬於這個slot,通過(2)可知,這個情況是有可能的

step1:從自由鏈表中取出一個閒置slot,這個slot肯定不是x所屬的slot,也不是y所屬的slot,只是拿過來用

step2:在新slot中填入關鍵字y,修改指標,讓y使用這個新slot,而把原來的slot空出來還給x

step3:在x所屬的slot中填入關鍵字x

step4:修改“x所屬的slot”指標,類似(1)-step3

 

刪除操作時,令待刪除的關鍵字是x,釋放x所佔用的slot,具體可以分為以下幾種情況

(1)x所佔用的slot正是x所屬的slot,且slot->next=-1,即所有關鍵字中只有x屬於這個slot,x被刪除後,slot就空閑了

step1:釋放slot到自由鏈表中

(2)x所佔用的slot正是x所屬的slot,但還有別的關鍵字中只有x屬於這個slot,應該優先使用關鍵所屬於的slot,而釋放“不自己關鍵字的、臨時拿過來用的”slat

step1:從以slot為頭結點的隊列中另選一個slot2,slot2的關鍵字屬於slot而不屬於slot2,只是因為slot被佔用,所以才用slot2

step2:把slot2的內容填入slot

step3:修改指標,讓slot代替slot2存在於隊列中,不同的是slot還是隊列頭

step4:釋放slot2到自由鏈表中

(3)x所佔用的slot不是x所屬的slot,這個種情況下,這個slot一定不是隊列頭,還有別的關鍵字存在於隊列中,並且佔用了x所屬的slot

step1:把x所佔用的slot從“以x所屬的slot為頭的隊列”中移出

step2:釋放slot到自由鏈表中

 

尋找操作,如果理解了插入和刪除,尋找操作就比較簡單了,令待尋找的關鍵字是x,也可分為幾種情況

(1)x所屬的slot未被佔用,即不存在與x相同slot的關鍵字,當然也不存在x了

(2)x所屬的slot被佔用了,但它所存的關鍵不屬於這個slot,與(1)相同,不存在與x相同slot的關鍵字

(3)x所屬的slot被佔用了,且它所存的關鍵屬於這個slot,即存在與x相同slot的關鍵字,只是不知這個關鍵字是不是x,需要進一步尋找

 

插入和刪除過程中反覆提到的“從自由鏈表中取出一個閒置slot”和“釋放slot到自由鏈表中”這兩個操作比較簡單,見代碼中的解釋

 

三、代碼
#include <iostream>#include <string>using namespace std;//slot結點struct node{int key;//關鍵字bool flag;//0:free,1:usedint pre;int next;};int Free = 0;//自由鏈表的頭slot//計算x所屬的slotint Hash(int x){return x % 20;}//從自由鏈表中取出一個閒置slot,指定取出的編號為h的slotint RemoveSlotFromFree(node *A, int h){//標記為usedA[h].flag = 1;//修改指標移出自由鏈表if(A[h].pre >= 0)A[A[h].pre].next = A[h].next;else Free = A[h].next;//如果被移出的剛好是表頭slot,還要更新表頭slot的位置if(A[h].next >= 0)A[A[h].next].pre = A[h].pre;//返回取出的slot的編號return h;}//將編號為h的slot釋放到自由鏈表中void FreeSlotToFree(node *A, int h){//標記為freeA[h].flag = 0;//修改指標,插入到鏈表頭A[h].next = Free;A[h].pre = -1;A[h].key = -1;//更新自由鏈表的頭slotFree = h;}//尋找操作int Search(node *A, int x){int h = Hash(x);//(1)x所屬的slot未被佔用,即不存在與x相同slot的關鍵字,當然也不存在x了//(2)x所屬的slot被佔用了,但它所存的關鍵不屬於這個slot,與(1)相同,不存在與x相同slot的關鍵字if(A[h].flag == 0 || Hash(A[h].key) != h)return -1;//(3)x所屬的slot被佔用了,且它所存的關鍵屬於這個slot//即存在與x相同slot的關鍵字,只是不知這個關鍵字是不是x,需要進一步尋找//尋找方法就是遍曆以slot為頭的隊列int p = h;while(p >=0 && A[p].key != x)p = A[p].next;if(A[p].key == x)return p;else return -1;}//插入操作時,從自由鏈表中取出一個空閑slot,填入關鍵字x,修改指標,鏈表相應的隊列中,void Insert(node *A, int x){//是否已經存在if(Search(A, x) >= 0){cout<<"error:exit"<<endl;return ;}//計算x所屬的slotint h = Hash(x);//(1)x所屬的slot未被佔用if(A[h].flag == 0){//step1:把這個slot從自由鏈表中移出int t = RemoveSlotFromFree(A, h);//step2:填入關鍵字xA[t].key = x;//step3:修改指標,在這種情況下其next和pre都置為-1A[t].next = -1;A[t].pre = -1;}//(2)x所屬的slot已經被佔用,令佔用這個slot的關鍵是y,y也屬於這個slotelse if(Hash(A[h].key) == h){//step1:從自由鏈表中取出一個閒置slot,這個slot肯定不是x所屬的slot,只是拿過來用int t = RemoveSlotFromFree(A, Free);//step2:填入關鍵字xA[t].key = x;//step3:修改指標,把slot鏈表入到“以x所屬的slot為頭結點的隊列”中A[t].next = -1;A[h].next = t;A[t].pre = h;}//(3)x所屬的slot已經被佔用,令佔用這個slot的關鍵是y,y不屬於這個slot,//通過(2)可知,這個情況是有可能的else{//step1:從自由鏈表中取出一個閒置slot,這個slot肯定不是x所屬的slot,也不是y所屬的slotint t = RemoveSlotFromFree(A, Free);//step2:在新slot中填入關鍵字y,修改指標,讓y使用這個新slot,而把原來的slot空出來還給xA[t] = A[h];A[A[h].pre].next = t;if(A[h].next >= 0)A[A[h].next].pre = t;//step3:在x所屬的slot中填入關鍵字xA[h].key = x;//step4:修改“x所屬的slot”指標,類似(1)-step3A[h].next = -1;A[h].pre = -1;}}//刪除操作時,令待刪除的關鍵字是x,釋放x所佔用的slotvoid Delete(node *A, int x){//是否存在int ret = Search(A, x);if(ret < 0){cout<<"error:not exit"<<endl;return ;} //(1)x所佔用的slot正是x所屬的slot,且slot->next=-1//即所有關鍵字中只有x屬於這個slot,x被刪除後,slot就空閑了if(ret == Hash(x) && A[ret].next == -1){FreeSlotToFree(A, ret);}//(2)x所佔用的slot正是x所屬的slot,但還有別的關鍵字中只有x屬於這個slot//應該優先使用關鍵所屬於的slot,而釋放“不自己關鍵字的、臨時拿過來用的”slatelse if(ret == Hash(x) && A[ret].next != -1){//step1:從以slot為頭結點的隊列中另選一個slot2//slot2的關鍵字屬於slot而不屬於slot2,只是因為slot被佔用,所以才用slot2int next = A[ret].next;//next就是slot2的編號//step2:把slot2的內容填入slotA[ret] = A[A[ret].next];//step3:修改指標,讓slot代替slot2存在於隊列中,不同的是slot還是隊列頭A[A[ret].next].pre = ret;//step4:釋放slot2到自由鏈表中FreeSlotToFree(A, next);}//(3)x所佔用的slot不是x所屬的slot,這個種情況下,這個slot一定不是隊列頭//還有別的關鍵字存在於隊列中,並且佔用了x所屬的slotelse if(ret != Hash(x)){//step1:把x所佔用的slot從“以x所屬的slot為頭的隊列”中移出A[A[ret].pre].next = A[ret].next;if(A[ret].next >= 0)A[A[ret].next].pre = A[ret].pre;//step2:釋放slot到自由鏈表中FreeSlotToFree(A, ret);}}//輸出slotvoid Print(node *A){int i;for(i = 0; i < 20; i++)cout<<A[i].flag<<' '<<A[i].key<<' '<<A[i].next<<' '<<A[i].pre<<endl;}int main(){int i;//構造一個擁有20個slot的散列表node A[20];for(i = 0; i < 20; i++){//初始時,所有slot都為freeA[i].flag = 0;if(i == 19)A[i].next = -1;else A[i].next = i + 1;A[i].pre = i - 1;A[i].key = -1;}//teststring str;int x;while(1){cin>>str;if(str == "I"){x = rand() % 100;cout<<x<<endl;Insert(A, x);}else if(str == "D"){cin>>x;Delete(A, x);}else if(str == "P")Print(A);}return 0;}

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.