在单向链表中拆除某个节点的方法

离开Turbo Pascal的5、6年时间里很少再写有关于数据结构的东西了。
最近自己写服务器程序,终于又爽了一回数据结构和算法。只是好多东西都快忘记,时常还需要翻书辅记。在Delphi的IniFile里看到一个HashTable实现,其中的Find方法用到了指向指针的指针,C中指针满天飞,而在Pascal语言中,指针的指针还是不常使用的。仔细一看,原来这是自己曾经常用的一种单向链表维护方法,可用于快速去除单向链表中某个节点。这里记述一下,当作对从前知识的复习。


首先看以下关键代码:


  PPHashItem = ^PHashItem;
  PHashItem = ^THashItem;
  THashItem = record   //Hash表的节点,整个Hash表是一个动态数组
    Next: PHashItem;   //对于发生碰撞的节点,它们会自动串接成为一个链表。
    Key: string;
    Value: Integer;
  end;

  Buckets: array of PHashItem;


function TStringHash.Find(const Key: string): PPHashItem;
var
  Hash: Integer;
begin
  Hash := HashOf(Key) mod Cardinal(Length(Buckets)); 
  //取模,保证Hash范围,在本文的讨论中可以先忽略它。
  Result := @Buckets[Hash]; 
  //碰撞链表的第一个节点位置
  while Result^ <> nil do  //开始遍历,获取需要的节点
  begin
    if Result^.Key = Key then 
    //Result.Key, 如果不使用指针来表示,其实是THashItem.Next.Key,
    //这里就是技巧所在,通过提前检测下一个节点,避免单独保存上级节点指针
      Exit
    else
      Result := @Result^.Next;
  end;
end;

//该方法则是对Find方法的一个使用
procedure TStringHash.Remove(const Key: string);
var
  P: PHashItem;
  Prev: PPHashItem;
  //该指针的指针变量名已经显示出他的实际作用,
  //他指向的其实是我们实际希望删除的节点的上级节点。
begin
  Prev := Find(Key);
  P := Prev^;
  if P <> nil then
  begin
    Prev^ := P^.Next;
    //所以这里直接把上级节点的Next指针指向要被删除的节点的Next指针指向的下级节点
    Dispose(P);
    //直接释放P。
  end;
end;

上面的简单注释肯定无法让人理解,下面我举个例子来说明。假设我们的数据结构如下:
数组Buckets:[A,B,C,D,E,F....Z]
                           |      |
                          1     2  (数字代表THashItem )
                           |    
                          3
                           |
                           4

我们需要删除3时,Find方法返回的指向指针的指针Prev实际指向的是 1.Next(请注意,是Next指针本身,而不是Next指针所指向的THashItem内存),所以P := Prev^,就相当于P指向了Next所指向的节点3(这里P才是指向Next指针指向的内存地址),Prev^  := P^.Next也就表示把Next指针指向的地址,变更为3.Next所指向的地址。于是,节点三从链表A中被删除,接下来就可以释放节点3所占用的内存了,Dispose(p)。
由此可见,采用这种方法,省去了在单向链表中单独记录上级指针的麻烦,这也就不难理解Prev命名的原因。

顺便,又想到了现在的程序开发。以我们公司的.NET程序员为例(特别是习惯于数据库开发的程序员),对于数据结构和算法知识的掌握真的很差,不少人根本不明白基本数据结构知识。当然,这也和大学里的教育有很大关系,很多大学教师,自己就没办法把这门有一定难度的课程说清楚,或者它们自己本身就不清楚(想想自己的大学时跟数据结构老师的争执,还是略为不爽)。软件开发领域算是工程应用,对语言本身掌握的不熟练,导致学生更难理解数据结构知识。而很多数据结构知识又是从实践应用中总结得到。自己学数据结构时,不觉吃力,很大原因是在那之前,我已经拿Turbo Pascal写了许多代码,看到数据结构里的线性表,发现那就是自己常用的技巧。可见在计算机学习的初期,放手让学生拥有充足的代码实战经验,才是继续提升他们能力的根本。

 

 

posted @ 2006-09-28 14:35  monkeyking  阅读(656)  评论(0编辑  收藏  举报