LeetCode-128. 最長連續序列
阿新 • • 發佈:2022-01-15
題目來源
題目詳情
給定一個未排序的整數陣列 nums
,找出數字連續的最長序列(不要求序列元素在原陣列中連續)的長度。
請你設計並實現時間複雜度為O(n)
的演算法解決此問題。
示例 1:
輸入: nums = [100,4,200,1,3,2]
輸出: 4
解釋: 最長數字連續序列是 [1, 2, 3, 4]。它的長度為 4。
示例 2:
輸入: nums = [0,3,7,2,5,8,4,6,0,1]
輸出: 9
提示:
0 <= nums.length <= 105
-109 <= nums[i] <= 109
題解分析
解法一:雜湊表
- 考慮到本題是求解最長連續序列,這與以前的子序列問題有點不同,這裡的連續指的是數字本身大小的連續,所以可能使用動態規劃有一定困難。
- 這題可以從另一個角度思考問題,因為總共輸入的序列是定長的,只有這麼多的元素在陣列中,所以,我們可以先把這些元素存入HashMap中,表示元素是否存在。對於連續元素,則可以依次遞增當前元素num,並判斷遞增後的元素是否也出現在HashMap中,如果出現則以當前元素開始的序列長度增一。
- 此外,本題需要滿足時間複雜度為\(O(n)\),所以使用列舉的方法可能會不滿足條件,而且裡面有很多情況是重複考慮的了。比如說,之前已經判斷了num, num+1,num+2,...,num+n,那麼下一次從num+1開始到num+n的元素就不需要再判斷了,因為這些元素已經存在HashMap中了。
- 最後,本題需要注意的一個地方是,需要使用HashSet來去重,這裡的連續元素指的是嚴格連續,不包括重複元素。
class Solution { public int longestConsecutive(int[] nums) { // 這裡使用HashSet,而不是HashMap,因為Set可以用來去重 Set<Integer> set = new HashSet<>(); for(int num : nums){ set.add(num); } int maxlength = 0; for(int num : nums){ if(!set.contains(num - 1)){ int curn = num; int length = 1; while(set.contains(curn + 1)){ curn++; length++; } maxlength = Math.max(maxlength, length); } } return maxlength; } }
解法二:雜湊表+動態規劃
- 前面也提到了,本題與以前的子序列問題有點不同,使用動態規劃可能比較困難,也很難想到狀態轉移方程。但是,這並不意味著不能使用動態規劃。
- 這是一種非常巧妙的做法,與思路2相同的一點是也利用了Map減小遍歷次數。但很重要的一點不同是其value表示的是num所在的連續區間長度。舉個例子,當Map的key為5,value為3時,這就表明當前有一個包含5且長度為3的連續區間,當然有多種可能,可以是[3,5],[4,6],[5,7]。
- 具體做法是:
3.1 遍歷nums陣列中的所有數字num
3.2 當num是第一次出現時:- 分別獲取到左相鄰數字num-1的連續區間長度left和右相鄰數字num+1的連續區間長度right;
- 計算得到當前的區間長度為curLen=left+right+1;
- 更新最長區間長度ans以及左右邊界的區間長度。
class Solution {
public int longestConsecutive(int[] nums) {
HashMap<Integer, Integer> map = new HashMap<>();
int maxlength = 0;
for(int num : nums){
if(!map.containsKey(num)){
// 取出該元素左區間的長度(當前元素作為右端點)
int left = map.getOrDefault(num-1, 0);
// 取出該元素右區間的長度(當前元素作為左端點)
int right = map.getOrDefault(num+1, 0);
int length = left + right + 1;
map.put(num, length);
// 更新原有左區間的左端點
map.put(num - left, length);
// 更新原來右區間的右端點
map.put(num + right, length);
maxlength = Math.max(maxlength, length);
}
}
return maxlength;
}
}