算法导论-11.2-4
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
5
A[A[h].next].pre = t;
//step3:在x所属的slot中填入关键字x A[h].key = x; //step4:修改“x所属的slot”指针,类似(1)-step3 A[h].next = -1; A[h].pre = -1; } } //删除操作时,令待删除的关键字是x,释放x所占用的slot void 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,而释放“不自己关键字的、临时拿过来用的”slat else if(ret == Hash(x) && A[ret].next != -1) { //step1:从以slot为头结点的队列中另选一个slot2 //slot2的关键字属于slot而不属于slot2,只是因为slot被占用,所以才用slot2 int next = A[ret].next;//next就是slot2的编号 //step2:把slot2的内容填入slot A[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所属的slot else 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); } } //输出slot void 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都为free A[i].flag = 0; if(i == 19)
自娱自乐
算法导论-11.2-4
分类: 算法导论 2012-07-03 16:31 75人阅读 评论(1) 收藏 举报 题目: 说明在散列表内部,如何通过将所有未占用的槽位链成一个自由表,来分配和去配元素的存储空间。假定一个槽位 可以存储一个标志、一个元素加上一个或两个指针。所有的字典操作和自由链表操作应具有O(1)的期望运行时间。 该自由链表是双链表吗?或者,是不是单链表就足够了? 思考: 已知(1)所有未占用的槽位链成一个自由链表(2)槽位即slot(3)Hash(x)返回x所属于的slot 一个slot存储以下内容,占用和未占用时表示的含义不同 struct node { int key; bool flag;//0:free,1:used int pre; int next; };
查找操作,如果理解了插入和删除,查找操作就比较简单了,令待查找的关键字是x,也可分为几种情况 (1)x所属的slot未被占用,即不存在与x相同slot的关键字,当然也不存在x了 (2)x所属的slot被占用了,但它所存的关键不属于这个slot,与(1)相同,不存在与x相同slot的关键字
3
(3)x所属的slot被占用了,且它所存的关键属于这个slot,即存在与x相同slot的关键字,只是不知这个关键字是 不是x,需要进一步查找
当这个slot未占用时,取值如下:
struct node { int key;//没有意义,初始化为-1 bool flag;//0:free int pre;//自由链表中上一个空闲slot,没有则为-1 int next;//自由链表中下一个空闲slot,没有则为-1 };
当这个slot被占用时,取值如下:
4
A[h].key = -1;
//更新自由链表的头slot Free = 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所属的slot int h = Hash(x); //(1)x所属的slot未被占用 if(A[h].flag == 0) { //step1:把这个slot从自由链表中移出 int t = RemoveSlotFromFree(A, h); //step2:填入关键字x A[t].key = x; //step3:修改指针,在这种情况下其next和pre都置为-1 A[t].next = -1; A[t].pre = -1; } //(2)x所属的slot已经被占用,令占用这个slot的关键是y,y也属于这个slot else if(Hash(A[h].key) == h) { //step1:从自由链表中取出一个空闲的slot,这个slot肯定不是x所属的slot,只是拿过来用 int t = RemoveSlotFromFree(A, Free); //step2:填入关键字x A[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所属的slot int t = RemoveSlotFromFree(A, Free); //step2:在新slot中填入关键字y,修改指针,让y使用这个新slot,而把原来的slot空出来还给x A[t] = A[来自百度文库]; A[A[h].pre].next = t; if(A[h].next >= 0) A[A[h].next].pre = t;
(1)x所属的slot未被占用,则
step1:把这个slot从自由链表中移出 1
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
2
(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到自由链表中
struct node { int key;//关键字 bool flag;//1:used int pre;//具有相同Hash值的下一个结点 int next;//具有相同Hash值的上一个结点 };
插入操作时,从自由链表中取出一个空闲slot,填入关键字x,修改指针,链表相应的队列中,具体可以分为以下几 种情况:
插入和删除过程中反复提到的“从自由链表中取出一个空闲的slot”和“释放slot到自由链表中”这两个操作比较 简单,见代码中的解释
代码:
#include <iostream> #include <string> using namespace std; //slot结点 struct node { int key;//关键字 bool flag;//0:free,1:used int pre; int next; }; int Free = 0;//自由链表的头slot //计算x所属的slot int Hash(int x) { return x % 20; } //从自由链表中取出一个空闲的slot,指定取出的编号为h的slot int RemoveSlotFromFree(node *A, int h) { //标记为used A[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) { //标记为free A[h].flag = 0; //修改指针,插入到链表头 A[h].next = Free; A[h].pre = -1; A[h].key = -1;
A[A[h].next].pre = t;
//step3:在x所属的slot中填入关键字x A[h].key = x; //step4:修改“x所属的slot”指针,类似(1)-step3 A[h].next = -1; A[h].pre = -1; } } //删除操作时,令待删除的关键字是x,释放x所占用的slot void 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,而释放“不自己关键字的、临时拿过来用的”slat else if(ret == Hash(x) && A[ret].next != -1) { //step1:从以slot为头结点的队列中另选一个slot2 //slot2的关键字属于slot而不属于slot2,只是因为slot被占用,所以才用slot2 int next = A[ret].next;//next就是slot2的编号 //step2:把slot2的内容填入slot A[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所属的slot else 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); } } //输出slot void 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都为free A[i].flag = 0; if(i == 19)
自娱自乐
算法导论-11.2-4
分类: 算法导论 2012-07-03 16:31 75人阅读 评论(1) 收藏 举报 题目: 说明在散列表内部,如何通过将所有未占用的槽位链成一个自由表,来分配和去配元素的存储空间。假定一个槽位 可以存储一个标志、一个元素加上一个或两个指针。所有的字典操作和自由链表操作应具有O(1)的期望运行时间。 该自由链表是双链表吗?或者,是不是单链表就足够了? 思考: 已知(1)所有未占用的槽位链成一个自由链表(2)槽位即slot(3)Hash(x)返回x所属于的slot 一个slot存储以下内容,占用和未占用时表示的含义不同 struct node { int key; bool flag;//0:free,1:used int pre; int next; };
查找操作,如果理解了插入和删除,查找操作就比较简单了,令待查找的关键字是x,也可分为几种情况 (1)x所属的slot未被占用,即不存在与x相同slot的关键字,当然也不存在x了 (2)x所属的slot被占用了,但它所存的关键不属于这个slot,与(1)相同,不存在与x相同slot的关键字
3
(3)x所属的slot被占用了,且它所存的关键属于这个slot,即存在与x相同slot的关键字,只是不知这个关键字是 不是x,需要进一步查找
当这个slot未占用时,取值如下:
struct node { int key;//没有意义,初始化为-1 bool flag;//0:free int pre;//自由链表中上一个空闲slot,没有则为-1 int next;//自由链表中下一个空闲slot,没有则为-1 };
当这个slot被占用时,取值如下:
4
A[h].key = -1;
//更新自由链表的头slot Free = 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所属的slot int h = Hash(x); //(1)x所属的slot未被占用 if(A[h].flag == 0) { //step1:把这个slot从自由链表中移出 int t = RemoveSlotFromFree(A, h); //step2:填入关键字x A[t].key = x; //step3:修改指针,在这种情况下其next和pre都置为-1 A[t].next = -1; A[t].pre = -1; } //(2)x所属的slot已经被占用,令占用这个slot的关键是y,y也属于这个slot else if(Hash(A[h].key) == h) { //step1:从自由链表中取出一个空闲的slot,这个slot肯定不是x所属的slot,只是拿过来用 int t = RemoveSlotFromFree(A, Free); //step2:填入关键字x A[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所属的slot int t = RemoveSlotFromFree(A, Free); //step2:在新slot中填入关键字y,修改指针,让y使用这个新slot,而把原来的slot空出来还给x A[t] = A[来自百度文库]; A[A[h].pre].next = t; if(A[h].next >= 0) A[A[h].next].pre = t;
(1)x所属的slot未被占用,则
step1:把这个slot从自由链表中移出 1
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
2
(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到自由链表中
struct node { int key;//关键字 bool flag;//1:used int pre;//具有相同Hash值的下一个结点 int next;//具有相同Hash值的上一个结点 };
插入操作时,从自由链表中取出一个空闲slot,填入关键字x,修改指针,链表相应的队列中,具体可以分为以下几 种情况:
插入和删除过程中反复提到的“从自由链表中取出一个空闲的slot”和“释放slot到自由链表中”这两个操作比较 简单,见代码中的解释
代码:
#include <iostream> #include <string> using namespace std; //slot结点 struct node { int key;//关键字 bool flag;//0:free,1:used int pre; int next; }; int Free = 0;//自由链表的头slot //计算x所属的slot int Hash(int x) { return x % 20; } //从自由链表中取出一个空闲的slot,指定取出的编号为h的slot int RemoveSlotFromFree(node *A, int h) { //标记为used A[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) { //标记为free A[h].flag = 0; //修改指针,插入到链表头 A[h].next = Free; A[h].pre = -1; A[h].key = -1;