Chinaunix首页 | 论坛 | 博客
  • 博客访问: 701855
  • 博文数量: 31
  • 博客积分: 330
  • 博客等级: 一等列兵
  • 技术积分: 3004
  • 用 户 组: 普通用户
  • 注册时间: 2012-09-05 22:38
个人简介

java开发工程师,专注于内核源码,算法,数据结构。 qq:630501400

文章分类
文章存档

2014年(2)

2013年(22)

2012年(7)

分类: C/C++

2012-12-22 20:46:42

      前一篇文章介绍了这种没有浪费空间的动态hash结构,介绍了复杂的插入过程,总体思想就是,对一个元素的掩码,如果超过了当前数组的范围,就把它暂时放到低位,等到数组的规模到达了可以放下原来那个掩码的元素的时候,这时把原来暂时放在低位的元素,重新放到属于这个元素掩码的高位,并且重新调整相同掩码的元素的next指针链表。

      说一下删除元素的过程,释放这个元素占有的空间,hash结构的实现是一个数组,一段连续的内存空间,所以删除元素只能删除这个数组的最后一个元素。删除某个hash表中的元素,首先要找到这个元素,通过元素的hash掩码和next指针找到这个元素,同时为了删除这个元素,如果这个元素有前一个元素(冲突链表中不是第一个元素),那么就把前一个元素的next指向当前元素的下一个,从而从冲突链表中去掉删除的元素。如果这个元素是冲突链表的第一个元素,就把next的数据copy到当前元素中,同时把链表中删除第二个元素。

      如图所示,hash中,a,b,c元素拥有相同的hash掩码,组成了冲突链式结构,如果删除的目标元素是b,那么需要把b的前一个元素anext指针指到c元素,b标志为空闲位;如果删除的目标元素是a,那么就需要把b元素的数据移动到a的位置,同时标志原来b的位置为空闲位。

删除元素的过程分为几种情况:

case1:如果空闲位置就是hash数组中的最后的一个位置,那么直接释放数组的最后一个位置的空间即可

case2:如果空闲位置不是数组的最后一个元素,计算一下数组最后一个元素的hash掩码,有可能这个掩码等于当前空闲位的掩码,出现这种情况的原因是,这个元素在空闲位的元素冲突,被移动到了数组的最后以为,这种情况,只需要把数组最后一个位置的数据移动到当前的空闲位置即可,然后释放数组最后一个元素占有的空间

case3:数组最后一个元素的hash掩码不等于空闲位置的位置,那么需要想办法把一些元素移动到empty的位置上,空出数组的最后一个元素。如果最后一个元素掩码所对应的hash数组中的元素掩码不相同,那么数组最后一个元素找到这个元素掩码的起点,所以这个掩码的元素不需要在重新设置next指针,原来这个位置的元素被放到了空闲的位置,这个元素需要被前一个元素的next指针指到empty的位置,这样数组的最后一个位置就被释放出来了,直接释放数组的最后一个位置的空间即可。

case4:这种情况如果case3中数组的最后一个元素的掩码相对位置的元素,这个元素记作aa和最后一个元素的掩码相同,这里掩码相同会有两种情况,一种可能是,无论records或者是records+1(删除操作的一开始records就减一了),最后一个元素拥的掩码和a都有相同掩码,另一种可能是a元素的掩码是这个元素的掩码加上blength/2,原因见文章1

case4-1:如果是第一种情况,就是records或者records+1都拥有相同的掩码,那么在records的时候,数组的最后一个元素掩码和pos位置的元素拥有相同的掩码,可以知道他们冲突队列是连在一起的,这时就需要把数组的最后一个元素移动到empty的位置,同时修正next指针,冲突队列连接到empty位置。这里有个特殊情况,就是records+1时,如果掩码就是records,就是对应数组的最后的一个位置,可以知道如果按照刚才的只修正next指针是不行的,因为这个冲突链表的起点就是数组的最后一位,这里如果按照records计算掩码,就会找不到pos之前的冲突链表,所以这种情况直接进入case4-2

上图所示,这种特殊情况,需要删除的元素是a,冲突链表的结构是a->b->pos->d,如果是简单的将a换到empty的位置,那么通过posnext指针是找不到ab元素的,那么这时需要将posnext指针指向ab链表,同时在结束时把b指向post->next,这样冲突链表就完整了。

