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


除自身以外数组的乘积

题目链接

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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;
 }

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

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

相关文章

【iOS】isKindOfClass和isMemberOfClass方法

前言 这个归根结底还是在考察我们对isa走向图和类的继承的理解&#xff0c;也就是苹果官方这幅图&#xff1a; 接下来的函数调用流程请参考这张图。 1 isKindOfClass方法 1.1 objc_opt_isKindOfClass C函数 查看源码可发现&#xff0c;无论是谁调用isKindOfClass方法都会…

了解Unity编辑器之组件篇Event(七)

Event&#xff1a;用于在对象之间进行通信和交互的机制。它可以帮助你实现触发和响应特定动作或状态的逻辑一、Event System&#xff1a;用于处理 UI 事件的系统组件 First Selected 属性&#xff1a;定义了在场景加载或 UI 激活时&#xff0c;哪个 UI 元素将成为首选的选中元素…

Kotlin多平台最佳架构指南

在这篇文章中&#xff0c;我们将对 Kotlin 多平台移动端的最佳架构进行深入探讨。在2023年&#xff0c;作为 Android 开发者&#xff0c;我们会倾向于采用 MVVM 架构&#xff0c;因为它简单、灵活且易于测试。而作为 iOS 开发者&#xff0c;我们可能会选择 MVC、Viper 等架构。…

win11安装appium

node安装 node下载网址: Download | Node.js 安装后对node安装包路径进行配置 npm config set prefix “E:\nodejs\node_global” //设置全局包目录 npm config set cache “E:\nodejs\node_cache” //设置缓存目录npm config list //查看npm配置npm install -g appium //安…

Windows SMB 共享文件夹 排错指南

1 排错可能 是否系统名称为全英文格式 如果不是则 重命名 根据如下排错可能依次设置 1&#xff0c;在运行里面输入"secpol.msc"来启动本地安全设置&#xff0c;\ 然后选择本地策略–>安全选项 -->网络安全LAN 管理器身份验证级别&#xff0c;\ “安全设置”…

C#实现数字验证码

开发环境&#xff1a;VS2019&#xff0c;.NET Core 3.1&#xff0c;ASP.NET Core API 1、建立一个验证码控制器 新建两个方法Create和Check&#xff0c;Create用于创建验证码&#xff0c;Check用于验证它是否有效。 声明一个静态类变量存放列表&#xff0c;列表中存放包含令…

如何维护你的电脑:提升性能和延长使用寿命

如何维护你的电脑&#xff1a;提升性能和延长使用寿命 &#x1f607;博主简介&#xff1a;我是一名正在攻读研究生学位的人工智能专业学生&#xff0c;我可以为计算机、人工智能相关本科生和研究生提供排忧解惑的服务。如果您有任何问题或困惑&#xff0c;欢迎随时来交流哦&…

【深度学习】从现代C++中的开始:卷积

一、说明 在上一个故事中&#xff0c;我们介绍了机器学习的一些最相关的编码方面&#xff0c;例如 functional 规划、矢量化和线性代数规划。 本文&#xff0c;让我们通过使用 2D 卷积实现实际编码深度学习模型来开始我们的道路。让我们开始吧。 二、关于本系列 我们将学习如何…

【Unity实用插件篇】| A* Pathfinding Project - A*寻路插件 的使用教程

前言【Unity实用插件篇】| A*寻路插件学习使用一、A*算法 简述二、A* Pathfinding Project 介绍2.1 A* Pathfinding Project 功能2.2 相关链接2.3 标准版和Pro版区别2.4 A* Pathfinding Project Free与Navigation的对比三、快速搭建一个自己的场景测试寻路3.1 寻路场景搭建3.2 …

python绘制3D条形图

文章目录 数据导入三维条形图bar3d 数据导入 尽管在matplotlib支持在一个坐标系中绘制多组条形图&#xff0c;效果如下 其中&#xff0c;蓝色表示中国&#xff0c;橘色表示美国&#xff0c;绿色表示欧盟。从这个图就可以非常直观地看出&#xff0c;三者自2018到2022年的GDP变化…

python使用CGI编程,网页写个标题

需要有个 Linux虚拟机&#xff0c;安装 apache&#xff0c; 本次使用 deepin v23&#xff0c;参考&#xff1a; sudo apt install apache2 #安装 apache2 systemctl start apache2 # 启动 apache2 sudo a2enmod cgi # 启用CGI模块 sudo mkdir /usr/lib/cgi-bin #创…

初识Mybatis,并创建第一个Mybatis项目(详细图文教程)

目录 前言 一、Mybatis是什么&#xff1f; 二、Mybatis的优点 三、创建第一个Mybatis项目 配置Mybatis开发环境 创建数据库 添加框架 配置连接字符串和Mybatis 使用Mybatis操作数据库 测试 前言 Spring 集成了 Mybatis 框架&#xff0c;方便我们更加便捷的使用&#…

关于自签名证书授权后在哪儿看

目录 firefox可以看到 chrome and edge firefox可以看到 chrome and edge 只能从打开的网站左上角进入

【数据结构】--八大排序算法【完整版】

匠心制作&#xff0c;后续有问题会加以修改的 &#xff0c;全文均是自己写的&#xff0c;几张图有参考网络 ———————————————— 目录 一、直接插入排序 二、希尔排序(直接插入排序的改良版) 三、选择排序&#xff08;直接选择排序&#xff09; 四、堆排序 …

前端实现导出excel表格(单行表头)

需求&#xff1a;实现勾选行导出为表格 一、安装插件 npm install --save file-saver xlsx运行项目报如下警告的话 运行npm install xlsx0.16.0 --save 来降低版本号&#xff08;最初我安装的版本号是0.18.16的版本&#xff09;再次运行项目就不会报如下警告了 二、新建一个ex…

SQL与NoSQL数据库选型及实际业务场景探讨

在企业系统架构设计中&#xff0c;选择合适的数据库类型是一项关键决策。本文将对比SQL和NoSQL数据库的特点&#xff0c;分析它们在数据模型、可扩展性、一致性与事务、查询复杂性与频率&#xff0c;以及性能与延迟等方面的优势和劣势。同时&#xff0c;结合轻易云数据集成平台…

系统学习Linux-MySQL用户权限管理(三)

一、用户权限管理概述 数据库用户权限管理是数据库系统中非常重要的一个方面&#xff0c;它用于控制不同用户访问和操作数据库的权限范围。数据库用户权限管理可以保护敏感数据和数据库结构&#xff0c;确保只有被授权的用户才可以操作和使用数据库&#xff0c;防止数据被修改…

Unity游戏源码分享-街机捕鱼2017版本

Unity游戏源码分享-街机捕鱼2017版本 完整的单机游戏 功能玩法&#xff0c;积分系统都已经齐全的了。 项目源码地址&#xff1a;https://download.csdn.net/download/Highning0007/88105459

个人博客系统 -- 登录页面添加图片验证码

目录 1. 功能展示 2. 前段代码 3. 后端代码 1. 功能展示 在登录页面添加验证码登录 1. 检测到没有输入验证码或者输入的验证码错误时,进行弹窗提示.并且刷新当前验证码图片 2. 点击验证码进行刷新 2. 前段代码 1. 添加验证码标签,在密码的下面,在login.html进行修改 主要…

1200*A. Cheap Travel

#include<bits/stdc.h> using namespace std; typedef long long ll; int n,m,a,b,res; int main(){cin>>n>>m>>a>>b;if(a*m<b) resa*n;else{if(n%m0) resn/m*b;else{resn/m*b;resmin(n%m*a,b);}}cout<<res;return 0; }