[LeetCode] 565. Array Nesting 数组嵌套
You are given an integer array nums
of length n
where nums
is a permutation of the numbers in the range [0, n - 1]
.
You should build a set s[k] = {nums[k], nums[nums[k]], nums[nums[nums[k]]], ... }
subjected to the following rule:
- The first element in
s[k]
starts with the selection of the elementnums[k]
ofindex = k
. - The next element in
s[k]
should benums[nums[k]]
, and thennums[nums[nums[k]]]
, and so on. - We stop adding right before a duplicate element occurs in
s[k]
.
Return the longest length of a set s[k]
.
Example 1:
Input: nums = [5,4,0,3,1,6,2] Output: 4 Explanation: nums[0] = 5, nums[1] = 4, nums[2] = 0, nums[3] = 3, nums[4] = 1, nums[5] = 6, nums[6] = 2. One of the longest sets s[k]: s[0] = {nums[0], nums[5], nums[6], nums[2]} = {5, 6, 2, 0}
Example 2:
Input: nums = [0,1,2] Output: 1
Constraints:
1 <= nums.length <= 105
0 <= nums[i] < nums.length
- All the values of
nums
are unique.
这道题让我们找嵌套数组的最大个数,给的数组总共有n个数字,范围均在 [0, n-1] 之间,题目中也把嵌套数组的生成解释的很清楚了,其实就是值变成坐标,得到的数值再变坐标。那么实际上当循环出现的时候,嵌套数组的长度也不能再增加了,而出现的这个相同的数一定是嵌套数组的首元素(因为这些数字没有重复,所以不可能在其他位置也有一个数指向同一个下标),博主刚开始没有想清楚这一点,以为出现重复数字的地方可能是嵌套数组中间的某个位置,于是用个 set 将生成的嵌套数组存入,然后每次查找新生成的数组是否已经存在。而且还以原数组中每个数字当作嵌套数组的起始数字都算一遍,结果当然是 TLE 了。其实对于遍历过的数字,我们不用再将其当作开头来计算了,而是只对于未遍历过的数字当作嵌套数组的开头数字,不过在进行嵌套运算的时候,并不考虑中间的数字是否已经访问过,而是只要找到和起始位置相同的数字位置,然后更新结果 res,参见代码如下:
解法一:
class Solution { public: int arrayNesting(vector<int>& nums) { int n = nums.size(), res = INT_MIN; vector<bool> visited(n, false); for (int i = 0; i < nums.size(); ++i) { if (visited[nums[i]]) continue; res = max(res, helper(nums, i, visited)); } return res; } int helper(vector<int>& nums, int start, vector<bool>& visited) { int i = start, cnt = 0; while (cnt == 0 || i != start) { visited[i] = true; i = nums[i]; ++cnt; } return cnt; } };
下面这种方法写法上更简洁一些,思路完全一样,参见代码如下:
解法二:
class Solution { public: int arrayNesting(vector<int>& nums) { int n = nums.size(), res = INT_MIN; vector<bool> visited(n, false); for (int i = 0; i < n; ++i) { if (visited[nums[i]]) continue; int cnt = 0, j = i; while(cnt == 0 || j != i) { visited[j] = true; j = nums[j]; ++cnt; } res = max(res, cnt); } return res; } };
下面这种解法是网友 @edyyy 提醒博主的,可以优化解法二的空间,我们并不需要专门的数组来记录数组是否被遍历过,而是在遍历的过程中,将其交换到其应该出现的位置上,因为如果某个数出现在正确的位置上,那么它一定无法组成嵌套数组,这样就相当于我们标记了其已经访问过了,思路确实很赞啊,参见代码如下:
解法三:
class Solution { public: int arrayNesting(vector<int>& nums) { int n = nums.size(), res = 0; for (int i = 0; i < n; ++i) { int cnt = 1; while (nums[i] != i) { swap(nums[i], nums[nums[i]]); ++cnt; } res = max(res, cnt); } return res; } };
Github 同步地址:
https://github.com/grandyang/leetcode/issues/565
类似题目:
参考资料:
https://leetcode.com/problems/array-nesting/
https://leetcode.com/problems/array-nesting/discuss/102432/C%2B%2B-Java-Clean-Code-O(N)