Shortest range covering all lists - PrismoSkills


LeetCode 632 - Smallest Range
Shortest range covering all lists - PrismoSkills
Problem: Given N sorted lists, find smallest range of elements which represent one element from each list.

Solution
  1. We start by creating a min-heap and a max-heap of the first elements of each list.
  2. In a loop,
    1. One element from the min-heap is removed and
    2. Next element from the corresponding list is put into the heap.
  3. At every step in the loop, min and max in the heap are analyzed.
  4. If their difference is lesser than the current minimum, then a new minimum is selected.

While this looks simple enough, note that currently Java does not provide a Double-Ended-Priority-Queue.
So we cannot get the minimum and maximum with just one heap.

This is fixed by creating two heaps - min and max heap.
During removing, minimum element is removed from min-heap but from max-heap, element is removed by reference.

To improve performance, every integer data from the lists is wrapped into an object which also stores the line-number of the list.
With this scheme, it is easy to load the next number from that list whose element is polled from the min-heap.

public class ShortestRangeCoveringAllLists
{

    // Wrapper class to store line-number along with the integer data in the Heap
    public static class DataAndLineNum
    {
        public int data;
        public int lineNum;
    }


    // Ascending Comparator
    public static class DateLineComparator implements Comparator<DataAndLineNum>
    {
        @Override
        public int compare(DataAndLineNum o1, DataAndLineNum o2)
        {
                return o1.data - o2.data;
        }
    }


    // Descending comparator
    public static class DateLineReverseComparator implements Comparator<DataAndLineNum>
    {
        @Override
        public int compare(DataAndLineNum o1, DataAndLineNum o2)
        {
                return o2.data - o1.data;
        }
    }

    public static void main(String[] args)
    {
        // Get some random input data
        int numLists = 5;
        int listSize = 4;
        Object lists[] = getRandomSortedLists (numLists, listSize);
   
        // Create ascending and descending comparators
        DateLineComparator cmp = new DateLineComparator ();
        DateLineReverseComparator revCmp = new DateLineReverseComparator();
   
        // Create min-Heap and max-Heap by using PriorityQueue
        PriorityQueue <DataAndLineNum> minPQ = new PriorityQueue<DataAndLineNum>(numLists, cmp);
        PriorityQueue <DataAndLineNum> maxPQ = new PriorityQueue<DataAndLineNum>(numLists, revCmp);
   
        // Put first element of each list into min-Heap and max-Heap
        // Each element is converted from normal integer to wrapper class DataAndLineNum before inserting
        for (int i=0; i<numLists; i++)
        {
            ArrayList<Integer> lst = (ArrayList<Integer>) lists[i];
       
            DataAndLineNum info = new DataAndLineNum();
            info.data = lst.get(0);
            info.lineNum = i;
       
            minPQ.add(info);
            maxPQ.add(info);
        }
   
   
        // Heaps are initialized with first element of each list.
        // Now, remove one element from min-Heap and remove corresponding element from max-Heap
        // From the removed element, get the line number
        // From the line-number, go directly to the list and take the next element from it.
   
        int minDiff = 0;
   
        while (minPQ.size() > 0)
        {
            if (minPQ.size() == numLists)
            {
                int diff = maxPQ.peek().data - minPQ.peek().data;
                if (minDiff == 0 || diff < minDiff)
                {
                    minDiff = diff;
                    printCurrentPQ (minPQ, minDiff);
                }
            }
           
            DataAndLineNum smallest = minPQ.poll(); // remove smallest from min-Heap
            maxPQ.remove(smallest);                 // remove same element from max-Heap
       
       
            // get next number from the line whose element is removed above
            int lineNum = smallest.lineNum;
            ArrayList<Integer> list = (ArrayList<Integer>) lists[lineNum];
            list.remove(0);
       
            if (list.size() > 0)
            {
                smallest.data = list.get(0); // re-use the wrapper object smallest
                minPQ.add(smallest);
                maxPQ.add(smallest);
            }
        }
    }

