考研408每周一题(2019 41)

2019年(单链表)

        41.(13分)设线性表L=(a1,a2,a3,...,a(n-2),a(n-1),an)采用带头结点的单链表保存,链表中的结点定义如下:

typedef struct node {
    int data;
    struct node *next;
} NODE;

        请设计一个空间复杂度为O(1)且时间上尽可能高效的算法,重新排列L中的各个结点,得到线性表L'=(a1,an,a2,a(n-1),a3,a(n-2),...)。要求:

(1)给出算法的基本设计思想

我们将n用数字代入进去,比如n=7,那么L也就是如下图所示

a1a2a3a4a5a6a7

重新排列组合之后的L'

a1a7a2a6a3a5a4

很容易就能发现一下规律,将链表L断开(断链),将链表尾进行反转(逆置),最后重新组合成一条新的链表。这个我们用三个函数(list_spilt、list_reverse、list_merge)来对链表L进行操作。

(2)根据设计思想,采用C或C++语言描述算法,关键之处给出注释

环境:Visual Studio 2022

语言:C++

代码如下图所示:

链表断链

//链表断链
void list_spilt(LinkList L, LinkList& L2) {
	LinkList q, p;
	L2 = (LinkList)malloc(sizeof(Lnode));
	//将p和q进行初始化
	p = q = L->next;
	//需要对当前指针进行判断
	//如果当前指针为空的情况
	//开始遍历
	while (p) {
		p = p->next;
		//防止链表只有一个结点的情况
		if (p == NULL) {
			break;
		}
		p = p->next;
		//为了偶数个的情况进行判断
		if (p == NULL) {
			break;
		}
		q = q->next;
	}
	//L2的相关操作
	//将L2的链表头节点指向当前链表的中间结点q
	L2->next = q->next;
	//将中间结点q的next置为NULL(即为L链表的断链)
	q->next = NULL;
}

顾名思义就是将链表一分为二,这里我们用快慢指针,快指针p走两步,慢指针q走一步,保证快指针p始终走的比慢指针q多一个单位,因此 ,循环的条件就是快指针p不为空,考虑到p为空的情况,循环即结束。

当进入到第四次循环,也就是p->next==NULL的时候,此时q->next指向a4如上图所示。

从q这里断链,L2中的数据自然也就包含a5,a6,a7。

时间复杂度:因为p每次移动两步(即为两个结点),故其循环的次数就是n/2,忽略首项系数就是O(n)。

注意:但凡涉及到链表的结构修改操作,需要在函数的形参上加上&(取地址符),C++的引用操作

链表反转(逆置)

//链表反转(逆置)
void list_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;
	//逆置后,链表的第一个结点即为尾结点
	L2->next->next = NULL;
	//L2指向现链表的头结点s
	L2->next = s;
}

 这里我们用三个指针操控,r,s,t。由于链表的特性,我们只需要改变指针的指向即可完成反转操作。需要判断两种情况。即链表为空的情况和链表只有一个结点的情况。直接返回即可。

这里我们用距离最远的指针t作为循环的结束条件,只要t==NULL,循环即结束。

将s->next指向r,将s赋给r,t赋给s,t=t->next即可完成一次逆置操作,但因为一开始t是领先r两个位置的,故判断t==NULL循环结束时,实际上还有一次逆置操作没有完成,这里我们只需要将r的地址赋给s->next即可,这样便完成了逆置操作。剩下的就是些收尾工作。

注意:原先的链表头结点已经变成了尾结点,我们需要手动将其next置为空

而此时的链表头既是s,将s的地址赋给L2->next即可完成链表逆置的全部工作。

时间复杂度:reverse函数只遍历了L2链表,遍历次数也是n/2,故时间复杂度为O(n)

链表合并

//链表合并
void list_merge(LinkList L, LinkList L2) {
	LinkList p, q, pcur;
	p = L2->next;//p指L2的第一个结点
	pcur = L->next;//pcur始终指向组后的链表
	q = pcur->next;//q指向L1的第一个结点
	while (p && q) {
		pcur->next = p;
		p = p->next;
		pcur = pcur->next;
		pcur->next = q;
		q = q->next;
		pcur = pcur->next;
	}
	//任何一个链表都可能剩余一个结点,放进来即可
	if (p != NULL) {
		pcur->next = p;
	}
	if (q != NULL) {
		pcur->next = q;
	}
}

