尋找最大的K個數: 終極解決方案

(終於等到你)php

 

寫在前面的一句話:那些年,虐過你的面試題是:_______。面試

 

 

 

今天繼續和你們聊尋找最大的K個數。編程

 

先來熟悉一下問題:數組

有不少個無序的數(咱們這裏假設爲正整數),並且各不相等,怎麼選出最大的K個數。編程語言

 

例如:2,5,7,1,3,9,3,6,7,8,5學習

最大的5個數爲:7,9,6,7,8優化

 

以前咱們給出了四種解法:ui

快速排序和選擇排序,文章詳情:尋找最大的K個數:快排和選擇(一)3d

快排優化和二分搜索,文章詳情:尋找最大的K個數:快排優化和二分搜索(二)排序

 

今天咱們繼續。開始前,先聊一會。

 

最終的那個方案,時間複雜度是線性的,也就是說,效率是六個解法中最高的。可是,它有本身的限制。

首先,全部的N個數都是正整數。

其次,它們的取值範圍都不太大。

 

這裏想說一個什麼問題呢?有所長,必有所短!

 

老天給你打開了一扇窗,必定會給你關閉一扇門。

 

老天給你打開了一扇門,必定會給你打開一扇窗。

 

若是你好,那麼你必定有某些限制;若是你慢,那麼你必定有其它優勢。請相信,只要存在,就是合理。

 

拿世界上最好的編程語言php來講(沒有之一),隨修改,隨運行,那叫一個6666。可是缺點也是不少,至於什麼,請自行百度。

 

道理很簡單,若是一個語言,又快,又好,那麼其它編程語言就能夠去死了。如今其它語言都活的好好的。

 

因此,在什麼狀況下選擇什麼語言,是很是明智的,也是聰明的。不要一味地抱殘守缺!

 

其實我很不理解一件事,就是面試問那麼多高深的技術,進去以後仍是去寫業務代碼。門檻如此之高,進去之初很容易摔跤。

 

先來講說最小堆的事。

 

假如,咱們有一個大小爲K的數組,數組是一個最小堆(數組第一個數爲整個數組最小的值)。請自行百度什麼是最小堆。

 

咱們從數組S中取一個值,這個值和最小堆的第一個數first對比,有兩種結果:

1,Si <= first  那麼咱們能夠不作任何處理,由於咱們找的是最大的K個數

2,Si > first 這時候,咱們須要將Si的值賦值給最小堆的首元素。這時候咱們可能會破壞最小堆的結構,因此須要重建最小堆。重建後,第一個元素一樣是最小的。就這樣一次比較下去。

 

不知道看沒看明白,咱們只須要一次將數組S中的元素與最小堆的首元素相比便可。

代碼以下:

package com.xylx.utils.selectK;

import com.xylx.utils.GsonUtils;

/**
 * Created by  on 17-5-10.
 */
public class MinHeap {
    public static void main(String[] args) {
        int[] arr = {2,6,3,9,4,8};
        buildMinHeap(arr);
        System.out.println(GsonUtils.getJsonFromObject(arr));

        change(5, arr);
        System.out.println(GsonUtils.getJsonFromObject(arr));

        change(6, arr);
        System.out.println(GsonUtils.getJsonFromObject(arr));
    }

    /**
     * 都建構建最小堆
     * @param arr
     */
    public static void buildMinHeap(int[] arr) {
        for (int i=0; i<arr.length; i++) {
            handleMinHeap(arr, arr.length, i); //從前向後構建最小堆
        }
    }

    /**
     * 檢查value是否比arr[0]大,若是大於,則對堆進行重構
     * @param value
     * @param arr
     */
    public static void change(int value, int[] arr) {
        if (value > arr[0]) {
            arr[0] = value;
            handleMinHeap(arr, arr.length, 0);
        }
    }

    /**
     * 最小堆排序
     * @param arr
     */
    public static void minHeapSort(int[] arr) {
        for (int i=arr.length-1; i>0; i--) {
            int temp = arr[0];
            arr[0] = arr[i];
            arr[i] = temp;
            handleMinHeap(arr, i, 0);
        }
    }

