数据结构2————链表的简单应用1

数据结构2————链表的简单应用1

一.前言

在我的上一篇博客中,介绍和链表是什么和链表的基本操作,即创建输出,增删改查。这些是链表应用的基础,几乎所有链表的应用都是在这些的基础上进行的延伸和融合。所以对于链表的基本操作一定要做到熟练掌握。

在我的上篇博客中还写了一些关于链表的应用。
1. 升序合并
2. 基于链表的冒泡和插入排序
3. 删除a链中于b链重复的节点
4. n个小孩报数问题
5. 简单的学生管理系统

在上篇博客写完后,又学习了一些关于链表的应用,所以写这片博客跟大家分享一下
1. 链表的逆置
2. 两个升序合并为降序
3. 两个长度不同的链表交叉合并
4. 简易通讯录
5. 约瑟夫环问题

二.链表的逆置

1. 方法一

思路:
灵活运用头插法进行逆置,初始为一个头节点,找一个临时指针存下一个节点地址,将下一个节点以头插法的形式插入(插入前备份指针域)。

核心代码

//插入节点的指针域(下一个节点的地址)备份
pTemp1=pTemp2;
pTemp2=pTemp1->next;

//头插
pTemp1->next=pHead->next;
pHead->next=pTemp1;

完整代码

void contrary(struct node *pHead)
{
	struct node *pTemp1=pHead->next,*pTemp2=pHead->next;
	pHead->next=NULL;
	while(pTemp2)
	{
		pTemp1=pTemp2;
		pTemp2=pTemp1->next;
		
		pTemp1->next=pHead->next;
		pHead->next=pTemp1;	
	}
}

完整代码地址(包含创建和输出)
点我查看
或者点我查看

2. 方法二

思路:
从第一个节点开始,改变每个节点的指针域指向,直到最后一个节点,是否原来不存数据的头节点,新申请一个头节点

核心代码

pi_q=pi;//移动 
pi=pi_h;
pi_h=pi->next;
		
//改变指向
pi->next=pi_q;

pi——当前要改变的节点
pi_q——当前要改变的节点前节点
pi_h——当前要改变的节点后节点

完整代码

struct lianbiao *Nizhi(struct lianbiao *pHead)
{
	struct lianbiao *pi=pHead,*pi_q,*pi_h=pHead->next,*p;
	while(pi_h)
	{
		pi_q=pi;//移动 
		pi=pi_h;
		pi_h=pi->next;
		
		pi->next=pi_q;
	} 
	free(pHead);
	pHead->next->next=NULL;//原来头部指针清0 
	p=(struct lianbiao *)malloc(sizeof(struct lianbiao));
	p->next=pi; 
	return p;
}     

完整代码地址(包含创建和输出)
点我查看
或者点我查看

二. 两个升序链表合并为降序

1. 方法一

将俩个升序链表合并升序链表,再调用上面的(逆置)函数进行逆置

2.方法二

思路
类似于两个升序链表合成一个升序链表,使用3个指针,a,b.a指向La链表链表当前节点,b指向Lb链表的当前节点,比较a,b所指的节点数据域大小,将小的以头插的方式插到La头节点后(插入前备份),然后小的后移。
核心代码

//备份和移动
La_t=La->next;
La->next=pHead_a->next;
//头插
pHead_a->next=La;
La=La_t;

完整代码

void merge(struct node *pHead_a,struct node *pHead_b)
{
	struct node *La,*Lb,*Lc,*La_t,*Lb_t;
	La=pHead_a->next;
	Lb=pHead_b->next;
	
	pHead_a->next=NULL;
	while(La&&Lb)
	{
		if((La->date)<(Lb->date))
		{
			La_t=La->next;
			La->next=pHead_a->next;
			pHead_a->next=La;
			La=La_t; 
			
		}
		else
		{
			Lb_t=Lb->next;
			Lb->next=pHead_a->next;
			pHead_a->next=Lb;
			Lb=Lb_t;	
		}
	}

	while(La)//假如b链表遍历完,a链表未遍历完,将a链表剩下的节点进行头插
	{
		La_t=La->next;
		La->next=pHead_a->next;
		pHead_a->next=La;
		La=La_t;	
	}
	while(Lb)
	{
		Lb_t=Lb->next;
		Lb->next=pHead_a->next;
		pHead_a->next=Lb;
		Lb=Lb_t;	
	}

}


完整代码地址(包含创建和输出)
点我查看
或者点我查看

三.交叉合并

题意要求
将两个长度不一样的链表进行交叉合并(即a1,b1,a2,b2),并以长度短的为开头。当短的排完后,剩下的节点全部为长链表

