【C/C++】565. 数组嵌套

133 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第28天,点击查看活动详情


题目链接:565. 数组嵌套

题目描述

索引从 0 开始长度为 N 的数组 A,包含 0N - 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 出现重复的元素。

提示:

  1. N[1, 100,000]之间的整数。
  2. A 中不含有重复的元素。
  3. 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 时,我们可以直接返回答案,这是因为每个节点只会在一个环中出现,所以剩下的节点数组成最大的环也不会超过当前最大值。

复杂度分析

  • 时间复杂度:O(n)O(n),其中 n 是数组 nums 的长度。
  • 空间复杂度:O(1)O(1),我们只需要常数的空间保存若干变量。

代码实现

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],这两个条件是作为解题的关键。
  • 将题目转化为图论题即可,题目将转化为找到图中环的最大节点个数。
  • 根据这道题的原理,有个有趣的 智力题 可以思考以下。
  • 测试结果:

565.png

565优化空间.png 可以看见采用原地标记的方法在空间优化上还是比较明显的。

565优化时间.png 通过提前跳出的方法可以在时间复杂度是进行一点优化,但是优化并不明显。

结束语

我们总会遇到难事,但相比抱怨,耐下心来去寻找解决办法会更有效。越是艰难,越要勇于攀爬,因为每一步不好走的路都是上坡路。当你保持耐心,专注投入某件事时,你就会发现心态变强了,困难也就变弱了。新的一天,加油!