LeetCode 581. 最短无序连续子数组/611. 有效三角形的个数/15. 三数之和/18. 四数之和(双指针)

Posted Zephyr丶J

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 581. 最短无序连续子数组/611. 有效三角形的个数/15. 三数之和/18. 四数之和(双指针)相关的知识,希望对你有一定的参考价值。

581. 最短无序连续子数组

2021.8.3 每日一题

题目描述

给你一个整数数组 nums ,你需要找出一个 连续子数组 ,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。

请你找出符合题意的 最短 子数组,并输出它的长度。

示例 1:

输入:nums = [2,6,4,8,10,9,15]
输出:5
解释:你只需要对 [6, 4, 8, 10, 9] 进行升序排序,那么整个表都会变为升序排序。

示例 2:

输入:nums = [1,2,3,4]
输出:0

示例 3:

输入:nums = [1]
输出:0

提示:

1 <= nums.length <= 104
-105 <= nums[i] <= 105

进阶:你可以设计一个时间复杂度为 O(n) 的解决方案吗?

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shortest-unsorted-continuous-subarray
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

第一种:排序

class Solution {
    public int findUnsortedSubarray(int[] nums) {
        //最长有序和最短无序
        //第一种,很直观的想到,排序,比对,哪个不一样
        int l = nums.length;
        int[] temp = Arrays.copyOf(nums, l);
        Arrays.sort(temp);
        int left = 0;
        int right = l - 1;

        while(left <= right && nums[left] == temp[left]){
            left++;
        }
        while(left <= right && nums[right] == temp[right]){
            right--;
        }

        return right - left + 1;

    }
}

第二种:
(加这些注释是为了记录我的思考过程)
我是这样想的,如果能知道左边开始的点的话,就能记录每个位置无序子串的长度,如果当前值大于目前最大值,那么这个点不需要处理,继续向后遍历;如果小于,那么说明当前位置的值肯定要移动。所以无序子串的右边界跳到这里
那么左边界怎么处理呢,刚开始想的是找到第一个要移动的点,下标为idx;然后在后面找idx右边的最小值,然后在左边idx前面(排好序的),找它插入的位置,就是左边的下标

但是写着写着,从右到左同理,不就也能找到左边位置了吗
然后就写出来了:

class Solution {
    public int findUnsortedSubarray(int[] nums) {
        //O(n)怎么做呢,隐隐感觉到是动规
        //主要是要找到第一个要交换的点,后面感觉就动规就可以
        //这样,先找到哪里开始是不是排序的
        /*
        int idx = 0;
        for(int i = 0; i < l - 1; i++){
            if(nums[i] > nums[i + 1]){
                idx = i + 1;
                break;
            }
        }

        //从这里向后,找到之后的最小值
        int min = Integer.MAX_VALUE;
        for(int i = idx; i < l; i++){
            min = Math.min()
        }
        */
        //写着写着,找到思路了,从左到右,查右边下标
        //从右到左,查左边下标
        int l = nums.length;
        //从左到右,找到右边应该排序的最大下标
        int right = 0;
        int max = Integer.MIN_VALUE;
        for(int i = 0; i < l; i++){
            if(nums[i] < max)
                right = i;
            else{
                max = nums[i];
            }
        }
        if(right == 0)
            return 0;
        int left = 0;
        int min = Integer.MAX_VALUE;
        for(int i = l - 1; i >= 0; i--){
            if(nums[i] > min){
                left = i;
            }else{
                min = nums[i];
            }
        }
        return right - left + 1;
    }
}

611. 有效三角形的个数

2021.8.4 每日一题

题目描述

给定一个包含非负整数的数组,你的任务是统计其中可以组成三角形三条边的三元组个数。

示例 1:

输入: [2,2,3,4]
输出: 3
解释:
有效的组合是:
2,3,4 (使用第一个 2)
2,3,4 (使用第二个 2)
2,2,3

注意:

数组长度不超过1000。
数组里整数的范围为 [0, 1000]。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/valid-triangle-number
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

二分查找

class Solution {
    public int triangleNumber(int[] nums) {
        //a + b > c,两个较小的边之和小于第三边
        //想到的第一个思路是二分

        int l = nums.length;
        Arrays.sort(nums);
        int count = 0;
        for(int i = 0; i < l - 2; i++){
            for(int j = i + 1; j < l - 1; j++){
                int a = nums[i];
                int b = nums[j];

                int c = nums[i] + nums[j];

                int left = j + 1;
                int right = l - 1;
                //找第一个小于c的
                while(left < right){
                    int mid = (right - left + 1) / 2 + left;
                    if(nums[mid] >= c){
                        right = mid - 1;
                    }else{
                        left = mid;
                    }
                }
                
                if(nums[left] < a + b)
                    count += left - j;
            }
        }
        return count;

    }
}

双指针,写了一年,一直错

class Solution {
    public int triangleNumber(int[] nums) {
        //a + b > c,两个较小的边之和小于第三边
        //双指针

        int l = nums.length;
        Arrays.sort(nums);
        int count = 0;
        for(int i = 0; i < l - 2; i++){
            int j = i + 1;
            int k = i + 2;
            while(j < l - 1 && k < l){
                //走到头了,那么就统计数
                while(k < l - 1 && nums[i] + nums[j] > nums[k]){
                    k++;
                }
                //如果k指向的数不满足条件,那么就执行这条
                if(nums[i] + nums[j] <= nums[k]){
                    k--;
                }
                count += Math.max(k - j, 0);
                //先让j加1
                
                j++;
                //如果a + b还小于c的话,继续增大j
                while(j < l - 2 && nums[i] + nums[j] <= nums[k]){
                    j++;
                }
            }  
        }
        return count;

    }
}
class Solution {
    public int triangleNumber(int[] nums) {
        //a + b > c,两个较小的边之和小于第三边
        //双指针
        //看了人家的双指针,我不知道是在写什么
        //枚举最大数简单

        int l = nums.length;
        Arrays.sort(nums);
        int count = 0;
        //枚举最大边
        for(int i = l - 1; i >= 0; i--){
            int left = 0;
            int right = i - 1;
            //左右两个双指针
            while(left < right){
                //如果满足条件,那么中间的left都满足条件
                if(nums[left] + nums[right] > nums[i]){
                    count += right - left;
                    right--;
                }else
                    left++;
            }
        }
        return count;

    }
}

15. 三数之和

题目描述

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

示例 1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]

示例 2:

输入:nums = []
输出:[]

示例 3:

输入:nums = [0]
输出:[]

提示:

0 <= nums.length <= 3000
-105 <= nums[i] <= 105

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/3sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

和上一道题一样,双指针做了一把
这里主要是要去重
看了力扣的官解,不禁感叹一句以前力扣的官解是真的良心啊

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        //三数之和,慕名而来
        //控制一个数,然后使剩下的两个数之和等于相反数
        //也就是两数之和

        //或者排序加双指针

        int l = nums.length;
        
        List<List<Integer>> res = new ArrayList<>();
        if(l < 3)
            return res;
        Arrays.sort(nums);

        for(int i = 0; i < l - 2; i++){
            //如果这个数计算过了
            if(i != 0 && nums[i] == nums[i - 1])
                continue;
            //加了这个判断,一下提升到很快
            if(nums[i] > 0)
                break;
            int left = i + 1;
            int right = l - 1;
            while(left < right){
                //如果大于了,那么应该减小
                if(nums[left] + nums[right] > -nums[i]){
                    right--;
                }else if(nums[left] + nums[right] < -nums[i]){
                    left++;
                }else{
                    List<Integer> list = new ArrayList<>();
                    list.add(nums[i]);
                    list.add(nums[left]);
                    list.add(nums[right]);
                    res.add(list);
                    left++;
                    //如果相同的值就剔除
                    while(left < right && nums[left] == nums[left - 1])
                        left++;
                    right--;
                    while(right > left && nums[right] == nums[right + 1])
                        right--;
                }
            }
        }
        return res;
    }
}

18. 四数之和

题目描述

给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。

注意:答案中不可以包含重复的四元组。

示例 1:

输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]

示例 2:

输入:nums = [], target = 0
输出:[]

提示:

0 <= nums.length <= 200
-109 <= nums[i] <= 109
-109 <= target <= 109

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/4sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

两层遍历加双指针
这次我没有用left和right,而是效仿三数之和官解的那种写法
剪枝没有加,因为第一时间没有想到

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        int l = nums.length;
        
        List<List<Integer>> res = new ArrayList<>();

        if(l < 4)
            return res;
        Arrays.sort(nums);

        for(int i = 0; i < l - 3; i++){
            if(i != 0 && nums[i] == nums[i - 1])
                continue;
            for(int j = i + 1; j < l - 2; j++){
                int sum = nums[i] + nums[j];
                
                if(j > i + 1 && nums[j] == nums[j - 1])
                    continue;
                int t = target - sum;
                int r = l - 1;
                for(int k = j + 1; k < r; k++){
                    if(k > j + 1 && nums[k] == nums[k - 1])
                        continue;
                    //移动右指针
                    while(k < r && nums[k] + nums[r] > t){
                        r--;
                    }
                    //如果重合了,那么就跳出
                    if(k == r)
                        break;
                    if(nums[k] + nums[r] == t){
                        List<Integer> list = new ArrayList<>();
                        list.add(nums[i]);
                        list.add(nums[j]);
                        list.add(nums[k]);
                        list.add(nums[r]);
                        res.add(list);
                    }
                }
            }
        }
        return res;
    }
}

以上是关于LeetCode 581. 最短无序连续子数组/611. 有效三角形的个数/15. 三数之和/18. 四数之和(双指针)的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode 581. 最短无序连续子数组(Shortest Unsorted Continuous Subarray)

LeetCode 581. 最短无序连续子数组/611. 有效三角形的个数/15. 三数之和/18. 四数之和(双指针)

LeetCode 581 最短无序连续子数组[排序] HERODING的LeetCode之路

LeetCode 581. Shortest Unsorted Continuous Subarray (最短无序连续子数组)

581-最短无序连续子数组

LeetCode:14. 最长公共前缀581. 最短无序连续子数组(python3)