LeetCode 334. 递增的三元子序列 / 747. 至少是其他数字两倍的最大数 / 373. 查找和最小的K对数字(多路归并滑动窗口+二分)

Posted Zephyr丶J

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 334. 递增的三元子序列 / 747. 至少是其他数字两倍的最大数 / 373. 查找和最小的K对数字(多路归并滑动窗口+二分)相关的知识,希望对你有一定的参考价值。

334. 递增的三元子序列

2022.1.12 每日一题

题目描述

给你一个整数数组 nums ,判断这个数组中是否存在长度为 3 的递增子序列。

如果存在这样的三元组下标 (i, j, k) 且满足 i < j < k ,使得 nums[i] < nums[j] < nums[k] ,返回 true ;否则,返回 false 。

示例 1:

输入:nums = [1,2,3,4,5]
输出:true
解释:任何 i < j < k 的三元组都满足题意

示例 2:

输入:nums = [5,4,3,2,1]
输出:false
解释:不存在满足题意的三元组

示例 3:

输入:nums = [2,1,5,0,4,6]
输出:true
解释:三元组 (3, 4, 5) 满足题意,因为 nums[3] == 0 < nums[4] == 4 < nums[5] == 6

提示:

1 <= nums.length <= 5 * 10^5
-2^31 <= nums[i] <= 2^31 - 1

进阶:你能实现时间复杂度为 O(n) ,空间复杂度为 O(1) 的解决方案吗?

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

思路

首先想最常规的解决方案,很容易联想到栈,单调递增的栈
但是简单模拟一下也很容易发现问题,如果发现一个更小的数把前两个数都弹出了,那么如果后面有一个大数,就不能正确给出答案了
所以又想到了如果弹出两个数的时候,就把此时栈顶的数记录下来,后面如果有比这个栈顶更大的数,就返回true
然后想了一下,好像没有什么漏洞
栈也可以变成三个变量,就实现了O1的空间复杂度
写完其实发现,不用record也可以,直接用second就可以了

class Solution 
    public boolean increasingTriplet(int[] nums) 
        //用两个变量来记录两个数,如果后面有小于当前数的,进行更换,
        //如果更换了第一个数,那么记录当前第二个数,后面如果有比第二个数大的直接返回true
        //如果没有,继续进行更换

        int n = nums.length;
        int first = nums[0];
        int second = Integer.MAX_VALUE;
        int record = second;
        for(int i = 1; i < n; i++)
            if(nums[i] > record)
                return true;
            if(nums[i] < first)
                first = nums[i];
                record = second;
                second = Integer.MAX_VALUE;
            
            else if(nums[i] > first && nums[i] < second)
                second = nums[i];
                record = second;
            
        
        return false;
    

另外,需要找到一个数左边小于它的数,右边大于它的数,所以可以用两个数组分别表示左边和右边的最小值,用双向遍历来处理,空间复杂度是On

747. 至少是其他数字两倍的最大数

2022.1.13 每日一题

题目描述

给你一个整数数组 nums ,其中总是存在 唯一的 一个最大整数 。

请你找出数组中的最大元素并检查它是否 至少是数组中每个其他数字的两倍 。如果是,则返回 最大元素的下标 ,否则返回 -1 。

示例 1:

输入:nums = [3,6,1,0]
输出:1
解释:6 是最大的整数,对于数组中的其他整数,6 大于数组中其他元素的两倍。6 的下标是 1 ,所以返回 1 。

示例 2:

输入:nums = [1,2,3,4]
输出:-1
解释:4 没有超过 3 的两倍大,所以返回 -1 。

示例 3:

输入:nums = [1]
输出:0
解释:因为不存在其他数字,所以认为现有数字 1 至少是其他数字的两倍。

提示:

1 <= nums.length <= 50
0 <= nums[i] <= 100
nums 中的最大元素是唯一的

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

思路

class Solution 
    public int dominantIndex(int[] nums) 
        //找前两大的数
        int n = nums.length;
        if(n == 1)
            return 0;
        int maxidx1 = 0;
        int maxidx2 = -1;
        for(int i = 1; i < n; i++)
            if(nums[i] > nums[maxidx1])
                maxidx2 = maxidx1;
                maxidx1 = i;
            else if(maxidx2 == -1 || nums[i] > nums[maxidx2])
                maxidx2 = i;
            
        
        if(nums[maxidx1] >= nums[maxidx2] * 2)
            return maxidx1;
        else
            return -1;
    

373. 查找和最小的K对数字

2022.1.14 每日一题

题目描述

给定两个以升序排列的整数数组 nums1 和 nums2 , 以及一个整数 k 。

定义一对值 (u,v),其中第一个元素来自 nums1,第二个元素来自 nums2 。

请找到和最小的 k 个数对 (u1,v1), (u2,v2) … (uk,vk) 。

示例 1:

输入: nums1 = [1,7,11], nums2 = [2,4,6], k = 3
输出: [1,2],[1,4],[1,6]
解释: 返回序列中的前 3 对数:
[1,2],[1,4],[1,6],[7,2],[7,4],[11,2],[7,6],[11,4],[11,6]

示例 2:

输入: nums1 = [1,1,2], nums2 = [1,2,3], k = 2
输出: [1,1],[1,1]
解释: 返回序列中的前 2 对数:
[1,1],[1,1],[1,2],[2,1],[1,2],[2,2],[1,3],[1,3],[2,3]

示例 3:

输入: nums1 = [1,2], nums2 = [3], k = 3
输出: [1,3],[2,3]
解释: 也可能序列中所有的数对都被返回:[1,3],[2,3]

提示:

1 <= nums1.length, nums2.length <= 10^4
-10^9 <= nums1[i], nums2[i] <= 10^9
nums1, nums2 均为升序排列
1 <= k <= 1000

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

思路

乍一看很容易相成双指针,但仔细一想,就发现不对劲
然后又写了一个把所有和放入优先队列中的方法,内存超出了
那么怎么改进呢?
需要记录第一个数组每一个位置和第二个数组中第一个位置对应的和
然后把最小的弹出,再放入和第二个位置的和,以此类推,找出k个或者全部
有点多路归并的意思

class Solution 
    public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) 
        //好像没有那么简单,之前和朋友讨论过这个问题
        //刚开始肯定是第一个数加第一个数,那么后面呢
        //是nums1[1] + nums2[0], nums1[0] + nums2[1] 的较小者
        //例如是前者,那么下面比啥呢,nums1[0] + nums2[1]

        //用一个数组表示第一个数组中对应第二个数组中的数加到第几个了
        int m = nums1.length;
        int n = nums2.length;
        List<List<Integer>> res = new ArrayList<>();

        //从小到大排序
        PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> (nums1[a[0]] + nums2[a[1]] - nums1[b[0]] - nums2[b[1]]));

        //先存入nums1中每个数和nums2中第一个数的和
        for(int i = 0; i < m; i++)
            pq.offer(new int[]i, 0);
        

        while(!pq.isEmpty() && k-- > 0)
            //首先弹出最小的
            int[] top = pq.poll();
            //放入结果集中
            List<Integer> list = new ArrayList<>()
                add(nums1[top[0]]);
                add(nums2[top[1]]);
            ;

            res.add(list);
            //然后把当前指向nums2的指针往后移动一位,并放入优先队列中
            if(top[1] + 1 < n)
                top[1] = top[1] + 1;
                pq.offer(top);
            
        
        return res;
    

先用二分查找前k个最小数对的界限值
找到这个界限值以后,就去找这个界限以内的数值对
都是用滑动窗口的方法

class Solution 
    public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) 
        //没想到还能二分
        //先用二分查找两个数之和的边界值,滑动窗口找小于这个值的是否有k个
        int l1 = nums1.length;
        int l2 = nums2.length;
        int left = nums1[0] + nums2[0];
        int right = nums1[l1 - 1] + nums2[l2 - 1];
        int sum = Integer.MAX_VALUE;
        while(left < right)
            int mid = (right - left) / 2 + left;
            //当前数目
            int count = 0;
            //nums1开始下标,nums2结束下标
            int start = 0;
            int end = l2 - 1;
            //怎么滑,左边加右边,如果小于mid,那么左边滑动;如果大于mid,那么右边滑动
            while(start < l1 && end >= 0)
                if(nums1[start] + nums2[end] > mid)
                    end--;
                else
                    count += end + 1;
                    start++;
                
            
            if(count < k)
                left = mid + 1;
            else
                sum = mid;
                right = mid;
            
        
        if(sum == Integer.MAX_VALUE)
            sum = left;
        
        //然后找到和小于等于sum的数值对,也是滑动窗口
        //先找小于的,再找等于的
        List<List<Integer>> res = new ArrayList<>();
        int start = 0;
        int end = l2 - 1;
        while(start < l1 && end >= 0)
            while(end >= 0 && nums1[start] + nums2[end] >= sum)
                end--;
            
            //固定nums1的位置,移动nums2
            for(int i = 0; i <= end && k > 0; i++, k--)
                List<Integer> temp = new ArrayList<>();
                temp.add(nums1[start]);
                temp.add(nums2[i]);
                res.add(temp);
            
            start++;
        

        start = 0;
        end = l2 - 1;
        while(start < l1 && end >= 0 && k > 0)
            while(end >= 0 && nums1[start] + nums2[end] > sum)
                end--;
            
            //如果等于了需要往前nums1数组往前看
            for(int i = start; i >= 0 && k > 0 && nums1[i] + nums2[end] == sum; i--, k--)
                List<Integer> temp = new ArrayList<>();
                temp.add(nums1[i]);
                temp.add(nums2[end]);
                res.add(temp);
            
            start++;
        
        return res;
    

以上是关于LeetCode 334. 递增的三元子序列 / 747. 至少是其他数字两倍的最大数 / 373. 查找和最小的K对数字(多路归并滑动窗口+二分)的主要内容,如果未能解决你的问题,请参考以下文章

Python|Leetcode《334》|递增的三元子序列

Python|Leetcode《334》|递增的三元子序列

Python描述 LeetCode 334. 递增的三元子序列

leetcode334 递增的三元子序列

Python描述 LeetCode 334. 递增的三元子序列

Python描述 LeetCode 334. 递增的三元子序列