1. 程式人生 > >劍指Offer:面試題30——最小的k個數(java實現)

劍指Offer:面試題30——最小的k個數(java實現)

問題描述:

輸入n個整數,找出其中最小的k個數

思路1:

先排序,再取前k個
時間複雜度O(nlogn)

下面給出快排序的程式碼(基於下面Partition函式的方法)

public void QuickSort(int[] arr, int start, int end){

    if(start == end){
        return;
    }
    int index = Partition(arr, start, end);

    if(index > start){
        QuickSort(arr, start, index
- 1); } if(index < end){ QuickSort(arr, index + 1, end); } }

相應的求最小的k個數的程式碼:

static boolean InvalidInput = false;
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {


        ArrayList<Integer> result = new ArrayList<Integer>();
        if
(input == null || input.length == 0 || k > input.length || k <= 0){ InvalidInput = true; return result; } int start = 0; int end = input.length - 1; QuickSort(input, start, end); for(int i = 0; i < k; i++){ result.add(input[i]); } return
result; }

思路2:(適合海量資料的輸入)

—維護一個大小為k的容器。
—每次從輸入整數讀取一個數,如果容器中的數字少於k個,則直接加入,否則,找出容器中的最大值,與當前數字比較,若大於最大值,則不管,若小於,則替換最大值。
—時間複雜度O(n*log*k)

關於容器的資料結構的選擇:

  1. 陣列:查詢最大值需要O(k)
  2. 最大堆:查詢最大值需要O(1),但需要O(logk)時間完成刪除及插入操作。
  3. 紅黑樹

思路3:基於Partition,會改變原始陣列

  • 首先來看看Partition的原理與具體實現以及結果

Partition:思想是隨機選擇一個數,調整陣列使得比所選數小的數字移動到陣列的左邊,比選擇的數字大的數字大的數移到陣列的右邊。上一文中也用到了Partition的思想,但是並沒有弄懂。下面對程式碼進行了詳細的註釋。

基於上述Partition函式的解析,我們能用它來實現本文中的求最小的k個數

思想:如果基於陣列的第k個數字來調整,使得比第k個數字小的所有數字都位於陣列的左邊,比第k個數字大的數字都位於陣列的右邊。這樣調整後,位於陣列中左邊的k個數字就是最小的k個數字。
缺陷:會改變原始陣列;所得到的k個最小的數字不是有序的。

程式碼:

import java.util.ArrayList;

public class Solution {
    static boolean InvalidInput = false;
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {


        ArrayList<Integer> result = new ArrayList<Integer>();
        if(input == null || input.length == 0 || k > input.length || k <= 0){
            InvalidInput = true;
            return result;
        }


        int start = 0;
        int end = input.length - 1;
        int index = Partition(input, start ,end);

        while(index != k-1){
            if(index > k - 1){
                end = index - 1;
                index = Partition(input, start ,end);
            }else{
                start = index + 1;
                index = Partition(input, start, end);
            }
        }



        for(int i = 0; i < k; i++){
            result.add(input[i]);
        }

        return result;
    }


    public int Partition(int[] arr, int start, int end){
        if(arr == null || arr.length == 0 || start < 0 || end < 0){
            return -1;
        }

        int index = start + (int)(Math.random() * ((end - start) + 1));//隨機選擇一個作為標杆的數字


        //將標杆放在陣列最後一位
        int tmp = arr[index]; arr[index] = arr[end]; arr[end] = tmp;

        int small = start - 1;//small用來儲存從右到左第一個小於標杆的數字的下標
        for(index = start; index < end; index++){
            if(arr[index] < arr[end]){//如果小於標杆
                small++;//更新第一個小的
                if(small != index){//如果當前遍歷的不是第一個小的
                    tmp = arr[index];arr[index] = arr[small];arr[small] = tmp;//將當前遍歷的數字放在第一個小的位置上

                }
            }
        }

        //由於small指示的是從右到左第一個小於標杆的,而此時標杆還放在陣列最後,因此,應該將標杆放在small後面一位。
        small++;
        tmp = arr[small];arr[small] = arr[end]; arr[end] = tmp;

        return small;//返回位置為所選擇的標杆最後的位置


    }
}