时间复杂度与空间复杂度题目讲解

前言:

在前面我们了解到了时间复杂度与空间复杂度,这里我们就可以尝试一下做一些关于时间复杂度与空间复杂度的题目。

1. 数组篇

题目一:消失的数字

消失的数字:. - 力扣(LeetCode)

思路:

看到这个题目,我们首先的思路是先排序,然后如果后一个数字不等于前一个数字+1的话,那么这个就是消失的数字,那么排序,我们选择冒泡排序呢还是qsort排序,我们发现这个题目限制了时间复杂度为0(N),冒泡排序的时间复杂度是O(N^2),这个不行,那么我们选择qsort排序吗?qsort排序的时间复杂度是O(N*logn),这个貌似也是不行的。

这里我们就需要利用到一种新的思路了,用异或^来进行解决,我们知道0^任何数都是任何数,相同的数异或就是0,比如说1^2^1的结果是2,我们利用这个思路,首先用0来异或0到N之间的所有数字,然后再把这个结果拿到数组里面进行异或,最后异或的结果就是那个消失的数字。

时间复杂度是O(N)

空间复杂度是O(1)

代码:

int missingNumber(int* nums, int numsSize){int ret = 0;//这里我们先异或0到N之间的数字for(int i = 0;i<=numsSize;i++){ret = ret^i;}//然后将异或完的结果拿到数组里面进行异或for(int i = 0;i < numsSize; i++){ret= ret^nums[i];}//最后的结果就是消失的数字,我们直接返回这个值return ret;
}

 题目二:轮转数组

轮转数组:. - 力扣(LeetCode)

思路:

首先看到这个题目,我们的想法是,先创建一个变量把数组的最后一个元素存起来,然后将数组整体完后移动一位,然后将存起来的元素放在数组的首元素,然后循环k次,这样就完成了,但是这个思路过不了这个题目。

这里力扣给出了一个超长的数组。所以过不了。

那我们就选择另一种思路:

三段逆置法

我们首先将数组的前numsSize-k个数据进行逆置,然后再将数组的后k个数据进行逆置,最后将我们的数组整体进行逆置,这样就完成了轮转数组。

时间复杂度为O(N)

空间复杂度为O(1)

当然,这一种思路可能不是那么容易想到

我们这里还有一种思路,创建一个数组,然后将原数组的后k个拷贝到新数组里面,然后再将前numsSize-k个拷贝到新数组里面,最后再将新数组里面的数据都拷贝到原数组里面,这样

也可完成这个轮转数组

时间复杂度为O(N)

空间复杂度为O(N)

三段逆置代码:

void revolve(int*a ,int m,int n)
{int left = m;int right = n;while(left < right){int tmp = a[left];a[left] = a[right];a[right] = tmp;left++;right--;}
}
void rotate(int* nums, int numsSize, int k) {k = k % numsSize;//将数组的前numsSize-k个逆置revolve(nums,0,numsSize-k-1);//将数组的后k个逆置revolve(nums,numsSize-k,numsSize-1);//将数组整体逆置revolve(nums,0,numsSize-1);
}

创建新数组拷贝方法:

void _rotate(int*nums,int numsSize,int k,int*tmp)
{k = k%numsSize;int n = numsSize;//将后k个数据拷贝到新数组里面memcpy(tmp,nums+n-k,sizeof(int)*k);//将前n-k个数据拷贝到新数组里面memcpy(tmp+k,nums,sizeof(int)*(n-k));//将新数组里面的数据全部拷贝到原数组里面memcpy(nums,tmp,sizeof(int)*n);
}
void rotate(int* nums, int numsSize, int k) {int tmp[numsSize];_rotate(nums,numsSize,k,tmp);}

 2. 单链表篇

题目一:返回倒数第k个节点

返回倒数第k个节点:. - 力扣(LeetCode)

这里如果说给我们加上一个限制条件,空间复杂度是O(1),只能遍历一遍链表,那么这个题应该怎么解呢

思路:

首先,我们用到我们之前使用过的快慢指针这样的一个解法,我们先让快指针走k步,然后它们两个同时走,当快指针走到空时,慢指针刚好走到倒数第k个节点。

时间复杂度为O(N)

空间复杂度为O(1)

代码:

typedef struct ListNode sl;
int kthToLast(struct ListNode* head, int k){//首先我们创建两个指针sl* slow = head;sl* fast = head;//让快指针先走k步while(k--){fast = fast->next;}//再两个指针同时走while(fast){fast = fast->next;slow = slow->next;}//当快指针走到空时,这时慢指针刚好就是倒数第k个节点return slow->val;
}

题目二:回文链表

回文链表:. - 力扣(LeetCode)

 这里可能很多人不清楚,什么是回文,回文相当于是链表从中间切开是对称的。

思路:

我们这里首先要找到链表的中间节点,然后将中间节点之后的节点进行逆置,我们再从链表的第一个有效节点和链表的中间节点开始进行比对。

 代码:

typedef struct ListNode sl;sl* revse(sl* phead){sl* p1 = NULL;sl* p2 = phead;sl* p3 = phead->next;while(p2){p2->next = p1;p1 = p2;p2 = p3;if(p3!= NULL){p3 = p3->next;}}return p1;}
bool isPalindrome(struct ListNode* head) {//创建快慢指针,找中间节点sl* slow = head;sl* fast = head;while(fast&&fast->next){slow = slow->next;fast = fast->next->next;}//将中间节点之后的节点逆置sl* phead  = revse(slow);sl* pcur = head;//遍历链表进行比对while(phead){if(phead->val!= pcur->val){return false;}pcur = pcur->next;phead = phead->next;}return true;
}

题目三:相交链表

相交链表:. - 力扣(LeetCode)

 

 思路:

要解决这一题,首先我们要判断链表是否相交。怎么判断相交呢?我们直接看两个链表的尾节点的地址是否相同,如果相同,就是相交的,反之就是不相交,这里我们判断尾节点是否相同的时候,不要用节点里面的数据来判断是否相交,这样容易出错。

到这里,就说明它们两个相交了,那么这里我们有两种思路可以求解

思路1:暴力求解,将A链表的节点依次的跟B链表的所有节点进行比较,A链表某个节点跟B链表的某个节点相同,这个节点就是交点。我们考虑这个思路的时候也是可以分析一下它的时间复杂度,这个链表肯定需要两层循环来帮助我们进行判断,那么它的时间复杂度为O(N^2)。

思路2:若不想采取思路1的方法,我们是否可以找到更优的解法呢?当然有,就是让两个链表从同一个位置开始走,怎么样才能让两个链表从同一个位置走呢?我们先计算A链表的长度,在计算B链表的长度,算出它们长度的差,让开链表先走差距步,然后再让两个链表同时走,找相同节点的地址,这个节点就是它们的交点。我们简单分析一下,这个思路的时间复杂度为O(N),发现比前一个思路更优,那我们就采取第二种方法进行求解。

 代码:

