Medium
题目描述
给定一个长度为 n 的整数数组 nums,其中 nums 是范围 [0, n - 1] 内数字的一个排列。
你需要构建一个集合 s[k] = {nums[k], nums[nums[k]], nums[nums[nums[k]]], … },遵循以下规则:
- s[k] 中的第一个元素从选择索引为 k 的元素 nums[k] 开始
- s[k] 中的下一个元素应该是 nums[nums[k]],然后是 nums[nums[nums[k]]],以此类推
- 在 s[k] 中出现重复元素之前停止添加
返回最长的集合 s[k] 的长度。
示例 1:
输入:nums = [5,4,0,3,1,6,2]
输出:4
解释:
nums[0] = 5, nums[1] = 4, nums[2] = 0, nums[3] = 3, nums[4] = 1, nums[5] = 6, nums[6] = 2
最长的集合 s[k] 之一:
s[0] = {nums[0], nums[5], nums[6], nums[2]} = {5, 6, 2, 0}
示例 2:
输入:nums = [0,1,2]
输出:1
约束条件:
- 1 <= nums.length <= 10^5
- 0 <= nums[i] < nums.length
- nums 的所有值都是唯一的
解题思路
这是一个寻找环的问题。由于数组是 [0, n-1] 的排列,从任意位置开始按照规则跳转,最终一定会形成环。
解法一:暴力枚举(超时) 对每个起点都计算一遍环的长度,时间复杂度 O(n²)。
解法二:访问标记优化(推荐) 由于每个数字都是唯一的,从任意起点开始的路径最终会形成一个环,且每个数字只会属于一个环。我们可以用访问标记来避免重复计算:
- 对于每个未访问的位置,开始遍历环并标记所有访问过的位置
- 计算当前环的长度,更新最大值
- 已访问的位置不需要再作为起点计算
解法三:原地标记 利用数组元素的符号来标记访问状态,避免额外的空间开销。遍历过的位置将其值标记为负数。
时间复杂度:O(n) - 每个元素最多被访问一次 空间复杂度:O(1) - 原地标记不需要额外空间
代码实现
class Solution {
public:
int arrayNesting(vector<int>& nums) {
int n = nums.size();
int maxLen = 0;
for (int i = 0; i < n; i++) {
if (nums[i] >= 0) {
int len = 0;
int j = i;
while (nums[j] >= 0) {
int next = nums[j];
nums[j] = -1; // 标记为已访问
j = next;
len++;
}
maxLen = max(maxLen, len);
}
}
return maxLen;
}
};
class Solution:
def arrayNesting(self, nums: List[int]) -> int:
n = len(nums)
max_len = 0
for i in range(n):
if nums[i] >= 0:
length = 0
j = i
while nums[j] >= 0:
next_j = nums[j]
nums[j] = -1 # 标记为已访问
j = next_j
length += 1
max_len = max(max_len, length)
return max_len
public class Solution {
public int ArrayNesting(int[] nums) {
int n = nums.Length;
int maxLen = 0;
for (int i = 0; i < n; i++) {
if (nums[i] >= 0) {
int len = 0;
int j = i;
while (nums[j] >= 0) {
int next = nums[j];
nums[j] = -1; // 标记为已访问
j = next;
len++;
}
maxLen = Math.Max(maxLen, len);
}
}
return maxLen;
}
}
var arrayNesting = function(nums) {
const n = nums.length;
let maxLen = 0;
for (let i = 0; i < n; i++) {
if (nums[i] >= 0) {
let len = 0;
let j = i;
while (nums[j] >= 0) {
const next = nums[j];
nums[j] = -1; // 标记为已访问
j = next;
len++;
}
maxLen = Math.max(maxLen, len);
}
}
return maxLen;
};
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 原地标记 | O(n) | O(1) |
| 访问数组标记 | O(n) | O(n) |
说明:
- 时间复杂度:O(n),每个元素最多被访问一次
- 空间复杂度:O(1),原地修改数组进行标记
相关题目
. Nested List Weight Sum (Medium)
. Flatten Nested List Iterator (Medium)
. Nested List Weight Sum II (Medium)