【內含乾貨】611. 有效三角形的個數

題目地址(611. 有效三角形的個數)

https://leetcode-cn.com/probl...git

題目描述

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

示例 1:

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

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

前置知識

  • 排序
  • 雙指針
  • 二分法
  • 三角形邊的關係

暴力法(超時)

思路

首先要有一個數學前提: 若是三條線段中任意兩條的和都大於第三邊,那麼這三條線段能夠組成一個三角形。即給定三個線段 a,b,c,若是知足 a + b > c and a + c > b and b + c > a,則線段 a,b,c 能夠構成三角形,不然不能夠。github

力扣中有一些題目是須要一些數學前提的,不過這些數學前提都比較簡單,通常不會超太高中數學知識,而且也不會特別複雜。通常都是小學初中知識便可。面試

若是你在面試中碰到不知道的數學前提,能夠尋求面試官提示試試。

關鍵點解析

  • 三角形邊的關係
  • 三層循環肯定三個線段

代碼

代碼支持: Python算法

class Solution:
    def is_triangle(self, a, b, c):
        if a == 0 or b == 0 or c == 0: return False
        if a + b > c and a + c > b and b + c > a: return True
        return False
    def triangleNumber(self, nums: List[int]) -> int:
        n = len(nums)
        ans = 0
        for i in range(n - 2):
            for j in range(i + 1, n - 1):
                for k in range(j + 1, n):
                    if self.is_triangle(nums[i], nums[j], nums[k]): ans += 1

        return ans

複雜度分析數組

  • 時間複雜度:$O(N ^ 3)$,其中 N 爲 數組長度。
  • 空間複雜度:$O(1)$

優化的暴力法

思路

暴力法的時間複雜度爲 $O(N ^ 3)$, 其中 $N$ 最大爲 1000。通常來講, $O(N ^ 3)$ 的算法在數據量 <= 500 是能夠 AC 的。1000 的數量級則須要考慮 $O(N ^ 2)$ 或者更好的解法。函數

OK,到這裏了。我給你們一個乾貨。 應該是其餘博主不太會提的。緣由多是他們不知道, 也多是他們以爲過小兒科不須要說。優化

  1. 因爲前面我根據數據規模推測到到了解法的複雜度區間是 $N ^ 2$, $N ^ 2 * logN$,不多是 $N$ (WHY?)。
  2. 下降時間複雜度的方法主要有: 空間換時間排序換時間(咱們通常都是使用基於比較的排序方法)。而排序換時間僅僅在整體複雜度大於 $O(NlogN)$ 才適用(緣由不用多說了吧?)。

這裏因爲整體的時間複雜度是 $O(N ^ 3)$,所以我天然想到了排序換時間。當咱們對 nums 進行一次排序以後,我發現:spa

  • is_triangle 函數有一些判斷是無效的
def is_triangle(self, a, b, c):
        if a == 0 or b == 0 or c == 0: return False
        # a + c > b 和  b + c > a 是無效的判斷,由於恆成立
        if a + b > c and a + c > b and b + c > a: return True
        return False
  • 所以咱們的目標變爲找到a + b > c便可,所以第三層循環是能夠提早退出的。
for i in range(n - 2):
    for j in range(i + 1, n - 1):
        k = j + 1
        while k < n and num[i] + nums[j] > nums[k]:
            k += 1
        ans += k - j - 1
  • 這也僅僅是減枝而已,複雜度沒有變化。經過進一步觀察,發現 k 沒有必要每次都從 j + 1 開始。而是從上次找到的 k 值開始就行。緣由很簡單, 當 nums[i] + nums[j] > nums[k] 時,咱們想要找到下一個知足 nums[i] + nums[j] > nums[k] 的 新的 k 值,因爲進行了排序,所以這個 k 確定比以前的大(單調遞增性),所以上一個 k 值以前的數都是無效的,能夠跳過。
for i in range(n - 2):
    k = i + 2
    for j in range(i + 1, n - 1):
        while k < n and nums[i] + nums[j] > nums[k]:
            k += 1
        ans += k - j - 1

因爲 K 不會後退,所以最內層循環總共最多執行 N 次,所以總的時間複雜度爲 $O(N ^ 2)$。指針

這個複雜度分析有點像單調棧,你們能夠結合起來理解。

關鍵點分析

  • 排序

代碼

class Solution:
    def triangleNumber(self, nums: List[int]) -> int:
        n = len(nums)
        ans = 0
        nums.sort()
        for i in range(n - 2):
            if nums[i] == 0: continue
            k = i + 2
            for j in range(i + 1, n - 1):
                while k < n and nums[i] + nums[j] > nums[k]:
                    k += 1
                ans += k - j - 1
        return ans

複雜度分析code

  • 時間複雜度:$O(N ^ 2)$
  • 空間複雜度:取決於排序算法

更多題解能夠訪問個人 LeetCode 題解倉庫:https://github.com/azl3979858... 。 目前已經 30K star 啦。

關注公衆號力扣加加,努力用清晰直白的語言還原解題思路,而且有大量圖解,手把手教你識別套路,高效刷題。

相關文章
相關標籤/搜索