多個方法 LeetCode 有效三角形的個數

背景:這個題目解法是利用排序降低複雜度的一個例子

 

給定一個包含非負整數的數組,你的任務是統計其中可以組成三角形三條邊的三元組個數。

示例 1:

輸入: [2,2,3,4]
輸出: 3
解釋:
有效的組合是: 
2,3,4 (使用第一個 2)
2,3,4 (使用第二個 2)
2,2,3

注意:

  1. 數組長度不超過1000。
  2. 數組裏整數的範圍爲 [0, 1000]。

分析:

 三角形的條件:第三邊大於兩邊差,小於兩邊之和 因此,對於這個問題,需要判斷可能的搭配:

c > abs(a - b)
c < a + b

那麼,窮舉每種可能來判斷就可以啦:代碼如下

class Solution:
    def triangleNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        rnum = 0
        bvld = False
        p1 = 0
        p2 = 1
        p3 = 2
        le = len(nums)
        for p1 in range(le - 2):
            for p2 in range(p1 + 1, le - 1):
                ma = nums[p1] + nums[p2]
                mi = abs(nums[p1] - nums[p2])
                for p3 in range(p2 + 1, le):
                    if nums[p3] in range(mi + 1, ma):
                        rnum += 1
                        #print(mi, ma)
                        #print(p1, p2, p3)
        return rnum

算法複雜度o(n^3),時間超限

轉變思路:能否不遍歷每種可能性?可以,只要數組有序,那麼找到邊界就可以找到所有滿足的組合!因此,思路如下:

1. 排序
2. 找邊界

第一版:外層做2層循環,第三層變遍歷爲找邊界,兩邊同時開始找,找到以後,左右邊界可以做差得到滿足結果的組合數量,累加得到結果即可:如下:

class Solution:
    def triangleNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        rnum = 0
        nums.sort()
        p1 = 0
        p2 = 1
        p3 = 2
        le = len(nums)
        for p1 in range(le - 2):
            for p2 in range(p1 + 1, le - 1):
                bvld = False
                ma = nums[p1] + nums[p2]
                mi = abs(nums[p1] - nums[p2])
                pl = p2 + 1
                pr = le - 1
                while pr >= pl:
                    if nums[pr] < ma:
                        bvld = True
                        break
                    if nums[pr] >= ma:
                        pr -= 1                    
                if bvld:
                    rnum += pr - pl + 1
        return rnum

速度提升的比較明顯,第一個算法只能跑一半的用例,這個算法能跑到最後幾個,但是還是時間超限。接下來的任務還是降低複雜度,能不能把外層的2層循環拆掉一層呢?

即:每次先拿到第三條邊,然後找前兩條邊的範圍!這樣複雜度可以下降到0(n^2)!代碼如下(C++)

class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        int count = 0, size = nums.size();
        sort(nums.begin(), nums.end());
        for (int i = size - 1; i >= 2; i--) {    // 先拿到第3條邊
            int left = 0, right = i - 1;    // 前2條邊
            while(left < right) {
                if (nums[left] + nums[right] > nums[i]) {
                    count += (right - left);    // 找到區間以後,就更新第2條邊,構建新組合,並記錄上個組合的總數
                    right--;
                }
                else {
                    left++;    // 調整第1條邊
                }
            }
        }
        return count;
    }
};

解析如代碼註釋,這個算法更好的利用了排序的結果

總結:

算法中的數組如果有明顯的大小比較判定環節,如果不排序的算法複雜度已經到了0(n^2)這個級別,那麼先用O(nlgn)排序再設計往往能降低複雜度。如果複雜度到不了這個級別,排序似乎沒有太大必要,畢竟排序的開銷也不能忽視。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章