leetcode 632. Smallest Range

题目描述

You have k lists of sorted integers in ascending order. Find the smallest range that includes at least one number from each of the k lists.

We define the range [a,b] is smaller than range [c,d] if b-a < d-c or a < c if b-a == d-c.

Example 1:

Input:[[4,10,15,24,26], [0,9,12,20], [5,18,22,30]]
Output: [20,24]
Explanation: 
List 1: [4, 10, 15, 24,26], 24 is in range [20,24].
List 2: [0, 9, 12, 20], 20 is in range [20,24].
List 3: [5, 18, 22, 30], 22 is in range [20,24].

Note:

  1. The given list may contain duplicates, so ascending order means >= here.
  2. 1 <= k <= 3500
  3. -105 <= value of elements <= 105.
  4. For Java users, please note that the input type has been changed to List>. And after you reset the code template, you'll see this point.

思路一:

这道题给了我们一些数组,都是排好序的,让我们求一个最小的范围,使得这个范围内至少会包括每个数组中的一个数字。虽然每个数组都是有序的,但是考虑到他们之间的数字差距可能很大,所以我们最好还是合并成一个数组统一处理比较好,但是合并成一个大数组还需要保留其原属数组的序号,所以我们大数组中存pair对,同时保存数字和原数组的序号。

然后我们重新按照数字大小进行排序,这样我们的问题实际上就转换成了求一个最小窗口,使其能够同时包括所有数组中的至少一个数字。这不就变成了那道https://leetcode.com/problems/minimum-window-substring/。

step1:用两个指针left和right来确定滑动窗口的范围,还要用一个哈希表来建立每个数组与其数组中数字出现的个数之间的映射,变量cnt表示当前窗口中的数字覆盖了几个数组,diff为窗口的大小。

step2:让right向右滑动,然后判断如果right指向的数字所在数组没有被覆盖到,cnt自增1,然后哈希表中对应的数组出现次数自增1,然后循环判断如果cnt此时为k(数组的个数)且left不大于right,那么我们用当前窗口的范围来更新结果.

step3: 然后此时我们想缩小窗口,通过将left向右移,移动之前需要减小哈希表中的映射值,因为我们去除了数字,如果此时映射值为0了,说明我们有个数组无法覆盖到了,cnt就要自减1。

python实现:

https://blog.csdn.net/fuxuemingzhu/article/details/82932656

import collections
class Solution:
    def smallestRange(self, nums):
        v=list()
        #将三个list中的数据存入一个大的list中
        for i in range(len(nums)):
            for num in nums[i]:
                v.append((num,i))
        v.sort()#大list统计v中每个value出现的次数
        l,r,n=0,0,len(v) #左右指针和v中所有的元素数
        d=collections.defaultdict(int)  #用defaultdict
        k=len(nums) #排序list的个数
        cnt=0
        res=[0,0]
        diff=float('inf')
        while r

 

java实现:

import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;

class Ng {
    int num;
    int group;
    public Ng(int n, int g) {
        this.num = n;
        this.group = g;
    }
}

class smallestRange632 {
    public int[] smallestRange(List> nums) {
        List list = new ArrayList<>();
        for (int g = 0; g < nums.size(); g++) {
            for (Integer i: nums.get(g)) {
                list.add(new Ng(i, g));
            }
        }
        Collections.sort(list, (a, b)->a.num-b.num);
        int start = 0, end = list.size();//左右指针,负责窗口滑动
        int count = 0;
        int l = 0, r = 0;
        int[] groups = new int[nums.size()];
        while (l < list.size()) {
            if (count < nums.size() && r < list.size()) {
                Ng cur = list.get(r);
                groups[cur.group]++;
                if (groups[cur.group] == 1) count++;
                r++;
            } else {
                Ng cur = list.get(l);
                groups[cur.group]--;
                if (groups[cur.group] == 0) count--;
                l++;
            }
            if (count == nums.size()) {
                if (list.get(r-1).num-list.get(l).num < list.get(end-1).num-list.get(start).num) {
                    start = l;
                    end = r;
                }
            }
        }
        
        return new int[]{list.get(start).num, list.get(end-1).num};
    }


	public static void main(String[] args) {
		smallestRange632 sr=new smallestRange632();
		List list1=new ArrayList<>(Arrays.asList(4,10,15,24,26));
		List list2=new ArrayList<>(Arrays.asList(0, 9, 12, 20));
		List list3=new ArrayList<>(Arrays.asList(5, 18, 22, 30));
		List> nums=new ArrayList<>();
		nums.add(list1);
		nums.add(list2);
		nums.add(list3);
		System.out.println(Arrays.toString(sr.smallestRange(nums)));

	}

}

 

思路二、


初始化大小为k的最小堆,k个数字是每个数组中的最小值,设置变量maxValue记录k个数字中最大值,删除堆顶元素,将原堆顶元素对应的数组中下一个值加入到堆中,调整堆,并且记录当前区间范围(maxValue - minValue),重复执行直到某个数组所有值都被删除。

 

比如.
List 1: [4, 10, 15, 24, 26]
List 2: [0, 9, 12, 20]
List 3: [5, 18, 22, 30]

 

最小堆大小为3. 从三个数组中取最小值
Heap [0, 4, 5] maxValue 5
Range - 6

 

删除0 ,加入9
Heap [4, 9, 5] maxValue 9
Range - 6

 

删除4 ,加入10
Heap [5, 9, 10] maxValue 10
Range - 6

 

重复执行,最终得到结果

实现:

当做merging k sorted array来做,建立一个heap并按照每个数组最小元素排序,
每次pop出最小的的元素并把数组中下一个元素加回到heap里

public int[] smallestRange3(List> a) {
        PriorityQueue q = new PriorityQueue<>(Comparator.comparingInt(o -> a.get(o[0]).get(o[1])));
        int max = Integer.MIN_VALUE, start = 0, end = Integer.MAX_VALUE;
        for (int i = 0; i < a.size(); i++) {
            q.offer(new int[]{i, 0});
            max = Math.max(max, a.get(i).get(0));
        }
        while (q.size() == a.size()) {
            int e[] = q.poll(), row = e[0], col = e[1];
            if (end - start > max - a.get(row).get(col)) {
                start = a.get(row).get(col);
                end = max;
            }
            if (col + 1 < a.get(row).size()) {
                q.offer(new int[]{row, col + 1});
                max = Math.max(max, a.get(row).get(col + 1));
            }
        }
        return new int[]{start, end};
    }

 

参考:

http://www.cnblogs.com/grandyang/p/7200016.html

http://www.cnblogs.com/kira2will/p/4019588.html

 

你可能感兴趣的:(leetcode)