【leetcode刷题】:双指针篇(有效三角形的个数、和为s的两个数)

文章目录

一、有效三角形的个数

题目解析

有效三角形的个数【点击跳转】

题目意思很好理解:就是在一堆非负整数的数组里,随机选三个数进行搭配,然后统计这个数组中任意三个数能组成三角形的组合个数。

注意: 判断任意三个数是否能组成三角形的条件是 任意 两个数相加大于第三个数。

【示例一】:
输入: nums = [2,2,3,4]
输出: 3

四个数选三个数总共有四种情况

情况一:2 2 3 (任意两个数大于第三个数)满足题意

情况二:2 2 4 (2 + 2等于四)不满足题意

情况三:2 3 4 (任意两个数大于第三个数)满足题意

情况四:2 3 4 (任意两个数大于第三个数)满足题意

可以组成三个三角形,所以输出为3

【示例二】:
输入: nums = [4,2,3,4]
输出: 4

四个数选三个数总共有四种情况

情况一:4 2 3 (任意两个数大于第三个数)满足题意

情况二:4 2 4 (任意两个数大于第三个数)满足题意

情况三:4 3 4 (任意两个数大于第三个数)满足题意

情况四:2 3 4 (任意两个数大于第三个数)满足题意

可以组成四个三角形,所以输出为4

算法原理

解法一:暴力枚举(超时)

将所有的情况全部枚举出来,然后统计符合题意的情况。
暴力枚举代码:

cpp 复制代码
// 暴力枚举
class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        int count = 0;
        // 固定第一个数
        for(int i = 0; i < nums.size(); i++)
        {
            // 固定第二个数
            for(int j = i + 1; j < nums.size(); j++)
            {
                // 固定第三个数
                for(int k = j + 1; k < nums.size(); k++)
                {
                    if((nums[i] + nums[j] > nums[k]) && (nums[i] + nums[k] > nums[j]) && (nums[j] + nums[k] > nums[i]))
                        count++;
                }
            }
        }
        return count;
    }
};

解法二:排序 + 双指针

首先根据题目的意思是数组里的元素都是大于等于零的,这里我们就可以利用组成三角形的一个特性:

假设三个数a、b、c,最大数是c,那么:

  • 1、a + c > b
  • 2、b + c > a
  • 3、a + b > c
    这是判断是否能构成三角形的条件

其实第一种和第二种情况可以合并成一种情况,因为c是最大的那个数,一个最大的数加一个大于零的数,其结果一定是大于另外一个数的

知道了这个特性,我们的算法就有了优化的空间。

我们可以先给数组里的元素排个序,先固定一个最大的数,然后利用双指针算法结合上面讲的特性来优化算法:

根据上面的图可以看到,最大的数是13,然后会有下面这几种情况:

  • 情况一nums[left] + nums[right] > c
  • 情况二nums[left] + nums[right] <= c 小于和等于属于同一种情况,即不能构成三角形

根据单调性,假设是情况一,因为left往右的数都是比left大的数,left加上right已经大于最大的数了,那么一个大于left的数加上right,那必定是大于最大的数c的 ,所以left往右就没必要计算了,因为left与right这个区间再加上最大数构成的三个数一定是能构成三角形的,能构成三角形的个数就是right - left ,接下来的操作就是要right往左移,然后继续判断

假设是情况二,left加上right是小于最大值c的,那么right往左的数与left相加也一定是小于最大数c,所以接下来的操作是要left往右移,然后继续判断。

最后双指针判断结束后,只需要更新最大值(最大值往左移),然后重新利用双指针进行判断。

代码编写

C++代码:

cpp 复制代码
class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        // 排序
        sort(nums.begin(), nums.end());
        int n = nums.size(), ret = 0;
        // 固定最大数
        for(int i = n - 1; i >= 2; i--)
        {
            int left = 0, right = i - 1;
            while(left < right)
            {
                if(nums[left] + nums[right] > nums[i])
                {
                    ret += right - left;
                    right--;
                }
                else
                {
                    left++;
                }
            }
        }
        return ret;
    }
};

C语言代码:

c 复制代码
// 比较函数
int cmpfunc (const void * a, const void * b)
{
   return ( *(int*)a - *(int*)b );
}

int triangleNumber(int* nums, int numsSize) {
    // 排序
    qsort(nums, numsSize, sizeof(int), cmpfunc);
    int ret = 0;
    // 固定最大数
    for(int i = numsSize - 1; i >= 2; i--)
    {
        int left = 0, right = i - 1;
        while(left < right)
        {
            if(nums[left] + nums[right] > nums[i])
            {
                ret += right - left;
                right--;
            }
            else
            {
                left++;
            }
        }
    }
    return ret;
}

Python代码:

python 复制代码
class Solution:
    def triangleNumber(self, nums: List[int]) -> int:
        # 排序
        nums.sort()
        ret = 0
        # 固定最大数
        for i in range(len(nums) - 1, 1, -1):
            left, right = 0, i - 1
            while left < right:
                if nums[left] + nums[right] > nums[i]:
                    ret += right - left
                    right -= 1
                else:
                    left += 1
        return ret

