陣列巢狀(力扣第565題)
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出現重複的元素。
示例 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是[1, 20,000]之間的整數。
A中不含有重複的元素。
A中的元素大小在[0, N-1]之間。
分析:
可以將S集合看作是一個環形連結串列,那麼集合的大小就是環形連結串列的長度。因為陣列的索引是0N-1,陣列的元素值也是0N-1,所以元素值就可以看作是下一個結點的“地址”,比如對於示例1,S[0]集合來說,就是形成了這樣一個環形連結串列:
0->5->6->2->0,
可以定義一個隊首指標head,head = nums[0],然後定義一個遍歷指標變數point,初始值指向隊首元素的下一個元素:point=nums[head],然後它進行的向後走的操作是:point=nums[point]
當head == point時,說明環形連結串列走完。
因此,我們對陣列中每個元素進行這樣的操作之後,求出最大值就是最終的結果。
public int arrayNesting(int[] nums) { if (nums == null || nums.length == 0){ return 0; } int maxLength = 0; for (int i = 0; i < nums.length; i++) { int head = nums[i]; int point = nums[head]; int curLength = 1; while (point != head) { point = nums[point]; curLength++; } if (curLength > maxLength) { maxLength = curLength; } } return maxLength; }
該方法雖然解決了問題,但是時間複雜度太高,效能不好,有點像暴力法了。其實可以優化一下的,因為我們要求的是長度最大的那個環形連結串列的長度,而每個環形連結串列都是一個閉環,即環外的元素永遠不會指向環中的元素,環中的元素也不會指向環外元素,求所有的S集合的操作相當於將原始陣列的元素劃分為了幾個不相交的集合。那麼既然是一個閉環,那麼從環中任何一個元素出發,最終形成的都是相同的環,長度自然也就相同。既然這樣就無需對原始陣列逐個遍歷求長度了,而是對於同一個環中的元素,訪問過之後就無需再訪問了。這樣會大大節省時間。
例如:
S[0] : 0->5->6->2->0
S[2] : 2->0->5->6->2
S[5] : 5->6->2->0->5
public int arrayNesting2(int[] nums) {
if (nums == null || nums.length == 0){
return 0;
}
int maxLength = 0;
for (int i = 0; i < nums.length; i++) {
int count = 0;
for (int j = i; nums[j] != -1 ; ) {
count++;
int temp = nums[j];
nums[j] = -1;
j = temp;
}
if (count > maxLength){
maxLength = count;
}
}
return maxLength;
}