查找和最小的 K 对数字

news2024/11/25 4:41:24

优质博文IT-BLOG-CN

一、题目

给定两个以 非递减顺序排列 的整数数组nums1nums2, 以及一个整数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]

1 <= nums1.length, nums2.length <= 105
-109 <= nums1[i], nums2[i] <= 109
nums1nums2均为 升序排列
1 <= k <= 104
k <= nums1.length * nums2.length

二、代码

优先队列

在这里插入图片描述

对于已经按升序排列的两个数组nums1,nums2,长度分别为length1,length2​,我们可以知道和最小的数对一定为 (nums1[0],nums2[0]),和最大的数对一定为(nums1[length1−1],nums2[length2−1])。本题要求找到最小的k个数对,最直接的办法是可以将所有的数对求出来,然后利用排序或者TopK解法求出最小的k个数对即可。实际求解时可以不用求出所有的数对,只需从所有符合待选的数对中选出最小的即可,假设当前已选的前n小数对的索引分别为(a1,b1),(a2,b2),(a3,b3),…,(an,bn),由于两个数组都是按照升序进行排序的,则可以推出第n+1小的数对的索引选择范围为(a1+1,b1),(a1,b1+1),(a2+1,b2),(a2,b2+1),(a3+1,b3),(a3,b3+1),…,(an+1,bn),(an,bn+1),假设我们利用堆的特性可以求出待选范围中最小数对的索引为(ai,bi),同时将新的待选的数对(ai+1,bi),(ai,bi+1)加入到堆中,直到我们选出 kkk 个数对即可。

如果我们每次都将已选的数对(ai,bi)的待选索引(ai+1,bi),(ai,bi+1)加入到堆中则可能出现重复的问题,一般需要设置标记位解决去重的问题。我们可以将nums1的前k个索引数对(0,0),(1,0),…,(k−1,0)加入到队列中,每次从队列中取出元素(x,y)时,我们只需要将nums2的索引增加即可,这样避免了重复加入元素的问题。

class Solution {
    public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
        PriorityQueue<int[]> pq = new PriorityQueue<>(k, (o1, o2)->{
            return nums1[o1[0]] + nums2[o1[1]] - nums1[o2[0]] - nums2[o2[1]];
        });
        List<List<Integer>> ans = new ArrayList<>();
        int m = nums1.length;
        int n = nums2.length;
        for (int i = 0; i < Math.min(m, k); i++) {
            pq.offer(new int[]{i,0});
        }
        while (k-- > 0 && !pq.isEmpty()) {
            int[] idxPair = pq.poll();
            List<Integer> list = new ArrayList<>();
            list.add(nums1[idxPair[0]]);
            list.add(nums2[idxPair[1]]);
            ans.add(list);
            if (idxPair[1] + 1 < n) {
                pq.offer(new int[]{idxPair[0], idxPair[1] + 1});
            }
        }
        
        return ans;
    }
}

时间复杂度: O(klog⁡k),其中k是选择的数对的数目。优先队列中最多只保存k个元素,每次压入新的元素队列进行调整的时间复杂度为log⁡k,入队操作一共有2k次, 一共需要从队列中弹出k个数据。
空间复杂度: O(k)。优先队列中最多只保存k个元素。

二分查找

我们利用二分查找找到第k小的数对和为pairSum。利用滑动窗口即可计算出两个数组中满足数对和小于等于目标值target的数对有多少个,我们找到最小的target且满足小于等于它的数对数目刚好大于等于k即为目标值pairSum,然后在数组中找到最小的 kkk 个数对满足数对和小于等于pairSum

由于题目中数组nums1,nums2中的元素存在重复,因此我们不能简单的利用滑动窗口找到所有满足小于等于pairSum的数对。因为存在小于等于pairSum的数对和的数目大于k,因此数对和等于pairSum的数对不一定就属于最小的k个数对。
首先利用滑动窗口找到所有小于pairSum的数对,假设数对和小于pairSum的数目为x个,然后再利用二分查找在数组中找到k−x个和等于pairSum的数对即可。

class Solution {
    public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
        int m = nums1.length;
        int n = nums2.length;

        /*二分查找第 k 小的数对和的大小*/
        int left = nums1[0] + nums2[0];
        int right = nums1[m - 1] + nums2[n - 1];
        int pairSum = right;
        while (left <= right) {
            int mid = left + ((right - left) >> 1);
            long cnt = 0;
            int start = 0;
            int end = n - 1;
            while (start < m && end >= 0) {
                if (nums1[start] + nums2[end] > mid) {
                    end--;
                } else {
                    cnt += end + 1;
                    start++;
                }
            }
            if (cnt < k) {
                left = mid + 1;
            } else {
                pairSum = mid;
                right = mid - 1;
            }
        }

