LeetCode 26. 刪除排序數組中的重複項

26. 刪除排序數組中的重複項

[TOC]java

難度:簡單算法

題庫地址:leetcode-cn.com/problems/re…數組

1. 題目描述

給定一個排序數組,你須要在原地刪除重複出現的元素,使得每一個元素只出現一次,返回移除後數組的新長度。 不要使用額外的數組空間,你必須在原地修改輸入數組並在使用 O(1) 額外空間的條件下完成。bash

示例 1:函數

給定數組 nums = [1,1,2], 
函數應該返回新的長度 2, 而且原數組 nums 的前兩個元素被修改成 1, 2。 
你不須要考慮數組中超出新長度後面的元素。
複製代碼

示例 2:spa

給定 nums = [0,0,1,1,1,2,2,3,3,4],
函數應該返回新的長度 5, 而且原數組 nums 的前五個元素被修改成 0, 1, 2, 3, 4。
你不須要考慮數組中超出新長度後面的元素。
複製代碼

說明:.net

爲何返回數值是整數,但輸出的答案是數組呢? 請注意,輸入數組是以**「引用」**方式傳遞的,這意味着在函數裏修改輸入數組對於調用者是可見的。3d

你能夠想象內部操做以下:指針

// nums 是以「引用」方式傳遞的。也就是說,不對實參作任何拷貝
int len = removeDuplicates(nums);

// 在函數裏修改輸入數組對於調用者是可見的。
// 根據你的函數返回的長度, 它會打印出數組中該長度範圍內的全部元素。
for (int i = 0; i < len; i++) {
    print(nums[i]);
}
複製代碼

2. 解題思路

2.1 雙指針法

數組完成排序後,咱們能夠放置兩個指針 ij,其中 i 是慢指針,而 j 是快指針。只要 nums[i] = nums[j],咱們就增長 j 以跳太重複項。code

當咱們遇到 nums[j] \neq nums[i]nums[j]時,跳太重複項的運行已經結束,所以咱們必須把它(nums[j])的值複製到 nums[i + 1]。而後遞增 i,接着咱們將再次重複相同的過程,直到 j 到達數組的末尾爲止。

感受題解描述的很清楚,作了一個雙指針法圖示GIF圖以下:

慢指針 i 記錄最新獲取的數值,快指針 j 尋找下一個不一樣的數,在遍歷的過程當中,只要兩者不一樣,就將 j 位置的數值複製到 i 的下一個位置。

public int removeDuplicates(int[] nums) {
    if (nums == null || nums.length == 0) {
        return 0;
    } else {
        int i = 0;
        for (int j = 1; j < nums.length; j++) {
            if (nums[j] != nums[i]) {
                i++;
                nums[i] = nums[j];
            }
        }
        // i 是索引位置,返回的是長度,別忘了 +1
        return i + 1;
    }
}
複製代碼

複雜度分析:

  • 時間複雜度:O(n), 假設數組的長度是 n,那麼 ij 分別最多遍歷 n 步。
  • 空間複雜度:O(1)

2.2 被本身蠢哭的解法🤪

剛開始想到的方法是用一個指針 i 記錄當前遊標位置,默認從 1 開始,tmp記錄上一個不一樣的值,默認 nums[0]

  1. 遍歷數組,當找到和tmp值不一樣時,說明是一個重複段的開始,記爲 segmentStart
  2. 繼續日後遍歷,當遊標下一個值和當前值不一樣 nums[i] == nums[i + 1],說明當前遊標是重複段的結束,假設是位置 p
  3. 將索引是 [p + 1, nums.length - 1] 範圍內的數據依次向前移動 offset = i - segmentStart 位置,數組長度減去 offset ,下一次從segmentStart + 1 索引開始遍歷。

把問題搞得很複雜,邊界條件處理起來也很麻煩,致使程序運行結果以下:

Runtime: 39 ms
Memory Usage: 44.1 MB
複製代碼

當看了題解的算法,感受豁然開朗,真是被本身蠢哭。

下面是辣眼睛的算法:

public int removeDuplicates(int[] nums) {
    if (nums == null || nums.length == 0) {
        return 0;
    } else {
        int num = 1;
        int tmp = nums[0];
        int segmentStart = 0;
        int len = nums.length;
        for (int i = 1; i < len; i++) {
            if (nums[i] != tmp) {
                num++;
                tmp = nums[i];
                segmentStart = i;
            } else {
                while (i < len - 1 && nums[i] == nums[i + 1]) {
                    i++;
                }
                int offset = i - segmentStart;
                len -= offset;
                move(nums, i + 1, len + offset, offset);
                i = segmentStart;
            }
        }
        return num;
    }
}

/** * 數組從start索引開始按個前移offset位 */
private void move(int[] nums, int start, int len, int offset) {
    if (nums != null && len > 0) {
        for (int i = start; i < len; i++) {
            nums[i - offset] = nums[i];
        }
    }
}
複製代碼
相關文章
相關標籤/搜索