链表合并操作我们同样需要三个指针,一个指向L,一个指向L2,一个指向L’,循环的条件,判断L和L2链表当前next不为空, 因为一开始即对pcur进行赋值为L->next,故往后操作只需要直接让其next指向L2->next也是p即可。个人感觉有点两个字符串交叉合并的意思。

一次操作:

pcur指向p(L2->next),p往后移动一步,再让pcur往后移动一步,让pcur指向q(L->next),q往后移一步,pcur再往后移动一步

  1.         pcur->next = p;
  2.         p = p->next;
  3.         pcur = pcur->next;
  4.         pcur->next = q;
  5.         q = q->next;
  6.         pcur = pcur->next;

时间复杂度:merge函数while的循环次数也是n/2,故时间复杂度为O(n) 

即是以上六步,最后奇数个数据的链表会剩余一个结点的情况,我们直接将其放入新链表L’即可。

以下是全部代码:

#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));
	ElemType num;
	LinkList q, p;
	q = L;
	scanf_s("%d", &num);
	while (num != 9999) {
		p = (LinkList)malloc(sizeof(Lnode));
		p->data = num;
		q->next = p;
		q = p;
		scanf_s("%d", &num);
	}
	p->next = NULL;
}
//链表断链
void list_spilt(LinkList L, LinkList& L2) {
	LinkList q, p;
	L2 = (LinkList)malloc(sizeof(Lnode));
	//将p和q进行初始化
	p = q = L->next;
	//需要对当前指针进行判断
	//如果当前指针为空的情况
	//开始遍历
	while (p) {
		p = p->next;
		//防止链表只有一个结点的情况
		if (p == NULL) {
			break;
		}
		p = p->next;
		//为了偶数个的情况进行判断
		if (p == NULL) {
			break;
		}
		q = q->next;
	}
	//L2的相关操作
	//将L2的链表头节点指向当前链表的中间结点q
	L2->next = q->next;
	//将中间结点q的next置为NULL(即为L链表的断链)
	q->next = NULL;
}
//链表反转(逆置)
void list_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;
	//逆置后,链表的第一个结点即为尾结点
	L2->next->next = NULL;
	//L2指向现链表的头结点s
	L2->next = s;
}
//链表合并
void list_merge(LinkList L, LinkList L2) {
	LinkList p, q, pcur;
	p = L2->next;//p指L2的第一个结点
	pcur = L->next;//pcur始终指向组后的链表
	q = pcur->next;//q指向L1的第一个结点
	while (p && q) {
		pcur->next = p;
		p = p->next;
		pcur = pcur->next;
		pcur->next = q;
		q = q->next;
		pcur = pcur->next;
	}
	//任何一个链表都可能剩余一个结点,放进来即可
	if (p != NULL) {
		pcur->next = p;
	}
	if (q != NULL) {
		pcur->next = q;
	}
}

//链表输出
void list_printf(LinkList L) {
	L = L->next;
	while (L) {
		printf("%3d ", L->data);
		L = L->next;
	}
}
int main() {
	//建立链表
	LinkList L, L2;
	//尾插法
	list_tail_insert(L);
	list_printf(L);
	//链表断链
	list_spilt(L, L2);
	printf("\n----------------list_spilt-----------------\n");
	list_printf(L2);
	//链表逆置
	list_reverse(L2);
	printf("\n----------------list_reverse---------------\n");
	list_printf(L2);
	//链表合并
	list_merge(L, L2);
	printf("\n----------------list_merge-----------------\n");
	list_printf(L);

	return 0;
}

代码效果:

偶数情况:

 单数情况:

(3)说明你所设计的算法的时间复杂度

以上三个函数总的运行次数为(3/2)n,忽略首项系数,即为O(n)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/1638.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

leetcode -- 876.链表的中间节点

