每日一题——除自身以外数组的乘积

news2025/5/21 13:16:44

除自身以外数组的乘积

题目链接

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ofNWTntp-1690449867248)(C:\Users\HUASHUO\AppData\Roaming\Typora\typora-user-images\image-20230727154709013.png)]


这一题乍一看好像十分简单,先用一趟循环遍历所有数据,得到数据所有元素的乘积,再用一趟循环将这个乘积除以每个元素,这样不就得到了除自身以外数组的乘积吗?我们先来看看代码:

int* productExceptSelf(int* nums, int numsSize, int* returnSize){
    int *ret = (int *)malloc(sizeof(int) * numsSize);
    *returnSize = numsSize;

    int sub = 1;
    for(int i = 0; i < numsSize; i++)
        sub *= nums[i];

    for(int i = 0; i < numsSize; i++)
        ret[i] = sub / nums[i];

    return ret;
}

得到了这样的结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KOPXRbr0-1690449867249)(C:\Users\HUASHUO\AppData\Roaming\Typora\typora-user-images\image-20230727155226543.png)]

提示我们不能进行除0操作

我们来看看它给的错误用例:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gW6ux2A8-1690449867250)(C:\Users\HUASHUO\AppData\Roaming\Typora\typora-user-images\image-20230727155336665.png)]

这下我们就清楚了,当数组元素存在0时,由于不能进行除0操作,那我们这个最容易想到的方法就失效了,我们必须另寻出路。


方法一:构造左右乘积列表

假设我们要求下标为i的元素外数组的乘积,那我们可以分别求出它左边所有元素的乘积和右边所有元素的乘积,再相乘即可。而为了方便得到这两个乘积,我们可以构造两个数组,分别存储下标为i(0 <= i < numsSize)左边所有元素的乘积和右边所有元素的乘积

具体构造步骤如下:

  1. 定义两个数组leftright其大小和给定数组的大小一致
  2. left数组用来存放下标为i(0 <= i < numsSize)左边所有元素的乘积。当i为0(数组最左边的元素)时,其左边没有数据,因此left[0]为1。对于i > 0的情况,left[i] = left[i - 1] * nums[i - 1](从左向右计算)
  3. 同理对于right数组,i = numsSize - 1时(数组最右边的元素),其右边没有元素,因此right[numsSize - 1]为1。对于i < numsSize - 1的情况,right[i] = right[i + 1] * nums[i + 1](从右向左计算)

构造动图:

在这里插入图片描述

得到了leftright两个数组,我们就可以通过索引i直接得到最后结果了

这个方法的时间复杂度和空间复杂度都为O(N)

 int* productExceptSelf(int* nums, int numsSize, int* returnSize){
     //为返回数组申请内存
     int *ret = (int *)malloc(sizeof(int) * numsSize);
     *returnSize = numsSize;
	
     //申请左右乘积列表的内存
     int *left = (int *)malloc(sizeof(int) * numsSize);
     int *right = (int *)malloc(sizeof(int) * numsSize);

     //得到左右乘积列表
     left[0] = 1;
     right[numsSize - 1] = 1;
     for(int i = 1; i < numsSize; i++)
         left[i] = left[i - 1] * nums[i - 1];
     for(int i = numsSize - 2; i >= 0; i--)
         right[i] = right[i + 1] * nums[i + 1];

     //最后的结果就是数据左边所有元素的乘积乘以右边所有元素的乘积
     for(int i = 0; i < numsSize; i++)
         ret[i] = left[i] * right[i];

     //返回得到的结果
     return ret;
 }

方法一的优化

尽管方法一已经可以较好地解决问题,但是由于方法一除了返回数组外,还额外申请了两个数组,因此空间复杂度为O(N)(返回的数组不计入空间复杂度),我们可以对方法一进行优化,从而使空间复杂度为O(1)

由于返回的数组和左右乘积列表left、right是相同的大小,因此我们可以直接在返回数组ret的基础上直接先求出left或者right,然后再通过一次循环来更新右边所有元素的乘积,就可以得到最后结果了。

以下我们以先在ret的基础上求出left为例:

  int* productExceptSelf(int* nums, int numsSize, int* returnSize){
      //为返回数组申请内存
      int *ret = (int *)malloc(sizeof(int) * numsSize);
      *returnSize = numsSize;
	
      //在ret的基础上求出left(左边所有数的乘积)
      ret[0] = 1;
      for(int i = 1; i < numsSize; i++)
          ret[i] = ret[i - 1] * nums[i - 1];

      //从右向左遍历数组,不断更新sub(右边所有元素的乘积),最后得到结果
      int sub = 1;
      for(int i = numsSize - 1; i >= 0; i--)
      {
          ret[i] = ret[i] * sub;
          sub *= nums[i];
      }

      return ret;
  }

这样,我们就不需要申请额外的空间,从而使空间复杂度为O(1)

(推荐)方法二:左右指针

我们可以维护两个指针leftSubrightSub,这两个指针分别用来计算左边所有数的乘积和右边所有数的乘积

具体步骤如下:

  1. 和上面的方法类似,先用leftSub,利用一趟循环从左向右遍历,得到每个位置左边所有数据的乘积(也可以先用right计算右边所有数据的乘积)
  2. 然后再用rightSub,用一趟循环从右向左遍历,利用ret[i] *= rightSub就可以得到最后的结果
int* productExceptSelf(int* nums, int numsSize, int* returnSize){
    //为返回数组申请内存    
    int *ret = (int *)malloc(sizeof(int) * numsSize);
    *returnSize = numsSize;

    //求出左边所有数据的乘积
    int leftSub = 1;
    for(int i = 0; i < numsSize; i++)
    {
        ret[i] = leftSub;
        leftSub *= nums[i];
    }

    //在已经得到左边所有数据的乘积的基础上,在乘以右边所有数据的乘积,得到最后结果
    int rightSub = 1;
    for(int i = numsSize - 1; i >= 0; i--)
    {
        ret[i] *= rightSub;
        rightSub *= nums[i];
    }

    return ret;
}

方法二优化

方法二我们用了两次循环来解决问题,实际上,我们可以将这两个循环合并到一起(整体逻辑一样)

   int* productExceptSelf(int* nums, int numsSize, int* returnSize){
    //为返回数组申请内存        
     int *ret = (int *)malloc(sizeof(int) * numsSize);
     *returnSize = numsSize;

     //将返回数组初始化为1
     for(int i = 0; i < numsSize; i++)
         ret[i] = 1;

     /*
     	leftSub从左边开始,不断更新左边所有数据的乘积
     	rightSub从右边开始,不断更新右边所有数据的乘积
     */
     int leftSub = 1;
     int rightSub = 1;
     for(int i = 0, j = numsSize - 1; i < numsSize; i++,j--)
     {
         ret[i] *= leftSub;
         ret[j] *= rightSub;

         leftSub *= nums[i];
         rightSub *= nums[j];
     }

     return ret;
 }

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

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

相关文章

janus-Gateway的服务端部署

janus-Gateway 需求是前后端的webRTC推拉流&#xff0c;但是后端用的是c&#xff0c;于是使用了这个库做视频流的推送和拉取&#xff0c;记录踩坑过程。 如果你也需要自己部署janus的服务端并在前端拉流测试&#xff0c;希望对你有所帮助。 由于janus的服务器搭建需要linux环境…

Python——Windows下载ffmpeg

目录 前言 一、下载 &#xff08;3种下载方式&#xff09; 1、第一种下载方式——我上传的文件 2、第二种下载方式——GitHub下载 3、第三种下载方式——官网下载 二、解压 三、配置环境变量 四、验证是否安装成功 五、其他 关于ffmpeg其他安装教程 ffmpeg的延迟问题 …

重生之再学mysql-数据库三范式

数据库三范式 介绍第一范式&#xff1a;确保每列都是不可拆分的第二范式&#xff1a;在第一范式的基础上&#xff0c;确保非主键列完全依赖于主键&#xff0c;而不是依赖于主键的一部分第三范式&#xff1a;第二范式的基础上&#xff0c;确保非主键列不存在传递依赖 介绍 什么…

“可以黑掉整个宇宙”的Metasploit Framework

0x01、 简述 Metasploit Framework(MSF)是一款开源安全漏洞检测工具&#xff0c;他带有数千个已知的软件漏洞&#xff0c;目前人在持续更新。Metasploit可以用来信息收集、漏洞探测、漏洞利用等渗透测试的全流程&#xff0c;被安全社区冠以“可以黑掉整个宇宙”之名。在普通的…

Nginx最佳实践优化(动静分离、资源压缩、负载均衡、黑白名单等等)

一、前言 Nginx是目前负载均衡技术中的主流方案&#xff0c;几乎绝大部分项目都会使用它&#xff0c;Nginx是一个轻量级的高性能HTTP反向代理服务器&#xff0c;同时它也是一个通用类型的代理服务器&#xff0c;支持绝大部分协议&#xff0c;如TCP、UDP、SMTP、HTTPS等。 二、…

4.3. 重载和重写的区别

重载就是同样的一个方法能够根据输入数据的不同&#xff0c;做出不同的处理 重写就是当子类继承自父类的相同方法&#xff0c;输入数据一样&#xff0c;但要做出有别于父类的响应时&#xff0c;你就要覆盖父类方法 重载&#xff1a; 发生在同一个类中&#xff08;或者父类和子…

Python入门【函数用法和底层分析、函数简介 、函数的定义和调用、形参和实参、文档字符串(函数的注释) 、函数也是对象,内存底层分析】(十)

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱敲代码的小王&#xff0c;CSDN博客博主,Python小白 &#x1f4d5;系列专栏&#xff1a;python入门到实战、Python爬虫开发、Python办公自动化、Python数据分析、Python前后端开发 &#x1f4e7;如果文章知识点有错误…

一文让你知道等保测评以及渗透测试的区别与联系

我国等保政策已经严格落地执行&#xff0c;不少企业准备办理过等保。但大家对于等保相关政策不是很了解&#xff0c;例如傻傻分不清楚等保测评和渗透测试&#xff0c;这里一文让你知道等保测评以及渗透测试的区别与联系&#xff0c;仅供参考哦&#xff01; 等保测评以及渗透测试…

SpringBoot 项目使用 Redis 对用户 IP 进行接口限流

一、思路 使用接口限流的主要目的在于提高系统的稳定性&#xff0c;防止接口被恶意打击&#xff08;短时间内大量请求&#xff09;。 比如要求某接口在1分钟内请求次数不超过1000次&#xff0c;那么应该如何设计代码呢&#xff1f; 下面讲两种思路&#xff0c;如果想看代码可…

记一次Apache HTTP Client问题排查

现象 通过日志查看&#xff0c;存在两种异常情况。第一种&#xff1a;开始的时候HTTP请求会报超时异常。 762663363 [2023-07-21 06:04:25] [executor-64] ERROR - com.xxl.CucmTool - CucmTool|sendRisPortSoap error,url:https://xxxxxx/realtimeservice/services/RisPort o…

【NodeJs】如何将Markdown文件生成HTML文件在线浏览

经常用的编辑器是Markdown&#xff0c;有自带预览排版效果功能的&#xff0c;预览的是HTML网页&#xff0c;如果想要将它转换成HTML网页文件&#xff0c;要怎么做呢。 首先&#xff0c;借助Node的插件来做&#xff0c;在使用前&#xff0c;确保电脑已安装了NodeJS应用&#xf…

git本地库和远程库的相关操作命令

目录 一、分支概念&#xff1a; 二、 本地库分支管理&#xff1a; 1. 查看分支情况&#xff1a; 命令1&#xff1a;git branch 2. 新建分支 命令1&#xff1a; git branch <分支名> 命令2&#xff1a; git branch <新建分支名> <源分支名> 命令3&…

【温故而知新】【中间件】Redis为什么这么快?

时间&#xff1a;2023年07月27日 作者&#xff1a;小蒋聊技术 邮箱&#xff1a;wei_wei10163.com 微信&#xff1a;wei_wei10 【温故而知新】【中间件】Redis为什么这么快&#xff1f;_小蒋聊技术_免费在线阅读收听下载 - 喜马拉雅欢迎收听小蒋聊技术的类最新章节声音“【温…

【渗透测试】PNG图片隐藏部分恢复

1、图片原尺寸还原方法一 缺点就是有点慢&#xff0c;毕竟遍历的次数比较多 import binascii import struct import sysfilename sys.argv[1] crcbp open(filename, "rb").read() # 打开图片 crc32frombp int(crcbp[29:33].hex(), 16) # 读取图片中的CRC校验值 …

【密码学】三、AES

AES 1、AES产生2、数学基础2.1有限域GF(2^8^)2.1.1加法运算2.1.2乘法运算2.1.3x乘运算2.1.4系数在GF(2^8^)上的多项式 3、AES算法描述3.1字节代换3.2行移位3.3列混合3.4轮密钥加3.5密钥扩展 1、AES产生 征集AES算法的活动&#xff0c;目的是确定一个非保密的、公开的、全球免费…

Cesium态势标绘专题-直角箭头(标绘+编辑)

标绘专题介绍:态势标绘专题介绍_总要学点什么的博客-CSDN博客 入口文件:Cesium态势标绘专题-入口_总要学点什么的博客-CSDN博客 辅助文件:Cesium态势标绘专题-辅助文件_总要学点什么的博客-CSDN博客 本专题没有废话,只有代码,代码中涉及到的引入文件方法,从上面三个链…

国产化 | 记一次基于达梦创建数据库模式思考过程

开篇 首先&#xff0c;我们先来了解一下达梦数据库中用户与模式的概念&#xff0c;以及用户与模式之间的关系。 用户&#xff1a;主要是用来登录连接数据库&#xff0c;以及操作数据库对象等等。 模式&#xff1a;数据库中相关对象的集合。 关系&#xff1a;用户&#xff0…

测评7大热门订房APP,用好结尾这三点,分分钟帮你省掉好多钱

出去旅行预订酒店的时候&#xff0c;相信大家都有过纠结&#xff0c;那么多订房渠道到底应该选哪家。难道要把每个APP都下载下来试一遍吗&#xff1f; 所以&#xff0c;今天笔者给大家带来各大订房APP的测评。 先说结论&#xff1a;仅从性价比来看&#xff0c;民宿优于酒店&a…

Cesium态势标绘专题-燕尾箭头(标绘+编辑)

标绘专题介绍:态势标绘专题介绍_总要学点什么的博客-CSDN博客 入口文件:Cesium态势标绘专题-入口_总要学点什么的博客-CSDN博客 辅助文件:Cesium态势标绘专题-辅助文件_总要学点什么的博客-CSDN博客 本专题没有废话,只有代码,代码中涉及到的引入文件方法,从上面三个链…

LeetCode | Bit Manipulation, heap | 190. 191. 136. 137. 201.215.

190. Reverse Bits 191. Number of 1 Bits 一个一个数就行了。比较简单。 136. Single Number XOR的点在于&#xff0c;两个一样的数字a^a&#xff0c;结果是0. 且XOR是可以换位置的&#xff0c;所以把所有东西XOR在一起&#xff0c;剩下的就是单呗的。 137. Single Number I…