C语言题解——除自身以外数组的乘积(力扣 第238题)

简介: 这是力扣题库中的一个中等难题,说是存在一个整型数组,求出各元素位上除此数外其他元素的乘积,比如存在数组[1,2,3,4],按照题目应该该输出[24,12,8,6],我们的解题思想为:求出各元素的左积和右积(当然不包含自己),然后将左积与右积相乘,就可以得到目标积数,拿上面的例子来说,下标0的左积为1(默认数组外为1),右积为24,相乘得到目标积24,其他元素也是依次类推。下面来看看具体讲解吧

🍉前言


 这是力扣题库中的一个中等难题,说是存在一个整型数组,求出各元素位上除此数外其他元素的乘积,比如存在数组[1,2,3,4],按照题目应该该输出[24,12,8,6],我们的解题思想为:求出各元素的左积和右积(当然不包含自己),然后将左积与右积相乘,就可以得到目标积数,拿上面的例子来说,下标0的左积为1(默认数组外为1),右积为24,相乘得到目标积24,其他元素也是依次类推。下面来看看具体讲解吧:

f342ab178220373008876b67cec8499.png


原题链接: 238. 除自身以外数组的乘积 - 力扣(LeetCode)

🍉正文


 前面提到过,我们需要得到左积与右积,已知第一个元素的左积为1,最后一个元素的右积也为1,随着元素的变化,积数也会发生变化,因此我们可以以此作为突破点,当然我们要先创建一个数组,这里我们用指针来代替(动态内存开辟,得到一片连续空间)。


🍍空间开辟


 我们会用到malloc函数进行动态内存开辟,这样无论它传过来多大的数组,我们都可以得到足够的空间(因为要返回这片空间,所以我们不进行内存释放)

fe7f963961cf28889df4776d0319a01.png



🍌关于题目中给定的变量


*nums 就是指向原数组的指针,可以通过它的偏移访问到原数组中不同的元素

numsSize 是原数组的长度(个数)

*returnSize 是我们目标数组的长度指针,因为0也会放入目标数组中,因此我们的两个数组长度都是一样的,这里直接赋值即可

🍌malloc 函数


 这是C语言中的一个库函数,作用就是在堆区上开辟一块空间供我们使用,为了函数的普适性,malloc 的返回类型是空指针(需要我们根据需要进行转换),空间大小也是根据我们的需要进行设置,比如我们需要10个 int 类型数据的空间,也就是40字节大小,我们需要在malloc中写成sizeof(int) * 10,即4 * 10 = 40,这是官方规定的标准写法。

3ae745f0c28e2dd1f5a19f7d30173a3.png


malloc函数的相关标准

当然有开辟就会有释放,我们在使用完 malloc 后,一般会使用它的孪生兄弟 free 帮忙释放申请的空间,指向那块空间的指针也会被置空,防止出现内存泄漏和野指针。

b51efea69e0ee7f0c93f271b2bb927c.png


free函数的相关标准

 malloc 一般和 free 搭配使用,但是因为本题是接口型,而且没有把目标数组的地址传过来,因此我们不能对空间进行释放,不然程序就会运行错误(已测试),但在日常使用中不能忘记。


🍌具体代码实现


代码没有多少,就是赋值、开辟、判断

749062fe0768ffafbf8d5743d8254e2.png


aba8bb15abc2a7e6263831c235e244c.png



当然我们这里需要用块空间(因为其中包含了目标数组),所有我们不对其进行释放!


🍍计算左积


 前面说过,我们需要求出各元素的左积与右积,第一个元素的左积为1,最后一个元素的右积也为1。因此我们求左积的过程可以分为三步:获取、存入、变化。


🍌获取


左积,顾名思义是从最左边开始求,也就是第一个元素,我们先定义一个初始积 mul为1,把它作为第一个元素的左积。


2019932416db874319d7f6dea6c705f.png


🍌存入


既然得到了左积,我们就需要把它存入目标数组中(即前面开辟空间的 ptr),为了做到位置对应,我们会对其进行 i 大小的偏移

3708557a776fc9988072ba277a406e9.png



🍌变化


如果说第一步的获取是为了首尾元素,那么变化这一步就是服务于其他元素,因为是累乘,需要用到前一个左积值。

edce05b40b85509a1f3118f0d540590.png