        List<List<Integer>> ans = new ArrayList<>();
        int pos = n - 1;
        /*找到小于目标值 pairSum 的数对*/
        for (int i = 0; i < m; i++) {
            while (pos >= 0 && nums1[i] + nums2[pos] >= pairSum) {
                pos--;
            }
            for (int j = 0; j <= pos && k > 0; j++, k--) {
                List<Integer> list = new ArrayList<>();
                list.add(nums1[i]);
                list.add(nums2[j]);
                ans.add(list);
            }
        }

        /*找到等于目标值 pairSum 的数对*/
        pos = n - 1;
        for (int i = 0; i < m && k > 0; i++) {
            int start1 = i;
            while (i < m - 1 && nums1[i] == nums1[i + 1]) {
                i++;
            }
            while (pos >= 0 && nums1[i] + nums2[pos] > pairSum) {
                pos--;
            }
            int start2 = pos;
            while (pos > 0 && nums2[pos] == nums2[pos - 1]) {
                pos--;
            }
            if (nums1[i] + nums2[pos] != pairSum) {
                continue;
            }
            int count = (int) Math.min(k, (long) (i - start1 + 1) * (start2 - pos + 1));
            for (int j = 0; j < count && k > 0; j++, k--) {
                List<Integer> list = new ArrayList<>();
                list.add(nums1[i]);
                list.add(nums2[pos]);
                ans.add(list);
            }
        }
        return ans;
    }
}

时间复杂度: O(k+(m+n)×log⁡(diff(nums1)+diff(nums2))),其中m,n表示数组nums1,nums2的长度,diff(arr)\textit{diff}(arr)diff(arr) 表示数组arr中最大元素与最小元素之差,diff(nums1)=max⁡(nums1)−min⁡(nums1),diff(nums2)=max⁡(nums2)−min⁡(nums2))。我们利用二分查找找到满足要求的数对和的时间复杂度为(m+n)×log⁡(diff(nums1)+diff(nums2)),我们利用滑动窗口找到小于等于目标值的k个数对的时间复杂度为O(2×(k+m+n)),所以总的时间复杂度O(2×(k+m+n)+(m+n)×log⁡(diff(nums1)+diff(nums2)))=k+(m+n)×log⁡(diff(nums1)+diff(nums2))
空间复杂度: O(1),除了函数返回值以外,不需要额外的存储空间。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1512169.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PTA L2-007 家庭房产

给定每个人的家庭成员和其自己名下的房产&#xff0c;请你统计出每个家庭的人口数、人均房产面积及房产套数。 输入格式&#xff1a; 输入第一行给出一个正整数N&#xff08;≤1000&#xff09;&#xff0c;随后N行&#xff0c;每行按下列格式给出一个人的房产&#xff1a; …

k8s中calico网络组件部署时一个节点一直处于Pending状态

k8s中calico网络组件部署时一个节点一直处于Pending状态 故障截图 故障排查思路&#xff0c;通过describe查看具体原因 ~]# kubectl describe pod calico-node-pzlfv -n kube-system通过describe查看得知报错 Warning FailedScheduling 58s (x23 over 23m) default-sche…

pytest生成allure的报告

首先要下载安装配置allure allure serve ./outputs/allure_report 可以生成html的文件自动在默认浏览器中打开

【NR 定位】3GPP NR Positioning 5G定位标准解读(十六)-UL-AoA 定位

前言 3GPP NR Positioning 5G定位标准&#xff1a;3GPP TS 38.305 V18 3GPP 标准网址&#xff1a;Directory Listing /ftp/ 【NR 定位】3GPP NR Positioning 5G定位标准解读&#xff08;一&#xff09;-CSDN博客 【NR 定位】3GPP NR Positioning 5G定位标准解读&#xff08;…

Java输出流之BufferWriter类

咦咦咦&#xff0c;各位小可爱&#xff0c;我是你们的好伙伴——bug菌&#xff0c;今天又来给大家普及Java SE相关知识点了&#xff0c;别躲起来啊&#xff0c;听我讲干货还不快点赞&#xff0c;赞多了我就有动力讲得更嗨啦&#xff01;所以呀&#xff0c;养成先点赞后阅读的好…

<AI大模型学习>——《人工智能AI》

&#xff1c;AI大模型学习&#xff1e;——《人工智能AI》 一、AI大模型通识 1.AI介绍 人工智能&#xff08;Artificial Intelligence&#xff09;&#xff0c;英文缩写为AI。 是新一轮科技革命和产业变革的重要驱动力量&#xff0c; 是研究、开发用于模拟、延伸和扩展人的智…

【滤波专题-第8篇】ICA降噪方法——类EMD联合ICA降噪及MATLAB代码实现(以VMD-ICA为例)

今天来介绍一种效果颇为不错的降噪方法。&#xff08;针对高频白噪声&#xff09; 上一篇文章我们讲到了FastICA方法。在现实世界的许多情况下&#xff0c;噪声往往接近高斯分布&#xff0c;而有用的信号&#xff08;如语音、图像特征等&#xff09;往往表现出非高斯的特性。F…