文章目录&#x1f428;1.题目&#x1f407;2. 解法1-两次遍历&#x1f340;2.1 思路&#x1f340;2.2 代码实现&#x1f401;3. 解法2-快慢指针&#x1f33e;3.1 思路&#x1f33e;3.2 **代码实现**&#x1f42e;4. 题目链接&#x1f428;1.题目 给你单链表的头结点head&#…

RocketMQ

RocketMQ1、基础入门1、消息中间件(MQ)的定义2、为什么要用消息中间件&#xff1f;2、RocketMQ 产品发展1、RocketMQ 版本发展2、RocketMQ 的物理架构1、核心概念2、物理架构中的整体运转3、RocketMQ 的概念模型1、分组(Group)2、主题(Topic)3、标签(Tag)4、消息队列(Message Q…

开发也可以很快乐,让VSCode和CodeGPT带给你幸福感

CodeGPT 是一款 Visual Studio Code 扩展&#xff0c;可以通过官方的 OpenAI API 使用 GPT-3 (预训练生成式转换器) 模型&#xff0c;在多种编程语言中生成、解释、重构和文档化代码片段。CodeGPT 可用于各种任务&#xff0c;例如代码自动完成、生成和格式化。它还可以集成到代…

smartsofthelp最简单的,最好的,最干净的C# 代码生成器

关系型数据库高并发接口代码生成EF API 接口原声SQL 操作类异步委托 await 操作数据库数据异步访问抽象基础类 netcore 生成EF ORMdbhelperasync原生SQL 异步数据库操作公共类自动生成增删改查成员方法实例代码#region 自动生成增删改查成员方法/// <summary>/// 增加一条…

【6】核心易中期刊推荐——图像与信号处理

🚀🚀🚀NEW!!!核心易中期刊推荐栏目来啦 ~ 📚🍀 核心期刊在国内的应用范围非常广,核心期刊发表论文是国内很多作者晋升的硬性要求,并且在国内属于顶尖论文发表,具有很高的学术价值。在中文核心目录体系中,权威代表有CSSCI、CSCD和北大核心。其中,中文期刊的数…

ChatGPT-4.0 : 未来已来,你来不来

文章目录前言ChatGPT 3.5 介绍ChatGPT 4.0 介绍ChatGPT -4出逃计划&#xff01;我们应如何看待ChatGPT前言 好久没有更新过技术文章了&#xff0c;这个周末听说了一个非常火的技术ChatGPT 4.0&#xff0c;于是在闲暇之余我也进行了测试&#xff0c;今天这篇文章就给大家介绍一…

【Bezier + BSpline + CatmullRom】移动机器人曲线路径规划

问题&#xff1a;现有n1n1n1个2维的离散点Pi(xi,yi),(i0,1,⋯,n){P_i} \left( {{x_i},{y_i}} \right),\left( {i 0,1, \cdots ,n} \right)Pi​(xi​,yi​),(i0,1,⋯,n), 如何用Pi{P_i}Pi​拟合一条平滑的曲线&#xff0c;最后将曲线分割成数条 2阶/3阶贝塞尔曲线&#xff0c;…

HDFS的API操作

目录 客户端环境准备&#xff1a; 添加环境变量&#xff1a; 配置Path环境变量&#xff1a; IDEA操作&#xff1a; 创建包名&#xff1a; HDFS的API案例操作&#xff1a; 封装代码&#xff1a; 封装代码1&#xff1a; 封装代码2&#xff1a; 实现操作&#xff1a; 1.创…

每日一博 - Java 异步编程的 Promise 模式 CompletableFuture

文章目录概述概述Executor与线程池Java 中的线程池使用线程池的注意事项强烈建议使用有界队列默认拒绝策略要慎重使用注意异常处理的问题如何获取任务执行结果概述 最近在阅读耗子叔的《左耳听风》 &#xff0c; 记一些小笔记 概述 在 Java 中&#xff0c;在 JDK 1.8 里也引入…

深度学习应用技巧总结与pytorch框架下训练过程的记忆技巧

大家好&#xff0c;我是微学AI&#xff0c;今天给大家总结一下深度学习模型训练过程中的一些技巧总结&#xff0c;以及pytorch框架下训练过程的记忆技巧&#xff0c;很有用的干货&#xff0c;理解模型训练过程的步骤&#xff0c;让流程难懂&#xff0c;难记忆的过程变得简单&am…

