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

news2025/1/11 4:53:08

文章目录

      • 一、有效三角形的个数
        • 题目解析
        • 算法原理
        • 代码编写
      • 二、和为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

算法原理

解法一:暴力枚举(超时)
将所有的情况全部枚举出来,然后统计符合题意的情况。
暴力枚举代码:

// 暴力枚举
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++代码:

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语言代码:

// 比较函数
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代码:

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++代码:

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++代码:

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语言代码:

/**
 * 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代码:

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]

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2274706.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Unity3D】apk加密(global-metadata.dat加密)

涉及&#xff1a;apk、aab、global-metadata.dat、jks密钥文件、APKTool、zipalign 使用7z打开apk文件观察发现有如下3个针对加密的文件。 xxx.apk\assets\bin\Data\Managed\Metadata\global-metadata.dat xxx.apk\lib\armeabi-v7a\libil2cpp.so xxx.apk\lib\arm64-v8a\libil…

机器学习之贝叶斯分类器和混淆矩阵可视化

贝叶斯分类器 目录 贝叶斯分类器1 贝叶斯分类器1.1 概念1.2算法理解1.3 算法导入1.4 函数 2 混淆矩阵可视化2.1 概念2.2 理解2.3 函数导入2.4 函数及参数2.5 绘制函数 3 实际预测3.1 数据及理解3.2 代码测试 1 贝叶斯分类器 1.1 概念 贝叶斯分类器是基于贝叶斯定理构建的分类…

前端报告 2024:全新数据,深度解析未来趋势

温馨提示: 此报告为国际版全球报告,其中所涉及的技术应用、工具偏好、开发者习惯等情况反映的是全球前端开发领域的综合态势。由于国内外技术发展环境、行业生态以及企业需求等存在差异,可能有些内容并不完全契合国内的实际情况,请大家理性阅读,批判性地吸收其中的观点与信…

科普CMOS传感器的工作原理及特点

在当今数字化成像的时代&#xff0c;图像传感器无疑是幕后的关键 “功臣”&#xff0c;它宛如一位神奇的 “光影魔法师”&#xff0c;通过光电效应这一奇妙的物理现象&#xff0c;将光子巧妙地转换成电荷&#xff0c;为图像的诞生奠定基础。而在众多类型的图像传感器中&#xf…

word论文排版常见问题汇总

word论文排版常见问题汇总 常用快捷键&#xff1a; Alt F9 正常模式与域代码模式切换 Ctrl F9 插入域代码 F9 刷新域代码显示&#xff0c;要注意选定后刷新才会有效果 word中在当前列表的基础上修改列表 在使用word时&#xff0c;我们会定义一个列表&#xff0c;并将其链接…

使用PVE快速创建虚拟机集群并搭建docker环境

安装Linux系统 这里以安装龙蜥操作系统AnolisOS8.9为例加以说明。 通过PVE后台上传操作系统ISO镜像。 然后在PVE上【创建虚拟机】&#xff0c;选定上传的龙蜥操作系统镜像进行系统安装。 注意&#xff1a;在安装过程中&#xff0c;要设定语言、时区、超管用户root的密码、普…

某音响制造公司发展战略转型项目成功案例纪实

面对产业结构变化、海外订单缩减、劳动力成本攀升、缺乏自主品牌等原因导致的利润空间急剧下降的挑战&#xff0c;面向海外市场的代工厂如何在严峻的经济形势下克服发展障碍&#xff0c;成功实现转型与发展&#xff1f; 某音响制造公司&#xff0c;面临着订单量减少、成本增高…

redis的学习(三)

6. set集合 集合&#xff1a;把一些有关联的数据放在一起。 1、集合中的元素是无序的&#xff0c;即数据存放顺序不重要&#xff0c;变化一下顺序&#xff0c;集合依旧是之前的集合。 2、集合中的元素是不能重复的&#xff08;唯一性&#xff09;与list类似的是集合中的每一个元…

点赞系统设计(微服务)

点赞业务是一个常见的社交功能&#xff0c;它允许用户对其他用户的内容&#xff08;如帖子、评论、图片等&#xff09;表示喜欢或支持。在设计点赞业务时&#xff0c;需要考虑以下几个方面&#xff1a; 一、业务需求 点赞业务需要满足以下特性&#xff1a; 通用&#xff1a;…

C#进阶-在Ubuntu上部署ASP.NET Core Web API应用

随着云计算和容器化技术的普及&#xff0c;Linux 服务器已成为部署 Web 应用程序的主流平台之一。ASP.NET Core 作为一个跨平台、高性能的框架&#xff0c;非常适合在 Linux 环境中运行。本篇博客将详细介绍如何在 Linux 服务器上部署 ASP.NET Core Web API 应用&#xff0c;包…

设计模式-结构型-桥接模式

1. 什么是桥接模式&#xff1f; 桥接模式&#xff08;Bridge Pattern&#xff09; 是一种结构型设计模式&#xff0c;它旨在将抽象部分与实现部分分离&#xff0c;使它们可以独立变化。通过这种方式&#xff0c;系统可以在抽象和实现两方面进行扩展&#xff0c;而无需相互影响…

python学习笔记—16—数据容器之元组

1. 元组——tuple(元组是一个只读的list) (1) 元组的定义注意&#xff1a;定义单个元素的元组&#xff0c;在元素后面要加上 , (2) 元组也支持嵌套 (3) 下标索引取出元素 (4) 元组的相关操作 1. index——查看元组中某个元素在元组中的位置从左到右第一次出现的位置 t1 (&qu…

基础算法--查找

一、线性枚举 1、线性枚举定义 线性枚举指的就是遍历某个一维数组&#xff08;顺序表&#xff09;的所有元素&#xff0c;找到满足条件的那个元素并且返回&#xff0c;返回值可以是下标&#xff0c;也可以是元素本身。 由于是遍历的&#xff0c;穷举了所有情况&#xff0c;所…

G1垃圾回收器的FullGC

如何确定GarbageFirst回收器发生的是FullGC ? 必须出现FullGC字样才算是FUllGC&#xff0c;例如下图&#xff1a;因为内存分配失败&#xff08;Allocation Failure&#xff09;导致 如果不出现FullGC的字样说明它不是FUllGC&#xff0c;并不像Serial GC、ParallelGC的在老年代…

Golang的代码压缩技术应用案例分析与研究实践

Golang的代码压缩技术应用案例分析与研究实践 一、介绍 是一种具有强大性能和便捷开发特性的编程语言&#xff0c;除了其优秀的语法和标准库外&#xff0c;它还拥有很多高级特性&#xff0c;其中之一就是代码压缩技术。本文将从常见的Golang代码压缩技术应用案例出发&#xff0…

【Uniapp-Vue3】image媒体组件属性

如果我们想要在页面上展示图片就需要使用到image标签。 这部分最重要的是图片的裁剪&#xff0c;图片的裁剪和缩放属性&#xff1a; mode 图片裁剪、缩放的模式 默认值是scaleToFill 我将用两张图片对属性进行演示&#xff0c;一张是pic1.jpg&#xff08;宽更长&#xf…

【网络协议】交换机概念与配置(第一部分)

概述 本文将探讨交换机的概念以及交换机的基础配置&#xff0c;并以此引入对 VLAN 的讨论。 文章目录 概述CSMA/CD以太网通信单播&#xff08;Unicast&#xff09;多播&#xff08;Multicast&#xff09;广播&#xff08;Broadcast&#xff09; MAC 地址以太网中的双工设置半双…

oracle位运算、左移右移、标签算法等

文章目录 位运算基础与或非同或同或应用场景 异或异或应用场景 什么是真值表 oracle基础函数创建bitor(按位或)函数bitnot(按位非)函数bitxor(按位异或)函数左移函数BITSHIFT()函数(实测不可用&#xff0c;废弃掉该方案)右移函数(略&#xff0c;有此场景吗?) 实际应用资质字典…

(五)ROS通信编程——参数服务器

前言 参数服务器在ROS中主要用于实现不同节点之间的数据共享&#xff08;P2P&#xff09;。参数服务器相当于是独立于所有节点的一个公共容器&#xff0c;可以将数据存储在该容器中&#xff0c;被不同的节点调用&#xff0c;当然不同的节点也可以往其中存储数据&#xff0c;关…

《零基础Go语言算法实战》【题目 1-18】切片的反转

《零基础Go语言算法实战》 【题目 1-18】切片的反转 请编写一个名为 reverse 的函数&#xff0c;采用整数切片并在不使用临时切片的情况下将切片反转。 【解答】 可以通过 for 循环交换切片中每个元素的值&#xff0c;使其从左向右滑动。最终&#xff0c;所有元素都将 被反转。…