[路飞]_算法_队列_查找和最小的K对数字——优先队列

226 阅读1分钟

题目描述

给定两个以升序排列的整数数组 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 <= 104
  • -109 <= nums1[i], nums2[i] <= 109
  • nums1, nums2 均为升序排列
  • 1 <= k <= 1000

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

解题思路

根据题目,我们需要返回最小的k组数据,所以我们只需要维护包含k个最小值的数组就行。

优先队列:
  • 最小优先队列,维护一个最大长度为k的队列list,每个元素为由nums1的第i个元素和nums2的第j个元素组成的数组。即[nums1[i],nums2[j]],并按优先级排列元素。
  • 由题干要求知,和越小的优先级越高,所以排在队列前面。遇到相同的后来的排后面
  • 双重遍历两个数组,得到所有组合
  • list长度小于k时,把元素按以上优先级加入到合适的位置(遍历list找到和更大的元素,插入这个元素前面,如果没有比现在大的,就加到队列最后)
  • list大于k时,还是执行以上插入动作,然后从队列尾部删除一个和最大的组合
  • 遍历时,可以通过一些判断减少遍历数量,比如数组都是升序排列的,所以在遍历nums2时,如果list已经满了,且(nums2[j]+nums1[i])值比list末尾元素和值还大,就可以break跳出循环,直接进入下个循环。

代码

/**
 *
 * @param {number[]nums1
 * @param {number[]nums2
 * @param {numberk
 * @return {number[][]}
 */
var kSmallestPairs = function(nums1, nums2, k) {
    this.list=[];//用来保存前k小组合的队列
    let last=null;//记录队列满了之后最大值
    for(let i=0;i<nums1.length;i++){
        for(let j=0;j<nums2.length;j++){
            // 如果当前的i+j已经大于队列的最大值就没必要继续遍历了
            if(last&&this.list.length>=k&&(nums2[j]+nums1[i])>last){
                break;
              }  
            //遍历所有结果,执行添加操作
            add([nums1[i],nums2[j]],this.list,k);
            //添加过后超出k了,就删除最后一个,最后一个最大
            if(this.list.length>k){
                this.list.pop();
                last=this.list[this.list.length-1][0]+this.list[this.list.length-1][1]
            
            }
        }
  // 如果当前的i+j已经大于队列的最大值就没必要继续遍历了
        if(last&&this.list.length>=k&&(nums1[i]+nums2[0])>last){
            break;
        }  
    }
    return this.list;

};
/**添加元素
 */
var add=function(item,list,k){
    if(list.length===0){
        list.push(item);
        return;
    }
    //如果这个元素比栈尾大就直接淘汰(元素满的时候)
     if(list.length>=k&&compare(item,list[list.length-1])) return;
    //按组合元素之和从小到大的优先级入队
        for(let i=0;i<list.length;i++){
            if(compare(list[i],item)){
                //找到比自己大的元素,就排他前面
                list.splice(i,0,item);
                 return;  
            }
          
        }
          //遍历结束还没找到比大的就呆在末尾
            list.push(item);

}
/**比较两个集合元素相加大小,前大于后返回true
 */
function compare(item1,item2){
    return (item1[0]+item1[1])> (item2[0]+item2[1])
}