双链表
单链表和双链表的区别
单链表节点中只有一个指向其后续的指针,想要访问某个节点的前驱时,只能从头开始遍历,为了方便,引入双链表
双链表节点中有两个指针prior和next,分别指向直接前驱和直接后继
双链表示意图:
双链表的定义
typedef struct DNode
{
int data;
struct DNode *prior, *next;//前驱和后继两个指针
} DNode, *DLinkList;
双链表和单链表相似,但在插入和删除中有所不同,在修改过程中也要对前驱指针进行修改,确保不断链,同时,因为有前驱指针,可以很方便查找到当前节点的前驱,删除插入操作为O(1)
双链表的初始化
bool InitDlinkList(DLinkList &L)
{
L = (DNode *)malloc(sizeof(DNode));
if (L == NULL)
return false;
L->prior = NULL;
L->next = NULL;
初始化过程中,头节点的前驱指针和后继指针都需指向NULL。
双链表的插入
在p节点后插入s节点
1 s->next = p->next;
2 p->next->prior = s;
3 s->prior = p;
4 p->next = s;
上述操作中顺序不唯一,但1要在4之前,否则p的后继节点就会丢掉
一定要注意不能将指针丢掉
具体实现中还需要留意是否为尾节点
bool InsertNextDNode(DNode *p, DNode *s)
{
if (p == NULL || s == NULL)
return false;
s->next = p->next;
if (p->next != NULL)
{
p->next->prior = s;
}
s->prior = p;
p->next = s;
return true;
}
双链表的删除操作
删除p的后继节点q
1 p->next = q->next;
2 q->next->prior = p;
3 free(p);
具体实现中也要考虑完善
bool DeleteNextDNode(DNode *p)
{
if (p == NULL)
return false;
DNode *q = p->next;
if (q == NULL)
return false;
p->next = q->next;
if (q->next != NULL)
q->next->prior = p;
free(q);
}
循环链表
循环链表分为循环单链表和循环双链表,先从循环单链表开始讲起
循环单链表的尾指针不为NULL,而是指向头节点,由此构成一个环,故称为循环单链表,一般叫做循环链表(不特地说明,一般默认为循环链表)
特殊的地方
循环单链表定义和单链表定义相同,但在初始化的过程中有所区别
bool InitList(LinkList &L)
{
L = (LNode *)malloc(sizeof(LNode));
if (L == NULL)
{
return false;
}
L->next = L;
return true;
}
尾指针指向头节点,所以在判断是否为空的时候,
bool Empty(LinkList L)
{
if (L->next == L)
return true;
else
return false;
}
只需要判断头节点的后继是否是头节点,就可以判断是否为空
同样,判断是否为尾节点,只需要判断该节点的后继是否为头节点即可
bool isTail(LinkList L, LNode *p)
{
if (p->next == L)
return true;
else
return false;
}
循环双链表
循环双链表和循环单链表类似,即把双链表的首位相连就是
静态链表
typedef struct ListNode
{
ElemType data;
int cur; //静态链表中的游标
}ListNode;
静态链表实际上就是一个结构体数组
该节点的游标指向下一个节点索引,链式前向星也是这样实现(存图)
静态链表的存储单元地址一定连续,链式结构的内部存储单元地址一定连续
静态链表本质是一个数组,所以线性表的存储单元地址可能连续,也可能不连续