数组a,长度为n(索引为0至n-1)。现要求更新数组的各个元素,使新数组的第i个元素等于原数组中除第i个元素之外各元素之积

http://blog.youkuaiyun.com/codearhat/article/details/6839897


a[0]变为a[1]到a[n-1]的积,a[1]变为a[0]和a[2]到a[n-1]的积,……a[n-1]为a[0]到a[n-2]的积。要求:具有线性复杂度。不能使用除法运算符。”

讨论:最后两项要求恰好排除了最直观的两种方法"两层循环乘法"、“求全部元素的积P,然后New[i] = P / Old[i]”,卡死了思路,完全没想出来难过

后来参考了 帖子  16楼 qq120848369 的答案才明白。(第一次还没看懂,跳过去了尴尬

倒推出解题思路:新数组每个元素都是前、后两段“部分积”相乘的结果。前方的部分积很容易想到用数组保存结果,在O(n)的时间内计算。

关键是后方的部分积也可以同样算出,只是要反向累乘。逆向思维……

[cpp]  view plain copy
  1. #include <iomanip>  
  2. #include <iostream>  
  3. using namespace std;  
  4.   
  5. void show(int v[], int n, char const * name)  
  6. {  
  7.   cout << name << ": ";  
  8.   for(int i = 0; i < n; ++i){  
  9.     cout << setw(7) << v[i];  
  10.   }  
  11.   cout << '\n';  
  12. }  
  13.   
  14. int main()  
  15. {  
  16.   int a[] = {2, 3, 5, 7, 11}; // 用质数测试保证每个积都唯一。  
  17.   int const N = sizeof(a) / sizeof(a[0]);  
  18.   
  19.   int prod_fore[N];  
  20.   int prod_back[N];  
  21.   prod_fore[0  ] = 1;  
  22.   prod_back[N-1] = 1;  
  23.   
  24.   for(int i = 1; i < N; ++i){  
  25.     prod_fore[i] = a[i-1] * prod_fore[i-1];  
  26.   }  
  27.   
  28.   for(int i = N-2; i >= 0; --i){  
  29.     prod_back[i] = a[i+1] * prod_back[i+1];  
  30.   }  
  31.   
  32.   show(a        , N, "source   ");  
  33.   
  34.   for(int i = 0; i < N; ++i){  
  35.     a[i] = prod_fore[i] * prod_back[i];  
  36.   }  
  37.   
  38.   show(prod_fore, N, "prod_fore");  
  39.   show(prod_back, N, "prod_back");  
  40.   show(a        , N, "result   ");  
  41. }  


注意:题目中未对空间复杂度做要求。不能一开始就武断认为只能申请一个长度为N的数组。

其实,只需要一个长度为N的辅助数组就可以,如下题(其中数组b[N]可认为是辅助数组):

问题描述:两个数组a[N],b[N],其中A[N]的各个元素值已知,现给b[i]赋值,b[i] = a[0]*a[1]*a[2]...*a[N-1]/a[i];
要求:
1.不准用除法运算
2.除了循环计数值,a[N],b[N]外,不准再用其他任何变量(包括局部变量,全局变量等)

3.满足时间复杂度O(n),空间复杂度O(1)


算法实现:

[cpp]  view plain copy
  1. int main(int argc, char* argv[])  
  2. {  
  3.     int i;  
  4.     int a[10] = {1, 2, 1, 2, 1, 2, 1, 2, 1, 2};  
  5.     int b[10];  
  6.       
  7.     b[0] = 1;  
  8.     for(i = 1; i < 10; i++)  
  9.     {  
  10.         b[0] *= a[i - 1];  
  11.         b[i] = b[0];  
  12.     }  
  13.     b[0] = 1;  
  14.     for(i = 8; i > 0; i--)  
  15.     {  
  16.         b[0] *= a[i + 1];  
  17.         b[i] *= b[0];  
  18.     }  
  19.     b[0] *= a[1];//注意下这个地方 
  20.       
  21.     for(i = 0; i < 10; i++)  
  22.     {  
  23.         cout << b[i] << " ";  
  24.     }  
  25.     cout << endl;  
  26.       
  27.     return 0;  
  28. }  


<think>好的,我在需要解释为什么在这段Java代码中,数组长度被设置为N+1,比如t、d、l、st数组都是new int[N + 1]或者boolean[N + 1]。这个问题看起来是关于数组索引1开始还是从0开始的常见问题。用户可能对数组长度的设定有疑问,尤其是为什么不是直接用N而是N+1。 首先,我应该回忆Java数组的基本知识。Java数组索引是从0开始的,所以通常声明数组时,如果需要有N个元素数组长度是N,索引0到N-1。但在这段代码中,数组被声明为N+1长度,比如t数组是int[N + 1],然后循环是从i=1到i<=N,也就是使用了索引1到N。这意味着数组有效数据是从1开始存储的,而索引0的位置可能未被使用或者被忽略。 接下来,我需要分析为什么这样做。可能的因有几个:一是程序员习惯使用1-based索引,比如在算法题中,有时为了方便,尤其是当问题描述中的输入数据从1开始编号时,使用1-based数组可以避免转换;二是可能在某些情况下,索引0有特殊的用途,比如作为哨兵值或其他用途,但根据代码中的初始化部分来看,数组初始化后,循环从i=1开始填充数据,所以索引0的位置可能未被使用;三是可能代码的后续部分需要处理到N个元素,而使用1到N的索引更直观,比如在某些动态规划问题中,0可能表示初始状态,但这里的情况可能更简单。 然后,我需要考虑这样做的优缺点。优点可能是代码的可读性更好,尤其是当输入的飞机编号是从1到N时,数组索引直接对应飞机的编号,不需要调整。缺点则是浪费了一个数组元素的空间(索引0的位置未被使用),这在N很大时可能会有内存问题,但通常情况下,这种开销可以忽略不计。 用户的问题直接是为什么数组长度要是N+1,而他们的示例代码中确实使用了N+1长度。因此,我需要明确说明,这是因为数组索引1开始到N,所以需要N个元素,而数组长度必须是N+1才能包含索引0到N,其中索引1到N被使用,而索引0未被使用。 可能用户没有考虑到数组索引0开始的问题,所以需要解释清楚Java数组索引机制,以及为什么声明N+1长度才能让索引1到N有效。同时,可能需要指出这样做的好处,比如避免在循环中进行i-1的转换,提高代码可读性,尤其是在处理与问题描述中编号对应的数据时。此外,如果有后续的代码逻辑需要从1开始访问数组,比如可能与某种算法逻辑相关,比如动态规划中的状态定义,但根据当前代码片段,可能只是简单的数据存储。 另外,需要检查是否存在其他可能的因,比如多线程或并发处理中的特殊需求,但根据当前代码片段,这似乎是一个单线程的输入处理,所以可能性较低。因此,主要因是程序员选择使用1-based索引来匹配问题中的编号,从而声明数组长度为N+1,以便索引1到N有效,而索引0未被使用。 总结来说,用户的问题的答案是因为数组索引被设计为从1开始到N,因此需要长度为N+1数组,这样才能合法地访问到索引N的位置,而Java数组索引是从0开始的,长度为N+1允许索引0到N,但实际只使用1到N的部分。这样做通常是为了让索引与问题中的编号(如第1架到第N架飞机)直接对应,简化代码逻辑,避免在访问数组时频繁地进行索引调整。</think>在Java代码中,数组索引默认从 **0** 开始。如果代码中声明数组长度为 **`N + 1`**,通常是因为程序员希望**将数据的存储位置从索引 `1` 开始使用**,而索引 `0` 可能被忽略或留作特殊用途。以下是具体因和解释: --- ### **为什么数组长度是 `N + 1`?** 1. **索引对齐问题** - 如果输入的飞机编号是 `1` 到 `N`(而非 `0` 到 `N-1`),使用索引 `1` 到 `N` 存储数据可以**直接让数组下标与飞机编号一一对应**。例如: ```java // 假设输入的第 i 架飞机对应数组的 t[i], d[i], l[i] for (int i = 1; i <= N; i++) { t[i] = sc.nextInt(); // 第 i 架飞机的到达时间 d[i] = sc.nextInt(); // 第 i 架飞机的盘旋时间 // ... } ``` - 这样设计能**避免索引转换**(如 `i-1`),使代码逻辑更直观。 2. **动态规划或递归问题中的常见设计** - 在某些算法场景(如动态规划),索引 `0` 可能被用作**边界条件或初始状态**。例如: ```java dp[0] = 0; // 初始化一个虚拟状态 for (int i = 1; i <= N; i++) { dp[i] = ...; // 基于 dp[i-1] 计算当前状态 } ``` - 虽然当前代码片段没有明确使用 `0` 索引,但预留 `0` 的位置可能是为了后续逻辑的扩展。 3. **代码可读性** - 如果问题描述中的飞机编号是 `1` 到 `N`,使用 `1` 到 `N` 的索引会让代码更贴近自然语言描述,降低理解成本。 --- ### **为什么不直接用 `N`?** - 如果数组长度为 `N`,则有效索引是 `0` 到 `N-1`。如果强行用 `1` 到 `N` 的索引访问数组,会导致 **`ArrayIndexOutOfBoundsException`** 异常。 - 例如:当 `N=3` 时,数组长度为 `3`,索引范围是 `0,1,2`。若尝试访问 `t[3]`,会直接越界。 --- ### **示例说明** 假设 `N=3`(3架飞机): - 如果数组声明为 `int[N+1]`(即长度4),则索引为 `0,1,2,3`。 - 输入时使用索引 `1,2,3` 存储数据,而索引 `0` 未使用: ```java t[1] = 飞机1的到达时间 t[2] = 飞机2的到达时间 t[3] = 飞机3的到达时间 ``` --- ### **总结** - **`N + 1` 的长度**是为了让数组索引从 `1` 开始使用,直接对齐输入数据的编号(如第1架到第N架)。 - 这种设计在算法题中非常常见,尤其是当问题描述的实体编号从 `1` 开始时。 - 虽然会浪费索引 `0` 的空间,但通常影响可忽略不计,且能显著提升代码可读性和逻辑简洁性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值