2.6单链表的合并
1.建立两个带头结点的有序单链表La,Lb,利用La,Lb的结点构造一个新的单链表Lc,使得合并后的Lc表仍然有序。
2.实验要求及说明
程序需要设立三个指针:Pa,pb,pc。其中pa,pb分别指向La,Lb中当前待比较、准备插入Lc表的结点;pc指向Lc表中当前最后一个结点,pc的初值指向La表的头结点。La表和Lb表的长度是隐含的,当pa或pb为空时,表示La表或Lb表扫描完毕。两个单链表合并前、后状态示意图如下:
3.参考程序
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#define OK 1
#define ERROR -1
#define OVERFLOW 0
typedef struct LNode
{
int data;
struct LNode *next;
}LNode,*LinkList;
int InitList_L(LinkList *L)
{
*L = (LinkList)malloc(sizeof(LNode));
if(!(*L))
exit(OVERFLOW);
(*L)->next = NULL;
return OK;
}
int TravseList_L(LinkList L)
{
LinkList p;
p = L->next;
while(p)
{
printf("%d ",p->data);
p = p->next;
}
printf("\n");
return OK;
}
int CreateList_L(LinkList L,int n)
{
LinkList p,q;
int i;
printf("Input the datas in increasing order: ");
q = L;
for(i=0;i<n;i++)
{
p = (LinkList)malloc(sizeof(LNode));
scanf("%d",&p->data);
p->next = q->next;
q->next = p;
q = p;
}
return OK;
}
int MergeList_L(LinkList La,LinkList Lb,LinkList Lc)//合并两个有序单链表La,Lb到Lc,使Lc表仍然有序
{
LNode *pa, *pb, *pc;
pa = La->next;
pb = Lb->next;
pc = La; // pc指针指向La表的头结点
while(pa && pb)
{
if(pa->data <= pb->data)
{
pc->next = pa; //插入La表的结点
pc = pa;
pa = pa->next;
}
else
{
pc->next = pb; //插入Lb表的结点
pc = pb;
pb = pb->next;
}
}
pc->next = pa?pa:pb; //插入剩余结点
free(Lb);
return OK;
}
void main() //两个有序单链表合并的主函数
{
int n;
LinkList La,Lb,Lc;
InitList_L(&La);
InitList_L(&Lb);
printf("Input the length of the list La: ");
scanf("%d",&n);
CreateList_L(La,n); //建立有序的La表
TravseList_L(La); //遍历La表
printf("Input the length of the list Lb: ");
scanf("%d",&n);
CreateList_L(Lb,n); //建立有序的Lb表
TravseList_L(Lb); //遍历Lb表
Lc = La; //Lc指向La表的头结点
MergeList_L(La,Lb,Lc); //合并La,Lb表
printf("Output the datas of La and Lb in Lc: ");
TravseList_L(Lc); //遍历Lc表
}
4.思考题
(1)如果用单链表表示两个集合A,B,如何求两个集合的差A-B
//两个集合的差A-B
int DifferenceList_L(LinkList A,LinkList B)
{
LNode *a,*b,*e;
int flag=0;
a = A->next;
b = B->next;
e = A;
while(1)
{
while(1)
{
if(a->data == b->data)
{
flag = 1;
break;
}
if(b->next == NULL)
break;
b = b->next;
}
if(flag)
{
e->next = a->next;
if(a->next == NULL)
{
free(a);
break;
}
free(a);
a = e->next;
flag = 0;
}
else
{
e = a;
a = a->next;
}
b = B->next;
}
return OK;
}
(2)如何将一个带头结点的单链表La分解成两个同样结构的单链表Lb,Lc,使得Lb中只含La表中的奇数结点,Lc中只含La表中的偶数结点。
//拆分链表La
int SplitList_L(LinkList La,LinkList Lb,LinkList Lc)
{
LNode *a,*b,*c;
a = La->next;
b = Lb;
c = Lc;
while(1)
{
if(a->data % 2 == 0)
{
b->next = a->next;
c->next = a;
c = c->next;
c->next = NULL;
a = b->next;
}
else
{
a = a->next;
b = b->next;
}
if(a == NULL)
return OK;
}
}
2.7双向链表的建立及遍历
1.建立一个带头结点的含n个数据元素的双向链表。N个结点的值由键盘输入,元素类型为整型,然后遍历该链表。
2.实验要求及说明
在双向链表中,每个节点有两个指针域,一个指向其直接前趋,另一个指向其直接后继。参考程序中,采用尾插法建立双向链表,定义了两个指针变量P、q,P总是指向新结点,q总是指向当前的表尾结点。利用P、q的交替移动,实现双向链表的建立过程,双向链表的遍历过程类似于单链表的遍历。建立双向链表的示意图如下:
3.参考程序:
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#define OK 1
#define ERROR -1
#define OVERFLOW 0
typedef struct DLNode //双链表结点类型定义
{
int data;
struct DLNode *prior; //前趋指针域
struct DLNode *next; //后继指针域
}DLNode,*DLinkList;
int InitList_DL(DLinkList *L) //初始化函数,建立只含一个头结点的空双向链表
{
*L = (DLinkList)malloc(sizeof(DLNode));
if(!(*L))
exit(OVERFLOW);
(*L)->prior = NULL;
(*L)->next = NULL;
return OK;
}
int CreateList_DL(DLinkList L,int n) //采用尾插法建立含N个结点的双向链表
{
DLNode *p, *q;
int i;
printf("Input the datas: ");
q = L;
for(i=0;i<n;i++)
{
p = (DLNode *)malloc(sizeof(DLNode));
scanf("%d",&p->data);
p->next = q->next; //p结点插入到双向链表的尾结点之后
q->next = p;
p->prior = q;
q = p; //q指向新的尾结点
}
return OK;
}
int TravseList_DL(DLinkList L) //遍历双向链表L
{
DLNode *p;
p = L->next;
while(p)
{
printf("%d ",p->data);
p = p->next;
}
printf("\n");
return OK;
}
void main() //双向链表的建立及遍历主函数
{
DLinkList L;
int n;
InitList_DL(&L); //初始化双链表
printf("Input the Length of the Double LinkList: ");
scanf("%d",&n);
CreateList_DL(L,n); //建立含有n个结点的双链表
printf("Output the Double Linklist: ");
TravseList_DL(L); //遍历双向链表。
}
4.思考题
(1)如何在带头结点的双向链表中的第i个节点之前插入一个新结点呢?
//在i结点之前插入
int InsertList_DL(DLinkList L,int n,int i)
{
DLNode *p,*q;
p = L->next;
int j;
for(j=1;j<i-1;j++)
p = p->next;
q = (DLNode *)malloc(sizeof(DLNode));
printf("Input insert i:");
scanf("%d",&q->data);
q->prior = p;
q->next = p->next;
p->next->prior = q;
p->next = q;
return OK;
}
(2)如何在带头结点的双向链表中删除第i个结点?
//删除第i个结点
int DeleteList_DL(DLinkList L,int i)
{
DLNode *p;
p = L->next;
int j;
for(j=0;j<i-1;j++)
p = p->next;
p->prior->next = p->next;
p->next->prior = p->prior;
return OK;
}
2.8双向循环链表的建立及插入元素
1.建立一个带头结点的含n个元素的双向循环链表L;然后再表L中的第i个元素之前插入一个新元素e。
2.实验要点及说明
注意双向循环链表的头结点及尾结点指针域的变化,注意在查找过程中循环条件的变化及插入元素过程中的指针运算。建立非空双向循环链表由CreateList_CDL函数实现,其中设置了两个指针变量p、q,p始终指向新结点,q始终指向表尾结点,新结点p总是插入表尾。表建立之后,调用GetElemP_CDL函数查找第i个元素,若查找成功,返回其地址,否则返回空指针。在双向循环链表L的第i个元素之前插入新元素e的算法示意图如下所示:
3.参考程序
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#define OK 1
#define ERROR -1
#define OVERFLOW 0
typedef struct DLNode //双链表节点类型定义
{
int data;
struct DLNode *prior;
struct DLNode *next;
}DLNode,*DLinkList;
int InitList_CDL(DLinkList *L) //初始化,建立一个只含有头结点的空双向循环链表
{
*L = (DLinkList)malloc(sizeof(DLNode));
if(!(*L))
exit(OVERFLOW);
(*L)->next = *L;
(*L)->prior = *L;
return OK;
}
int CreateList_CDL(DLinkList L, int n)//利用尾插法,建立一个带头结点的含n个元素的双向循环链表L
{
DLNode *p,*q;
int i;
printf("Input the datas: ");
q = L;
for(i=0;i<n;i++)
{
p = (DLNode *)malloc(sizeof(DLNode));
scanf("%d",&p->data);
p->next = q->next; //新元素p总是插入表尾
q->next = p;
p->prior = q;
L->prior = p; //修改头结点的prior的值,指向新结点p
q = p; //q指向新的尾结点
}
return OK;
}
int TraverseList_CDL(DLinkList L) //遍历双向循环链表
{
DLNode *p;
p = L->next;
while(p!=L)
{
printf("%d ",p->data);
p = p->next;
}
printf("\n");
return OK;
}
DLNode *GetElem_CDL( DLinkList L, int i)
{ // 查找第i个结点,若找到,返回其存储位置,否则返回NULL
DLNode *p;
int j;
p = L->next;
j = 1;
while( p!=L && j<i)
{
p = p->next;
++j;
}
if( p==L || j>i) return NULL;
return p;
}
int ListInsert_CDL(DLinkList L, DLNode *p, int e)//在表L第i个元素之前插入一个新元素e或插入到表尾元素之后
{
DLNode *s;
if(!p) //如果p为空,新结点插入表尾
{
if(!( s = (DLNode *) malloc(sizeof(DLNode))))
return ERROR;
s->data = e;
s->prior = L->prior; //开始插入到表尾
L->prior->next = s;///
s->next = L;
L->prior = s;
return OK;
}
else if(!( s = (DLNode *) malloc(sizeof(DLNode))))
return ERROR;
else {
s->data = e; //s指向新元素e
s->prior = p->prior; //开始插入到第i个元素前
p->prior->next = s;
s->next = p;
p->prior = s;
return OK;
}
}
void main()
{
DLinkList L;
int i,n,e;
DLNode *p;
InitList_CDL(&L);
printf("Input the length of the Circle DLinkList: ");
scanf("%d",&n);
CreateList_CDL(L,n);
printf("Output the circle DLinkList: ");
TravseList_CDL(L);
printf("Input the insert location: ");
scanf("%d",&i);
printf("Input the insert data: ");
scanf("%d",&e);
p = GetElem_CDL(L, i);/查找第i个结点的位置(存储地址)
if(ListInsert_CDL( L, p, e)==1) //插入成功地话,显示遍历过程。
{
printf("Output the circle DLinkList: ");
TravseList_CDL(L);
}
else
printf("Can't insert the data!\n");
}
4.思考题
(1)如何利用头插法建立带头结点的双向循环链表
int CreateList_1_CDL(DLinkList L,int n) //利用头插法,建立一个带头结点的含n个元素的双向循环链表L
{
DLNode *p,*q;
int i;
printf("Input the datas: ");
q = L;
for(i=0;i<n;i++)
{
p = (DLNode *)malloc(sizeof(DLNode));
scanf("%d",&p->data);
if(i==0) //第一个往后移,结点的next一直链接头结点
{
p->next = q->next;
q->next = p;
p->prior = q;
L->prior = p;
}
else //其他结点
{
p->prior = q;
p->next = q->next;
q->next->prior = p;
q->next = p;
}
}
return OK;
}
(2)如何将一个循环双链表L=(a,b,c,d)的前两个元素交换,即转换为L=(b,a,c,d)?
int ExchangeList_CDL(DLinkList L)
{
DLNode *p,*q;
p = L->next;
q = L->next->next;
q->prior = p->prior; //第二个结点的prior指向头节点的next
p->prior->next = q;
p->next = q->next; //第一个结点的next指向第三个结点的prior
q->next->prior = p;
q->next = p;
p->prior = q;
return OK;
}
2.9双向循环链表元素的查找及删除
1.建立一个带头结点的含n个元素的双向循环链表L;然后在表L中查找第i个元素,若查找成功,则删除该元素,否则返回NULL
2.实验要求及说明
注意双向循环链表的头结点及尾结点指针域的变化,注意在查找过程中循环条件的变化及删除元素过程中的指针运算,参考程序中,要在双向循环链表L中删除第i个元素,有一个查找过程。若表中存在第i个元素,通过函数GetElemP_CDL返回第i个元素的地址,否则返回NULL。删除双向循环链表L的第i个元素的算法示意图如下:
3.参考程序
#include "CDLinkList.h" // 源代码见 实验八:双向循环链表的建立及插入
// 建立一个带头结点的含n个元素的双向循环链表L
int CreateList_CDL( DLinkList L, int n ) // 源代码见: 实验八
{ // 建立一个带头结点的含n个元素的双向循环链表L
DLNode *p, *q;
int i;
printf ("Input the datas: ");
q = L;
for(i=0; i<n; i++)
{
p = (DLNode *)malloc(sizeof(DLNode));
scanf ("%d",&p->data);
p->next = q->next; // 新元素p总是插入表尾
q->next = p;
p->prior = q;
L->prior = p; // 修改头结点的prior的值,指向新结点p
q = p; // q指向新的尾结点
}
return OK;
}
DLNode *GetElemP_CDL( DLinkList L, int i )
{ // 在表L中查找第i个元素。若存在,返回它的地址,否则返回NULL
DLNode *p;
int j;
p = L->next;
j = 1;
while( p!=L && j<i )
{
p = p->next;
++j;
}
if (p==L || j>i ) return NULL;
else return p;
}
int ListDelete_CDL( DLinkList L, DLNode *p, int *e )
{ // 删除表中由p指向的第i个元素。若存在,由变量*e返回其值,否则返回ERROR
if(!p)
return ERROR; // 第i个结点不存在
*e = p->data;
p->prior->next = p->next;
p->next->prior = p->prior;
free(p);
return OK;
}
void main( )
{
DLinkList L;
int i, n, e;
DLNode *p;
InitList_CDL(&L);
printf ("Input the Length of the Circle DLinkList: ");
scanf ("%d", &n);
CreateList_CDL( L, n );
printf ("Output the Double Circle lists: ");
TraverseList_CDL( L );
printf ("Input the delete location: ");
scanf ("%d", &i);
p = GetElemP_CDL( L, i );
if (ListDelete_CDL(L, p, &e)==1)删除成功地话,输出遍历结果。
{
printf ("Output the Circle DLinkList: ");
TraverseList_CDL( L);
}
else
printf ("Can't delete the data!\n");
}
4.思考题
(1)从键盘输入若干个字符,以#字符结束,建立带表头结点的双向循环链表
int CreateList_CDL2(DLinkList L) //采用头插法建立双链表
{
DLNode *p, *q;
int flag=1;
char e;
q = L;
while(flag)
{
printf("Input the datas and input # End: ");
scanf ("%c",&e);
getchar();
if(e != '#')
{
p = (DLNode *)malloc(sizeof(DLNode));
p->data = e;
p->next = q->next;
q->next = p;
p->prior = q;
L->prior = p;
q = p;
}
else
flag = 0;
}
return OK;
}
(3)求双向循环链表的长度(表中元素的个数)
int LengthList_CDL(DLinkList L)
{
int i=0;
DLNode *p;
p = L->next;
while(p!=L)
{
++i;
p = p->next;
}
return i;
}
(3)求双向循环链表中相同元素的个数
//输入e,返回双循环链表中与e值相同的元素个数
int RepeatElemList_L(DLinkList L,int e)
{
DLNode *p;
int count=0;
p = L->next;
while(p!=L)
{
if(p->data == e)
count++;
p = p->next;
}
return count;
}
void main()
{
DLinkList L;
int i, n;
char e;
DLNode *p;
InitList_CDL(&L);
CreateList_CDL2( L);
printf ("Output the Double Circle lists: ");
CharTravseList_CDL( L );
int length;
length = LengthList_CDL(L);
printf("Its length is :%d",length);
printf("Input the search element e:");
scanf("%c",&e);
i = RepeatElemList_L(L,e);
printf("与%c 相同的个数为%d",e,i);
}