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

目录

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

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

[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;
    }
相关推荐
qq_3273427318 分钟前
Java实现离线身份证号码OCR识别
java·开发语言
Swift社区1 小时前
LeetCode - #139 单词拆分
算法·leetcode·职场和发展
Kent_J_Truman1 小时前
greater<>() 、less<>()及运算符 < 重载在排序和堆中的使用
算法
阿龟在奔跑2 小时前
引用类型的局部变量线程安全问题分析——以多线程对方法局部变量List类型对象实例的add、remove操作为例
java·jvm·安全·list
飞滕人生TYF2 小时前
m个数 生成n个数的所有组合 详解
java·递归
代码小鑫2 小时前
A043-基于Spring Boot的秒杀系统设计与实现
java·开发语言·数据库·spring boot·后端·spring·毕业设计
真心喜欢你吖2 小时前
SpringBoot与MongoDB深度整合及应用案例
java·spring boot·后端·mongodb·spring
激流丶2 小时前
【Kafka 实战】Kafka 如何保证消息的顺序性?
java·后端·kafka
IT 青年2 小时前
数据结构 (1)基本概念和术语
数据结构·算法
Dong雨2 小时前
力扣hot100-->栈/单调栈
算法·leetcode·职场和发展