[M双指针] lc611. 有效三角形的个数(二分+双指针+线性扫描+算法优化)

文章目录

    • 1. 题目来源
    • 2. 题目解析

1. 题目来源

链接:611. 有效三角形的个数

2. 题目解析

两种做法。体会二分和双指针的联系与区别。

方法一:排序+二分

  • 先排序,然后枚举最小边 a,次小边 b,最长边要满足 a+b>c,故要找到小于 a+b 的最大的一个,二分找就行了。
  • 二分时需要注意下边界问题,每次从 [b, n-1] 开始二分是正确的,找不到的话将停留在 b 下标处,算答案的时候不会出错。
  • 否则,遇见 [0, 0, 0] 这样的数据,从 l=0, r=n-1 开始二分的话,会出错。
  • 当然,当遇见边长为 0 的边,应该直接跳过,这样的话,a+b 一定是 >b 的,那么即便二分没找到答案,也会停留在小于 a+b 的最大位置,也就是 b 的位置。

  • 时间复杂度 O ( n 2 l o g n ) O(n^2logn) O(n2logn)
  • 空间复杂度 O ( n ) O(n) O(n)

方法二:双指针+线性扫描

  • 转变一下枚举顺序,先枚举最大边 a再枚举次大边 b,次大边从 a-1 开始向前枚举,b 呈递减趋势。最小边 c 从 0 开始向 b 枚举,成递增趋势
  • 那么,只要满足 c+b>a 即可。
  • b 越来越小,c 要越来越大,所以满足双指针的特性。
  • 并且 c 会递增枚举,和 b 一样,最多只会枚举 n 次,故总的时间复杂度是 O ( n 2 ) O(n^2) O(n2) 的。
  • 额外注意下双指针的时间复杂度计算。

  • 时间复杂度 O ( n 2 ) O(n^2) O(n2)
  • 空间复杂度 O ( 1 ) O(1) O(1)

代码:

方法一:排序+二分

class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        int res = 0;
        for (int a = 0; a < nums.size(); a ++ ) 
            if (nums[a] > 0) {			// 判断边长不为 0
                for (int b = a + 1; b < nums.size(); b ++ ) {
                    int s = nums[a] + nums[b];
                    int l = 0, r = nums.size() - 1;    	// 可以从 0 开始
                    while (l < r) {
                        int mid = l + r + 1 >> 1;
                        if (nums[mid] < s) l = mid;
                        else r = mid - 1;
                    }
                    res += r - b;
                }
            }
        
        return res;
    }
};
class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        int res = 0;
        for (int a = 0; a < nums.size(); a ++ ) 
            for (int b = a + 1; b < nums.size(); b ++ ) {
                int s = nums[a] + nums[b];
                // 注意这个细节,令左区间二分起点为b,不要从0开始,避免[0,0,0]这样的样例
                int l = b, r = nums.size() - 1;    
                while (l < r) {
                    int mid = l + r + 1 >> 1;
                    if (nums[mid] < s) l = mid;
                    else r = mid - 1;
                }
                res += r - b;
            }
        
        return res;
    }
};

方法二:双指针+线性扫描

class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        int res = 0;
        for (int a = 0; a < nums.size(); a ++ ) 
            for (int b = a - 1, c = 0; b > 0 && c < b; b -- ) {
                while (c < b && nums[c] + nums[b] <= nums[a]) c ++ ;
                res += b - c;       // c停下位置是第一个合法位置,相遇为0,相邻为1,故为b-c
            }
        
        return res;
    }
};

你可能感兴趣的:(LeetCode,#,双指针,#,二分)