堆排序的Java實現

2021-07-22 05:34:55 字數 3096 閱讀 3179

由於這裡是堆排序,因此還是說明下堆排序的原理:

一般堆是用陣列來進行模擬的,arr[k]的子節點為arr[k * 2+1]和 arr[k * 2+2] .

堆排序就是解決以下兩個問題:

1、如何將乙個待排序陣列(含有n個元素)建成堆?

2、輸出堆頂元素之後,怎麼樣調整剩餘的n-1個元素使其變為乙個新堆然後進行這一步。

以最小堆為例

至於第乙個問題:就是將二叉堆的非葉子節點(對應的陣列的arr[(len/2)-1]~arr[0])都依次向後沉。」向後沉」的意思如果父節點的值大於兩個子節點中的最小值就進行交換然後繼續進行這樣的判斷。

至於第二個問題:堆化陣列後,第一次將a[0]與a[n - 1]交換,再對a[0…n-2]重新恢復堆(這裡的恢復堆就是將a[0]向後沉即可)。第二次將a[0]與a[n – 2]交換,再對a[0…n - 3]重新恢復堆,重複這樣的操作直到a[0]與a[1]交換。由於每次都是將最小的資料併入到後面的有序區間,故操作完成後整個陣列就有序了。

注意使用最小堆排序後是遞減陣列,要得到遞增陣列,可以使用最大堆。

由於每次重新恢復堆的時間複雜度為o(logn),共n - 1次重新恢復堆操作,再加上前面建立堆時n / 2次向下調整,每次調整時間複雜度也為o(logn)。二次操作時間相加還是o(n * logn)。故堆排序的時間複雜度為o(n * logn)。

按照上面兩步的思路實現的**如下:

public

static

void heapsort(int arr, int len)

for(int i=(len/2)-1;i>=0;i--)

// system.out.println("陣列調整之後為:");

// print(arr,len);

//第二步,開始排序

int begin = 0;

int end = len-1;

while(begin

}

其中,二叉堆中」下沉」 某個元素的實現**如下:

//函式功能:將arr[index]元素向後沉。

private

static

void siftdown(int arr, int

index, int len)

int half = len/2;

int val = arr[index];

while(index

int leftchild = (index

<<1) + 1;

int rightchild = leftchild + 1;

int minindex = leftchild;

if(rightchild//將arr[minindex]與 val進行比較

if(arr[minindex]>=val)

//交換

arr[index] = arr[minindex];

index = minindex;

}arr[index] = val;

}

上面還涉及到乙個交換陣列中兩個位置的元素的方法如下:

//函式功能:完成陣列位置begin/end的元素的交換

private static void swap(int arr, int

begin, int

end)

int temp = arr[begin];

arr[begin] = arr[end];

arr[end] = temp;

}

測試**如下:

public

static

void

main(string args)

// int len = 10;

// int arr = ;

heapsort(arr,len);

system.out.println("陣列排序後的結果為:");

print(arr,len);}}

//函式功能:列印輸出

private

static

void

print(int arr,int len)

for(int i=0;iout.print(arr[i]+" ");

}}

以上就是關於堆排序的乙個實現。還是比較簡單哈。

上面是採用的是最小堆完成的堆排序,是逆序。下面是採用最大堆的形式完成的堆排序。

與最小堆相比,改動的地方就是「下沉」函式siftdown。在最小堆中,是父節點大於兩個子節點的最大值就下層。而在最大堆中,是父節點小於兩個子節點的最小值就下層。就這一點區別。

siftdown函式**如下:

//函式功能:將arr[index]元素向後沉。

private

static

void siftdown(int arr, int

index, int len)

int half = len/2;

int val = arr[index];

while(index

int leftchild = (index

<<1) + 1;

int rightchild = leftchild + 1;

//現在是以最大堆為例,則是小的往下沉

int maxindex = leftchild;

if(rightchildarr[leftchild])

//將arr[maxindex]與 val進行比較

if(arr[maxindex]<=val)

//交換

arr[index] = arr[maxindex];

index = maxindex;

}arr[index] = val;

}

自己以前一直沒有研究過堆排序,原因在於感覺挺複雜的,這兩天看了priorityqueue和priorityblockingqueue這兩個類的原始碼之後,發現,原來二叉堆的「下沉」和「上浮」還是挺有意思的,於是自己也就一下子就理解了。因此就有了這篇關於堆排序的實現。

關於堆和堆排序的介紹還可以參考這篇博文:

java實現堆排序

許多應用要用堆,比如,優先佇列,即佇列的擴充,佇列的每個元素增加乙個稱為優先順序的字段,優先佇列可以是線性結構的,但要有較高的效率,應把它組織成堆結構。元素入隊是,相當於執行堆的插入演算法 但在隊頭每次刪除的總是隊頂具有最大優先順序的元素,因此可以把它稱為最大優先順序佇列。再比如,堆排序是選擇排序中...

堆排序java實現

公升序用大根堆,降序用小根堆 調整堆 從根節點開始向下調整堆 先建立初始堆,從n 2 1 第乙個非葉子節點 節點開始,將之看做根節點,向下調整堆,到根節點結束 迴圈i from n 1 to 0,每次取出堆的最頂層根節點,即陣列下標為0,然後與節點i交換,這樣對於大根堆而言,最大值總是在後面,再從根...

Java 實現堆排序

堆排序主要是利用堆在根節點上的特性,既在大頂堆的情況下,堆的第乙個元素是堆當中最大的元素,而在小頂堆的情況下,堆的第乙個元素是最小的節點。所以在堆排序的過程,主要的操作就是對每次取出堆頂的元素之後 在這裡使用大頂堆 我們就選出了堆中的最大元素,然後對堆剩下的元素進行再次進行大頂堆的調正,得到乙個新的...