有效三角形的个数——每日一题第一天

每日一题:
给定一个包含非负整数的数组,你的任务是统计其中可以组成三角形三条边的三元组个数。

输入: [2,2,3,4]
输出: 3
解释:
有效的组合是:
2,3,4 (使用第一个 2)
2,3,4 (使用第二个 2)
2,2,3
注意:
1、数组长度不超过1000。
2、数组里整数的范围为 [0, 1000]。

这是题目描述,下面来看题解。
要组成三角形,那三条边就要满足这个关系:
1、a + b > c;
2、a + c > b;
3、b + c > a;
一、暴力枚举
三个for循环枚举,可以得出答案,但是耗时长,不建议使用,我也没写,可以自己写写试试。

二、排序+枚举+二分查找
综合组成三角形的条件,不难得出,若将三角形三条边进行排序,如a < b < c;则综合1、2、3条件可以得出,只要满足a + b > c;条件就成立了,所以思路如下:
设a = nums[i] //b = nums[j]
①对数组进行由小到大的排序
②双重for循环遍历i与j(a与b边)
③在 [j+1,size) 里查找c边(二分查找)
代码如下

class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        sort(nums.begin(),nums.end());//从小到大排序
        int big = nums.size();//长度
        int a = 0;
        for(int i = 0;i < big;i++)
        {
            for(int j = i+1;j < big;j++)
            {
                int num = nums[i] + nums[j];
                int left = j+1;
                int right = big-1;
                int k = j;
                while(left <= right)
                {
                    int mid = (right + left)/2;
                    if(0 == nums[i])
                        mid = j;
                    if(nums[mid] == num)
                    {
                        right = mid - 1;
                    }
                    else if(nums[mid] < num)
                    {
                        k = mid;
                        left = mid+1;
                    }
                    else if(nums[mid] > num)
                    {
                        right = mid - 1;
                    }
                }
                a += k-j;
            }
        }
        return a;
    }
};

注意:int k = j;这段代码,本来是定义k=0的,在大多数情况下都可以,但是当[j+1,size)里没有满足

条件的数的时候,就会出现k - j < 0,显然这是错误的情况,所以定义k为j可以有效避免这个bug。

同时,不难发现我们要得到的是下标最大的满足条件的数,所以二分查找变为查询右边界。当两边之

和恰好等于第三边时,由于是从小到大排序的,所以应该往前找 left = mid+1;(这里我想了老半

天才明白,我真傻,,)
三、双指针
nums[i] + nums[j] > nums[k];不难得出,当固定左侧一个数例如i时,若j不断增大,则k也不断增大.所以算法如下:
①固定i
②j由i开始,每次增加1,j → j + 1;
③k由j+1开始,循环查找满足三边大小条件的最大下标
④k找到最大下标或者已经移动到边界,则i+1,再次开始①②③步骤
代码如下;

class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        int n = nums.size();
        int ans = 0;
        for (int i = 0; i < n; ++i) 
        {
            int k = i;
            for (int j = i + 1; j < n; ++j) 
            {
                while (k + 1 < n && nums[k + 1] < nums[i] + nums[j]) 
                {
                    ++k;
                }
                ans += max(k - j, 0);//可能出现k-j<0的情况,所以取max
            }
        }
        return ans;
    }
    };

思路暂时就只有这三个,后面想到新的解题方法会留在评论区,也欢迎大家在评论区讨论其他方法。

我是Pico。www.liujianhua.xyz

你可能感兴趣的:(LeetCode,算法,数据结构,快速排序,leetcode)