咨询DSA中i-array.length等数组索引计算的含义与工作原理
在DSA问题里,你提到的这些索引表达式都是针对特定场景设计的,背后都有明确的逻辑,而且只要是正规实现,都会提前保证索引的合法性,不会出现负索引越界的问题。下面逐个拆解:
1. i - array.length
这个表达式多用于反向映射或批量遍历场景,核心是把大于等于数组长度的i值,转换成从0开始的正向索引。比如当你需要同时遍历原数组和另一个长度相同的辅助数组时,可能会让i从array.length开始遍历到2*array.length-1,此时i - array.length就对应原数组的正常索引。
举个简单例子:
arr = [10, 20, 30] for i in range(len(arr), 2*len(arr)): idx = i - len(arr) print(arr[idx]) # 依次输出10、20、30
这里i的取值范围是[3,6),计算后的idx正好是0、1、2,完全在数组有效索引范围内。
2. i - array[i]
这是原地哈希类算法的常用操作,比如寻找重复元素、缺失正数这类问题。这类场景下,数组元素通常被限制在特定范围(比如1到数组长度n),通过当前索引i和元素值array[i]的差值,来定位元素应该在的目标位置,或者标记元素是否被访问过。
比如在寻找数组中重复元素的实现里:
int[] nums = {2, 3, 1, 0, 2, 5, 3}; for (int i = 0; i < nums.length; i++) { int targetIdx = Math.abs(nums[i]) - 1; if (nums[targetIdx] < 0) { System.out.println("重复元素:" + Math.abs(nums[i])); } else { nums[targetIdx] *= -1; } }
注:类似i - array[i]的变体,核心都是利用元素值和索引的绑定关系,而且代码会通过取绝对值、范围校验等方式,确保最终索引合法。
3. i + variable
这是最基础的偏移访问,variable可以是常数、变量或表达式,用来访问当前索引的后续位置。常见场景包括滑动窗口、成对元素处理、跳跃遍历等。
比如滑动窗口算法中,用i + k -1定位窗口右端:
vector<int> arr = {1,3,-1,-3,5,3,6,7}; int k = 3; for (int i = 0; i <= arr.size() - k; i++) { int right = i + k - 1; // 处理窗口[i, right]内的元素 }
这里循环条件i <= arr.size() -k保证了right不会超过数组最后一个索引(arr.size()-1),完全合法。
4. i - j - 1
这个表达式主要用来计算两个索引之间的元素数量,或者定位区间边界,常见于子数组/子串长度计算、区间反转等场景。比如当j是上一个重复字符的索引,i是当前字符索引时,i - j -1就是当前无重复子串的长度;或者在反转数组的某个子区间时,用来确定区间的范围。
比如反转数组中j+1到i-1的子区间:
let arr = [1,2,3,4,5]; let j = 0, i = 4; let left = j + 1; let right = i - 1; while (left < right) { [arr[left], arr[right]] = [arr[right], arr[left]]; left++; right--; } // 反转后arr变为[1,4,3,2,5]
这里i - j -1等于3,正好是要反转的子区间(索引1-3)的元素个数。
为什么不会出现负索引?
这些表达式的使用都有一个核心前提:代码会通过循环边界控制、条件判断、范围校验等方式,确保计算后的索引落在数组有效范围内(0 ≤ idx < array.length)。比如:
- 用
i - array.length时,会保证i ≥ array.length; - 用
i - array[i]时,会限制元素值的范围,或对结果做修正(比如取绝对值); - 用
i + variable时,会确保偏移后的索引不超过数组长度; - 用
i - j -1时,会保证i > j + 1,或者仅在正数场景下使用。
只要是正规的DSA实现,都会提前处理这些边界情况,不会出现负索引导致的未定义行为。
内容的提问来源于stack exchange,提问作者thegamerguy