    /**
     * 節點轉換
     * @param arr
     * @param heapSize
     * @param current
     */
    public static void handleMinHeap(int[] arr, int heapSize, int current) {
        int leftChildIndex = getLeftChiledIndex(current);
        int rightChildIndex = getRightChildIndex(current);
        int minIndex = current; //找到三個節點中最小的那個
        if (leftChildIndex<heapSize && arr[current] > arr[leftChildIndex]) {
            minIndex = leftChildIndex;
        }
        if (rightChildIndex<heapSize && arr[minIndex] > arr[rightChildIndex]) {
            minIndex = rightChildIndex;
        }
        if (minIndex != current) {
            int tmp = arr[current]; //將最小的值轉移到父節點位置,這樣可能會破壞原先子節點的結果,因此須要遞歸
            arr[current] = arr[minIndex];
            arr[minIndex] = tmp;
            handleMinHeap(arr, heapSize, minIndex);
        }
    }

    /**
     * 獲取左孩子節點位置
     * @param current
     * @return
     */
    public static int getLeftChiledIndex(int current) {
        return (current<<1) + 1;
    }

    /**
     * 獲取右孩子節點位置
     * @param current
     * @return
     */
    public static int getRightChildIndex(int current) {
        return (current<<1) + 2;
    }

    /**
     * 獲取父節點位置
     * @param current
     * @return
     */
    public static int getParentIndex(int current) {
        return (current-1)>>1;
    }
}

 

說完了上面那個,讓咱們看看最終的大boss。

 

下面這個是面試中大部分面試官想要的結果。它的工做流程是:

獲得數組中最大的值MAX,這樣數組中全部的數都在[0, MAX]之間。申請一個數組countArr,大小爲MAX+1。countArr每條記錄是這個下標所表明的數組在數組中出現的次數。這樣,咱們只須要從前向後找到前K個最大的數便可。

 

代碼以下:

package com.xylx.utils.selectK;

/**
 * Created by zongjh on 17-5-8.
 */
public class FinalSelectK {
    public static void main(String[] args) {
        int[] arr = Constans.getLengthArr(100);
        Constans.printArr(arr);
        selectK(arr);
    }

    public static void selectK(int[] arr) {
        int max = getMax(arr);
        System.out.println("max="+max);
        int[] countArr = new int[max+1];
        initArr(countArr);
        for (int i=0; i<arr.length; i++) {
            int index = arr[i];
            countArr[index] = countArr[index] + 1;
        }
        int num = getK(countArr, max);
        System.out.println("num="+num);
        printK(arr, num);
    }

    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+"    ");
        }
    }

    /**
     * 尋找第K大的數
     * @param countArr
     * @param max
     * @return
     */
    public static int getK(int[] countArr, int max) {
        int num = 0;
        int sumCount = 0;
        for (int i=max-1; i>0; i--) {
            sumCount += countArr[i];
            if (sumCount >= Constans.K) {
                num = i;
                break;
            }
        }
        return num;
    }

    public static void initArr(int[] arr) {
        for (int i=0; i<arr.length; i++) {
            arr[i] = 0;
        }
    }

    /**
     * 獲得數組最大值
     * @param arr
     * @return
     */
    public static int getMax(int[] arr) {
        if (arr == null || arr.length < 1) {
            return Integer.MIN_VALUE;
        }
        int max = Integer.MIN_VALUE;
        for (int i=0; i<arr.length; i++) {
            if (arr[i] > max) {
                max = arr[i];
            }
        }
        return max;
    }
}

 

這裏的輔助類,請參考以前的文章,看開頭。

 

終於寫完了,這個尋找最大的K個數。

 

最後一句話:說不定哪天你就用到這個面試題了!

 

其餘技術文章:

DNS域名解析

CDN知道爲何是你?

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

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

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

【花生讀書匯】

相關文章
相關標籤/搜索