尋找最大的K個數(二):快排優化和二分搜索

(總要更好的,等你去發現)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來補齊。

 

題外話:

總有最好的,等你去發現。就像這個程序,尋找一下仍是有更好解法的。

 

有時候,坑你的不是別人,而是本身。當咱們解決一個問題以後,每每都會停留下來,不多可以主動想想還有沒有更好的方法。也就是最近比較流行的:呆在本身的溫馨區。

 

本身給本身挖坑每每是最隱祕的。咱們或多或少都在本身挖的坑裏,有的舒服,有的痛苦。

 

舒服的一方就像溫水煮蛙裏那隻還很舒服的青蛙。痛苦的是那隻已經快被煮熟的青蛙。

 

一個問題,一般都會有好多個解法,而咱們老是淺嘗輒止。一份工做一般都有更優的解法,而咱們老是去選擇忽略。

 

因此,沒事別忘了虐一虐本身,問一問本身:

 

是否在本身的溫馨區呆過久了!!!

 

那些年虐咱們的面試題:

尋找最大的K個數:快排和選擇(一)

靠譜的TCP:三次握手和四次揮手

CDN知道爲何是你?

DNS域名解析

喜歡聊技術或者聊觀點,能夠加入公號:【花生讀書匯】

一塊兒:勵志,成長,學習,分享。

【花生讀書匯】

相關文章
相關標籤/搜索