单链表合并+双向链表+双向循环链表

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);
}
  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值