    // Helper method to print the priority queue
    static void printCurrentPQ(PriorityQueue<DataAndLineNum> pq, int minDiff)
    {
        System.out.print("Diff = " + minDiff);
        System.out.println();
   
        DataAndLineNum []arr = new DataAndLineNum[pq.size()];
        int i=0;
        while (pq.size() > 0)
        {
            arr[i++] = pq.poll();
            System.out.println(arr[i-1].data + "(Line " + arr[i-1].lineNum + ")");
        }
        for (DataAndLineNum d : arr)
            pq.add(d);
        System.out.println();
    }
There is no need for two priority queues, minPQ is enough.

The maxPQ usage also slows down the solution asymptotically
because maxPQ.remove is linear.
https://github.com/mnorbi/algo-practice/blob/master/prismo/ShortestRangeCoveringAllLists.java
static int[] solve(int[][] mat){
    Comparator<Cell> comp = (c1, c2) -> Integer.compare(mat[c1.row][c1.col], mat[c2.row][c2.col]);
    Cell rangeStart = findRangeStart(mat, comp);
    return createRange(mat, comp, rangeStart);
}

private static Cell findRangeStart(int[][] mat, Comparator<Cell> comp) {
    int ROWS = mat.length;
    int COLS = mat[0].length;
    PriorityQueue<Cell> minPq = new PriorityQueue<>(comp);
    minPq.add(new Cell(0, 0));
    Cell max = minPq.peek();
    for(int i = 1; i < ROWS; ++i){
        Cell cell = new Cell(i, 0);
        minPq.add(cell);
        if (comp.compare(max, cell) < 0){
            max = cell;
        }
    }
    Cell ret = null;
    int widthOpt = Integer.MAX_VALUE;
    while(true){
        Cell min = minPq.remove();
        int width = mat[max.row][max.col]-mat[min.row][min.col];
        if (widthOpt > width){
            widthOpt = width;
            ret = min;
        }
        if (min.col == COLS-1) break;
        Cell cell = new Cell(min.row, min.col +1);
        minPq.add(cell);
        if (comp.compare(max, cell) < 0){
            max = cell;
        }
    }
    return ret;
}

private static int[] createRange(int[][] mat, Comparator<Cell> comp, Cell rangeStart) {
    int ROWS = mat.length;
    int[] ret = new int[ROWS];
    PriorityQueue<Cell> minPq = new PriorityQueue<>(comp);
    for(int i = 0; i < ROWS; ++i){
        Cell cell = new Cell(i, 0);
        minPq.add(cell);
    }

    for(int i = 0;;){
        Cell min = minPq.peek();
        if (min.row == rangeStart.row && min.col == rangeStart.col){
            while(!minPq.isEmpty() ){
                Cell cell = minPq.remove();
                ret[i++] = mat[cell.row][cell.col];
            }
            return ret;
        }
        minPq.remove();
        Cell cell = new Cell(min.row, min.col+1);
        minPq.add(cell);
    }
}
class Cell {
    int row, col;
    Cell(int row, int col){
        this.row = row;
        this.col = col;
    }
}

http://www.fgdsb.com/2015/01/17/smallest-range/
You have k lists of sorted integers. Find the smallest range that includes at least one number from each of the k lists.
For example,
List 1: [4, 10, 15, 24, 26]
List 2: [0, 9, 12, 20]
List 3: [5, 18, 22, 30]
The smallest range here would be [20, 24] as it contains 24 from list 1, 20 from list 2, and 22 from list 3.
G家题,实际上就是k路归并的变种题。维护一个长度为n的min heap(n为数组个数),每次找一个最小的,同时保持记录一个最大的。
比如那个例子:
heap初始化为[0,4,5],min = 0, max = 5,当前最小range为[0,5]
pop掉最小的再push,[4,5,9],min = 4, max = 9,当前最小range还是[0,5]
继续,[5,9,10],min = 5, max = 10,当前最小range还是[0,5]
继续,[9,10,18],min = 9, max = 18,当前最小range还是[0,5]
继续,[10,12,18],min = 10, max = 18,当前最小range还是[0,5]
继续,[12,15,18],min = 12, max = 18,当前最小range还是[0,5]
继续,[15,18,20],min = 15, max = 20,当前最小range还是[0,5]
继续,[18,20,24],min = 18, max = 24,当前最小range还是[0,5]
继续,[20,22,24],min = 20, max = 24,当前最小range变成[20,24]
依次类推,最小范围为[20,24]




pair<int,int> min_range(const vector<vector<int>>& nums) {
// first: array id, second: element id
using _pair = pair<int,int>;
priority_queue<_pair,vector<_pair>,function<bool(_pair&,_pair&)>> heap {
[&](_pair& a, _pair& b) { return nums[a.first][a.second] >= nums[b.first][b.second]; }
};
int cur_max = INT_MIN;
for(auto i = 0; i < nums.size(); ++i) {
if(nums[i].empty()) continue;
heap.emplace(i, 0);
cur_max = std::max(cur_max, nums[i][0]);
}
if(heap.empty()) return {-1, -1};
int begin = nums[heap.top().first][heap.top().second];
int min_len = cur_max - begin;
while (heap.size() == nums.size()) {
auto top = heap.top();
heap.pop();
if(top.second + 1 < nums[top.first].size()) {
int new_begin = nums[heap.top().first][heap.top().second];
int new_num = nums[top.first][top.second + 1];
new_begin = std::min(new_begin, new_num);
cur_max = std::max(cur_max, new_num);
if(cur_max - new_begin < min_len) {
begin = new_begin;
min_len = cur_max - new_begin;
}
heap.emplace(top.first, top.second + 1);
}
}
return {begin, begin + min_len};
}

void findSmallestRange(int arr[][N], int k)
{
    // Create a min heap with k heap nodes. Every heap node
    // has first element of an list
    int range = INT_MAX;
    int min = INT_MAX, max = INT_MIN;
    int start, end;
    MinHeapNode *harr = new MinHeapNode[k];
    for (int i = 0; i < k; i++)
    {
        harr[i].element = arr[i][0]; // Store the first element
        harr[i].i = i; // index of list
        harr[i].j = 1; // Index of next element to be stored
                       // from list
        // store max element
        if (harr[i].element > max)
            max = harr[i].element;
    }
    MinHeap hp(harr, k); // Create the heap
    // Now one by one get the minimum element from min
    // heap and replace it with next element of its list
    while (1)
    {
        // Get the minimum element and store it in output
        MinHeapNode root = hp.getMin();
        // update min
        min = hp.getMin().element;
        // update range
        if (range > max - min + 1)
        {
            range = max - min + 1;
            start = min;
            end = max;
        }
        // Find the next element that will replace current
        // root of heap. The next element belongs to same
        // list as the current root.
        if (root.j < N)
        {
            root.element = arr[root.i][root.j];
            root.j += 1;
            // update max element
            if (root.element > max)
                max = root.element;
        }
        // break if we have reached end of any list
        else break;
        // Replace root with next element of list
        hp.replaceMin(root);
    }
    cout << "The smallest range is " << "["
         << start << " " << end << "]" << endl;;
}

https://gist.github.com/elderdigiuseppe/6224350
http://algorithms.tutorialhorizon.com/shortest-range-in-k-sorted-lists/

Related: Find the smallest subarray covering all values - EPI
Read full article from Shortest range covering all lists - PrismoSkills

Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts