数据结构与算法第二章(线性表的链式结构中的循环链表与双向链表)
循环链表
循环链表,是指链表的最后一个结点的指针域指向头结点,使得链表头尾结点相连,从而形成一个环。
其基本功能操作与单链表类似,唯一不同指出在于判断链表是否结束时,不在为判断结点的指针域是否为空,而是判断结点是否指向头结点。也就是:
while(p->Next != L)
另外,循环链表可以从链表的任意位置开始遍历链表,得到的结果都是完整的链表
双向链表
在之前对单链表进行删除的时候,我们用到了两个指针以及两层循环去解决,这样导致空间与时间上的浪费,而双向链表便可以轻松的解决这个问题。在这里,我们定义每个节点除了数据域data外,包含了两个不同的指针,一个指针名叫prior,指向它的前驱结点;另一个名叫next,指向它的后继结点,所以双向链表的定义如下:
typedef struct LNode{
ELemType data;
struct LNode *piror,*next;
}LNode;
由于双向链表的指针定义,我们得出:
p->next->prior <=> p; //假设p是LinkList型指针,<=>为两边都等价
p->prior->next <=> p;
由于双向链表的指针的特殊性,它的基本操作与单链表有所不同,以下为双向链表的基本操作:
1.初始化
Status InitList(LinkList &L)
{
if(L == NULL)
return 0;
L = (LinkList *)malloc(sizeof(LNode));
L->next = NULL:
L->prior = NULL; //使前驱指针与后继指针均初始化为空
return 1;
}
2.创建链表
Status CreateList(LinkList &L,int m)
{
InitList(L);
LinkList p;
p = L; //定义LinkList类型的指针q,并使q指向p
for(int i = 0;i < m;i++)
{
LinkList q;
q = (LinkList *)malloc(sizeof(LNode)); //定义LinkList类型的指针p
q->data = rand()%100+1; //赋予q->data一个随机数
q->prior = p; //先使q的前驱指针指向p,也就是指向头结点
q->next = NULL; //使q的后继指针初始化为空
p->next = q; //p的后继指针指向q
p=q; //p向后移动一个位置
}
}
插入指针的顺序为:
(1)先赋给q->data数值
(2)使q的前驱指针指向p,也就是指向头结点
(3)使q的后继指针初始化为空,这步至关重要,它影响后续的插入,如果不初始化为空,q->next将成为野指针
(4)p的后继指针指向q
(5)p向后移动一个位置,进行下一次插入
3.双向链表的遍历与单链表相同
4.删除指定位置的数
Status DeleteLinkList_D(LinkList &L,int i,ElemType &e)
{
LinkList p;
int n =0;
while(p != NULL && n<i)
{
p=p->next;
n++;
}
if(p=NULL || n>i)
return 0;
e = p->data;
p->prior->next = p->next; //将p之前一个元素的后继指针指向p的下一个元素
p->next->prior = p->prior; //将p之后的元素的前置指针指向p的前一个元素
//此时p彻底被切断
free(P); //将指针p释放
return 1;
}