case4-2:这种情况是在hash数组最后一个元素的掩码在recordsrecords+1时不一样,这时,可以想到hash结构在records+1时和records时是两个不同的冲突链表,在删除一个元素后,冲突链表变为了一个,需要把records+1时剩下的冲突链表后面的元素mergepos处的链表中,处理方法就是,把hash结构最后一个元素依然是移动到empty的位置,同时把posnext指向最后一个元素的冲突链表,这个链表最后在链接在posnext元素上,过程类似于上图调整后的结构。

  1. 504 my_bool my_hash_delete(HASH *hash, uchar *record)
  2. 505 {
  3. 506   uint blength,pos2,idx,empty_index;
  4. 507   my_hash_value_type pos_hashnr, lastpos_hashnr;
  5. 508   HASH_LINK *data,*lastpos,*gpos,*pos,*pos3,*empty;
  6. 509   DBUG_ENTER("my_hash_delete");
  7. 510   if (!hash->records)
  8. 511     DBUG_RETURN(1);
  9. 512 
  10. 513   blength=hash->blength;
  11. 514   data=dynamic_element(&hash->array,0,HASH_LINK*); //取得hash结构的第一个元素的地址
  12. 515   /* Search after record with key *///取得record的掩码
  13. 516   pos= data + my_hash_mask(rec_hashnr(hash, record), blength, hash->records);
  14. 517   gpos = 0;
  15. 518 
  16. 519   while (pos->data != record)//从冲突链中查询record
  17. 520   {
  18. 521     gpos=pos;
  19. 522     if (pos->next == NO_RECORD)
  20. 523       DBUG_RETURN(1);                   /* Key not found */
  21. 524     pos=data+pos->next;
  22. 525   }
  23. 526 //records减一
  24. 527   if ( --(hash->records) < hash->blength >> 1) hash->blength>>=1;
  25. 528   lastpos=data+hash->records;//取得hash结构中最后一个元素的位置
  26. 529 
  27. 530   /* Remove link to record */
  28. 531   empty=pos; empty_index=(uint) (empty-data);//记录empty的位置
  29. 532   if (gpos)
  30. 533     gpos->next=pos->next;               /* unlink current ptr */ //从冲突链表中删除record
  31. 534   else if (pos->next != NO_RECORD)
  32. 535   {
  33. 536     empty=data+(empty_index=pos->next);
  34. 537     pos->data=empty->data;
  35. 538     pos->next=empty->next;
  36. 539   }
  37. 540 //如果空闲位就是最后一位,直接释放数组最后一个地址空间就可以,case1
  38. 541   if (empty == lastpos)                 /* last key at wrong pos or no next link */

  1. 541   if (empty == lastpos)                 /* last key at wrong pos or no next link */
  2. 542     goto exit;
  3. 543 
  4. 544   /* Move the last key (lastpos) */
  5. 545   lastpos_hashnr=rec_hashnr(hash,lastpos->data);//重新计算最后一个位置的hash
  6. 546   /* pos is where lastpos should be */
  7. 547   pos= data + my_hash_mask(lastpos_hashnr, hash->blength, hash->records);//计算掩码//如果空闲位是最后以为掩码的位置,case2
  8. 548   if (pos == empty)                     /* Move to empty position. */
  9. 549   {
  10. 550     empty[0]=lastpos[0];
  11. 551     goto exit;
  12. 552   }
  13. 553   pos_hashnr=rec_hashnr(hash,pos->data);
  14. 554   /* pos3 is where the pos should be */  //计算pos位的hash掩码
  15. 555   pos3= data + my_hash_mask(pos_hashnr, hash->blength, hash->records);
  16. 556   if (pos != pos3)//如果pos位的元素掩码不等于poscase3
  17. 557   {                                     /* pos is on wrong posit */
  18. 558     empty[0]=pos[0];                    /* Save it here */
  19. 559     pos[0]=lastpos[0];                  /* This should be here */
  20. 560     movelink(data,(uint) (pos-data),(uint) (pos3-data),empty_index);
  21. 561     goto exit;
  22. 562   }//计算数组最后一位的掩码(records等于减一之前的值),case4
  23. 563   pos2= my_hash_mask(lastpos_hashnr, blength, hash->records + 1);
  24. 564   if (pos2 == my_hash_mask(pos_hashnr, blength, hash->records + 1))
  25. 565   {                                     /* Identical key-positions */
  26. 566     if (pos2 != hash->records)//进入case4-1
  27. 567     {
  28. 568       empty[0]=lastpos[0];
  29. 569       movelink(data,(uint) (lastpos-data),(uint) (pos-data),empty_index);
  30. 570       goto exit;
  31. 571     }
  32. 572     idx= (uint) (pos-data);             /* Link pos->next after lastpos */
  33. 573   }
  34.           //case4-2
  35. 574   else idx= NO_RECORD;          /* Different positions merge */
  36. 575 
  37. 576   empty[0]=lastpos[0];
  38. 577   movelink(data,idx,empty_index,pos->next);
  39. 578   pos->next=empty_index;
  40. 579 
  41. 580 exit:

总结:动态hash结构删除元素,总体目标就是,找到删除的元素,然后删除它所在冲突链表的位置,移动一些元素,想办法将数组最后一个元素移动到刚才删除元素的地方,同时还要保持冲突链表的完整性,比较重要的一点就是由于records的减小,一些元素的掩码在原来不同的情况下,现在变成掩码相同了,这时候需要合并冲突链表。

阅读(4194) | 评论(0) | 转发(0) |
0

上一篇:MySQL动态hash结构

下一篇:Pid NameSpace浅分析

给主人留下些什么吧!~~