理解堆的操作並實現優先級隊列

堆的操作,最主要的就是向調整向下調整,用這個兩個操作來保證堆的性質,最主要的應用有兩個。一個是排序算法——堆排序,另一個就是數據結構——優先級隊列

什麼是堆?

堆其實就是一個完全二叉樹,但是它存儲在數組裏面,利用數組的下標來進行操作結點,數組和堆就是相對應的
在這裏插入圖片描述
性質
最大堆:每個結點都大於或者等於其兩個子結點
最小堆:每個結點都小於或者等於其兩個子結點

向上調整和向下調整

這兩種操作都是爲了維護最大堆或者最小堆的的結構
這裏倘若是最大堆
向上調整:某個結點的值大於它的父結點,這時應該將父結點與這個結點進行交換,而當前就是父結點
向下調整:某個結點的值小於它的子結點,這時將子結點和當前結點進行交換,當前這個結點就是子結點

向上調整

 public void shiftUp(int child){
        int parent = (child-1)>>1;
        //向上調整到根節點,就不能在調整
        while (parent >= 0) {
            //父結點小於子結點
            if (arr[parent] < arr[child]) {
                swap(parent, child);
                //較小的孩子和雙親後,可能會導致上層不滿足小堆的性質
                child = parent;
                parent = (child - 1) >> 1;
            } else {
                return;
            }
        }
    }

向下調整

public void shifDown(int parent){
	//child標記parent子結點中較大的孩子
	int child = parent*2+1;//默認情況下,先標記左孩子,因爲可能不存在右孩子
	int len = arr.length;
	while(child < len){
		//判斷是否存在右孩子並符合條件
		if(child + 1 < len && arr[child] < arr[child+1]){
			child+=1;
		}
		if(arr[parent] < arr[child]){
			swap(child,parent);
			//交換之後可能會導致其子樹不是大堆結構
			parent = child;
			child = parent*2+1;
		}else{
		    return;
		}
	}
}

優先級隊列

這種數據結構,當你插入或者刪除數據的時候,元素會自動的排序,其底層的原理就是堆的操作。
插入:insert方法就是先將插入的元素放在堆底的最後,然後進行向上調整,讓其調整到正確的位置

  boolean insert(int x){
        //將元素插入到數組中
        array[size++] = x;
        //向上調整
        shiftUp(size-1);
        return true;
    }

刪除:poll方法就是先將堆頂元素A和堆底的最後一個元素B進行交換,然後刪除A,向下調整B,直到調整到正確的位置

 int poll(){
        int ret = arr[0];
        swap(0,size-1);
        size--;
        shifDown(0);
        return ret;
 }

一個優先級隊列就實現了,插入和刪除的時間複雜度爲O(logN),N爲堆的中元素的總數

堆排序

堆排序是利用堆這種數據結構而設計的一種排序算法,其實就是一個選擇排序(後面的元素已經是有序的,只需要排前面的元素),其時間複雜度爲O(nlog n)。
升序(構建大堆),降序(構建小堆)
堆排序的思想
1、先將待排序的元素,利用向下調整的操作構建一個大頂堆;
2、再利用堆刪除的思想將堆頂元素和堆底的最後一個元素進行交換,此時末尾就是最大的元素,但是此時堆結構可能已經破壞,從堆頂開始進行向下調整維護堆的結構,再利用堆刪除,如此反覆執行,直到所有的元素都是有序的

第一步:構建大堆
在這裏插入圖片描述
第二步:利用堆刪除思想,將堆頂元素和堆底的最後一個元素進行交換,此時最後一個元素就是最大的,依次將剩下的元素進行堆的重新構建,如此反覆得到的就是一個有序的序列
在這裏插入圖片描述

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