重復值問題

題目:Contains Duplicate

Given an array of integers, find if the array contains any duplicates. Your function should return true if any value appears at least twice in the array, and it should return false if every element is distinct.

Input:

  • 數組 :: int[]

Output:

  • 數組是否有重復的數 :: boolean

Intuition:

最最直接的想法是用set,因為這是set最最明顯的性質了。如果某個數無法加入set中那么就是duplicates了。注意set里的search與insert都是constant time。

Code:

TC: O(n) SC: O(n)

public boolean containsDuplicate(int[] nums) {
  Set<Integer> set = new HashSet<>();
  for (int n: nums){
    if (set.contaiAns(n)){
      return true;
    }
    set.add(n);
  }
  return false;
}

題目:Contains Duplicate II

Given an array of integers and an integer k, find out whether there are two distinct indices i and j in the array such that nums[i] = nums[j] and the absolute difference between i and j is at most k.

Input:

  • 數組 :: int[]
  • 兩duplicates的index差最多為k :: int

Output:

  • 數組是否有重復的數 :: boolean

Intuition:

沿用之前題目的使用set的思想。但此時,我們不僅要往set里放值,而且還要往外拿。為什么?因為當set里的值的index與當前值的index大于k時,他對于解就沒有價值了,那么干啥還要留著他捏?所以我們保持一個大小為k的Hashset就夠了。另外remove() 的時間復雜度也是constant的。

Code:

TC:O(n) SC: O(min(n, k))

public boolean ContainsDuplicateII(int[] nums, int k){
  Set<Integer> set = new HashSet<>();
  for (int n: nums) {
    if (set.contains(n)){
      return true;
      }
      set.add(n);
      //Sliding window
      if (set.size() > k) {
            set.remove(nums[i - k]);
      }
    }
    return false;
}

題目:Contains Duplicate III

Given an array of integers, find out whether there are two distinct indices i and j in the array such that the absolute difference between nums[i] and nums[j] is at most t and the absolute difference between i and j is at most k.

Input:

  • 數組 :: int[]
  • 兩duplicates的index差最多為k :: int
  • 兩duplicates的值差最多為t :: int

Output:

  • 數組是否有重復的數 :: boolean

Intuition:

這回不僅在index做了限制,連值上也做了限制。那最直接的想法就是在確保一個條件滿足的情況下,去檢查另一個條件是否滿足。
如果用treeset的話,因為已經是sorted的情況,那么就檢查某一個值的相鄰最近的值(ceiling和floor)滿不滿足值差最多為t的條件。注意Treeset的sort復雜度是nlg(n), 增刪改查的復雜度是lg(n).同樣的使用sliding window的思路。

Code

TC:O(nlog(min(n,k))) SC:O(nlog(min(n,k)))

public boolean ContainsDuplicatesIII(int[]nums, int k, int t){
  Set<Integer> treeset = new TreeSet<>();
  for (int i = 0; i < nums.length; i++){
    //check ceiling
    int ceiling = set.ceiling(nums[i]);
    if (ceiling != null && ceiling - t <= nums[i]){
      return true;
    }
    
    //check floor
    int floor = set.floor(nums[i]);
    if(floor != null && floor + t >= nums[i]){
      return true
    }
    set.add(nums[i]);
    //sliding window
    if(set.size() > k){
      set.remove(nums[i - k]);
    }
  }
  return false;
 }

有沒有O(n)的解法呢?想想看在O(n)內可以完成sort的算法是什么?桶排序對不對?當然我們這題沒有必要完全sort整個數組,只不過利用了每個桶中數值都在設定范圍內這一特性。

我們設桶的size為t,那么可以再k范圍內扔進一個桶的數一定能滿足條件。另外需要考慮的兩個地方就是這個桶兩邊的桶,也可能還有值差小于t的情況,要分別檢查下~

要注意的tricky的地方在于,對于負數,桶的選擇要先加1除以桶的size再減一。為什么這么做呢? 舉個??,在Java里,-3/5是0,而它應該是在index為-1的桶中。那么就需要這么做修正下。當然如果用python等別的語言不存在這個顧慮就不用考慮這個情況了。

Code

TC:O(nlog(min(n,k))) SC:O(nlog(min(n,k)))

public boolean ContainsDuplicatesIII(int[] nums, int k, int t){
  Map<Long, Long> map = new HashMap<>();
  int size = t + 1 // in case t == 0, we need to add extra 1
  for (int i = 0; i< nums.length; i++){
    int idx = getIdx(nums[i], size);
    //duplicates are in the same bucket
    if(map.containsKey(idx)){
      return true;
    }
    //duplicates are in neighbour buckets
    if (map.containsKey(idx + 1) && Math.abs(nums[i] - map.get(idx + 1))){
      return ture;
    }
    if (map.containsKey(idx - 1) && Math.abs(nums[i] - map.get(idx - 1))){
      return ture;
    }
    map.put(idx, (long)(nums[i]));
    if(map.size() > k){
      map.remove(getIdx(nums[i - k], size));
    }
  }
}

public int getIdx(int x, int size){
  if(x < 0){
    return (x - 1) / size + 1;
  }
  return x / size;
}

Reference

https://leetcode.com/problems/contains-duplicate/
https://leetcode.com/problems/contains-duplicate-ii/solution/
https://leetcode.com/problems/contains-duplicate-iii/solution/

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容