二、和为s的两个数

题目解析

和为s的两个数【点击跳转】

题目的大意就是给你一个递增的数组,然后找到两个数相加等于目标值,然后返回这两个数,返回的这两个数顺序随意。

算法原理

解法一:暴力枚举(超时)

直接两层for循环将所有的可能全部枚举出来,然后找到两个数相加等于目标值的两个数,直接返回。

暴力枚举C++代码:

cpp 复制代码
class Solution {
public:
    vector<int> twoSum(vector<int>& price, int target) {
        // 固定第一个数
        for(int i = 0; i < price.size(); i++)
        {
            // 固定第二个数
            for(int j = i + 1; j < price.size(); j++)
            {
                if(price[i] + price[j] == target)
                    // 这里是隐式类型转换,可以转换成vector<int>类型
                    return {price[i], price[j]};
            }
        }
        // 满足力扣的要求,这里的值可随意返回
        return {-1, -1};
    }
};

解法二:双指针

在暴力枚举的时候我们忽略了一个很重要的因素,那就是这个数组里的元素是单调递增的,只要是单调递增的数组,我们就可以大胆的利用双指针算法来解决问题。

1、第一种情况:sum = price[left] + price[right] > target 如果sum大于我们的目标值target,由于数组是单调递增的,price[left]已经是最小的值了,price[right]是数组中最大的那个数。一个最小的值加上一个最大的值最后大于目标值,而且left往后的值都是大于left的(数组递增),所以我们的操作就只要让right往左移就可以了。

2、第二种情况:sum = price[left] + price[right] < target 相加后的结果小于目标值target,说明left小了(right左边的值都是小于right的),所以我们的操作只需要让left向右移动即可。

3、第三种情况:sum = price[left] + price[right] == target 相加后的结果是等于目标值target,满足题目要求,直接结果即可。

相比于解法一的暴力解法,利用数组的单调递增的双指针解法效率更高,我们可以分析一下两种解法的时间复杂度对比。
暴力解法: O ( N 2 ) 双指针: O ( N ) \text{暴力解法:} O(N^2) \\ \text{双指针:} O(N) 暴力解法:O(N2)双指针:O(N)

可以看到时间复杂度直接优化了一个量级,说明我们的算法是比较优秀的算法了。

代码编写

C++代码:

cpp 复制代码
class Solution {
public:
    vector<int> twoSum(vector<int>& price, int target) {
        int n = price.size();
        // 定义左右双指针
        int left = 0, right = n - 1;
        while(left < right)
        {
            // sum > target
            if(price[left] + price[right] > target)
                right--;
            // sum < target
            else if(price[left] + price[right] < target)
                left++;
            // sum == target
            else
                return {price[left], price[right]};
        }
        return {-1, -1};
    }
};


C语言代码:

c 复制代码
/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* twoSum(int* price, int priceSize, int target, int* returnSize) {
    // 定义左右双指针
    int left = 0, right = priceSize - 1;
    *returnSize = 2;  // 输出型参数
    int *ret = (int*)malloc(*returnSize * sizeof(int));
    while(left < right)
    {
        // sum > target
        if(price[left] + price[right] > target)
            right--;
        // sum < target
        else if(price[left] + price[right] < target)
            left++;
        // sum == target
        else
        {
            ret[0] = price[left], ret[1] = price[right];
            return ret;
        }
    }
    // 释放空间
    free(ret);
    ret = NULL;
    return NULL;
}


Python代码:

python 复制代码
class Solution:
    def twoSum(self, price: List[int], target: int) -> List[int]:
        # 定义左右双指针
        left, right = 0, len(price) - 1
        while left < right:
            # sum > target
            if price[left] + price[right] > target:
                right -= 1
            # sum < target
            elif price[left] + price[right] < target:
                left += 1
            # sum == target
            else:
                return [price[left], price[right]]
        return [-1, -1]
相关推荐
白鹭float.1 小时前
【OpenGL/C++】面向对象扩展——测试环境
c++·图形学·opengl
小wanga1 小时前
【C++】类型转换
jvm·c++
我不是程序猿儿2 小时前
【C++】xml烧录 调用twinCat流程自动化
xml·c++·自动化
酒酿小圆子~2 小时前
NLP中常见的分词算法(BPE、WordPiece、Unigram、SentencePiece)
人工智能·算法·自然语言处理
小酒丸子3 小时前
基于QT和C++的实时日期和时间显示
c++·qt
huiyunfei3 小时前
MinorGC FullGC
java·jvm·算法
thisiszdy4 小时前
<C++> XlsxWriter写EXCEL
c++·excel
生信与遗传解读4 小时前
XGBoost算法在自定义数据集中预测疾病风险
人工智能·python·算法·数据分析
14_114 小时前
Cherno C++学习笔记 P51 创建并使用库
c++·笔记·学习