(總要更好的,等你去發現)java
若是對技術不感興趣的,能夠直接跳轉到最後的題外話。寫了一點點,支離破碎。也是從這個程序想到的一點點。面試
今天繼續和你們聊尋找最大的K個數。數組
先來熟悉一下問題:學習
有不少個無序的數(咱們這裏假設爲正整數),並且各不相等,怎麼選出最大的K個數。優化
例如:2,5,7,1,3,9,3,6,7,8,5ui
最大的5個數爲:7,9,6,7,8.net
昨天咱們給出了兩個解法:3d
快速排序和選擇排序,文章詳情:尋找最大的K個數:快排和選擇(一)排序
今天咱們繼續。get
回想一下快速排序,每次作完一次快速排序,數組S都會被分紅兩部分Sa和Sb。Sb的每個數都大於Sa的每個數。這時候會出現兩種狀況:
第一:Sb.length >= K,這時候咱們只須要關心Sb數組便可,由於前K個最大的數都在Sb中。
第二:Sb.length < K,這時候前K個最大的數爲Sb加上Sa數組中前K-Sb.length個數。
下面這段代碼,是在前面快速排序的基礎上修改的。主要是一次快速排序後比較K和
Sb數組的長度。
具體代碼以下:
package com.xylx.utils.selectK;
/**
* 優化快速排序,查找最大的K個輸
*/
public class OptQuickSortSelectK {
public static void main(String[] args) {
int[] arr = Constans.getLengthArr(100);
System.out.println("排序前:");
Constans.printArr(arr);
optQuickSort(arr, 0, arr.length-1, Constans.K);
System.out.println("排序後:");
Constans.printArr(arr);
System.out.println("排序是否正確: "+Constans.isOk(arr));
Constans.selectK(arr);
}
public static void optQuickSort(int[] arr, int start, int end, int k) {
int left = start;
int right = end;
int key = arr[left];
while (left < right) {
while (left<right && arr[right] > key) {
right --;
}
if (left < right) {
int tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
left ++;
}
while (left<right && arr[left] < key) {
left ++;
}
if (left < right) {
int tmp = arr[right];
arr[right] = arr[left];
arr[left] = tmp;
right--;
}
}
if (start < left-1) {
int rightLength = end - right + 1;
System.out.println("rightLength="+rightLength+" k="+k);
if (rightLength < k) { //右邊數組小於須要的K數
optQuickSort(arr, start, left-1, k-rightLength); //須要左邊數組k-rightLength個最大的數
}
}
if (right + 1 < end) {
int rightLength = end - right + 1;
if (rightLength > k) {
optQuickSort(arr, right+1, end, k);
}
}
}
}
上面這段代碼能大大下降排序的次數。
============分割線============
尋找前K個最大數,也就是選擇第K大的數。
若是數組S的中最大值爲max,最小值爲min。那麼第K大的值Vk必定知足下面的關係:
min<=Vk<=max。
咱們從中間值開始找起,mid=(min+max)/2。查找數組S中全部>=mid的數的個數total。這時候也會出現兩種狀況:
第一:total>=K, 證實查找出來的數比K多,咱們須要增長mid的值,也就是min=mid。
第二:total<K,證實查找出來的數比K少,咱們須要減小max的值,也就是max=mid。
這樣不斷循環,直到max-min <= 1。
代碼以下:
package com.xylx.utils.selectK;
import java.util.ArrayList;
import java.util.List;
/**
*/
public class BinSearchSelectK {
public static void main(String[] args) {
int[] arr = Constans.getLengthArr(100);
Constans.printArr(arr);
selectK(arr);
}
public static void selectK(int[] arr) {
List<Integer> result = getMaxMin(arr);
int max = result.get(0);
int min = result.get(1);
while (max - min > 1) {
int mid = (max + min)/2;
int total = getGTTotal(arr, mid);
if (total >= Constans.K) {
min = mid;
} else {
max = mid;
}
}
System.out.println("min="+min+" max="+max);
printK(arr, min);
}
private static void printK(int[] arr, int min) {
int index = 0;
System.out.println("最大的K個數:");
for (int i=0; i<arr.length; i++) {
if (arr[i] > min) {
System.out.print(arr[i]+" ");
index++;
}
}
for (int i=0; i<(Constans.K-index); i++) {
System.out.print(min+" ");
}
}
/**
* 查找數組中大於等於mid值大的數的個數
* @param arr
* @param mid
* @return
*/
public static int getGTTotal(int[] arr, int mid) {
int total = 0;
for (int i=0; i<arr.length; i++) {
if (arr[i] >= mid) {
total++;
}
}
return total;
}
/**
* 尋找數組中最大值和最小值
* @param arr
* @return 0:最大 1:最小
*/
public static List<Integer> getMaxMin(int[] arr) {
List<Integer> result = new ArrayList<Integer>();
if (arr == null || arr.length < 1) {
return result;
}
int max = Integer.MIN_VALUE;
int min = Integer.MAX_VALUE;
for (int i=0; i<arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
if (arr[i] < min) {
min = arr[i];
}
}
result.add(max);
result.add(min);
return result;
}
}
當循環結束以後,你會獲得一個區間min和max。min就是查找的第K大的數。這裏須要注意一下,min可能會有多個,不是全部的min都符合要求,因此咱們應該先查找比min大的數,查找到的數不夠K個,就用min來補齊。
題外話:
總有最好的,等你去發現。就像這個程序,尋找一下仍是有更好解法的。
有時候,坑你的不是別人,而是本身。當咱們解決一個問題以後,每每都會停留下來,不多可以主動想想還有沒有更好的方法。也就是最近比較流行的:呆在本身的溫馨區。
本身給本身挖坑每每是最隱祕的。咱們或多或少都在本身挖的坑裏,有的舒服,有的痛苦。
舒服的一方就像溫水煮蛙裏那隻還很舒服的青蛙。痛苦的是那隻已經快被煮熟的青蛙。
一個問題,一般都會有好多個解法,而咱們老是淺嘗輒止。一份工做一般都有更優的解法,而咱們老是去選擇忽略。
因此,沒事別忘了虐一虐本身,問一問本身:
是否在本身的溫馨區呆過久了!!!
那些年虐咱們的面試題:
喜歡聊技術或者聊觀點,能夠加入公號:【花生讀書匯】
一塊兒:勵志,成長,學習,分享。