数据结构:双链表的创建,在P结点前后插入结点,删除P前后的结点以及P结点
提到链表的首元结点,我们首先想到的是head标志的结点,但其实head结点只是人为规定的,首元结点既可以定义为head也可以定义为end,尾元结点也是如此,即首元结点和尾元结点可以任意定义。
有了以上的思想我们将结点看成火车的一节车厢,结点中的信息看成车厢中的货物,首元结点看作车头来看待链表。而双链表则可以看成有两个车头的火车,即首元结点和尾元结点都是车头,这样一来火车既可以正着开也可以逆着开,所以也就没有纯粹意义上的首元结点和尾元结点了,即首元结点和尾元结点等价。
所以在双链表中要实现形如下图的两种关系:
定义结构体
与单链表相比应该多增加一个指针
typedef struct node
{
int date;
struct node * pr;
struct node * next;
}Node,*LinkList;
链表初始化
初始化时,为了表示方便,以head来表示首元结点,用end来表示尾元结点,同时这两个结点不用来储存信息,由于将head看作第一个结点,end看作最后的结点,所以head->pr=NULL,end->next=NULL
void InitList(LinkList head,LinkList end)
{
head->next=end;
end->next=NULL;
end->pr=head;
head->pr=NULL;
}
链表的创建
void Creat(LinkList end,int n)
{
int i;
LinkList node;
for(i=1;i<=n;i++)
{
node=(LinkList)malloc(sizeof(Node));
node->date=i;
/*这里采用的是尾插法,即插在数据的尾部,但要注意,尾元结点必须在链表尾部,其实质是在尾元结点前插入新结点*/
node->next=end;
end->pr->next=node;
node->pr=end->pr;
end->pr=node;
}
}
从链表中取得信息
这里仅以head结点为基准取得信息
LinkList GetElem(LinkList head,int n)
{
LinkList h=head->next;
int i;
for(i=1;i<n;i++)
h=h->next;
return h;
}
将S结点的信息插在P结点前
void InsertBefore(LinkList P,LinkList S)
{/*将S结点的信息插在P结点前*/
S->pr=P->pr;
P->pr->next=S;
S->next=P;
P->pr=S;
}
将S结点的信息插在P结点后
void InsertAfter(LinkList P,LinkList S)
{/*将S结点的信息插在P结点后*/
P->next->pr=S;
S->pr=P;
S->next=P->next;
P->next=S;
}
删除P结点前的结点
void DeletBefore(LinkList P)
{
LinkList Q;
Q=P->pr;
P->pr=Q->pr;
Q->pr->next=P;
free(Q);
}
删除P结点后的结点
void DeletAfter(LinkList P)
{
LinkList Q;
Q=P->next;
P->next=Q->next;
Q->next->pr=P;
free(Q);
}
删除P结点
void DeletItself(LinkList P)
{
LinkList Q,R;
Q=P->pr;
R=P->next;
Q->next=R;
R->pr=Q;
free(P);
}
正序输出
void Show(LinkList head)
{
LinkList h=head->next;
while(h->next!=NULL)
{
printf("%d ",h->date);
h=h->next;
}
printf("\n");
}
逆序输出
void ReShow(LinkList end)
{
LinkList e=end->pr;
while(e->pr!=NULL)
{
printf("%d ",e->date);
e=e->pr;
}
printf("\n");
}
在主函数中验证是否正确
int main()
{
LinkList head,end;
head=(LinkList)malloc(sizeof(Node));
end=(LinkList)malloc(sizeof(Node));//特别注意,申请空间要放到InitList函数前,如果将空间的申请放到InitList函数里执行就会出错
InitList(head,end);//链表初始化
Creat(end,5);//创建五个结点,并将其连在结点上
puts("创建完成后,顺序输出:");
Show(head);//正序输出
puts("创建完成后,逆序输出:");
ReShow(end);//逆序输出
LinkList P,S;
P=GetElem(head,2);
S=(LinkList)malloc(sizeof(Node));
S->date=10;//为了表示方便,在这里将S中的信息设为10
printf("P结点储存的信息为:%d\n",P->date);
printf("S结点储存的信息为:%d\n",S->date);
InsertBefore(P,S);
puts("在P之前插入S(10),顺序输出:");
Show(head);//正序输出
puts("在P之前插入S(10),逆序输出:");
ReShow(end);//逆序输出
P=GetElem(head,2);//此时P=10
S=(LinkList)malloc(sizeof(Node));
S->date=11;//为了表示方便,在这里将S中的信息设为11
printf("P结点储存的信息为:%d\n",P->date);
printf("S结点储存的信息为:%d\n",S->date);
InsertAfter(P,S);
puts("在P后插入S(11),顺序输出:");
Show(head);//正序输出
puts("在P后插入S(11),逆序输出:");
ReShow(end);//逆序输出
printf("P结点储存的信息为:%d\n",P->date);
DeletBefore(P);
puts("删除P之前结点顺序输出:");
Show(head);
puts("删除P之前结点逆序输出:");
ReShow(end);
P=GetElem(head,2);//此时P=11
printf("P结点储存的信息为:%d\n",P->date);
DeletAfter(P);
puts("删除P之后的结点顺序输出:");
Show(head);
puts("删除P之后的结点逆序输出:");
ReShow(end);
P=GetElem(head,2);//此时P=11
printf("P结点储存的信息为:%d\n",P->date);
DeletItself(P);
puts("删除P结点顺序输出:");
Show(head);
puts("删除P结点逆序输出:");
ReShow(end);
}
自然,程序需要有头文件#include<malloc.h>
运行结果如下: