• [leetcode] Valid Triangle Number


    Given an array consists of non-negative integers, your task is to count the number of triplets chosen from the array that can make triangles if we take them as side lengths of a triangle.

    Example 1:

    Input: [2,2,3,4]
    Output: 3
    Explanation:
    Valid combinations are: 
    2,3,4 (using the first 2)
    2,3,4 (using the second 2)
    2,2,3

    Note:

    1. The length of the given array won't exceed 1000.
    2. The integers in the given array are in the range of [0, 1000].

    分析:题目翻译一下:要求在一个数组中找到三个数的组合,使得这三个数任意两数之和大于第三个数,不同位置重复的数字视为不同组合,求有多少种组合。
    首先,第一印象三层循环,因为要挑出三个数字。用个小技巧就是先排序,然后只需要比较较小两个数的和是否大于第三个数就行了。代码如下:
     1 class Solution {
     2     public int triangleNumber(int[] nums) {
     3         Arrays.sort(nums);
     4         int count = 0;
     5         for ( int i = 0 ; i < nums.length - 2 ; i ++ ){
     6             for ( int j = i + 1 ; j < nums.length - 1 ; j ++ ){
     7                 int doublesum = nums[i]+nums[j];
     8                 for ( int z = j + 1 ; z < nums.length ; z ++ ){
     9                     if ( nums[z] < doublesum ) count++;
    10                 }
    11             }
    12         }
    13         return count;
    14     }
    15 }

          运行时间235ms,很差的算法,时间复杂度为O(n^3)。但是这个是我们下一步改进的基础。


    思路二:上面一个算法中,我们比较关键的一步操作时先对数组进行排序,排序之后的数组是从小到大,然后我们找数字的时候,也就是第三层循环,可以直接用binarysearch方法去找到不满足条件最小的数字,就不用一个一个去遍历了。例如[1,3,4,5,8,9],如果选择前两个数字是3和4,那么第三个数字不能超过7,那我们就用binarysearch方法搜索7的位置,然后减去4的位置就是在这种情况下的值。也就是说利用这种思想可以把时间复杂度降到O(n^2)。查了一下binarysearch的api,发现很不好用,还不如自己写一个二分查找。代码如下:

     1 class Solution {
     2     public int triangleNumber(int[] nums) {
     3         Arrays.sort(nums);
     4         int count = 0;
     5         for ( int i = 0 ; i < nums.length - 2 ; i ++ ){
     6             for ( int j = i + 1 ; j < nums.length - 1 ; j ++ ){
     7                 int doublesum = nums[i] + nums[j];
     8                 //二分查找
     9                 int low = j + 1;
    10                 int high = nums.length;
    11                 while ( low < high ){
    12                     int mid = ( low + high ) / 2 ;
    13                     if ( nums[mid] < doublesum ) low = mid + 1;
    14                     else high = mid;
    15                 }
    16                 count += low - j - 1;
    17             }
    18         }
    19         return count;
    20     }
    21 }

          这种思路运行时间43ms,击败26.16%的提交。神了奇了,都缩短到O(n^2*longn)了,为啥还是不行。。。莫非还有O(n*longn)的算法存在?


    思路三:参考了solution的解法,我的理解是一层循环+滑动窗口。滑动窗口比较关键,从i+1到nums.length-1进行滑动窗口,每个窗口内应该包含的是使得三角形成立的最大情况。

    比如[2,5,6,7,9]

    i=0:1、left从5开始,right从6开始,首先right向又移动,一旦nums[i]+nums[left]>=nums[right],right停止一定,此时[left,right]窗口中包含right-left-1个解。

            2、left向右移动一位,right又向右移动,重复上面的步骤。

    具体的滑动窗口法在在另一个文章中有详细介绍。这里代码实现如下:

     1 class Solution {
     2     public int triangleNumber(int[] nums) {
     3         Arrays.sort(nums);
     4         int count = 0;
     5         for ( int i = 0 ; i < nums.length - 2 ; i ++ ){
     6             for ( int left = i + 1 ; left < nums.length - 1 ; left ++ ){
     7                 int right = left + 1;
     8                 while (right < nums.length && nums[i] + nums[left] > nums[right])  right++;
     9                 count += right - left - 1;
    10             }
    11         }
    12         return count;
    13     }
    14 }

           运行时间66ms,还是不够好。其实时间复杂度还是O(n^2*logn)。


    思路4:从大到小搜索,因为两数之和要大于第三个数,不妨从nums.length-1到2,下面问题就转变成在有序数组中求两个数之和大于指定数的个数了。

    例如:[2,5,6,7,8],当nums[i]=8时,因为2+7>8,所以278、578、678都可以,count+=right-left,然后right左移;2+6<=8,所以left右移,继续上面的判断。想法非常巧妙。

    代码如下:

     1 class Solution {
     2     public int triangleNumber(int[] nums) {
     3         Arrays.sort(nums);
     4         int count = 0;
     5         for ( int i = nums.length - 1 ; i > 1 ; i -- ){
     6             int target = nums[i];
     7             int low = 0;
     8             int high = i-1;
     9             while ( low < high ){
    10                 if ( nums[low] + nums[high] > target ){
    11                     count += high - low;
    12                     high--;
    13                 }else {
    14                     low ++;
    15                 }
    16             }
    17         }
    18         return count;
    19     }
    20 }

          运行时间18ms,时间复杂度o(n*longn)

          这个题目整体还是有很多种方法的,比较好理解的还是用二分查找的方法。最后一种设计很巧妙,学习了。

  • 相关阅读:
    Ansible-Tower--安装配置及破解
    Ansible-playbook--配置及使用
    Ansible-基础配置及常用模块
    基于SLB实现滚动发布
    CentOS7 部署yapi API 文档管理工具
    node及pm2环境安装
    MySQL参数max_connect_errors分析
    CentOS7搭建confluence企业级文档管理
    Gitlab用户密码忘记如何修改
    Docker私有仓库Harbor介绍与部署
  • 原文地址:https://www.cnblogs.com/boris1221/p/9337319.html
Copyright © 2020-2023  润新知