LeetCode(15) 3Sum

https://sodaoo.github.io/2017/11/21/LeetCode-14/
https://sodaoo.github.io

來我的網站查看更多 LeetCode 題解


Description :

An array S of n integers .are there elements a, b, c in S such that a+b+c=0 ?
Find all unique triplets in the array which gives the sum of zero.
Note: The solution set must not contain duplicate triplets.
For example, given array S = [-1, 0, 1, 2, -1, -4],
A solution set is:
[ [-1, 0, 1], [-1, -1, 2] ]


給你一個 int 類型的Array , 將所有相加等於0 的三個數找出來, 返回一個三元組的表 .
這個表一定不能重複 ,比如 : [-1,-1,2 ] 和 [-1,2,-1] 被認爲是重複的 .
A solution set is:
[ [-1, 0, 1], [-1, -1, 2] ]


常規思路 :
– 將這個數組排序, 比如排序之後是 nums=(-99,-56,-34,-1,0,22,33,44,77)
– 寫雙重的 for 循環 ,判斷 x = -(nums[i] , nums[j]) 是否在 nums[] 內
– 如果 x 在 nums[] 之內 : 說明找到了這樣三個數 ,如果不在 就繼續循環 .

for i in range(len(nums)):
   for j in range(i+1,len(nums)):
      if (0-nums[i]-nums[j]) in nums:
         a = list();
         a.append(0-nums[i]-nums[j]);a.append(nums[i]);a.append(nums[j])
         res.append(a)
# 有可能有重複的三元組, 我們對每一個三元組排序,再去重就可以了 .
# [-1,-1,2 ] 和 [-1,2,-1] 被認爲是重複的.但是這兩個被認爲是不相等的 .
# 所以我們排序, [-1,-1,2 ] 和 [-1,-1,2 ] 是相等的 .利用這一點去重 .
# 去重代碼 :
    for i in res:
        i.sort()     # 對三元組排序
    ress = list()    
    for i in res:
        if i not in ress:
            ress.append(i)

這個代碼寫出來 , 用了三千個數據一跑 ,光榮的達到了兩分鐘之久, 我們
import time ;
t1 = time.time() ; t2 = time.time()
print(“t1 到 t2 耗時共計”,t2-t1,”秒.”) 發現 ,
查找的代碼耗時 133s , 去重排了 3s 賊恐怖 .

科學計算
Python擁有一些內置的數據結構 ,比如str, int, list, tuple, dict等, collections模塊在這些內置數據結構的基礎上,提供了幾個額外的數據結構:
– namedtuple(): 生成可以使用名字來訪問元素內容的tuple子類
– deque: 雙端隊列,可以快速的從另外一側追加和推出對象
– Counter: 計數器,主要用來計數
– OrderedDict: 有序字典
– defaultdict: 帶有默認值的字典

忽然發現我們常用的 list , dict 處理大批數據的能力真的很慢 ,
(也許不是慢 ,是打開方式不對…)

有一個有100萬元素的列表Lis ,欲將每個元素和其出現次數成對放入一個字典中 .常規做法:

dic = {}
for i in Lis:
    dic[i] = Lis.count(i)

但發現非常耗時,一秒鐘只能遍歷300多個元素。 這就非常蛋疼了 .此時我們可以引入一個collections庫 ,
這個庫裏面有一個 計數器 Counter , 不知道採用了什麼樣的結構, 總之非常快
不到一秒鐘就完成了上面的任務 ,代碼 :

import collections
cou = collections.Counter(Lis)
print(cou)        #不到一秒鐘就完成了 


我們再來看一下計數器的特點 :

>>> a = [2,2,2,3,3,4]
>>> cou = collections.Counter(a)
>>> cou
Counter({2: 3, 3: 2, 4: 1})     # 元素後面是出現的次數
>>> 4 in cou
True
>>> 1 in cou
False

>>> cou[3]
2
>>> cou[4]
1
>>> cou[2]
3
# cou[n] 是表示 n 這個元素出現的次數, 如示 3 這個元素出現了3 次, 2 這個元素出現了 3 次.

依據這一特點, 我們對我們耗時 3 分鐘的代碼做一下改進 :
改進之後, 查找的時間降低到了 4 s 去重的時間降低到了 0.003 s

import collections
class Solution:
    def threeSum(self, nums):
        counter = collections.Counter(nums)
        del nums ; nums = counter # nums現在是一個計數器 .
        res = list()
        for i in range(len(nums)):
            for j in range(i+1,len(nums)):
# 下面都是一樣的了........


雖然速度快了非常多, 但是還是超時 ,下面我們看一個 AC 的 702 ms 的代碼:

from collections import Counter
class Solution:
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        counter = collections.Counter(nums)
        # 如果 0 出現的次數 > 3 ,就加入一個 [0,0,0] 的三元組
        ans = [] if counter[0] < 3 else [[0, 0, 0]]

        # 負數由大到小, 如 (-2 到 -77777)
        ngs = sorted([x for x in counter if x<0], reverse=True)
        # 正數由小到大, 如 (2 到 88888 )
        n_ngs = sorted([x for x in counter if x>=0])

        # 正負排序之後 , 更利於查找
        # 雙重循環
        for n_ng in n_ngs:
            for ng in ngs:
                need = -(ng + n_ng)    # <-need是第三個數
                if need in counter:
                    # 根據大小決定在元組的順序, 不用去重了 .
                    if (need == ng or need == n_ng) and counter[need] > 1:
                        ans.append([ng, need, n_ng])
                    elif need < ng: ans.append([need, ng, n_ng])
                    elif n_ng < need: ans.append([ng, n_ng, need])
        return ans
  # (time :1522 ms ,beat 51.82%)


C++ 解法 :

vector<vector<int> > threeSum(vector<int> &num) {
  vector<vector<int> > res;
  std::sort(num.begin(), num.end());
  for (int i = 0; i < num.size(); i++) {
    int target = -num[i];
    int front = i + 1;
    int back = num.size() - 1;
    while (front < back) {
      int sum = num[front] + num[back];
      // Finding answer which start from number num[i]
      if (sum < target)
        front++;
      else if (sum > target)
        back--;
      else {  //find sum == target that is -num[i] .
        vector<int> triplet(3, 0);
        triplet[0] = num[i];
        triplet[1] = num[front];
        triplet[2] = num[back];
        res.push_back(triplet);

        // Processing duplicates of Number 2
        // Rolling the front pointer to the next different number forwards
        while (front < back && num[front] == triplet[1]) front++;
        // Processing duplicates of Number 3
        // Rolling the back pointer to the next different number backwards
        while (front < back && num[back] == triplet[2]) back--;
      }
    }
    // Processing duplicates of Number 1
    while (i + 1 < num.size() && num[i + 1] == num[i])
      i++;
  }
  return res;
}
  //  (time :105ms ,beat 80.2% )
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章