双向链表是 在单链表的基础上多加了前驱指针域,用来存放前一个结点的地址,并且双向链表结合了循环链表的特征,头结点的前驱指针域存放尾结点的地址,尾结点的后继指针域存放头结点的地址。
单链表只能依次前进,即使删除结点也只能删除后继结点,循环链表是可以删除前驱结点,但是想删除前驱结点必须的循环一周,成本较高,双向列表增加了前驱指针域,可以很方便的结点指针前移寻找要删除的前驱结点,查找即删除速度较快。
遇到的问题:只有一个问题思考了有两个小时多,是出现在两个双向链表的归并函数,对于结点重新整合时对于前驱指针域和后继指针域的重新赋值有些混乱,这种问题最好的解决方式是画图和多设置变量,有些变量你可能已经改变,但是在使用的时候你又按照未改变前使用,这样就容易出错,我的问题就是出在了这里,最后我多设置了变量s1,s2用于L1链表与L2链表结点的后移解决了问题。最后需要注意的是什么时候你定义的结点结束了,要掌控好每个结点的状态,是你已经归并链接了还是任然是未被归并的结点。有时候就特别容易混淆导致出错。
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#define OK 1
#define TRUE 1
#define ERROR 0
#define FALSE 0
#define LIST_INIT_SIZE 100
#define LISTINCREMENT 10
typedef int Status;
typedef Status ElemType;
typedef ElemType * List;
typedef struct LNode{
ElemType data; //每一个结点的数据域
struct LNode *prior; //指针域1指向结点的直接前驱 (存放前一个结点的地址)
struct LNode *next; //指针域2指向结点的直接后继 (存放后一个结点的地址)
}LNode, *LinkList;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
void MergeList(LinkList &L1,LinkList &L2,LinkList &L3);
void CreateList(LinkList &L,ElemType n);
Status ListInsert(LinkList &L,ElemType i,ElemType e);
Status ListDelete(LinkList &L,ElemType i,ElemType &e);
int main(int argc, char *argv[]) {
LinkList L1;
LinkList L2;
LinkList L3;
Status i,m,n,u;
printf("创建链表L1的长度为:");
scanf("%d",&n);
printf("输入链表各结点的数据:");
CreateList(L1,n);
printf("\n");
LinkList b5=L1->next;
printf("请输出创建好的链表1:");
for(i=0;i<n;i++){
printf(" %d ",b5->data);
b5=b5->next;
}
printf("\n");
//测试:ListInsert函数;
LinkList a1=L1->next; //从第一个数据域不为空的结点开始
printf("测试:在L1链表的第三个结点前插入数据域为5的结点;\n");
if(ListInsert(L1,3,5)){
printf("请输出插入后的队列:");
for(i=0;i<=n;i++){
printf(" %d ",a1->data);
a1=a1->next;
}
}
printf("\n");
//测试:ListDelete函数;
LinkList b1=L1->next;
printf("测试:删除L1链表的第四个结点:\n");
if(ListDelete(L1,4,u)){
printf("删除结点的数据为:%d\n",u);
}
printf("请输出删除后的队列:");
for(i=0;i<n;i++){
printf(" %d ",b1->data);
b1=b1->next;
}
printf("\n");
printf("\n");
printf("创建链表L2的长度为:");
scanf("%d",&m);
printf("输入链表各结点的数据:");
CreateList(L2,m);
printf("\n");
MergeList(L1,L2,L3);
LinkList a3=L3->next;
LinkList a4=L3->next;
ElemType q=0;
while(a4->next!=L3){
q++;
a4=a4->next;
}
printf("新链表结点的个数:%d\n",q);
printf("请输出归并后L3的元素序列:");
for(i=0;i<q;i++){
printf(" %d ",a3->data);
a3=a3->next;
}
printf("\n");
return 0;
}
void CreateList(LinkList &L,ElemType n){ //创建一个长度为n的单链表
ElemType i;
L=(LinkList)malloc(sizeof(LNode)); //申请一个结点大小的地址空间
L->next=L; //头结点为空,直接前驱与直接后继均指向本身
L->prior=L;
LinkList p,q;
q=L;
//总体链表创建的过程是逆着进行,先创建最后一个结点,依次在该结点的前边添加新结点。
for(i=0;i<n;i++){
p=(LinkList)malloc(sizeof(LNode));
scanf("%d",&(p->data)); //为增加的结点添加数据域
//增加结点前驱不用变
/***********起始对添加结点的链接过程想不明白**********/
p->prior=q;
q->next=p;
L->prior=p;
p->next=L;
q=q->next;
}
}
Status ListInsert(LinkList &L,ElemType i,ElemType e){ //在L的第i个元素前插入元素e
LinkList p=L; //初始化头节点
ElemType j=0;
while(p&&j<i-1){ //寻找第i个结点的前一个结点
p=p->next; //循环完成找到第i-1个结点
++j;
}
if(!p||j>i-1) return ERROR;
LinkList s=(LinkList)malloc(sizeof(LNode)); //增加新结点
s->data=e; s->prior=p; s->next=p->next; //新结点的指针域存放原来第i个结点的地址。
p->next->prior=s;
p->next=s;
return OK;
}
Status ListDelete(LinkList &L,ElemType i,ElemType &e){ //删除链表的第i个结点,用e返回该节点的值。
LinkList p=L; //初始化头节点
ElemType j=0;
while(j<i){ //寻找第i个结点
p=p->next; //循环完成找到第i个结点
++j;
}
if(j>i) return ERROR;
p->prior->next = p->next;
p->next->prior = p->prior;
e=p->data; //将删除结点的数据域赋给变量e
free(p); //释放删除结点所占用的空间
return OK;
}
void MergeList(LinkList &L1,LinkList &L2,LinkList &L3){ //单链表的归并
LinkList p1,p2,p3,s1,s2;
p1=s1=L1->next;
p2=s2=L2->next;
L3=(LinkList)malloc(sizeof(LNode));
L3->next=L3;
L3->prior=L3;
p3=L3;
while((p1!=L1)&&(p2!=L2)){ //结束条件有一个链表的指针域为空
if((p1->data)<=(p2->data)){
s1=s1->next; L3->prior=p1; p3->next=p1; p1->next=L3; p1->prior=p3; p3=p1; p1=s1;
}else{
s2=s2->next; L3->prior=p2; p3->next=p2; p2->next=L3; p2->prior=p3; p3=p2; p2=s2;
}
}
//将剩余结点拼接
//这里需要找到最后一个结点的地址
LinkList c1=p1,c2=p2;
while(c1!=L1){ //找L1的最后一个结点
c1=c1->next;
}
if(p1!=L1){
L3->prior=c1;
c1->next=L3;
p3->next=p1;
p1->prior=p3;
}
while(c2!=L2){ //找L2最后一个结点
c2=c2->next;
}
if(p2!=L2){
L3->prior=c2;
c2->next=L3;
p3->next=p2;
p2->prior=p3;
}
free(L1); //释放列表1的头结点
free(L2); //释放列表2的头结点
}