1. 程式人生 > 其它 >LeetCode-128. 最長連續序列

LeetCode-128. 最長連續序列

題目來源

128. 最長連續序列

題目詳情

給定一個未排序的整數陣列 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

題解分析

解法一:雜湊表

  1. 考慮到本題是求解最長連續序列,這與以前的子序列問題有點不同,這裡的連續指的是數字本身大小的連續,所以可能使用動態規劃有一定困難。
  2. 這題可以從另一個角度思考問題,因為總共輸入的序列是定長的,只有這麼多的元素在陣列中,所以,我們可以先把這些元素存入HashMap中,表示元素是否存在。對於連續元素,則可以依次遞增當前元素num,並判斷遞增後的元素是否也出現在HashMap中,如果出現則以當前元素開始的序列長度增一。
  3. 此外,本題需要滿足時間複雜度為\(O(n)\),所以使用列舉的方法可能會不滿足條件,而且裡面有很多情況是重複考慮的了。比如說,之前已經判斷了num, num+1,num+2,...,num+n,那麼下一次從num+1開始到num+n的元素就不需要再判斷了,因為這些元素已經存在HashMap中了。
  4. 最後,本題需要注意的一個地方是,需要使用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;
    }
}

解法二:雜湊表+動態規劃

  1. 前面也提到了,本題與以前的子序列問題有點不同,使用動態規劃可能比較困難,也很難想到狀態轉移方程。但是,這並不意味著不能使用動態規劃。
  2. 這是一種非常巧妙的做法,與思路2相同的一點是也利用了Map減小遍歷次數。但很重要的一點不同是其value表示的是num所在的連續區間長度。舉個例子,當Map的key為5,value為3時,這就表明當前有一個包含5且長度為3的連續區間,當然有多種可能,可以是[3,5],[4,6],[5,7]。
  3. 具體做法是:
    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;
    }
}

結果展示

Either Excellent or Rusty