新的起点
做自己喜欢并适合的事情

导航

 

  前段时间工作比较忙,然后一起玩的同事都在看《男人帮》,于是花了一周多看完,以免与其他人没有共同话题。继续回到算法学习,链式表多用于动态分配和增长的内存块管理,与线性表表相比其优缺非常明显:优点是无需预先知道表的大小,随使用动态增长;缺点也是由此带来的,反复开辟内存管理麻烦且容易形成碎片。

  这里先直接给出实现的单链表的代码: LListDemo.zip。下面讨论几个问题:

  1.提高单链表的访问效率:对于单链表(仅在当前节点存在指向下一个节点的指针),在当前位置插入新节点操作时需要修改前一个节点(前驱)指向当前节点的指针,那么普通做法需要从头节点起直至遍历到前驱位置,显然效率低下;删除当前位置的节点存在相同的问题。普遍的做法是将当前节点指向前驱,而通过curr.next的方式访问当前节点,从而插入操作简化为如下两个语句:

new_node.next = curr.next;
curr.next = new_node;

 删除操作简化为:

curr.next=curr.next.next;

   2.实现链表时对于链表头和尾的处理(哑铃结是否需要):之前算法入门 提到哨兵位的小技巧,那么哑铃结也可以理解为是链表中特殊的哨兵位,即使用两个指针分别标记出链表头和尾(所实现的代码中采用),但是这样做是否高效呢?从内存的角度,哑铃结增加了两个指针的开销,再从算法的角度,对于链表头,由于单链需要使用前驱来提升效率,故链表头值得加入(创建链表时立刻插入一个节点作为固定的链表头),而链表尾则不见得必须,可以使用“后续节点为NULL则必然为尾节点”这样的规则来做为天然的哨兵位,而使用指针标记链表尾则需要增加许多额外的处理(尤其是空链表的插入,和最后一个节点的删除)。故得出结论,应当使用一个空节点作为链表头,同时使用后续节点为NULL判断尾节点。

  3.实现了线性表和链表以后在思考一个问题:为什么要将数据结构进行封装?首先必然的是进行代码模块化和标准化,方便重用。然而比较容易忽略掉的则是提供统一访问接口以及对内部成员进行保护(提供访问权限策略),这一点是后来面向对象语言(尤其是Java/C#)做得比较好的,也是享受C语言高效和自由的过程中(尤其是指针)容易忽略的。一个已经工作几年的朋友说重构是奢侈品,的确,工作过程中确实体会到在公司的环境中一个平台或者实现方式一旦确定则非常难以该换,即便是这个平台或者方式需要更高的开发周期和成本,但是由于前期的积累无法抛弃,那么基本上只有继续使用。但是学习的过程则更加宽松一些,所以决定将前面的代码进行重新设计和实现,很快会补充代码。

                                    BSKER于2011.11.30

posted on 2011-11-30 23:39  Kevin W  阅读(426)  评论(0编辑  收藏  举报