思路
使用Lx,Ly,Lz三个指针,Lx指向短链表,Ly指向长链表,Lz指向已排好序的链表尾部,使用指针通过Lz,Lx,Ly将它们交叉串起来

核心代码

Lz->next=Lx; //连接lz和短链表的当前节点
Lx_h=Lx->next;//备份Lx的指针域
		
Lx->next=Ly;//连接Lx当前节点和Ly当前节点

Lz=Ly;//移动lz,使lz成为当前已排好节点的尾部
Lx=Lx_h;//移动lx,使lx成为短链表未排好序的第一个
Ly=Ly->next;//移动ly,使ly成为长链表未排好序的第一个

完整代码

void merge(struct node *pHead_x,struct node *pHead_y)
{
	struct node *Lx,*Lx_h,*Ly,*Lz;
	Lx=pHead_x->next;
	Ly=pHead_y->next;
	Lz=pHead_x;
	while(Lx)
	{
		Lz->next=Lx;
		Lx_h=Lx->next;
		
		Lx->next=Ly;
		Lz=Ly;
		
		Lx=Lx_h;
		Ly=Ly->next;
		
	}
	Lz->next=Ly;	
}

完整代码地址(包含创建和输出)
点我查看
或者点我查看

四.简单通讯录

题意:
通信录应该包括联系人姓名,电话,类别
实现通讯录的信息的增删改查
拓展:输出按人名的字典顺序输出
思路
这道题是链表的使用链表的基本操作,创建,遍历,增删改查,排序的糅合

完整代码地址
点我查看
或者点我

五.约瑟夫环

1. 基本款

题意:
n个人按顺时针方向围坐在一张圆桌周围,每个人手中持有一张密码。 一开始任选将第一个人的密码牌作为报数上限m,从第一个人开始从1报数,直到报到m为止,报道m的人出队,将他的密码作为新的m,继续报数,重复直到只剩一个人的时候停止。要求密码随机
思路:

  • 创建一个循环链表(和单链表相比,头节点存数据,尾节点指针域存头指针)。每个节点的数据域由两部分构成,序号和密码,密码由随机函数rand()产生随机数,序号由建立时依次填入。
  • 进行循环,先使用指针找到报到m的节点(人),再对这个节点进行删除操作(不释放空间)。然后将这个节点的密码赋给m,输出本次出队结果。然后再进行循环。
  • 循环的停止,当指针的和他的指针域相同时,出循环。此时剩下的节点为最后一个人。
    核心代码
while(pi!=pi->next)
	{ 
		for(i=0;i<M-1;i++)//寻找本次需要删除的节点 
		{
			pi_q=pi;
			pi=pi->next;
		}
			M=pi->num;//换密码 
		pi_q->next=pi->next;//出队 
		pi=pi->next;//移动`
	}

完整代码

struct node *f1(struct node *pHead,int iCound)
{
	struct node *pi=pHead,*pi_q=pHead->next;
	int i,M,a[Long]={0},j;
	while(pi_q->next!=pi)
		pi_q=pi_q->next;
	M=pi->num;
	while(pi!=pi->next)
	{ 
		for(i=0;i<M-1;i++)//寻找本次需要删除的节点 
		{
			pi_q=pi;
			pi=pi->next;
		}
		printf("本次密码为%d ",M);//输出查看 
		printf("本次序号为%d出队\n",pi->NO);
		a[pi->NO]++;//表示出队
		printf("已出队:");
		for(j=0;j<Long;j++)
			if(a[j]!=0)
				printf("%-5d",j);
		printf("\n还在队中:");
		for(j=0;j<iCound;j++)
			if(a[j]==0)
				printf("%-5d",j);	
		printf("\n\n"); 
		 
		M=pi->num;//换密码 
		pi_q->next=pi->next;//删除 
		pi=pi->next;
	}
	printf("最后在队中的为%d\n",pi->NO); 
}

完整代码地址
点我查看
或者点我查看

2. 扩展1

拓展内容:
不从1号开始报数,第一个报数的人随机选择

思路:
大致同第一个一样,不过再进循环前找到随机开始的第一个报数人

新增代码

M=rand()%(iCound-1)+1;
printf("从%d个人开始报数\n",M);
for(i=0;i<M-1;i++)//寻找第一个人 
{
	pi_q=pi;
	pi=pi->next;
}
M=pi->num;

完整代码

