優先級隊列(堆)總結

基礎概念

  1. 堆邏輯上是一棵完全二叉樹,將二叉樹用層序遍歷保存在數組中。
  2. 滿足任意結點的值都大於其子樹中結點的值,叫做大堆,或者大根堆,或者最大堆
  3. 滿足任意結點的值都小於其子樹中結點的值,則是小堆,或者小根堆,或者最小堆
    在這裏插入圖片描述
  4. 堆的基本作用是,快速找集合中的最值
下標關係

已知雙親(parent)的下標,則:
左孩子(left)下標 = 2 * parent + 1;
右孩子(right)下標 = 2 * parent + 2;
已知孩子(不區分左右)(child)下標,則:
雙親(parent)下標 = (child - 1) / 2;

向下調整成小堆

根據下標找到index的左孩子,堆如果沒有左孩子一定沒有右孩子,右孩子等於左孩子下標+1,如果超過size,說明沒有右孩子。
找出左右孩子中的最小的一個,然後和array[index]比較,比array[index]小就交換,index就變爲了min,左孩子下標=2*index+1也發生了改變,然後繼續while循環,直到左孩子不存在也就是left>=size的時候。如果比array[index]大,則不用交換

 private static void swap(int[] array, int index, int min) {
        int k=array[index];
        array[index]=array[min];
        array[min]=k;
    }
 private static void shiftDownSmall(int[]array, int index, int size) {
       int left=2*index+1;
       while(left<size){
           int right=left+1;
           int min=left;
           if(right<size){
               if(array[right]<array[left])
                   min=right;
           }
           if(array[index]>array[min]){
               swap(array,index,min);
               index=min;
               left=2*index+1;
           }else{
               break;
           }
       }
    }

在這裏插入圖片描述

向下調整成大堆

和向下調整成小堆是同理的。只不過比array[index]大才交換。

private static void shiftDownBig(int[] array, int index, int size) {
        int left=2*index+1;
        while(left<size){
            int right=left+1;
            int max=left;
            if(right<size){
                if(array[right]>array[left])
                    max=right;
            }
            if(array[index]<array[max]){
                swap(array,index,max);
                index=max;
                left=2*index+1;
            }else{
                break;
            }
        }
    }
建小堆

建堆首先要從倒數第一個非葉子結點開始向下調整,最後一個葉子結點下標爲size-1,它的父母結點爲(size-2)/2(即倒數第一個非葉子結點)。

 public static void createHeapSmall(int[] a, int s) {
        for(int i=(s-2)/2;i>=0;i--){
            shiftDownSmall(a,i,s);
        }
    }
建大堆

和建小堆同理哦。

public static void createHeapBig(int[] a, int s) {
        for(int i=(s-2)/2;i>=0;i--){
            shiftDownBig(a, i, s);
        }
    }

在這裏插入圖片描述

入隊列
  1. 首先按尾插方式放入數組2. 比較其和其雙親的值的大小,如果雙親的值小,則滿足堆的性質,插入結束。3.否則,交換其和雙親位置的值,重新進行 2、3 步驟4. 直到根結點
public class MyPriorityQueue {
    // 不做擴容考慮
    private int[] array;
    private int size;

    MyPriorityQueue() {
        array = new int[16];
        size = 0;
    }

    public void offer(int element) {
        array[size++] = element;
        shiftUpSmall(array, size - 1);
    }
}
public static void shiftUpSmall(int[] array, int i){
        // 直到 i == 0 之前,一直
        // 先找到 i 的雙親的下標
        // 比較 array[parent] 和 array[i]
        // 如果滿足條件,調整結束
        // 否則,交換,然後 讓 i = parent 繼續
       while(i!=0){
           int p=(i-1)/2;
           if(array[p]<=array[i]){
               break;
           }
           swap(array,p,i);
           i=p;
       }
    }
出隊列

爲了防止破壞堆的結構,刪除時並不是直接將堆頂元素刪除,而是用數組的最後一個元素替換堆頂元素,然後通過向下調整方式重新調整成堆。

public int poll() {
        int element = array[0];
        array[0] = array[--size];
        Heap.shiftDownSmall(array, 0, size);
        return element;
    }
//返回隊首元素
    public int peek() {
        // 不做錯誤處理
        return array[0];
    }

堆排序

升序建大堆,將第一個結點(max)與堆中最後一個葉子結點交換,然後將此葉子結點之前的結點重新進行向下調整成大堆,直到i=size-1。

/堆排序(升序用大堆,降序用小堆)
    public static void heapSort(int[] array){
        createHeapBig(array,array.length);
        for(int i=0;i<array.length-1;i++){
            // 無序 [0, array.length - i)
            // 有序 [array.length - i, array.length)
            swap(array, 0, array.length - i - 1);
            // 無序 [0, array.length - i - 1)
            // 長度 array.length - i - 1
            shiftDownBig(array,0,array.length-i-1);
        }
    }

降序建小堆,同理噠。

import java.util.Arrays;

public class Solution1 {
    //堆排序
    public static void HeapSort(int array[]){
        creatDownSmall(array,array.length);
        for(int i=0;i<array.length-1;i++){
            swap(array,0,array.length-i-1);
            shiftDownSmall(array,0,array.length-i-1);
        }
    }

    private static void shiftDownSmall(int[] array, int index, int size) {
        int left=2*index+1;
        while(left<size){
            int right=left+1;
            int min=left;
            if(right<size){
                if(array[right]<array[left])
                    min=right;
            }
            if(array[index]>array[min]){
                swap(array,index,min);
                        index=min;
                left=2*index+1;
            }else{
                break;
            }
        }
    }

    private static void swap(int[] array, int i, int i1) {
        int k=array[i];
        array[i]=array[i1];
        array[i1]=k;
    }

    private static void creatDownSmall(int[] array, int size) {
        for(int i=(size-2)/2;i>=0;i--){
            shiftDownSmall(array,i,size);
        }
    }

    public static void main(String[] args) {
        int[] a = { 9,5,2,7,3,4,5,2,6,8 };
        HeapSort(a);
        System.out.println(Arrays.toString(a));
    }
}

TOP k問題

在海量數據中,尋找前k大的數。考慮到快排,但是不能解決海量問題,所以只能用堆。建含有k個元素的小堆,用於存儲當前最大的k個元素,接着,從k+1個元素開始掃描,和堆頂元素進行比較,如果被掃描的元素大於堆頂,則替換堆頂元素,並調整堆。時間複雜度爲O(n*log k),空間複雜度爲O (k)。
尋找前k小的數,建大堆。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章