数组题目:数组嵌套

题目

标题和出处

标题:数组嵌套

出处:565. 数组嵌套

难度

4 级

题目描述

要求

给你一个长度为 n \texttt{n} n 的整数数组 nums \texttt{nums} nums nums \texttt{nums} nums [0,   n   -   1] \texttt{[0, n - 1]} [0, n - 1] 范围内的数字的一个排列。

你需要建立集合 s[k]   =   {nums[k],   nums[nums[k]],   nums[nums[nums[k]]],   … } \texttt{s[k] = \{nums[k], nums[nums[k]], nums[nums[nums[k]]], \ldots \}} s[k] = {nums[k], nums[nums[k]], nums[nums[nums[k]]], } 且遵守以下的规则:

  • s[k] \texttt{s[k]} s[k] 的第一个元素从选定的 nums[k] \texttt{nums[k]} nums[k] 开始;
  • s[k] \texttt{s[k]} s[k] 的下一个元素是 nums[nums[k]] \texttt{nums[nums[k]]} nums[nums[k]],然后是 nums[nums[nums[k]]] \texttt{nums[nums[nums[k]]]} nums[nums[nums[k]]],以此类推;
  • s[k] \texttt{s[k]} s[k] 出现重复元素之前,停止添加元素。

返回集合 s[k] \texttt{s[k]} s[k] 的最大长度。

示例

示例 1:

输入: nums   =   [5,4,0,3,1,6,2] \texttt{nums = [5,4,0,3,1,6,2]} nums = [5,4,0,3,1,6,2]
输出: 4 \texttt{4} 4
解释:
nums[0]   =   5,   nums[1]   =   4,   nums[2]   =   0,   nums[3]   =   3,   nums[4]   =   1,   nums[5]   =   6,   nums[6]   =   2 \texttt{nums[0] = 5, nums[1] = 4, nums[2] = 0, nums[3] = 3, nums[4] = 1, nums[5] = 6, nums[6] = 2} nums[0] = 5, nums[1] = 4, nums[2] = 0, nums[3] = 3, nums[4] = 1, nums[5] = 6, nums[6] = 2
其中一种最长的 s[k] \texttt{s[k]} s[k]
s[0]   =   {nums[0],   nums[5],   nums[6],   nums[2]}   =   {5,   6,   2,   0} \texttt{s[0] = \{nums[0], nums[5], nums[6], nums[2]\} = \{5, 6, 2, 0\}} s[0] = {nums[0], nums[5], nums[6], nums[2]} = {5, 6, 2, 0}

示例 2:

输入: nums   =   [0,1,2] \texttt{nums = [0,1,2]} nums = [0,1,2]
输出: 1 \texttt{1} 1

数据范围

  • 1 ≤ nums.length ≤ 10 5 \texttt{1} \le \texttt{nums.length} \le \texttt{10}^\texttt{5} 1nums.length105
  • 0 ≤ nums[i] < nums.length \texttt{0} \le \texttt{nums[i]} < \texttt{nums.length} 0nums[i]<nums.length
  • nums \texttt{nums} nums 中不含有重复的元素

解法

思路和算法

最朴素的思想是,对于从 0 0 0 n − 1 n-1 n1 的每个下标,分别以每个下标作为开始下标,寻找对应的 s s s。最坏情况下,对于一个下标寻找 s s s 的时间复杂度是 O ( n ) O(n) O(n),总时间复杂度是 O ( n 2 ) O(n^2) O(n2),会超出时间限制。

换一个角度考虑。假设其中的一个集合 s s s 包含 k k k 个元素,其中 k ≤ n k \le n kn,每个元素分别位于下标 i 1 , i 2 , … , i k i_1, i_2, \ldots, i_k i1,i2,,ik,且满足 nums [ i j ] = i j + 1 ( 1 ≤ j < k ) \textit{nums}[i_j]=i_{j+1}(1 \le j<k) nums[ij]=ij+1(1j<k) nums [ i k ] = i 1 \textit{nums}[i_k]=i_1 nums[ik]=i1

k k k 个元素形成一个闭环: i 1 → i 2 → i 3 → … → i k → i 1 i_1 \rightarrow i_2 \rightarrow i_3 \rightarrow \ldots \rightarrow i_k \rightarrow i_1 i1i2i3iki1。无论从这 k k k 个元素中的哪一个元素开始访问,都会遍历这 k k k 个元素,最后回到开始的元素,即得到的集合 s s s 都是相同的,除了访问元素的顺序不同。因此,一旦这 k k k 个元素中的任意一个元素被访问过,其余 k − 1 k-1 k1 个元素一定也被访问过,不需要对已经访问过的元素再次访问。由此可以得到一个优化的做法,数组 nums \textit{nums} nums 中的每个元素都只访问一次,时间复杂度可以优化到 O ( n ) O(n) O(n)

具体做法是,从下标 0 0 0 开始,遍历数组 nums \textit{nums} nums 中的元素,根据开始下标得到对应的集合 s s s。如果当前下标尚未被访问,则以当前下标为开始下标,遍历元素得到集合 s s s,直到回到开始下标,并将所有访问过的下标记为被访问,同时更新当前集合 s s s 的大小,维护最大的集合 s s s 的大小。如果当前下标已经被访问,则当前下标所属的集合 s s s 中的每个元素都已经被访问,因此跳过当前下标。该做法确保数组 nums \textit{nums} nums 中的每个元素只会被访问一次,不会被访问第二次,因此时间复杂度是 O ( n ) O(n) O(n)

为了记录数组 nums \textit{nums} nums 中的每个元素是否被访问过,需要新建一个长度为 n n n 的数组 visited \textit{visited} visited,记录每个元素是否被访问过。如果要节省空间,也可以原地修改数组 nums \textit{nums} nums 中的元素,已经被访问过的位置的元素值修改成一个小于 0 0 0 或大于 n − 1 n-1 n1 的数。

代码

class Solution {
    public int arrayNesting(int[] nums) {
        int n = nums.length;
        boolean[] visited = new boolean[n];
        int maxLength = 0;
        for (int i = 0; i < n; i++) {
            if (!visited[i]) {
                int curLength = 0;
                int index = i;
                while (!visited[index]) {
                    int num = nums[index];
                    visited[index] = true;
                    index = num;
                    curLength++;
                }
                maxLength = Math.max(maxLength, curLength);
            }
        }
        return maxLength;
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 nums \textit{nums} nums 的长度。数组 nums \textit{nums} nums 中的每个元素都只会访问一次。

  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 nums \textit{nums} nums 的长度。需要创建长度为 n n n 的数组 visited \textit{visited} visited 记录每个元素是否被访问过。如果原地修改 nums \textit{nums} nums 的元素值,则空间复杂度可以降低到 O ( 1 ) O(1) O(1)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伟大的车尔尼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值