双指针 — 有效三角形的个数

目录

[一. 题目解析](#一. 题目解析)

[二 .讲解算法原理](#二 .讲解算法原理)

[1. 判断方法:](#1. 判断方法:)

方法一:

方法二:

[2. 提升](#2. 提升)

暴力解法

提升点一

提升点二

[3. 最优解法](#3. 最优解法)

[4. 总结步骤](#4. 总结步骤)

[三. 编写代码](#三. 编写代码)

[1. 步骤精讲](#1. 步骤精讲)

2[. 完整代码](#. 完整代码)


一. 题目解析


有效三角形的个数

给定一个包含非负整数的数组 nums ,返回其中可以组成三角形三条边的三元组个数。

题目的意思是:给了我们一个数组nums,让我们在数组中任意挑选三个数,看这三个数能否组成一个三角形,然后对于整个数组,有几种能够组成三角形的挑法

哪怕组合重复,也要统计到有效组合中的,因为选法不同 ,如示例一


二 .讲解算法原理


1. 判断方法:


如果有三个数a,b,c,判断这三个数是否能构成三角形,利用任意两边之和大于第三边的条件:


方法一:

(a+b>c)&&(a+c>b)&&(b+c>a),这种方法虽然简单,但是要判断三次;


方法二:

我们有一种更加优秀的方法,仅仅需要判断一次,就能看这三个数能否构成三角形:

我们只要知道a,b,c 的三个数的大小顺序,那么只要判断a+b是否大于c,即可判断a,b,c能否构成三角形。


这时候,有uu就会有疑问,不就是多判断两次吗?时间复杂度能多到哪去?


接下来,我们来讲通过第二种判断方法的解法,就会发现,利用第二种判断方法,时间复杂度会有质的提升。

因为这个题目给我们的数组是无序的,我们想用第二种判断方法判断,就必须先给数组排序。


2. 提升


暴力解法

暴力枚举所有的三个数:

上图是一个伪代码,只是用来展示暴力枚举的思路 ,通过暴力枚举的解法的总时间复杂度,来给大家解释为什么要优化判断方法.


提升点一

暴力解法的时间复杂度也非常好看,三层for()循环,时间复杂度就是O(N^3)。


这时候我们在来看常数级别,如果暴力解法中:


check()方法用的是第一种判断方法,那么时间复杂度会达到O(3*N^3);

check()方法用的是第二种判断方法,我们要把对数组排序的时间复杂度也算入总的复杂度中,排序的时间复杂度为O(N*logN);那么时间复杂度会达到O(N^3+N*logN)。


因此,我们通过暴力枚举,运用不同的判断方法,会发现对时间复杂度的优化是巨大的。


提升点二

在使用第二种判断方法时,因为我们的数组就已经有序了,我们就能通过单调性,作出更多的优化 ;排完序后的数组,对于我们来说,更容易想到优化的点,如二分算法。但是对于这道题,二分算法并不是最优秀的优化方法,我们就先不讲;

我们利用最优秀的解法来解决这道题目, 就是利用单调性,使用双指针算法来解决问题。


3. 最优解法


如上文,我们已经说了,就是通过判断方法二,数组已经排好序,利用单调性,使用双指针算法来解决问题:

我们先固定最大的数组元素,也就是数组最后一位元素,我们设被固定的数为 c ;

我们开始枚举剩下的两个数,但是不是胡乱枚举,否则时间复杂度又是O(N^3);

所以再定义两个指针 left,right 分别指向数组第一个元素和倒数第二个元素,设left指的数是a,right指的数是b;


我们观察a+b,可以得出两种情况: a+b>c 或者 a+b<=c ;

我们来看看,能不能通过数组有序,写出一些优化方法。


情况一:a+b>c

如果 a+b>c ,那么a,b 之间的所有数字都可以替换 a( left指针可以指向 a b 之间的任意一个数),此时这种情况下的a b c都可以构成三角形;

也就是通过a+b>c这一次比较,就发现了好几种符合题意的情况,刚好是(right-left)种情况。

得到(right-left)种情况后,right所指向的元素就没有再枚举的必要了,让right--

情况二: a+b<=c

对于上图,我们在移动 right 指针后,会出现 a+b<=c 的情况,这时候,a 和 b之间的所有数都小于 b ,要想继续找能构成三角形的 a,b,c,我们就要令left++

直到 left 和 right 相遇,说明固定10的所有情况都已经找到:

这时候,我们就需要固定的指针向左移动,去枚举固定第二个最大数时,能构成三角形的所有情况

4. 总结步骤


  1. 优化判断方法,排序;
  2. 定义 ret 记录数组所有能构成三角形的情况个数,作为返回的结果;
  3. 固定最大的数;
  4. 在最大的数的左区间内,使用双指针算法,快速统计出符合要求的三元组个数;
  5. 时间复杂度:第一步固定一个数,需要一层循环,第二步双指针,也需要一层循环,所以时间复杂度为O(N^2),是远远优于暴力枚举的策略的。

三. 编写代码


1. 步骤精讲


1.优化判断方法,排序:


2.定义 ret 记录数组所有能构成三角形的情况个数,作为返回的结果:


  1. 固定最大的数:

注意,这里 i 固定最后一个元素,所以 i 从 n - 1 的位置开始,又因为前面还要定义两个指针,这两个指针所指的值,与要固定的值,是不能重复的;所以可以少枚举两次,i>=2 而不是 i>=0


4. 在最大的数的左区间内,使用双指针算法,快速统计出符合要求的三元组个数:

left ,right两个指针,必须是在固定最大数之后,再定义;

所以left ,right 要在第一层for循环内定义,因为每次循环都要改变;

right=i-1 表示 right 在固定好数后,从该数的左边开始枚举


2. 完整代码

java 复制代码
 public int triangleNumber(int[] nums) {
        int n =nums.length;
        Arrays.sort(nums);
        int 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;
    }
相关推荐
灰色孤星A1 分钟前
瑞吉外卖项目学习笔记(四)@TableField(fill = FieldFill.INSERT)公共字段填充、启用/禁用/修改员工信息
java·学习笔记·springboot·瑞吉外卖·黑马程序员·tablefield·公共字段填充
liuming19926 分钟前
Halcon中histo_2dim(Operator)算子原理及应用详解
图像处理·人工智能·深度学习·算法·机器学习·计算机视觉·视觉检测
逊嘘7 分钟前
【Java数据结构】ArrayList相关的算法
java·开发语言
Y编程小白21 分钟前
SpringBoot的创建方式
java·spring boot·后端
sc写算法23 分钟前
Hash 映射
数据结构·算法·哈希算法
总是学不会.29 分钟前
【集合】Java 8 - Stream API 17种常用操作与案例详解
java·windows·spring boot·mysql·intellij-idea·java集合
雅妮yyn35 分钟前
头歌数据结构-排序的实现及其应用
数据结构·算法
云边有个稻草人36 分钟前
【优选算法】—移动零(双指针算法)
算法·排序算法·双指针算法
潜意识起点38 分钟前
【潜意识Java】javaee中的SpringBoot在Java 开发中的应用与详细分析
java·spring boot·后端
mxbb.40 分钟前
单点Redis所面临的问题及解决方法
java·数据库·redis·缓存