1. 程式人生 > >LeetCode——解決刪除陣列中重複元素問題三種方式

LeetCode——解決刪除陣列中重複元素問題三種方式

情景要求:
(1)輸入的陣列元素為基本型別int。
(2)返回不含重複元素的陣列,其型別為基本型別int。
(3)對最後的結果陣列元素順序沒有要求,可亂序也可排序。
經過一系列的思考與實踐,總結出了以下三種不同情形“刪除陣列重複元素”的解決辦法。

1、通過原始陣列刪除重複元素

方法一:不重新開闢記憶體(不建立list、set等),可以在原始陣列中原地刪除重複元素。
由於陣列的長度是固定的,所以沒有像list那樣的remove刪除方法,只能通過簡單的演算法構造解決。

(1)簡單過程演算法描述:

(1)從i=0開始與nums[i+1]元素比較,即巢狀迴圈比較
(2)若元素相同,則記下相同元素的索引index,採用向前覆蓋num[index]=nums[index+1],覆蓋重複元素資料。
然後陣列長度減1(去掉末尾元素)同時復位i=0,返回(1)
(3) 若不相同則繼續(1)。
(4)直到i=nums.length成立,終止

(2)java程式碼:

    public int[] removeDuplicates(int[] nums) {
    int index = -1;
    int i = 0;
    if (nums.length == 0) {
        return null;
    }
    loop: while (i != nums.length) {
        if (i == nums.length - 1) {
            break;
        }
        for (int k = i + 1; k < nums.length
; k++) { if (nums[i] == nums[k]) { index = k; // 刪除重複的第一個 for (int j = index; j < nums.length; j++) { if (j != nums.length - 1) { nums[j] = nums[j + 1];// 向前覆蓋 } else {// 處理最後一位超出情況 nums[j] = nums[j]; } } nums = Arrays.copyOf(nums, nums.length
- 1);// 覆蓋之後長度減1 i = 0; // 如果有重複的元素,則重新迴圈新陣列nums(已經去掉了上一個重複元素的陣列) // 此處必須要用continue,不能用break。不然沒法讓i=0重新開始迴圈 continue loop; } } i++;// 若沒有再重複的元素就i++,繼續迴圈 } return nums; }

(3)優缺點說明

  • 優點:很明顯地節約了空間,在此過程中沒有任何建立多餘儲存空間,即建立新的陣列,集合等操作;演算法簡單,容易理解。
  • 缺點:迴圈次數多,時間複雜度高,犧牲了時間節約了空間;雖然保持原始的元素順序,但無法同時排序

2、通過List刪除陣列重複元素

方法一:遍歷陣列比較,若相等則break中斷,若不相等則加入list集合

(1)簡單過程演算法描述:

(1)迴圈遍歷比較陣列與list集合
(2)若相等則break中斷本次迴圈,繼續(1)下次迴圈
(3)若不相等則加入list結果集合
(4)list結果集合轉為陣列結果集

(2)java程式碼:

 public int[] removeDuplicates(int[] nums) {  
      List<Integer> result = new ArrayList<>();      
      boolean flag;  //是否重複標誌位
      for(int i=0;i<nums.length;i++){  
          flag = false;  
          for(int j=0;j<result.size();j++){  
              if(nums[i] == result.get(j)){  
                  flag = true;  
                  break;  
              }  
          }  
          if(!flag){  
              result.add(nums[i]);  
          }  
      }  
      //list元素轉為新陣列
      int arr[] = new int[result.size()];
      int j = 0;
      for (int i : result) {
        arr[j] = i;
        j++;
    }
      return arr;
  }

(3)優缺點說明:

  • 優點:過程簡單,容易理解;儲存空間較少,節約記憶體;利用list集合操作元較為素方便,可以同時滿足排序等情況。
  • 缺點:巢狀迴圈,導致時間複雜度較高;

方法二:利用一次for迴圈,進行相鄰資料的比較即可得到結果。
此方法是我再網上查的資料,然後經過實踐測試總結到的,但是有個很明顯的不足,最後我將以彌補得到相對完美的方法。
先將之前的不足方法進行介紹:
(1)簡單過程演算法描述:

(1)初始化集合list,加入陣列第一個元素。
(2)迴圈遍歷陣列與集合中最後一個元素進行比較
(3)若相等則結束本次迴圈,繼續(2)下一次迴圈
(4)若不相等則加入list結果集合,返回(2)繼續
(5)list結果集合轉為陣列結果集

(2)java程式碼:

 public int[] removeDuplicates(int[] nums) {  
        List<Integer> result = new ArrayList<>(); 
        result.add(nums[0]);  
        for(int i=1;i<nums.length;i++){  
            if(nums[i] != (result.get(result.size()-1))){  
                result.add(nums[i]);  
            }  
        }  
      //list元素轉入新陣列
      int arr[] = new int[result.size()];
      int j = 0;
      for (int i : result) {
        arr[j] = i;
        j++;
    }
          return arr;
      }

(3)結果分析:

此方法重點是“相鄰元素比較”。而忽略瞭如果出現[4,5,4]重複元素“隔空”情況時,就不能滿足。

input: int [] nums = { 6, 6, 5, 6, 8, 8, 7 };
output: nums = [6, 5, 6, 8, 7]

顯然這是明顯錯誤的,網上有些的方法只是經過自己的推測猜想,並沒有認真謹慎的實踐。

(4)問題優化:

之所以出現以以上不正確的結果,是因為並沒有考慮到不重複的元素已經在集合list中,下一次的比較只比較相鄰的元素,並不能確定同樣的元素是否已經在list集合中。那麼我們只需要加一個條件即可解決。

!result.contains(nums[i])

程式碼如下:

 public int[] removeDuplicates(int[] nums) {  
        List<Integer> result = new ArrayList<>(); 
        result.add(nums[0]);  
        for(int i=1;i<nums.length;i++){  
            if(nums[i] != (result.get(result.size()-1)) && !result.contains(nums[i])){  
                result.add(nums[i]);  
            }  
        }  
      //list元素轉入新陣列
      int arr[] = new int[result.size()];
      int j = 0;
      for (int i : result) {
        arr[j] = i;
        j++;
    }
          return arr;
      }

經過測試結果是正確的:

input: int [] nums = { 6, 6, 5, 6, 8, 8, 7 };
output: nums = [6, 5, 8, 7]

(5)優缺點說明

  • 優點:程式碼簡單,容易理解;儲存記憶體佔用少,節約了空間;只有一次迴圈,時間複雜度相比較之下大大降低。
  • 缺點:陣列需要轉為list集合,轉的過程中需要注意基本型別與泛型的區別。

3、利用List/Set//HashSet/TreeSet刪除陣列重複元素

最簡單最好用的應該就是集合自帶的方法與特性操作元素,從而可以做到刪除陣列重複元素,那麼就需要儘可能將陣列轉為集合。所以在此之前需要了解一下四個方面。

(1)陣列轉List集合,陣列最好為Integer、String、Double等包裝型別,如果為基本型別的話,要轉為其對應的包裝型別。也可以將陣列中元素老老實實的取出放入list集合中。

(2)集合List轉Set集合會同時解決自動去重、排序兩個問題。

(3)List集合轉Set集合的方式有:一是:Set<Integer> set= new HashSet(list);,二是:Set<Integer> set = new HashSet<Integer>();set.addAll(list);

(4)Set集合轉陣列:第一是輸出包裝類Integer等,之後再轉為基本型別。第二是直接Object array [] = set.toArray()先轉為Object物件陣列,再轉為基本型別陣列。

方法一:利用List、HashSet、TreeSet實現陣列去重

(1)簡單過程描述:

(1)若為基本型別陣列,則轉為其包裝型別以便於轉為List集合。
(2)list集合轉為HashSet或者TreeSet(其實是加入填充到Set集合中)。
(3)HashSet/TreeSet轉為Array陣列。

(2)java程式碼:

 public int[] removeDuplicates(int[] nums) {  
    //int 變Integer
     Integer[] array = new Integer[nums.length];

     for(int i=0; i<nums.length; i++)
     {
          Integer integer = new Integer(nums[i]);
          array[i] = integer;
     }
     //Arrays.asList接受泛型。HashSet+TreeSet接受list
        TreeSet<Integer> treeSet = new TreeSet<Integer>(Arrays.asList(array));
        //也可以HashSet hashSet = new HashSet(Arrays.asList(array));
        int arr[] = new int[treeSet.size()];
        int i = 0;
        for (Integer integer : treeSet) {
            arr[i] = integer;
            i++;
        }
       return arr;  
   }

(3)輸出結果:

input: nums = {1,3,2,3,5}
output:nums=[1,2,3,5]

由結果我們可以知道,不但已經除去了重複專案而且已經升序排好了。

(4)優缺點說明:

  • 優點:程式碼簡單,容易理解;自動排序、去重;無需構造演算法,只需轉型別。
  • 缺點:轉型別的過程中可能會出現意想不到的錯誤,導致程式終止,比如TreeSet集合不允許元素為空;排好序的集合沒法知道其元素在原始陣列的索引。

方法二:利用List、Set解決陣列去重問題

(1)簡單過程描述:

(1)將array轉為List
(2)將list加入set集合
(3)set集合變array陣列返回結果

(2)java程式碼:

public int[] removeDuplicates(int[] nums) {  
        List<Integer> numList = new ArrayList<Integer>();
        //陣列變list
        for (int i : nums)
            numList.add(i);
        //list變set
        Set<Integer> numSet = new HashSet<Integer>();
        numSet.addAll(numList);
       //set變陣列
        Object[] arr = numSet.toArray();
        int result[] = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            result[i] = (int) arr[i];
        }
      return result;
  }

備註說明:

在這裡只是記錄下本人的日常學習過程,當然也希望大神能提供更好的方法解決此類問題,特別是不利用其它儲存空間,只能利用傳入陣列nums自身去重問題(我想了很長時間也沒有想到比較好的方法)
同時也希望大家學習copy程式碼的時候一定要加以實踐測試,說不定我的程式碼也有很多問題。