考研408必刷经典真题 2019年41题

1、系列文章目录


题目详情:
在这里插入图片描述


解题思路: 先找到链表的中间结点,前面一半是链表 L,将链表的后半部分给一个新的头结点 L2,然后将链表 L2 进行原地逆置,然后再将 L 和 L2 链表进行合并


2、如何找到链表的中间结点?

定义两个指针 pcur,ppre,我们让pcur 指针每次走两步,ppre 指针每次走一步,这样当 pcur 指针走到最后,那么 ppre 指针刚好在中间

图片如下

在这里插入图片描述

代码如下:

//找到中间结点并分开
void find_middle(LinkList L, LinkList& L2)//因为L2头指针会改变,需要&,L则已经分配好了头结点地址
{
	L2 = (LinkList)malloc(sizeof(LNode));//先为第二条链表申请空间
	LinkList ppre, pcur;//双指针法遍历链表
	ppre = pcur = L->next;//都在第一个结点位置
	while (pcur != NULL)//pcur偏转大,利用它作条件
	{
		pcur = pcur->next;
		if (NULL == pcur)//先偏转一次,防止链表为0的情况
		{
			break;
		}
		pcur = pcur->next;//继续偏转
		if (NULL == pcur)//为了使偶数个结点的链表时,pcur已经为NULL了,ppre依然指向a1,a2,到a6 中的a3结点
		{
			break;
		}
		ppre = ppre->next;
	}
	L2->next = ppre->next;//由L2头结点指向后面一半链表
	ppre->next = NULL;//前面一半链表的最后一个结点,next要为NULL
}

3、如何让L2逆置?

定义三个指针r、s、t,使它们分别指向链表开始的三个结点,先让s->next=r,则a2指向a1,之后r、s、t依次后移,依次完成a3指向a2,a4指向a3,再后移发现t为空,此时结束循环,但最后的a5还没指向,所以在循环外要再执行依次s->next=r。

最后需要让L2->next->next=NULL(此时L2->next->next拿到的是a1),因为原有链表的头结点变成最后一个结点,需要让最后一个结点的next为NULL,再让L2->next=s,使s成为逆置后的链表的第一个结点。

图片如下

在这里插入图片描述

代码如下:

//L2进行逆置
void reverse(LinkList L2) {
	LinkList r, s, t;
	r = L2->next;//指向第一个结点
	if (r == NULL) {
		return;//链表为空,不需要逆置
	}
	s = r->next;//指向第二个结点
	if (s == NULL) {
		return;//链表只有一个结点,不需要逆置
	}
	t = s->next;
	while (t) {
		s->next = r;//反方向连接,开始逆置
		r = s;//以下三步是三个指针的偏转
		s = t;
		t = t->next;
	}
	s->next = r;//当t指针为空时,还差最后一次连接倒置
	L2->next->next = NULL;//意思是(L2->next)为第一个结点,然后作为新链表最后一个结点时,它的->next为空
	L2->next = s;//s变成了新链表的第一个结点,头结点L2指向它
}

4、如何轮流合并?

定义三个指针pcur、p、q,让pcur始终指向合并后的新链表的尾部,初始化为pcur=L->next,让p始终指向L待插入的结点,初始化为p=pcur->next,q始终指向L2待插入的结点,初始化为q=L2->next

循环到最后,L或L2总会有一个链表剩一个结点没放进去,此时判断p不为NULL或者q不为NULL,再将p或q放进去即可

图片如下:

在这里插入图片描述

代码如下:

//进行轮流合并
void merge(LinkList L, LinkList L2) {
	LinkList pcur, p, q;
	pcur = L->next;//pcur从L的第一个结点开始,始终指向组合后链表的链表尾
	p = pcur->next;//p指向L的第二个结点,因为第二个位置要插入L2的结点,所以他指向第二个结点
	q = L2->next;//q指向L2的第一个结点
	while (p != NULL && q != NULL) {
		pcur->next = q;//合并L2的结点
		q = q->next;//q继续偏转
		pcur = pcur->next;//pcur也继续偏转
		pcur->next = p;//合并L的结点
		p = p->next;//p继续偏转
		pcur = pcur->next;//pcur也继续偏转
	}
	//L和L2链表都可能剩余一个结点,要判断后放进来
	if (p != NULL) {
		pcur->next = p;//pcur始终指向组合后链表的链表尾
	}
	if (q != NULL) {
		pcur->next = q;
	}
	/*因为L和L2尾部都有NULL,不需要再操作了*/
}

5、完整代码:

#include <stdio.h>
#include <stdlib.h>
typedef int Elemtype;
typedef struct LNode {
	Elemtype data;
	struct LNode* next;
}LNode,*LinkList;
void list_tail_insert(LinkList& L) {
	L = (LinkList)malloc(sizeof(LNode));
	LinkList s;
	Elemtype x;
	scanf("%d", &x);
	LinkList r = L;
	while (x != 9999) {
		s = (LinkList)malloc(sizeof(LNode));
		s->data = x;
		r->next = s;
		r = s;
		scanf("%d", &x);
	}
	r->next = NULL;
}
void print_list(LinkList L) {
	LinkList r = L->next;
	while (r != NULL) {
		printf("%3d", r->data);
		r = r->next;
	}
	printf("\n");
}
//找到中间结点并分开
void find_middle(LinkList L, LinkList& L2)//因为L2头指针会改变,需要&,L则已经分配好了头结点地址
{
	L2 = (LinkList)malloc(sizeof(LNode));//先为第二条链表申请空间
	LinkList ppre, pcur;//双指针法遍历链表
	ppre = pcur = L->next;//都在第一个结点位置
	while (pcur != NULL)//pcur偏转大,利用它作条件
	{
		pcur = pcur->next;
		if (NULL == pcur)//先偏转一次,防止链表为0的情况
		{
			break;
		}
		pcur = pcur->next;//继续偏转
		if (NULL == pcur)//为了使偶数个结点的链表时,pcur已经为NULL了,ppre依然指向a1,a2,到a6 中的a3结点
		{
			break;
		}
		ppre = ppre->next;
	}
	L2->next = ppre->next;//由L2头结点指向后面一半链表
	ppre->next = NULL;//前面一半链表的最后一个结点,next要为NULL
}
//L2进行逆置
void reverse(LinkList L2) {
	LinkList r, s, t;
	r = L2->next;//指向第一个结点
	if (r == NULL) {
		return;//链表为空,不需要逆置
	}
	s = r->next;//指向第二个结点
	if (s == NULL) {
		return;//链表只有一个结点,不需要逆置
	}
	t = s->next;
	while (t) {
		s->next = r;//反方向连接,开始逆置
		r = s;//以下三步是三个指针的偏转
		s = t;
		t = t->next;
	}
	s->next = r;//当t指针为空时,还差最后一次连接倒置
	L2->next->next = NULL;//意思是(L2->next)为第一个结点,然后作为新链表最后一个结点时,它的->next为空
	L2->next = s;//s变成了新链表的第一个结点,头结点L2指向它
}
//进行轮流合并
void merge(LinkList L, LinkList L2) {
	LinkList pcur, p, q;
	pcur = L->next;//pcur从L的第一个结点开始,始终指向组合后链表的链表尾
	p = pcur->next;//p指向L的第二个结点,因为第二个位置要插入L2的结点,所以他指向第二个结点
	q = L2->next;//q指向L2的第一个结点
	while (p != NULL && q != NULL) {
		pcur->next = q;//合并L2的结点
		q = q->next;//q继续偏转
		pcur = pcur->next;//pcur也继续偏转
		pcur->next = p;//合并L的结点
		p = p->next;//p继续偏转
		pcur = pcur->next;//pcur也继续偏转
	}
	//L和L2链表都可能剩余一个结点,要判断后放进来
	if (p != NULL) {
		pcur->next = p;//pcur始终指向组合后链表的链表尾
	}
	if (q != NULL) {
		pcur->next = q;
	}
	/*因为L和L2尾部都有NULL,不需要再操作了*/
}
int main() {
	LinkList L;
	list_tail_insert(L);
	print_list(L);
	printf("----------------------------------------\n");
	LinkList L2;//先定义第二条链表
	find_middle(L, L2);
	printf("找到中间结点并分开后\n");
	print_list(L);
	print_list(L2);
	printf("----------------------------------------\n");
	reverse(L2);
	printf("L2逆置后为\n");
	print_list(L2);
	printf("----------------------------------------\n");
	merge(L, L2);
	free(L2);//因为组合后的链表是L,L2没用需要释放
	printf("合并后为\n");
	print_list(L);
	return 0;
}

制作不易,如果可以帮到你,请多多支持并指正!!

  • 13
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值