携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第28天,点击查看活动详情
题目链接:565. 数组嵌套
题目描述
索引从 0 开始长度为 N 的数组 A,包含 0 到 N - 1 的所有整数。找到最大的集合 S 并返回其大小,其中 S[i] = {A[i], A[A[i]], A[A[A[i]]], ... } 且遵守以下的规则。
假设选择索引为 i 的元素 A[i] 为 S 的第一个元素,S 的下一个元素应该是 A[A[i]],之后是A[A[A[i]]]... 以此类推,不断添加直到 S 出现重复的元素。
提示:
N是[1, 100,000]之间的整数。A中不含有重复的元素。A中的元素大小在[0, N-1]之间。
示例 1:
输入: A = [5,4,0,3,1,6,2]
输出: 4
解释:
A[0] = 5, A[1] = 4, A[2] = 0, A[3] = 3, A[4] = 1, A[5] = 6, A[6] = 2.
其中一种最长的 S[K]:
S[0] = {A[0], A[5], A[6], A[2]} = {5, 6, 2, 0}
整理题意
题目给定 n 个节点,第 i 个节点向 nums[i] 连边,问在这 n 个节点构成的有向图中,最大环的节点个数。
题目保证
nums中不含有重复的元素,且nums[i]的取值范围在[0, n - 1]
解题思路分析
由于题目保证 nums 中不含有重复的元素,所以图中每个节点的出度和入度均为 1。在这种情况下,有向图必然由一个或多个环组成。我们可以通过遍历图,找到节点个数最大的环。
具体实现
- 代码实现时需要用一个
vis数组来标记访问过的节点。但是考虑到nums中不含有重复的元素,且nums[i]的取值范围在[0, n - 1],我们可以利用nums数组来进行标记达到空间优化的目的。 - 同时我们可以考虑到当遍历到图中一个环中的节点数量大于
n / 2时,我们可以直接返回答案,这是因为每个节点只会在一个环中出现,所以剩下的节点数组成最大的环也不会超过当前最大值。
复杂度分析
- 时间复杂度:,其中
n是数组nums的长度。 - 空间复杂度:,我们只需要常数的空间保存若干变量。
代码实现
class Solution {
public:
int arrayNesting(vector<int>& nums) {
int ans = 0, n = nums.size();
for(int i = 0; i < n; i++){
//cnt记录环的大小
int cnt = 0;
int j = i;
//遍历环
while(nums[j] < n){
int t = j;
j = nums[t];
//原地标记
nums[t] = n;
cnt++;
}
//维护最大值
ans = max(ans, cnt);
//优化时间
if(ans > n / 2) return ans;
}
return ans;
}
};
总结
- 题目规定的两个条件很重要:保证
nums中不含有重复的元素,且nums[i]的取值范围在[0, n - 1],这两个条件是作为解题的关键。 - 将题目转化为图论题即可,题目将转化为找到图中环的最大节点个数。
- 根据这道题的原理,有个有趣的 智力题 可以思考以下。
- 测试结果:
可以看见采用原地标记的方法在空间优化上还是比较明显的。
通过提前跳出的方法可以在时间复杂度是进行一点优化,但是优化并不明显。
结束语
我们总会遇到难事,但相比抱怨,耐下心来去寻找解决办法会更有效。越是艰难,越要勇于攀爬,因为每一步不好走的路都是上坡路。当你保持耐心,专注投入某件事时,你就会发现心态变强了,困难也就变弱了。新的一天,加油!