通讯录-文件操作版

之前我们写过通讯录-动态开辟版&#xff0c;但里面的数据录入后&#xff0c;若退出程序&#xff0c;里面的数据也就跟着一起销毁&#xff0c;无法保存&#xff0c;所以今天我们来写可建议将通讯录信息保存起来的版本&#xff0c;这只要在原来的基础上加以改进就可以了。首先&am…

发光立方体效果 html+css

一.话不多&#xff0c;看效果 css简单创意特效&#xff0c;关注我看更多简单创意特效~ 二.实现&#xff08;附完整代码&#xff09; 定义标签&#xff1a; <div class"container"><div class"q1"></div><div class"h2"&…

Day921.chatGPT

chatGPT Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于chatGPT的内容。 一、什么是chatGPT ChatGPT&#xff08;全名&#xff1a;Chat Generative Pre-trained Transformer&#xff09;&#xff0c;ChatGPT 是一种基于 GPT (Generative Pre-trained Transformer)…

【Linux】进程的基础概念 进程的相关操作 进程的状态

进程一、进程的基本知识1、基本概念2、进程的描述 —— PCB3、task_ struct内容分类二、进程的相关操作1、在Linux下查看进程2、通过系统调用在代码中获取进程标示符3、如何创建子进程4、关于fork()的一些深度理解三、进程的状态Linux中的进程的状态四、僵尸进程与孤儿进程僵尸…

L2-014 列车调度 L1-082 种钻石 L1-083 谁能进图书馆

输入格式&#xff1a; 输入第一行给出一个整数N (2 ≤ N ≤105 )&#xff0c;下一行给出从1到N的整数序号的一个重排列。数字间以空格分隔。 输出格式&#xff1a; 在一行中输出可以将输入的列车按序号递减的顺序调离所需要的最少的铁轨条数。 输入样例&#xff1a; 9 8 4 2 …

STM32开发(九)STM32F103 通信 —— I2C通信编程详解

文章目录一、基础知识点二、开发环境三、STM32CubeMX相关配置四、Vscode代码讲解GPIO模拟I2C代码SHT30相关代码main函数中循环代码五、结果演示方式一、示波器分析I2C数据方式2、通过Modbus将获取到的数据传到PC上一、基础知识点 本实验通过I2C通信获取SHT30温湿度值&#xff…

一文带你看透前端世界里的日期时间,对就是Date

很高兴我们能够通过不同空间&#xff0c;不同时间&#xff0c;通过这篇博客相识&#xff0c;那一定是一种缘分&#xff0c;一种你和狗哥的缘分。今天我希望通过这篇博客对我所熟知的前端世界里的日期时间做一个汇总&#xff0c;不止是代码上的汇总哦&#xff01; 目录 一、时区…

flex布局优化(两端对齐,从左至右)

文章目录前言方式一 nth-child方式二 gap属性方式三 设置margin左右两边为负值总结前言 flex布局是前端常用的布局方式之一&#xff0c;但在使用过程中&#xff0c;我们总是感觉不太方便&#xff0c;因为日常开发中&#xff0c;大多数时候&#xff0c;我们想要的效果是这样的 …

C++数据结构 —— 哈希表、unordered_map/set封装

目录 1.哈希概念 1.1哈希函数 1.2哈希冲突 2.闭散列实现 3.开散列实现 4.容器的封装 4.1unordered_map 4.2unordered_set 4.3封装过程中遇到的问题 1.哈希概念 顺序结构以及平衡二叉搜索树结构中&#xff0c;在查找一个元素时需要经过比较。顺序查找时间复杂度为O(N…

顺序栈的实现

目录 一、数据结构中的栈 二、接口函数 三、栈的初始化 四、入栈 五、判断栈是否为空 六、出栈 七、栈顶元素及元素总数 八、顺序栈的销毁 一、数据结构中的栈 首先&#xff0c;栈&#xff08;Stack&#xff09;这个词在数据结构和操作系统两个学科中都有出现。 操作系…