3.1基础概念
链表是由通常被称为单元格的对象构建而成。单元格类包含链表必须存储的数据和到一个单元格的链接。该链接是一个简单的引用或是指到另一个单元格类对象的指针。通常单元格的指针域被称为Next。
链表通常用图来表示,用小方格代表单元格并用箭头代表链接。
用含有x的小方格来代表一个没有指向单元格的链接。(在编程语言中,对应的链接指针没有值、或者是空指针、或者其他一些特定语言的值,表示该指针没有指向任何内容。)
除了链表本身,一个程序需要一个指向链表的变量,这样便于代码能够找到该链表。往往这个变量被命名为top,以表明它表示该链表的顶端。top变量可以是一个单元格类的变量,也可以是一个指向链表第一个单元的指针。
如果数据中的项会随着时间变化而增加或减少时,链表是一种很好的存储方式。若要添加一个新的单元格,只需要在链表的开头或结尾添加该单元格。与此相反,一个数组的大小是固定的,所以很难扩大数组来实现添加更多的项。
以下各节将介绍一些可以用来操纵链表的算法。其中许多算法是通过展示链表在某操作被执行前和执行后的状态来进行描述说明的。
3.2单链表
在一个单链表中,每个单元格由一个单链路连接到下一个单元格。
如果要使用一个链表,就需要一系列算法来遍历链表、将项添加到链表中、查找链表中的项、删除链表中的项。以下各节将描述一些可能需要使用的算法。
3.2.1 遍历链表
假设一个程序已经建立了一个链表,遍历它的单元格是比较容易的。下面的算法展示了如何遍历链表中的单元格,并使用某种方法对单元格中的值进行处理。本例中使用Print方法来显示单元格的值,但也可以用其他任何方法来代替Print方法对单元格进行操作。
Iterate(Cell: top)
While(top != null)
Print top.Value
top = top.Next
End While
End Iterate
注 这些算法假定参数top 是按值传递的,所以该代码可以修改它而不会改变调用代码中的 top 的值。
该算法开始于一个 while循环,只要top单元格指针不为空则该循环就执行。在循环里,该算法调用Print方法显示top单元格的值。然后,top被设置为指向当前top单元格后面的单元格。
这个过程一直进行到 top 被设置为空指针,此时 while 循环停止。
该算法将检查链表的每一个单元格,因此如果该列表包含N个单元格,它的运行时间为 O(N)。
3.2.2查找单元格
查找链表中的某个单元格只需要遍历链表直到找到要查找的单元格为止。下面的算法遍历一个链表并返回包含目标值的单元格:
Cell:FindCell(Cell:top,Value: target)
While(top != null)
If(top.Value == target)Then Return top
top = top.Next
End while
//如果我们进行了这么多步,而目标不在链表中
Return nullEnd FindCell
该算法进人一个只要top不为空就执行的while循环。在循环中,该算法将top单元格的值与目标值进行比较。如果值匹配,该算法返回top。如果这两个值不匹配,则该算法将top指向下一个单元格。
如果一直运行到top指针为空,那么目标值不在链表中,这时算法返回空。(另外,法将 top 指向链表的下一个单元格。算法可以提示异常,或者增加某些类型的错误,这取决于采用的编程语言。)下面的章节中将会介绍,在处理链表中某个单元格的时候,如果有一个指向该单元格。前一个单元格的指针,往往会让处理更加容易。
3.2.3 使用哨兵
如果仔细研究上述 Findcel1Before算法,可能会找到该算法的一个失败之处。如果在链表中的第一个单元格中就包含目标值,但在它之前却没有单元格,因此该算法不能返回它。该算法检查的第一个值位于链表中的第二个单元格,并且它从不回查。
处理这种情况的一种方式是添加专用代码明确地在第一个单元格中寻找目标值然后再特殊处理。该程序可能需要将这种情况作为一个特例处理,这样程序可能会变得混乱。
另一种方法是在列表的开始处创建一个哨兵。一个哨兵是一个单元格,这个单元格是链[58]表的一部分,但不包含任何有意义的数据。它仅作为一个占位符,这样算法就可以找到第个单元格之前的单元格。
下面的伪代码显示了使用标记修改过的前一个 FindCellBefore算法:
Ce11:FindCellBefore(Cell:top,Value: target)
// 查找目标值
While(top.Next != null)
If(top.Next,Value == target) Then Return top
top = top.Next
End while
//如果运行到此,那么目标没有在链表中Return null
End FindCellBefore
该算法只执行两个步骤,所以不管链表中包含多少个单元格,它的运行时间为 O(1)。