593af0eb9699db606daa722df062bce.png




好了,现在我们已经得到各元素对应的左积值了,下面进行下一步同时也是最后一步(计算左积,同时把左积和右积的乘积和再次存入目标数组中即可)


🍍计算右积&&计算最终值


 计算左积是从最左(第一个元素)开始,那么计算右积就是从最右(最后一个元素开始),当然我们的 for 循环中的 i 要从 numsSize - 1 处开始,当得到右积后,就可以进行左右积(左右积的位置要对应上)的乘法计算了,然后把计算值存入目标数组对应位置中。


🍌计算右积


右积的计算和左积完全一致,最后一个元素的右积值也是1,因此我们要先将mul重赋值为1,也是分为获取、存入、变化三步走,不过这次是从右往左进行计算。


3cea87c6ee70f09777a3bfd43eaeaca.png

d1e4a94de632de4590cd464aaf4e639.png



🍌计算最终值


最终值的计算很简单,无非就是两次求积值相乘,为了避免产生过多的内存浪费,我们把计算最终值集成到了计算右积的步骤中,思想为:目标数组中的左积 * 计算出的右积,然后存入数组中


7246337a0767b3008628bdd341bcdd6.png

e07ade7e5e45a1bba2cd086909565c7.png


7d0d267703331632fbd89f743cf9f0c.png



🍍效果


因为是在两个数组间的重复计算,所以占用内存和消耗时间都比较少,自然空间、时间复杂度比较优秀,下面力扣网的程序运行通过截图。

6662a7057578f065cf6b9539ec856f4.png



🍍源码


下面是原码展示


/力扣 23.除自身以外数组的乘积
//左右互乘法
#include<stdlib.h>
int* productExceptSelf(int* nums, int numsSize, int* returnSize) {
    *returnSize = numsSize;//返回大小就是原数组大小
    int* ptr;
    ptr = (int*)malloc(sizeof(int) * numsSize);
    if (NULL == ptr)
    {
        perror("ptr == NULL!");
        return 0;
    }
    int mul = 1;
    int i = 0;
    for (i = 0; i < numsSize; i++)
    {
        ptr[i] = mul;
        mul *= nums[i];
    }
    mul = 1;
    for (i = numsSize - 1; i >= 0; i--)
    {
        ptr[i] *= mul;
        mul *= nums[i];
    }
    return ptr;
}


🍍关于


题目出自力扣网(Leetcode),链接为:238. 除自身以外数组的乘积 - 力扣(LeetCode)

前面提到的malloc标准相关的网站为C Plus Plus,是一个国外网站,但访问速度不错,可惜全英文。这是网站地址:https://cplusplus.com

代码为函数,只是一个接口,缺少主函数和函数传参,需要自行添加。


🍉总结


 回顾整个题解过程,我们进行了两次循环求数,用到了动态内存管理、数值传递等思想,力扣网给的难度评级是中等,难就难在方法比较难想到,如果不用这种方法,就需要用到很多数组,进行很多计算,而且很复杂。总的来说,这种方法属于一点就通的那种,学习就是一个不断积累的过程,慢慢学嘛,如果看不懂,就多看几遍,实在看不懂可以换篇文章嘛,总会有学懂的时候。


 当然这只是我的一种方法而已,如果你能学到知识,那么这篇文章就值了,关于这题肯定有更好的解法供大家学习,希望大家都能找到属于自己的解法!


 如果你觉得本文写的还不错的话,期待留下一个小小的赞👍,你的支持是我分享的最大动力!


 如果本文有不足或错误的地方,随时欢迎指出,我会在第一时间改正!


