1. 程式人生 > >[LeetCode] Valid Triangle Number 合法的三角形個數

[LeetCode] Valid Triangle Number 合法的三角形個數

Given an array consists of non-negative integers, your task is to count the number of triplets chosen from the array that can make triangles if we take them as side lengths of a triangle.

Example 1:

Input: [2,2,3,4]
Output: 3
Explanation:
Valid combinations are: 
2,3,4 (using the first 2)
2,3,4 (using the second 2)
2,2,3

Note:

  1. The length of the given array won't exceed 1000.
  2. The integers in the given array are in the range of [0, 1000].

這道題給了我們一堆數字,問我們能組成多少個正確的三角形,我們初中就知道三角形的性質,任意兩條邊之和要大於第三邊。那麼問題其實就變成了找出所有這樣的三個數字,使得任意兩個數字之和都大於第三個數字。那麼可以轉變一下,三個數字中如果較小的兩個數字之和大於第三個數字,那麼任意兩個數字之和都大於第三個數字,這很好證明,因為第三個數字是最大的,所以它加上任意一個數肯定大於另一個數。這樣,我們就先要給陣列排序,博主最先嚐試了暴力破解法,結果TLE了(不要吐槽博主哈,博主就是喜歡霸王硬上弓~),後來優化的方法是先確定前兩個數,將這兩個數之和sum作為目標值,然後用二分查詢法來快速確定第一個小於目標值的數,這種情況屬於博主之前的部落格

LeetCode Binary Search Summary 二分搜尋法小結中總結的第二類的變形,我們找到這個臨界值,那麼這之前一直到j的位置之間的數都滿足題意,直接加起來即可,參見程式碼如下:

解法一:

class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        int res = 0, n = nums.size();
        sort(nums.begin(), nums.end());
        for (int i = 0; i < n; ++i) {
            
for (int j = i + 1; j < n; ++j) { int sum = nums[i] + nums[j], left = j + 1, right = n; while (left < right) { int mid = left + (right - left) / 2; if (nums[mid] < sum) left = mid + 1; else right = mid; } res += right - 1 - j; } } return res; } };

其實還有更進一步優化的方法,用的是博主之前那篇3Sum Smaller裡面的解法二,明明博主以前都總結過,換個題目情景就又沒想到,看來博主的舉一反三能力還是有所欠缺啊。沒辦法,只能繼續刻意練習了。這種方法能將時間複雜度優化到O(n2), 感覺很叼了。思路是排序之後,從數字末尾開始往前遍歷,將left指向首數字,將right之前遍歷到的數字的前面一個數字,然後如果left小於right就進行迴圈,迴圈裡面判斷如果left指向的數加上right指向的數大於當前的數字的話,那麼right到left之間的數字都可以組成三角形,這是為啥呢,相當於此時確定了i和right的位置,可以將left向右移到right的位置,中間經過的數都大於left指向的數,所以都能組成三角形,就說這思路叼不叼!加完之後,right自減一,即向左移動一位。如果left和right指向的數字之和不大於nums[i],那麼left自增1,即向右移動一位,參見程式碼如下:

解法二:

class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        int res = 0, n = nums.size();
        sort(nums.begin(), nums.end());
        for (int i = n - 1; i >= 2; --i) {
            int left = 0, right = i - 1;
            while (left < right) {
                if (nums[left] + nums[right] > nums[i]) {
                    res += right - left;
                    --right;
                } else {
                    ++left;
                }
            }
        }
        return res;
    }
};

類似題目:

參考資料: