编程之美【02】

2、题目:从很多无序的数中(姑且假定各不相等),选出其中最大的K个数。

解法一、

思路:最直接的办法是对无序数按大小进行排序,将无序数有序化后,依次取出前K个数值即可。如排序算法为快排,时间复杂度为O(N * log N + K)

解法二、

思路:构造K长度的有序数组,并依次遍历每个无序数,若遍历无序数的数值大于该有序数组重的最小数值,则将该无序数数值替换掉有序数组最小数值,并重新调整有序数组的有序性。该算法时间复杂度为O(K * N)

计算:

1、对无序数组的前K个数进行有序化(简单采用插入排序算法);

2、遍历无序数组的第K个数至最后一个数,若遍历数值大于第K – 1位数值,则需将该数插入至前K个的有序数组中(以保证前K个有序数为遍历至当前位置的最大K个数);

代码:

    private static void insertNumToSortedArray(int[] sortedArr, int begin, int end, int num) {          
        int key = sortedArr[end], inserIndex;
        for(inserIndex = end - 1; inserIndex >= 0 && key > sortedArr[inserIndex]; --inserIndex) {
            sortedArr[inserIndex + 1] = sortedArr[inserIndex];
        }
        
        sortedArr[inserIndex + 1] = key;
    }
    public void topK(int []arr, int k) throws Exception {
        for(int i = 1; i < k; i++) {
            insertNumToSortedArray(arr, 0, i - 1, arr[i]);
        }
        
        for(int i = k; i < arr.length; i++) {
            insertNumToSortedArray(arr, 0, k, arr[i]);
        }

 

解法三、

思路:解法三为解法二的改进版本,通过最小堆来替换之前的有序数组,堆的排序算法使得Top K的计算复杂度可降为O(log K * N)。

计算:

1、通过对无序数组的前K个数构造最小堆;

2、遍历无序数组的第K个数至最后一个数,若遍历数值大于最小堆堆顶数值,则需将最小堆堆顶数值替换为该遍历数值,并通过“堆下沉”使得该堆重新调整为最小堆;

代码:

    private static void siftUp(int[] arr, int currentPos) {
        int tmpNum;
        while(currentPos > 0 && arr[currentPos] < arr[(currentPos - 1) / 2]) {
            tmpNum = arr[currentPos];
            arr[currentPos] = arr[(currentPos - 1) / 2];
            arr[(currentPos - 1) / 2] = tmpNum;
            
            currentPos = (currentPos - 1) / 2;
        }
    }
    private static void siftDown(int[] arr, int currentPos, int endPos) {
        int minChild = currentPos * 2 + 1;
        
        int tmpNum;
        while(minChild <= endPos) {
            if(minChild + 1 <= endPos && arr[minChild] > arr[minChild + 1]) {
                minChild = minChild + 1;
            }
            
            if(arr[minChild] < arr[currentPos]) {
                tmpNum = arr[currentPos];
                arr[currentPos] = arr[minChild];
                arr[minChild] = tmpNum;
                
                currentPos = minChild;
                minChild = currentPos * 2 + 1;
            } else {
                break;
            }

        }
    }
    public void topK2(int[] arr, int k) throws Exception {
        for(int i = 0; i < k; i++) {
            siftUp(arr, i);
        }
        
        for(int i = k; i < arr.length; i++) {
            if(arr[0] < arr[i]) {
                arr[0] = arr[i];
                siftDown(arr, 0, k - 1);
            }
        }
    }

 

解法四、

思路:采用桶排序的思路,通过将无序数的数值映射到位置空间(桶),然后取出位置空间中最大前K个空间位置即可,该题目比较特殊,因此可直接采用bitmap作为位置空间对无序数进行空间存储。该算法时间复杂度为O(N + K),空间复杂度为O(M),M为无序数中的最大值。

计算:

1、构造位置空间bitmap,并将无序数的数值映射到位置空间bitmap中;

2、取未知空间bitmap中最大K个位置所对应的空间位置,即为无序数中最大前K个数;

代码:

    public void topK3(int []arr, int k) throws Exception {
        BitSet bs = new BitSet();
        for(int i = 0 ; i < arr.length; i++) {
            bs.set(arr[i]);
        }
        
        for(int i = bs.size(), count = 0; i >= 0 && count < k; i--) {
            if(bs.get(i)) {
                arr[count++] = i;
            }
        }
    }

未完待续!

posted @ 2013-01-05 22:57  @小小鸟@  阅读(1477)  评论(2编辑  收藏  举报