1. 程式人生 > >[leetcode]雙指標題:Three sum

[leetcode]雙指標題:Three sum

three sum

考慮三數之和,主要的考點在於:

  • 三數是否意味著需要設定三個變數?
  • 如何保證結果的去重?

針對第一點,這其實是指標題很喜歡的場景,因為對於新手可能一下子思路很直,單其實一般如果幾個變數之間存在一定的關係,都要採取化簡得形式,畢竟變數越少處理的時候就會越簡潔。
針對第二點,其實是這題的一個重點,我第一次的想法也是判斷一下數組裡面如果存在就不繼續新增這種線性的思維。其實應該判斷,如果是重複就不應該繼續走下面的尋找過程。

解題思路:
其實不難,核心思想就是將3sum分解為『1 Sum + 2 Sum』,在對陣列進行排序後,挑出第一個元素,然後在剩下的元素中利用首尾指標進行尋找。

解題的程式碼:
一般程式碼:

class Solution:
    """
    @param numbers: Give an array numbers of n integer
    @return: Find all unique triplets in the array which gives the sum of zero.
    """
    def threeSum(self, numbers):
        # write your code here
        numbers.sort()
        len_num=len(numbers)
        result=[]
        for i in range(len_num):
            if i>0 and numbers[i]==numbers[i-1]:
                continue
            temp=numbers[i]
            twosum=0-temp
            l=i+1
            r=len_num-1
            while l<r:
                two_temp=numbers[l]+numbers[r]
                if two_temp==twosum:
                    result.append([temp,numbers[l],numbers[r]])
                    l+=1
                    r-=1
                    while r>0 and numbers[r]==numbers[r+1]:
                        r-=1
                    while l<len_num and numbers[l]==numbers[l-1]:
                        l+=1
                elif two_temp>twosum:
                    r-=1
                else:
                    l+=1
        return result

leetcode上最快的程式碼:
跟普通做法不一樣的地方在於:

  • 用hash 表來表示去重後的序列,省掉了去重導致移動的判斷。
  • 只要遍歷陣列的其中一半就可以了。
  • 最難的地方引入瞭如下的公式,進一步縮減了搜尋範圍:
    -a - b = c <= keys[-1] >>>> b >= -keys[-1] - a
    b>>> a + 2b < a + b + c = 0 >>>> b < -a/2
from bisect import bisect_left, bisect_right

class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        m = {}
        result = []
        positive_keys = []
        negative_keys = []
        for n in nums:
            if n in m:
                m[n] += 1
            else:
                m[n] = 1

        if 0 in m and m[0] >=3:
            result.append([0, 0, 0])

        keys = list(m.keys())
        keys.sort()
        keys_num = len(keys)

        if keys_num == 0:
            return []

        end = bisect_left(keys, 0) # a < 0
        # begin = bisect_left(keys, -keys[-1]*2) # when b == c, a + b + c = a + 2c <= a + 2*max_c;
        # print begin
        # print keys[begin]
        for i in range(0, end):
            a = keys[i]

            #a == b
            if a != 0 and m[a] >= 2 and -2*a in m:
                result.append([a, a, -2*a])

            # -a - b = c <= keys[-1] >>>> b >= -keys[-1] - a
            min_b = -keys[-1] - a
            # b<c >>>> a + 2b < a + b + c = 0 >>>> b < -a/2
            max_b = -a/2

            b_begin = max(i + 1, bisect_left(keys, min_b))
            b_end = bisect_right(keys, max_b)
            for j in range(b_begin, b_end):
                b = keys[j]
                c = -a-b
                if c in m:
                    if b > c:
                        continue
                    #b<c or b==c
                    if b < c or m[b] >=2:
                        result.append([a, b, c])
        return result