从N个数中选最小的K个数

时间:2022-05-21 23:01:00

 

 

 

声明:题目来自: http://blog.csdn.net/v_JULY_v/archive/2010/11/17/6015165.aspx  JULY整理了100道微软等公司的面试题目,我想先不看答案:http://blog.csdn.net/v_JULY_v/archive/2011/01/10/6126406.aspx 自己先做一遍。

 

题目:

5.查找最小的k个元素

题目:输入n个整数,输出其中最小的k个。

例如输入123456788个数字,则最小的4个数字为1234

 

思路:

 

最简单的想法就是在内部分配一个K大小的数组,用于存储最小的K个数,这K个数从大到小排序 array[0..K-1],那么每次读入一个新的数 elem, 我们比较elem 和 array[0], 如果elem>array[0], 则elem肯定不是最小的K个数的候选,否则(当elem<array[0]),我们找到第一个位置P, 使得array[P-1]>elem>array[P], 然后: array[i]=array[i+1] for all i =(0..P-2);  array[P-1] = elem.

 

但是这样并不好,如果给定的数组是从大到小排好顺序的,那么每次我们都要遍历整个内部数组,其时间复杂度是O(N2).

 

其实我们可以把题目重新理解为:

我需要一个数据结构,

条件1) 能够在O(1)时间判断一个新的数字是不是已有的K个数字中最大的

条件2 )  如果不是现有的K个数中最大的话,我希望在最短时间内,丢弃现有K个数中的最大的那个数,放入新的数,并且最适当调整,使调整后的数据结构仍然满足 条件1).

 

结果就是 最大堆 (Max Binary Heap). 对于Binary Heap,每次调整,最坏情况时间复杂度是 log(k)。 前K个元素不许要调整(因为我还没有开始建立堆结构),剩下N-K个元素,最坏情况为 (N-K)log(K)。

 

代码:

 

/*
 * 题目:输入n个整数,输出其中最小的k个。
   例如输入1,2,3,4,5,6,7和8这8个数字,则最小的4个数字为1,2,3和4。
 */

public class MinNPicker {
   
    private int[] m_binaryHeap;
    private int m_numberChecked; //indicate how many elements have been checked
   
    public MinNPicker(int size){
       
        m_numberChecked = 0;
        m_binaryHeap = new int[size];
    }
   
    public void print(){
        for(int i: m_binaryHeap){
            System.out.println(i);
        }
    }
   
    public void add(int[] elems){
        for(int i: elems){
            add(i);
        }
    }
   
    public void reset(){
        m_numberChecked = 0;
       
    }
   
    public void add(int elem){
       
        if(m_numberChecked < m_binaryHeap.length){
            m_binaryHeap[m_numberChecked++] = elem;
           
            if(m_numberChecked == m_binaryHeap.length){
                //now let's construct the binary heap
                //for a binary heap of N size, N/2 to N elements are leaf nodes(leaf nodes are already heap)
           
                int smallestLeaf = m_binaryHeap.length/2;
               
                for(int i=smallestLeaf-1; i>=0; i--){
                    maxHeapify(m_binaryHeap, i);
                }
            }
            return;
        }
       
        if(elem < m_binaryHeap[0]){  //0 is the root, also the max element
           
            m_binaryHeap[0] = elem;
            maxHeapify(m_binaryHeap,0);
        }
    }
   
    private void maxHeapify(int[] array, int index){
        //we use a max heap to contain only N elements, everytime we're feed a new element
        //if new elem > max elem within heap, we replace that ex-max elem and redo max-heapify
       
        //our internal array to hold the heap is 0-based, so left-child(i)=2i+1, right-child(i)=2i+2
        //maxHeapipy assume that both left child and right child of i are already heap
       
        int largest = index;
       
        int size  = array.length;
        int left  = index*2 + 1;
        int right = index*2 + 2;
       
        if(left < size && array[index] < array[left]){  //! make sure left < size, otherwise exception
            //left child is bigger
            largest = left;
        }
       
        //now let's compare the right child and the largest
        if(right < size && array[largest] < array[right]){
            largest = right;
        }
       
        //only swap when largest != index
        if(largest != index){
            int tmp = array[index];
            array[index] = array[largest];
            array[largest] = tmp;
           
            //because we swap the content between index and largest, it's not sure if the original
            //left child/right child tree are still hold heap property
            //!!don't forget to recursive call maxHeapify itself
            maxHeapify(array,largest);
        }
       
       
    }
   
    public static void main(String[] args){
       
        int[] givenElems = {8,7,6,5,4,3,2,1};
       
        MinNPicker picker = new MinNPicker(4);
        picker.add(givenElems);
        picker.print();
       
        System.out.println("-------");
        int[] givenElems2 = {1,2,3,4,5,6,7,8};
        picker.reset();
        picker.add(givenElems2);
        picker.print();
       
       
       
    }
}