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

前言:

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

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;
}

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

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

相关文章

基于python-CNN卷积网络训练识别牛油果和猕猴桃-含数据集+pyqt界面

代码下载地址&#xff1a; https://download.csdn.net/download/qq_34904125/89383066 本代码是基于python pytorch环境安装的。 下载本代码后&#xff0c;有个requirement.txt文本&#xff0c;里面介绍了如何安装环境&#xff0c;环境需要自行配置。 或可直接参考下面博文…

.net8 blazor auto模式很爽(四)改造vs自动创建的Blazor auto为前后端分离模式(3)

BlazorApp1的appsettings改为下面的内容,注意 "BaseAddress": "https://localhost:7228"这个商端口号要和Properties的launchSettings内容一致&#xff1a; {"BaseAddress": "https://localhost:7228","Logging": {"L…

花卉识别-python-pytorch-CNN深度学习含数据集+pyqt界面

代码下载地址&#xff1a; https://download.csdn.net/download/qq_34904125/89383063 本代码是基于python pytorch环境安装的。 下载本代码后&#xff0c;有个requirement.txt文本&#xff0c;里面介绍了如何安装环境&#xff0c;环境需要自行配置。 或可直接参考下面博文…

Matlab使用Simulink仿真实现AM和BPSK信号的解调

前言 本篇实现了基于AM和BPSK调制的通信系统&#xff0c;采用Bernoulli Binary Generator生成随机二元序列&#xff0c;码元速率为0.5秒/个。AM调制使用Sine Wave模块生成载波&#xff0c;频率40Hz&#xff0c;相位π/2。BPSK调制通过Switch模块切换相位0和π的载波。信号传输…

sap怎么批量给信息记录打上删除标识

1.MEMASSIN-----事务代码 2.选择完成字段 3.根据条件查询需要冻结的信息记录 4.输入查询条件 5.全部勾选完成标识&#xff0c;点击保存&#xff0c;即可冻结完成

Spark groupByKey和reduceByKey对比

在 Apache Spark 中&#xff0c;groupByKey 和 reduceByKey 都是用于对键值对 (key-value) 数据集进行分组和聚合的操作。然而&#xff0c;它们在性能和使用场景上有显著的差异。 groupByKey 函数 groupByKey 将数据集中的所有键相同的值进行分组&#xff0c;然后返回一个键值…

Radis初阶 Radis基本命令与在Springboot中访问Radis

阿里网盘链接 文章目录 初始NoSQL数据库对比MySQL数据库从结构方面&#xff1a;从关系方面&#xff1a;从查询方式&#xff1a;从事物方面&#xff1a; Redis入门Redis数据结构访问Radis通用命令&#xff08;tab键&#xff1a;自动补全&#xff09;KEYSDELEXISTSEXPIRETTL Str…

【TB作品】MSP430G2553,DS1302,LCD1602,时间读取和显示,万年历,Proteus仿真

效果 部分代码 #include <MSP430.h> #include "ds1302.h" #include "LCD.h"//关掉ccs优化&#xff0c;并且Convert_BCD_To_Dec函数中只能是10.0f才行&#xff0c;不然有bugvoid main(void) {char cnt 0;char disp[16];WDTCTL WDTPW WDTHOLD; /* …

基于51单片机的智能水表

一.硬件方案 本设计主要以51单片机作为主控处理器的智能水表&#xff0c;该水表能够记录总的用水量和单次用水量&#xff0c;当用水量超出设定值时系统发出声光报警提醒&#xff0c;水量报警值能够通过按键进行自行设置&#xff0c;并且存储于AT24C02中&#xff0c;并且可以测…

【Ardiuno】使用ESP32单片机网络功能调用API接口(图文)

接着上文连通wifi后&#xff0c;我们通过使用HTTPClient库进行网络相关操作&#xff0c;这里我们通过http协议进行接口调用。 为了简化操作&#xff0c;小飞鱼这里使用了本地服务器上的文件作为接口&#xff0c;正常操作时会调用接口后&#xff0c;将服务器返回的数据进行解析…

Vue32-挂载流程

一、init阶段 生命周期本质是函数。 1-1、beforeCreate函数 注意&#xff1a; 此时vue没有_data&#xff0c;即&#xff1a;data中的数据没有收到。 1-2、create函数 二、生成虚拟DOM阶段 注意&#xff1a; 因为没有template选项&#xff0c;所以&#xff0c;整个div root都…

stable diffusion最全插件大全,新手必备指南

Stable diffusion30个必备插件推荐&#xff0c;给我点个赞吧&#xff0c;兄弟们 1&#xff0c;ComfyUI&#xff0c;SD扩展里面直接搜索就行&#xff0c; ComfyUI 是一个基于节点操作的UI界面&#xff0c;玩过建模的更容易学 安装后大概是这样的 评价&#xff1a;comfyui,更适…

换卡槽=停机?新手机号使用指南!

刚办理的手机号莫名其妙的就被停用了&#xff1f;这到底是怎么回事&#xff1f;这篇文章快来学习一下吧。 ​ 先说一下&#xff0c;你的手机为什么被停机&#xff1f; 现在运营商对于手机卡的使用有着非常严格的要求&#xff0c;尤其是刚办理的新号码&#xff0c;更是“严上加…

神经网络学习1—nn.Module

nn.module 为所有神经网络提供了一个模板 import torch.nn as nn import torch.nn.functional as Fclass Model(nn.Module):def __init__(self):super(Model, self).__init__()self.conv1 nn.Conv2d(1, 20, 5)self.conv2 nn.Conv2d(20, 20, 5)def forward(self, x):x F.rel…

解决Echarts图表中tooltip无法换行问题

解决Echarts图表中tooltip无法换行问题 这里设置宽度、颜色都是是可以生效的&#xff0c;但就是不换行 解决办法tooltip. extraCssText extraCssText: max-width:300px; white-space:pre-wraptooltip: { // 单个柱子显示悬浮内容extraCssText: max-width:300px; white-space…

工业网关在智能制造中的具体应用和效果-天拓四方

随着工业4.0时代的到来&#xff0c;智能制造正逐渐成为工业领域的发展趋势。作为连接物理世界与数字世界的桥梁&#xff0c;工业网关在智能制造中发挥着至关重要的作用。本案例将详细阐述工业网关在某一制造企业中的具体应用&#xff0c;展示其如何助力企业实现数字化转型&…

【数据挖掘】机器学习中相似性度量方法-欧式距离

写在前面&#xff1a; 首先感谢兄弟们的订阅&#xff0c;让我有创作的动力&#xff0c;在创作过程我会尽最大能力&#xff0c;保证作品的质量&#xff0c;如果有问题&#xff0c;可以私信我&#xff0c;让我们携手共进&#xff0c;共创辉煌。 路虽远&#xff0c;行则将至&#…

Visual Studio 使用第三方库管理工具 vcpkg

一、介绍 Windows下开发C/C程序&#xff0c;少不了用开源的第三方库。比如线性代数和矩阵分析的库eigen&#xff0c;或者图像处理的OpenCV库。虽然这些库都是开源的&#xff0c;但是由于要编译debug和release版本的&#xff0c;32位以及64位的&#xff0c;如果像FFmpeg…

跨境电商测评、采购大额下单自养号需要解决哪些技术原理?

市场上有许多伪装工具&#xff0c;但大多数只是为了方便开发人员测试系统程序&#xff0c;它们并不能针对特定的电商平台进行伪装。每个电商平台都有其独特的风控机制&#xff0c;因此&#xff0c;我们需要从硬件环境的底层配合软件控制&#xff0c;以满足各平台的检测规则。 …

【测试】软件测试方案—实际项目直接套用(Word原件)

1. 引言 1.1. 编写目的 1.2. 项目背景 1.3. 读者对象 1.4. 参考资料 1.5. 术语与缩略语 2. 测试策略 2.1. 测试完成标准 2.2. 测试类型 2.2.1. 功能测试 2.2.2. 性能测试 2.2.3. 安全性与访问控制测试 2.3. 测试工具 3. 测试技术 4. 测试资源 4.1. 人员安排 4.2. 测试环境 4.2.…