线性表的链式存储
为了客服顺序表的缺点,可以采用链接方式存储线性表。通常我们将采用链式存储结构的线性表称为链表。
本节将从两个角度来讨论链表:从实现角度看,链表可分为动态链表和静态链表;从链接方式的角度看,链表可分为单链表、循环链表和双链表。链接存储是最常用的存储方法之一,它不仅可以用来表示线性表,而且可以用来表示各种非线性的数据结果。
单链表
在顺序表中,我们是用一组地址连续的存储单元来依次存放线性表的结点,因此结点的逻辑次序和物理次序是一致的。而链表则不然,链表是用一组任意的存储单元来存放线性表的结点,这组存储单元可以是连续的,也可以是非连续的,甚至是零散分布在内存的任何位置上。因此,链表中结点的逻辑次序和物理次序不一定相同。为了正确地表示结点间的逻辑关系,必须在存储线性表的每个数据元素值的同时,存储指示其后继节点的地址(或位置)信息,这两部分信息组成的存储映象叫做结点(Node),如图2.5所示。
点链表包括两个域:数据域用来存储结点的值;指针域用来存储数据元素的直接后继的地址(或位置)。链表正是通过每个结点的指针域将线性表的n个结点按其逻辑顺序链接在一起。由于链表的每个结点只有一个指针域,故将这种链表又称为单链表。
由于单链表中每个结点的存储地址是存放在其前趋结点的指针域中的,而第一个结点无前趋,因而应设一个头指针H指向第一个结点。同时由于表中最后一个结点没有直接后继,则制定线性表中最后一个结点的指针域为“空”(NULL)。这样对于真个链表的存取必须从头指针开始。
例如:图2.6所示为线性表(A,B,C,D,E,F,G,H)的单链表存储结构,整个链表的存取需从头指针开始进行,依次顺着每个结点的指针域找到线性表的各个元素。
一般情况下,我们使用链表,只关心链表中结点间的逻辑顺序,并不关心每个结点的实际存储位置,因此我们通常用箭头来表示链域中的指针,于是链表就可以更直观地画成用箭头链接起来的结点序列。图2.6示例的单链表可表示为图2.7。
有时为了操作的方便,还可以在单链表的第一个结点之前附设一个头结点,头结点的数据域可以存储一些关于线性表的长度的附加信息, 也可以什么都不存;而头结点的指针域存储指向第一个结点的指针(即第一个结点的存储位置)。此时头指针就不再指向表中第一个结点而是指向头结点。如果线性表为空表,则头结点的指针域为“空”,如图2.8所示。
有上述可见,单链表可以有头文件唯一确定。单链表的存储结构描述如下:
typedef srtuct Node/*结点类型定义*/ { Elem Type data; struct Node *next; }Node,*LinkList;/*LinkList为结构指针类型*/ |
假设L是LinList型的变量,则L是一个结构指针,即单链表的头指针,它指向表中第一个结点(对于带头结点的单链表,则指向单链表的头结点),若L=NULL(对于带头结点的单链表为L->next=NULL),则表示点链表为一个空表,其长度为0。 若不是空表,则可以通过头指针访问表中结点,找到要访问的所有结点的数据信息。对于带头结点的单链表L,p=L->next指向表中的第一个结点a1,即p->data-a1,而p->next->data=a2。其余依次类推。