编程之美【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; } } }
未完待续!