[算法]最小的K個數和數據流中的中位數

1. 最小的K個數

題目描述

輸入n個整數,找出其中最小的K個數。例如輸入4,5,1,6,2,7,3,8這8個數字,則最小的4個數字是1,2,3,4。html

思路

Java 中的PriorityQueue是一個基於優先級堆的無界優先級隊列。優先級隊列的元素按照其天然順序進行排序,或者根據構造隊列時提供的 Comparator 進行排序,具體取決於所使用的構造方法。優先級隊列不容許使用 null 元素。依靠天然順序的優先級隊列還不容許插入不可比較的對象(這樣作可能致使 ClassCastException)。java

此隊列的頭是按指定排序方式肯定的最小元素。若是多個元素都是最小值,則頭是其中一個元素——選擇方法是任意的。隊列獲取操做 poll、 remove、peek 和 element 訪問處於隊列頭的元素。ide

關於PriorityQueue的更多介紹能夠查看:https://blog.csdn.net/x_i_y_u_e/article/details/46381481ui

關於採用PriorityQueue實現最大堆和最小堆,能夠參考:http://www.cnblogs.com/yongh/p/9945539.htmlspa

https://www.cnblogs.com/Elliott-Su-Faith-change-our-life/p/7472265.html.net

選擇最小的k個數能夠用冒泡排序,複雜度爲O(n*k),有點高。最經典的方法是使用最大堆,每次取數與堆頂的元素進行比較,若是堆頂元素大,則刪除堆頂元素,並添加這個新數到堆中。code

Java沒有堆的實現,現場寫也來不及,有的文獻說用TreeSet,好比劍指offer,可是TreeSet是一個set,相同的數只能存一個,相比之下,Java中的PriorityQueue卻是一個不錯的選擇。htm

代碼

經過PriorityQueue寫法:對象

import java.util.*;
public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {
        if (input == null || k <= 0 || k > input.length) {
            return new ArrayList<Integer>();
        }
        Queue<Integer> queue = new PriorityQueue<>(k, new Comparator<Integer>() {
            //降序
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });
        for (int i = 0; i < input.length; i++) {
            if(queue.size() == k){
                if(queue.peek() > input[i]){
                    queue.poll();
                    queue.add(input[i]);
                }
            }else{
                queue.add(input[i]);
            }
        }
        ArrayList<Integer> list = new ArrayList<>(queue);
        return list;
    }
}

本身實現大頂堆寫法:blog

import java.util.*;
public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList<Integer> list = new ArrayList<>();
        if (input == null || k <= 0 || k > input.length) {
            return list;
        }
        int[] kArray = Arrays.copyOfRange(input,0,k);
        // 建立大根堆
        buildHeap(kArray);

        for(int i = k; i < input.length; i++) {
            if(input[i] < kArray[0]) {
                kArray[0] = input[i];
                maxHeap(kArray, 0);
            }
        }

        for (int i = kArray.length - 1; i >= 0; i--) {
            list.add(kArray[i]);
        }

        return list;
    }

    public void buildHeap(int[] input) {
        for (int i = input.length/2 - 1; i >= 0; i--) {
            maxHeap(input,i);
        }
    }

    private void maxHeap(int[] array,int i) {
        int left=2*i+1;
        int right=left+1;
        int largest=0;

        if(left < array.length && array[left] > array[i])
            largest=left;
        else
            largest=i;

        if(right < array.length && array[right] > array[largest])
            largest = right;

        if(largest != i) {
            int temp = array[i];
            array[i] = array[largest];
            array[largest] = temp;
            maxHeap(array, largest);
        }
    }

}

2. 數據流中的中位數

題目描述

如何獲得一個數據流中的中位數?若是從數據流中讀出奇數個數值,那麼中位數就是全部數值排序以後位於中間的數值。若是從數據流中讀出偶數個數值,那麼中位數就是全部數值排序以後中間兩個數的平均值。咱們使用Insert()方法讀取數據流,使用GetMedian()方法獲取當前讀取數據的中位數。

思路

建立優先級隊列維護大頂堆和小頂堆兩個堆,而且小頂堆的值都大於大頂堆的值,2個堆個數的差值小於等於1,因此當插入個數爲奇數時:大頂堆個數就比小頂堆多1,中位數就是大頂堆堆頭;當插入個數爲偶數時,使大頂堆個數跟小頂堆個數同樣,中位數就是 2個堆堆頭平均數。也可以使用集合類的排序方法。

代碼

import java.util.Comparator;
import java.util.PriorityQueue;
public class Solution {

    PriorityQueue<Integer> minHeap = new PriorityQueue<>();
    PriorityQueue<Integer> maxHeap = new PriorityQueue<>(new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2.compareTo(o1);
        }
    });
    int count = 0;
    public void Insert(Integer num) {
        count++;
        //當數據個數爲奇數時,進入大根堆
        if((count & 1) == 1){
            minHeap.add(num);
            maxHeap.add(minHeap.poll());
        }else{
            maxHeap.add(num);
            minHeap.add(maxHeap.poll());
        }
    }

    public Double GetMedian() {
        if(count == 0){
            return null;
        }
        // 當數據個數是奇數時,中位數就是大根堆的頂點
        if ((count & 1) == 1) {
            return Double.valueOf(maxHeap.peek());
        } else {
            return Double.valueOf((minHeap.peek() + maxHeap.peek())) / 2;
        }
    }


}
相關文章
相關標籤/搜索