void f1_1()
{
	int j;
	printf("已出队:");
	for(j=0;j<Long;j++)
		if(a[j]!=0)
		printf("%-5d",j);
	printf("\n还在队中:");
	for(j=1;j<iCound+1;j++)
		if(a[j]==0)
			printf("%-5d",j);	
	printf("\n\n");
} 
struct node *f1(struct node *pHead,int iCound)
{
	struct node *pi=pHead,*pi_q=pHead->next;
	int i,M;
	while(pi_q->next!=pi)
		pi_q=pi_q->next;
	M=rand()%(iCound-1)+1;
	printf("从%d个人开始报数\n",M);
	for(i=0;i<M-1;i++)//寻找第一个人 
	{
		pi_q=pi;
		pi=pi->next;
	}
	M=pi->num;
	while(pi!=pi->next)
	{ 
		for(i=0;i<M-1;i++)//寻找本次需要删除的节点 
		{
			pi_q=pi;
			pi=pi->next;
		}
		printf("本次密码为%d ",M);//输出查看 
		printf("本次序号为%d出队\n",pi->NO);
		a[pi->NO]++;
	 	f1_1();
		 
		M=pi->num;//换密码 
		pi_q->next=pi->next;//删除 
		pi=pi->next;
	}
	printf("最后在队中的为%d\n",pi->NO); 
}

完整代码地址
点我查看
或者点我查看

3. 扩展2

拓展内容:
某个人出队时,再次开始报数,随机从左或从右随机开始报数

思路

  • 创建一个双向循环链表,与单表相比,指针域分为两部分,左指针和右指针(llink和rlink),左指针指向前一个节点,右指针指向后一个节点,头节点的左指针域存尾节点的地址。尾节点右指针存头节点地址。
  • 同二相似,随机选择第一个开始报数的人。选择后进入循环
  • 使用rand()函数,若值为0,使用左指针开始向前循环,若值为1,使用右指针开始向后遍历循环
  • 同一相似,开始遍历链表到m时,使这个节点出队。
  • m重新赋值,再次进行选择方向
  • 当节点的左右指针域都存着自己节点的地址时,停止循环。此时,剩下的节点为最后一个人。

核心代码

for(i=0;i<M-1;i++)//寻找本次需要删除的节点 
	{
		if(order)
		{
			pi_q=pi;
			pi=pi->rlink;
			pi_h=pi->rlink;
		}	
		else
		{
			pi_h=pi;
				pi=pi->llink;
			pi_q=pi->llink;
		}
	}
	if(M==1)//密码为1时进行的处理
	{
		if(order)
			pi_h=pi->rlink;	
		else
			pi_q=pi->llink;
			
	} 
	
	pi_q->rlink=pi->rlink;//删除 
	pi_h->llink=pi->llink;
	
	order=rand()%2; 方向选择
	if(order)
		i=pi->rlink;
	else
		pi=pi->llink;

完整代码

void f1_1()
{
	int j;
	printf("已出队:");
	for(j=0;j<Long;j++)
		if(a[j]!=0)
		printf("%-5d",j);
	printf("\n还在队中:");
	for(j=1;j<iCound+1;j++)
		if(a[j]==0)
			printf("%-5d",j);	
	printf("\n\n");
} 
struct node *f1(struct node *pHead,int iCound)
{
	struct node *pi=pHead,*pi_q=pHead->llink,*pi_h=pHead->rlink;
	int i,M;
	int order;
		
	M=rand()%(iCound-1)+1;
	printf("从%d个人开始报数\n",M);
	
	for(i=0;i<M-1;i++)//寻找第一个人 
	{
		pi_q=pi;
		pi=pi->rlink;
		pi_h=pi->rlink;
	}
	M=pi->num;
	order=rand()%2;
	while(pi->llink!=pi&&pi->rlink!=pi)
	{ 
		
		if(order) 
			printf("本次向右转  ");
		else
			printf("本次向左转  ");
		printf("本次密码为%d ",M); 
		for(i=0;i<M-1;i++)//寻找本次需要删除的节点 
		{
			if(order)
			{
				pi_q=pi;
				pi=pi->rlink;
				pi_h=pi->rlink;
			}	
			else
			{
				pi_h=pi;
				pi=pi->llink;
				pi_q=pi->llink;
			}
		}
		if(M==1)
		{
			if(order)
				pi_h=pi->rlink;	
			else
				pi_q=pi->llink;
			
		} 
		printf("本次序号为%d出队\n",pi->NO);//输出查看
		a[pi->NO]++;
	 	f1_1();
		 
		M=pi->num;//换密码 
		pi_q->rlink=pi->rlink;//删除 
		pi_h->llink=pi->llink;
		//free(pi);
		order=rand()%2;
		if(order)
			pi=pi->rlink;
		else
			pi=pi->llink;
		
	}
	printf("最后在队中的为%d\n",pi->NO); 
}

完整代码地址
点我查看
或者点我查看

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值