目录
相关文章
|
1月前
|
算法
LeetCode第53题最大子数组和
LeetCode第53题"最大子数组和"的解题方法,利用动态规划思想,通过一次遍历数组,维护到当前元素为止的最大子数组和,有效避免了复杂度更高的暴力解法。
LeetCode第53题最大子数组和
|
1月前
|
存储 Java API
LeetCode------合并两个有序数组(4)【数组】
这篇文章介绍了LeetCode上的"合并两个有序数组"问题,并提供了三种解法:第一种是使用Java的Arrays.sort()方法直接对合并后的数组进行排序;第二种是使用辅助数组和双指针技术进行合并;第三种则是从后向前的双指针方法,避免了使用额外的辅助数组。
LeetCode------合并两个有序数组(4)【数组】
LeetCode------找到所有数组中消失的数字(6)【数组】
这篇文章介绍了LeetCode上的"找到所有数组中消失的数字"问题,提供了一种解法,通过两次遍历来找出所有未在数组中出现的数字:第一次遍历将数组中的每个数字对应位置的值增加数组长度,第二次遍历找出所有未被增加的数字,即缺失的数字。
|
1月前
|
前端开发
LeetCode------移动零(5)【数组】
这篇文章介绍了LeetCode上的"移动零"问题,提出了一种使用双指针的原地操作解法,该方法首先将非零元素移动到数组前端并保持相对顺序,然后填充后续位置为零,以达到题目要求。
|
5天前
|
存储 编译器 C语言
【C语言基础考研向】09 一维数组
数组是一种有序集合,用于存储相同类型的数据,便于统一操作与管理。例如,将衣柜底层划分为10个格子存放鞋子,便于快速定位。在C语言中,数组定义格式为 `类型说明符数组名[常量表达式];`,如 `int a[10];` 表示定义了一个包含10个整数的数组。数组初始化时可以直接赋值,也可以部分赋值,且数组长度必须固定。数组在内存中连续存储,访问时需注意下标范围,避免越界导致数据异常。数组作为参数传递时,传递的是首地址,修改会影响原数组。
|
5天前
|
存储 C语言
【C语言基础考研向】10 字符数组初始化及传递和scanf 读取字符串
本文介绍了C语言中字符数组的初始化方法及其在函数间传递的注意事项。字符数组初始化有两种方式:逐个字符赋值或整体初始化字符串。实际工作中常用后者,如`char c[10]=&quot;hello&quot;`。示例代码展示了如何初始化及传递字符数组,并解释了为何未正确添加结束符`\0`会导致乱码。此外,还讨论了`scanf`函数读取字符串时忽略空格和回车的特点。
|
9天前
|
存储 人工智能 C语言
C语言程序设计核心详解 第六章 数组_一维数组_二维数组_字符数组详解
本章介绍了C语言中的数组概念及应用。数组是一种存储同一类型数据的线性结构,通过下标访问元素。一维数组定义需指定长度,如`int a[10]`,并遵循命名规则。数组元素初始化可使用 `{}`,多余初值补0,少则随机。二维数组扩展了维度,定义形式为`int a[3][4]`,按行优先顺序存储。字符数组用于存储字符串,初始化时需添加结束符`\0`。此外,介绍了字符串处理函数,如`strcat()`、`strcpy()`、`strcmp()` 和 `strlen()`,用于拼接、复制、比较和计算字符串长度。
|
1月前
|
算法
LeetCode第81题搜索旋转排序数组 II
文章讲解了LeetCode第81题"搜索旋转排序数组 II"的解法,通过二分查找算法并加入去重逻辑来解决在旋转且含有重复元素的数组中搜索特定值的问题。
LeetCode第81题搜索旋转排序数组 II
|
27天前
|
存储 编译器 数据处理
【编程秘籍】解锁C语言数组的奥秘:从零开始,深入浅出,带你领略数组的魅力与实战技巧!
【8月更文挑战第22天】数组是C语言中存储同类型元素的基本结构。本文从定义出发,详述数组声明、初始化与访问。示例展示如何声明如`int numbers[5];`的数组,并通过下标访问元素。初始化可在声明时进行,如`int numbers[] = {1,2,3,4,5};`,编译器自动计算大小。初始化时未指定的元素默认为0。通过循环可遍历数组,数组名视为指向首元素的指针,方便传递给函数。多维数组表示矩阵,如`int matrix[3][4];`。动态数组利用`malloc()`分配内存,需用`free()`释放以避免内存泄漏。掌握这些技巧是高效数据处理的基础。
46 2
|
1月前
|
算法 索引
LeetCode第34题在排序数组中查找元素的第一个和最后一个位置
这篇文章介绍了LeetCode第34题"在排序数组中查找元素的第一个和最后一个位置"的解题方法,通过使用双指针法从数组两端向中间同时查找目标值,有效地找到了目标值的首次和最后一次出现的索引位置。
LeetCode第34题在排序数组中查找元素的第一个和最后一个位置