typedef struct ListNode sl;
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {//创建两个指针遍历两个链表sl* pcura = headA;sl* pcurb = headB;//创建两个变量记录两个链表的长度int counta = 0;int countb  = 0;while(pcura->next){pcura = pcura->next;++counta;}while(pcurb->next){pcurb = pcurb->next;++countb;}//判断两个尾节点是否相同if(pcura != pcurb){return NULL;}//这里计算出它们两个的差距int gap = abs(counta-countb);//我这里先使用假设法,假设长链表是A链表,短链表是B链表sl* longlist = headA;sl* shortlist = headB;//这里再判断B链表节点的个数大于A链表节点,再设置谁是长链表,谁是短链表if(countb>counta){longlist = headB;shortlist = headA;}//让长链表先走差距步while(gap--){longlist = longlist->next;}//再两个链表同时走while(longlist!=shortlist){longlist = longlist->next;shortlist = shortlist->next;}//最后随便返回两个链表中的一个节点地址就好了return shortlist;}

题目四:环形链表Ⅰ

环形链表Ⅰ:. - 力扣(LeetCode)

思路:

这里我们采用快慢指针的思路来进行解题,这里我们让慢指针一次走一步,快指针一次走两步,这样就变成了一个追击问题了,如果快指针在环里面碰见了慢指针,说明带环,如果快指针走到了空,说明是不带环的。

代码:

typedef struct ListNode sl;
bool hasCycle(struct ListNode *head) {//创建快慢指针sl* slow = head;sl* fast = head;//循环遍历链表while(fast&&fast->next){slow = slow->next;fast = fast->next->next;//如果快指针在环里面遇见了慢指针if(fast == slow){//说明带环return true;}}//如果出了循环,代表链表不带环return false;
}

其实呢?这个题目的考点是这样的。

1.为什么一定会相遇,有没有可能会错过,永远追不上?请你证明

 2.slow每次走1步,fast每次走3步,4步,5步,N步,请证明一定会追上吗?

这里我们就简单用fast每次走三步来进行证明:

这里永远追不上的条件有两个

1.N是奇数  2.c-1是奇数

那么,存在同时N是奇数且C是偶数的情况吗?

 其实不会同时存在这两种情况,所以一定能追上。

其他fast走4步,5步,N步推理同理,只不过情况不一样。

题目五:环形链表Ⅱ

环形链表Ⅱ:. - 力扣(LeetCode)

思路:

这个题目需要我们判断链表是否带环,如果带环,找出入环的第一个节点。

思路1:这里我们找到它们的相遇点,然后然后创建一个指针newhead指向相遇点的下一个节点,再将相遇点的指针指向置为空,然后再定义一个指针指向原链表的头,这样就变成两个链表的相交问题了

思路2:直接使用双指针法,一个从头节点开始走,一个从相遇点开始走,当这两个指针再次相遇的地方就是链表入环的第一个节点。

这里我们采取的是思路二的代码 

代码:

typedef struct ListNode sl;
struct ListNode *detectCycle(struct ListNode *head){//创建快慢指针sl* slow = head;sl* fast = head;while(fast&&fast->next){slow = slow->next;fast = fast->next->next;//如果带环if(fast == slow){//创建指针指向相遇的节点sl* meet = fast;//慢指针从头开始走slow = head;//遍历链表while(slow){//如果相遇了if(meet == slow){//说明这个节点就是入环口return meet;}meet = meet->next;slow = slow->next;}}}return NULL;
}

 这里有一个问题,为什么从meet节点走和从头节点开始走时的路程是一样的。

题目六:随机链表的复制

随机链表的复制:. - 力扣(LeetCode)

什么是深拷贝?

深拷贝:拷贝一个值和指针指向跟当前链表一模一样的链表

思路:

这个题目拷贝链表不是问题,问题在于怎么把拷贝链表的random指针的指向弄准确,这里就有一个方法,我们把每个拷贝的节点都插入在链表节点的后面,那么拷贝节点就跟链表节点有了关联,再将拷贝节点的random指针指向对应的拷贝节点,最后再将拷贝节点从原链表上面拿下来。这样就完成了复杂链表的拷贝。

代码:

typedef struct Node sl;
struct Node* copyRandomList(struct Node* head) {sl* pcur = head;//将拷贝的每一个节点都尾插在原链表节点的后面while(pcur){sl* copy = (sl*)malloc(sizeof(sl));copy->val = pcur->val;copy->next = pcur->next;pcur->next = copy;pcur = copy->next;}pcur = head;//将拷贝的每个节点的random指向对应的拷贝节点while(pcur){sl* copy = pcur->next;if(pcur->random == NULL){copy->random = NULL;}else{copy->random = pcur->random->next;}pcur = copy->next;}//创建新链表进行接收拷贝的所有节点pcur = head;sl*phead = NULL;sl* ptail = NULL;while(pcur){sl* copy = pcur->next;sl*next = copy->next;if(phead == NULL){phead= ptail = copy;}else{ptail->next = copy;ptail = ptail->next;}pcur = next;}return phead;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Elixir学习笔记——进程(Processes)

在 Elixir 中&#xff0c;所有代码都在进程内运行。进程彼此隔离&#xff0c;彼此并发运行并通过消息传递进行通信。进程不仅是 Elixir 中并发的基础&#xff0c;而且还提供了构建分布式和容错程序的方法。 Elixir 的进程不应与操作系统进程混淆。Elixir 中的进程在内存和 CPU…

进程的创建和管理

一. 实验内容 1&#xff0e; 编写一个程序&#xff0c;程序中创建一个子进程。然后父、子进程各自独立运行&#xff0c;父进程不断地在标准输出设备&#xff08;即显示器&#xff09;上输出字母p和回车&#xff08;输出30次或以上&#xff09;&#xff0c;子进程不断地在标准输…

[面试题]Java【并发】

[面试题]Java【基础】[面试题]Java【虚拟机】[面试题]Java【并发】[面试题]Java【集合】[面试题]MySQL 因为 Java 并发涉及到的内容会非常多&#xff0c;本面试题可能很难覆盖到所有的知识点&#xff0c;所以推荐 《Java并发编程的艺术》 。 Java 线程 线程 通知 等待 线…

植物ATAC-seq文献集锦(三)——果实发育篇

ATAC-seq在植物研究领域的应用我们已经介绍2期了&#xff0c;本期我们聚焦ATAC-seq技术在果实发育方向的应用案例。 植物ATAC-seq文献集锦&#xff08;一&#xff09;——基因组篇 植物ATAC-seq文献集锦&#xff08;二&#xff09;——生长发育篇 文献一&#xff1a;Ident…

Linux 内核 (十二)进程间通讯 之 消息队列

前言 这个系列的上一篇介绍了进程间通讯关于管道相关的内容及代码实例,本章要介绍关于消息队列相关的内容. 消息队列交互图示 函数原型 #include <sys/msg.h> #include <sys/ipc.h> //创建 or 打开队列 成功返回队列ID,失败返回-1 int msgget(key_t key,int fla…

商城小程序系统:一站式搭建,轻松上线

前言 近年来&#xff0c;商城小程序系统以其便捷、高效的特点&#xff0c;正逐渐成为商品买卖的新宠。这类小程序&#xff0c;类似于我们熟知的京东、淘宝等电商巨头&#xff0c;为商家提供了一个全新的销售渠道&#xff0c;让商品买卖更加智能化、便捷化。 一、商城小程序有什…

【Linux】从零开始配置新的服务器的机器学习环境

终端远程登录 ssh -p [端口号] [服务器用户名][服务器IP]或者 ssh [用户名][主机地址]第二种的前提是在.ssh\config中配置了host 安装文本编辑器vim 主要用于后续的文本编辑&#xff0c;个人比较习惯用vim&#xff0c;根据自己喜好选择 更新apt sudo apt update安装文本编辑…

MyBatis使用 PageHelper 分页查询插件的详细配置

1. MyBatis使用 PageHelper 分页查询插件的详细配置 文章目录 1. MyBatis使用 PageHelper 分页查询插件的详细配置2. 准备工作3. 使用传统的 limit 关键字进行分页4. PageHelper 插件&#xff08;配置步骤&#xff09;4.1 第一步&#xff1a;引入依赖4.2 第二步&#xff1a;在m…

在VMware中安装CentOS7(超详细的图文教程)

1、CentOS7的下载 官网下载地址&#xff1a;Download。 进入CentOS下载官网&#xff0c;找到64位的CentOS7版本。 点进来后&#xff0c;发现它给我们列出了所在区域可用镜像源&#xff08;可以说是非常的良心的&#xff09;&#xff0c;我们随便选择一个&#xff0c;这里以阿…

面向对象编程垃圾回收机制

系列文章目录 文章目录 系列文章目录前言一、垃圾回收机制&#xff08;Garbage Collection&#xff09; 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用…

Python保姆级教程 数据类型—新手小白入门必看

python学习资料&#xff0c;下方已打包好 一、基本数据类型与变量&#xff08;上&#xff09; 2.1 注释 优点&#xff1a; 代码说明 没注释的代码 有注释的代码 不让解释器执行注释的那句话 2.2 单行注释 单行注释快捷键&#xff1a;ctrl &#xff1f; 2.3多行注释 …

leetcode刷题记录42-1584. 连接所有点的最小费用

问题描述 给你一个points 数组&#xff0c;表示 2D 平面上的一些点&#xff0c;其中 points[i] [xi, yi] 。 连接点 [xi, yi] 和点 [xj, yj] 的费用为它们之间的 曼哈顿距离 &#xff1a;|xi - xj| |yi - yj| &#xff0c;其中 |val| 表示 val 的绝对值。 请你返回将所有点连…

U-Mail国产信创邮件系统,让企业通信更加安全可控

信息技术应用创新产业&#xff0c;即信创产业&#xff0c;是信息化建设的核心&#xff0c;它涵盖了从硬件到软件的一系列关键技术。信创产业的目标是通过自主研发&#xff0c;减少对外部技术的依赖&#xff0c;增强信息安全&#xff0c;并提升国内产业的全球竞争力。该产业主要…

如何用Vue3和p5.js打造一个令人惊叹的3D球体在线展示

本文由ScriptEcho平台提供技术支持 项目地址&#xff1a;传送门 使用 p5.js 创建交互式 3D 图形 应用场景介绍 p5.js 是一个用于创建交互式图形和动画的 JavaScript 库。它被广泛用于教育、艺术和设计领域&#xff0c;让开发者可以轻松创建具有吸引力的可视化效果。 代码基…

什么是堡垒机?和跳板机是一个概念吗?

堡垒机&#xff0c;又称运维审计系统、跳板机&#xff0c;是一种位于内部网络与外部网络之间的安全防护设备&#xff0c;它充当了一个“中间人”的角色&#xff0c;所有对内部网络资源的远程访问都必须通过堡垒机进行。这一设计的核心目的&#xff0c;在于严格控制和记录所有进…

任务3.8.1 利用RDD实现词频统计

实战&#xff1a;利用RDD实现词频统计 目标 使用Apache Spark的RDD&#xff08;弹性分布式数据集&#xff09;模块实现一个词频统计程序。 环境准备 选择实现方式 确定使用Spark RDD进行词频统计。 Spark版本与Scala版本匹配 选择Spark 3.1.3与Scala 2.12.15以匹配现有Spar…

kafka原理简介

Kafka是由LinkedIn开发的一个分布式发布/订阅的消息系统和一个强大的队列&#xff0c;使用Scala编写&#xff0c;它以可扩展和高吞吐率而被广泛使用。 Kafka适合离线和在线消息消费。 Kafka消息保留在磁盘上&#xff0c;并在群集内以master-flower方式实现数据同步&#xff0c;…

用Canvas绘制2D平面近大远小的马路斑马线

用Canvas绘制2D平面近大远小的马路斑马线 设置canvas和上下文&#xff1a; 首先&#xff0c;你需要创建一个元素&#xff0c;并获取其2D渲染上下文。 绘制斑马线&#xff1a; 使用fillRect或strokeRect方法绘制斑马线。你可以通过循环和计算来绘制多条具有不同宽度和间隔的…

1.PyQt6库和工具库QTDesigner安装

1.安装PyQT6和pyqt6-tools 1. PyQt6库是PyQt的开发库 2.pyqt6-tool时QTDesigner设计器工具支持库 pip install PyQt6 pip install pyqt6-tools 2.在Pycharm中配置外部工具QTDesigner和PYGIC 配置外部工具QTDesigner 1. QTDesigner是QT界面设计器 2.打开Pycharm->Settin…

【最新鸿蒙应开发】——HarmonyOS沙箱目录

鸿蒙应用沙箱目录 1. 应用沙箱概念 应用沙箱是一种以安全防护为目的的隔离机制&#xff0c;避免数据受到恶意路径穿越访问。在这种沙箱的保护机制下&#xff0c;应用可见的目录范围即为应用沙箱目录。 对于每个应用&#xff0c;系统会在内部存储空间映射出一个专属的应用沙箱…