【大厂面试演练】知道ZooKeeper有什么应用场景吗

面试官&#xff1a;咳咳咳&#xff0c;看你简历写了精通ZooKeeper&#xff0c;那我就随便考考你吧 面试官&#xff1a;不用慌尽管说&#xff0c;错了也没关系&#x1f60a;。。。 每日分享大厂面试演练&#xff0c;感兴趣就关注我吧❤️ 面试官&#xff1a;知道ZooKeeper有什么…

Docker安装步骤笔记

一、环境准备 VM网络配置 打开VMware软件 --编辑 --虚拟网络编辑器 二、VM创建虚拟机 三、安装rhel8.9操作系统 1、rhel8.9 镜像下载 第一步&#xff1a;进入redhat官网进行注册第二步&#xff1a;下载rhel8.9镜像文件 https://access.redhat.com/downloads/content/rhel …

Pytorch搭建AlexNet 预测实现

1.导包 import torch import matplotlib.pyplot as plt import json from model import AlexNet from PIL import Image from torchvision import transforms 2.数据预处理 data_transform transforms.Compose([transforms.Resize((224, 224)), # 将图片重新裁剪transform…

JDBC连接Mysql(executeQuely)3/13

resultset-->executeQuery import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement;public class Demo3 {public static void main(String[] args) throws Exception {//1.注册驱动Class.forName("com.mys…

【Java,Redis】Redis 数据库存取字符串数据以及类数据

1、 字符串存取数据 Resource private StringRedisTemplate stringRedisTemplate;//从Redis中获取string字符串 stringRedisTemplate.opsForValue().get("cache:shop:"id); //Json -> class Shop shop JSONUtil.toBean(ShopJson,Shop.class); //字符串写入redis…

C# Stopwatch计算代码运行时间

文章目录 前言一、计算范围时间1、起始位置2、结束位置3、获取时间封装成对象&#xff08;1&#xff09;、完整代码&#xff08;2&#xff09;、使用示例 二、计算检查点时间1、初始化2、检查点封装成对象&#xff08;1&#xff09;、完整代码&#xff08;2&#xff09;、使用示…

SQL Server错误:15404

执行维护计划失败&#xff0c;提示SQL Server Error 15404 无法获取有关... 异常如下图&#xff1a; 原因&#xff1a;数据库用户名与计算机名称不一致 解决办法&#xff1a;1.重名称数据库用户名 将前缀改成计算机名 2.重启SQL Server代理

C++Qt学习——不用UI文件编程

在创建文件的时候不要选中Generate form这块 创建的文件如下图所示&#xff0c;比起之前的没有了form这一快 1、在mainwindow.h里面声明按钮对象 2、在mainwindow.cpp里实例化按钮 2.1、方法一 pushButton new QPushButton();pushButton->show(); 但是发现显示是分离的 2…

保研复习数据结构记(8)--排序

一.内部排序 1.概念 什么是排序&#xff1f;是将一个任意排列的记录或者是数据元素&#xff0c;排列成按关键字有序的序列什么是排序方法是稳定的&#xff1f;按照关键字排序的kikj&#xff0c;在排序之后&#xff0c;两个关键字相等的记录的顺序与排序之前相同&#xff0c;若…

ubuntu2004桌面系统英伟达显卡驱动安装方法

#如何查看显卡型号 lspci | grep -i vga#----output------ 01:00.0 VGA compatible controller: NVIDIA Corporation Device 1f06 (rev a1)根据 Device 后的 值 进入网站查询 pci-ids.ucw.cz/mods/PC/10de?actionhelp?helppci #根据显卡型号&#xff0c;下载对应系统的驱动…

Linux 硬件时间(RTC time)、系统时间(UTC时间、Universal time)、本地时间(Local time)、时区(Time zone)与夏令时(DST)解析

文章目录 理解时间&#xff1a;硬件时间、系统时间&#xff08;UTC时间&#xff09;、本地时间、时区与夏令时1. 硬件时间&#xff08;RTC time&#xff09;1.1 硬件时间简介1.2 如何使用硬件时间 2. 系统时间&#xff08;UTC时间&#xff09;&#xff08;Universal time&#…

TSINGSEE青犀煤矿矿井视频监控与汇聚融合管理视频监管平台建设方案

一、背景需求 随着我国经济的飞速发展&#xff0c;煤炭作为我国的主要能源之一&#xff0c;其开采和利用的重要性不言而喻。然而&#xff0c;煤矿事故频发&#xff0c;不仅造成了巨大的人员伤亡和财产损失&#xff0c;也对社会产生了深远的负面影响。视频监控系统作为实现煤矿智…

普发Pfeiffer OmniStar/ThermoStar GSD300/GSS300内部电路图装配安装3D图原理图电路板电路图详情内容看图片目录

普发Pfeiffer OmniStar/ThermoStar GSD300/GSS300内部电路图装配安装3D图原理图电路板电路图详情内容看图片目录