目录
- 双向链表
- 双链表的插入操作
- 双链表的删除操作
- 循环链表
- 循环双链表
- 静态链表
双向链表
单链表节节点中只有一个指向其后继的指针,使得单链表只能从头结点一次顺序的向后遍历。要访问某个记得点的前驱结点(插入、删除操作时),只能产品能够头开始遍历,访问后继节点的时间复杂度为O(1),访问前驱结点的时间复杂度为O(n)。
为了克服单链表的上述缺点,引入可双链表,双链表节点中有两个指针prior和next,分别指向其前驱节点和后继节点
双链表中结点类型的描述如下:
typedef struct DNode{ // 定义双链表节点类型
ElemType data ; // 数据域
struct DNode *prior , *next ; //前驱和后继指针
} DNode , *DLinkList
双链表在单链表的节点中增加了一个指向其前驱的prior指针,因此双链表中的按值查找和按位查找的操作与单链表的相同。但双链表在插入个删除操作的实现上,与单链表有着较大的不同。这是因为“链”变化还是也需要对prior指针做出修改,其关键是保证在修改的过程中不断链。此外双链表可以很方便地找到其前驱节点,因此,插入、删除操作的时间复杂度仅为O(1)
双链表的插入操作
插入操作的代码片断如下
s->next = p->next ; // 将节点*s插入到节点*p之后
p->next->prior = s ;
s->prior = p ;
p->next = s ;
上述代码的语句顺序不是唯一的,但也不是任意的,1和2两步必须在第4步之前,否则*p的后继节点的指针就会丢掉,导致插入失败
双链表的删除操作
删除双链表中节点p的后继节点q
删除操作的代码片段如下:
p->next = q ->next ;
q->next -> prior = p ;
free(q) ;
在建立双链表的操作中,也可以采用如同单链表的头插法和尾插法,但在操作上需要指针和单链表有所不同
循环链表
循环单链表和单链表的区别在于,表中最后一个节点的指针不是null,而改为指向头结点,从而整个链表形成一个环。
在循环单链表中表尾节点*r的next域指向L , 故表中没有指针域为null的节点,因此,循环单链表的判空条件不是头结点的指针是否为空,而是它是否等于头指针
循环单链表中的插入、删除算法与单链表的几乎一样,所不同的是若操作是在表尾进行,则指向的操作不同,以让单链表继续保持循环的性质。当然,正是因为循环单链表是一个环,因此在任何一个位置上的插入和删除操作都是等价的,无需判断是否是表尾
在单聊表中只能从表头节点开始往后顺序遍历整个链表,而循环单链表可以从表中的任意一个节点开始遍历整个链表。有时对单链表常做的操作是在表头和表尾进行的,此时对循环单链表不设头指针而仅设尾指针,从而使得操作效率更高。其原因是,若设的是头指针,对表尾进行操作需要O(n)的时间复杂度,而若设置的是尾指针r,r->next即为头指针,对于表头与表尾进行操作都只需要o(1)的时间复杂度
循环双链表
由循环单链表的定义不难推出循环双链表。不同的是在循环双链表中头结点的prior指针还要指向表尾节点。
在循环双链表L中,某节点*p为尾节点时,p->next ==L ; 当循环双链表为空表时,其头节点的prior域和next域都等于L
静态链表
静态链表借助数组来描述线性表的链式存储结构,节点也有数据域data和指针域next,与前面所讲的链表中的指针不同的是,这里的指针是节点的相对地址(数组下标),又称游标,静态链表也要预先分配一块连续的内存空间
静态链表结构类型的描述如下
#define MaxSize 50 // 静态链表的最大长度
typedef struct { // 静态链表结构类型的定义
ElemType data ; // 存储数据元素
int next ; // 下一个元素的数组下标
}SLinkList[MaxSize] ;
静态链表以next==-1 作为其结束的标志。静态链表的插入和删除操作也动态链表的相同,只需要修改指针,而不需要移动元素