A zero-indexed array A of length N contains all integers from 0 to N-1. Find and return the longest length of set S, where S[i] = {A[i], A[A[i]], A[A[A[i]]], … } subjected to the rule below.
Suppose the first element in S starts with the selection of element A[i] of index = i, the next element in S should be A[A[i]], and then A[A[A[i]]]… By that analogy, we stop adding right before a duplicate element occurs in S.
Example 1:
Input: A = [5,4,0,3,1,6,2]
Output: 6
Explanation:
A[0] = 5, A[1] = 4, A[2] = 0, A[3] = 3, A[4] = 1, A[5] = 6, A[6] = 2.One of the longest S[K]:
S[0] = {A[0], A[5], A[6], A[2]} = {5, 6, 2, 0}
Note:
- N is an integer within the range [1, 20,000].
- The elements of A are all distinct.
- Each element of A is an integer within the range [0, N-1].
给定一个数组,数组的长度为n,数组中包含的内容为0到n-1。我们需要找到一个最长的S,S的规则定义是这样的:用当前位置上的元素数字,这个元素数字作为下标,然后去数组中找到下一个元素,直到元素为0为止。也就是题中的例子:
所以得到的序列为:S={A[0],A[5],A[6],A[2]}={5,6,2,0},这个序列是最长的为4,需要注意题中Output是6是错误的。
最简单的思路,就是每遍历一个元素都顺着这个元素遍历下去,直到元素出现0或者等于本身结束,中间记录遍历的次数与最大的相比较进行替换。
/*class Solution {
public:
int arrayNesting(vector<int>& nums) {
int maxLen = 1;
int curLen = 1;
for(int i=0; i < nums.size(); i++) {
curLen = 1;
int curIndexNum = nums[i];//当前下标对应的数字
//如果当前下标对应的数字不等于0,并且不等于自身
while(curIndexNum && curIndexNum!=i) {
curLen++;
curIndexNum = nums[curIndexNum];//找到下一个数字
}
maxLen = max(maxLen,curLen);
}
return maxLen;
}
};*/
仔细想一下的话上面的思路是可以进行优化的。比如题中的例子,按照上面的思路来看,如果访问下标为0的元素,那么顺着下标跳转的路线如下:
但是继续往后访问的话,按照上面解题思路是每个元素都需要进行跳转访问的,所以访问到下标为5的时候,也会走如下的路线:
但很显然,这个是没有下标从0开始的长度大的,也就是说是如果前面有访问过的元素,后面的不需要再次访问了,毕竟后面访问的长度肯定没有前面的长。按照这个思路我们可以利用一个同样为n的数组,里面用来标记对应位置的元素是否被访问过了。如果没有访问的话才进行访问,如果已经被前面的访问过了就不需要再次访问了。这样虽然增加了空间复杂度从O(1)到O(n),但是时间复杂度就会降为O(n)。
class Solution {
public:
int arrayNesting(vector<int>& nums) {
int maxLen = 0;
int curLen = 0;
vector<bool> visied(nums.size());//代表着对应位置有没有被访问过
for(int i=0; i<nums.size(); i++) {
curLen = 0;
int curIndexNum = nums[i];
while(!visied[curIndexNum]) {//查看对应位置是否被访问过
curLen++;
int temp = curIndexNum;
curIndexNum = nums[curIndexNum];
visied[temp] = true;
}
maxLen = max(maxLen,curLen);
}
return maxLen;
}
};
按照上面的思路,时间复杂度为O(n),这个应该是不能再低了,但是空间复杂度是否可以再一步进行优化?我们上面的想法是用一个对应的数组来标记某个位置是否被访问过了,如果我们能够采用另一种不浪费额外空间的方法来进行标记就能够降低空间复杂度。
重点在于需要标记某个位置是否被访问过,如果访问过某个元素我们需要做个记号防止下次访问。我们可以利用一个在数组中不会出现的数字进行标记就可以了,按照题中的规则N的范围是[1, 20,000]。所以只要用一个不在这个范围内的数字进行标记就可以了,可以采用INT_MAX。
lass Solution {
public:
int arrayNesting(vector<int>& nums) {
int maxLen = 0;
int curLen = 0;
//对之前的进行优化,访问过的不需要再次访问了
for(int i=0; i < nums.size(); i++) {
curLen = 0;//从0开始,因为最后如果下标是0的话也会访问,所以需要从0开始计算
if(nums[i] != INT_MAX) {
int curIndexNum = nums[i];//找到下一个元素坐标
while(nums[curIndexNum] != INT_MAX) {//如果当前元素是最大值的话,说明已经访问过了不需要再次访问了,否则对其访问
curLen++;
int temp = curIndexNum;//记录下刚才访问的坐标
curIndexNum = nums[curIndexNum];//找到下一个元素坐标
nums[temp] = INT_MAX;//将刚才访问过的设置为INT_MAX
}
maxLen = max(maxLen,curLen);
}
}
return maxLen;
}
};