private
int
[]
MergeSort(
int
[]
i_list) {
if
(i_list.length
==
1
)
{
return
i_list;
}
else
{
int
[]
listL =
new
int
[i_list.length
/
2
];
int
[]
listR =
new
int
[i_list.length
- i_list.length /
2
];
int
Center
= i_list.length /
2
;
for
(
int
i
=
0
;
i < Center; i++) {
listL[i]
= i_list[i];
}
for
(
int
i
= Center, j =
0
;
i < i_list.length; i++, j++) {
listR[j]
= i_list[i];
}
int
[]
SortedListL=MergeSort(listL);
int
[]
SortedListR=MergeSort(listR);
int
[]
o_list = MergeTwoList(SortedListL, SortedListR);
return
o_list;
}
}
private
int
[]
MergeTwoList(
int
[]
listL,
int
[]
listR) {
int
i
=
0
,
j =
0
;
int
[]
o_list =
new
int
[listL.length
+ listR.length];
int
foot
=
0
;
while
(i
< listL.length && j < listR.length) {
if
(listL[i]
<= listR[j]) {
o_list[foot]
= listL[i];
i++;
}
else
{
o_list[foot]
= listR[j];
j++;
}
foot++;
}
if
(i
== listL.length) {
while
(j
< listR.length) {
o_list[foot++]
= listR[j++];
}
}
else
{
// j==listR.length
while
(i
< listL.length) {
o_list[foot++]
= listL[i++];
}
}
return
o_list;
}
}
當面試的時候被問到,你對堆有沒有做過一些瞭解??
當時我我反問了一下:您是指堆數據結構還是JVM裏面存儲對象等信息的堆時??
當時感覺自己還挺高端的,居然可以反問,但是當面試官說是堆排序的時候我就傻眼了,
不會,只記得個大概,於是今天花了點時候把堆排序搞懂了!!
詳解如下:
對於一個數組:
int a[] = { 0, 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 };
他可以用來表示堆,堆是一顆完全二叉樹,分大值堆和小值堆,最後得出的分別是從大到小的排列順序和從小到達的排列順序,看你要排序的順序覺定大小堆!
0(1)
4 (2) 1(3)
3(4) 2(5) 16(6) 9(7)
10(8) 14(9) 8(10) 7(11)
存在一個如上圖的關係,
left[i] = 2* i ;
right[i] = 2+i +1;
知道這個關係後,我們就可以開始建堆了:
從 a.length/2 到第一個元素 開始對每個元素進行建堆(遞歸),使每個元素下的樹都維持最小值堆的特性;
a.length/2+1 到 a.length 個元素都是葉子節點,所以不需要維持最小值堆操作,因爲已經是最小值堆了!!
代碼如下:
public static void max_heapify(int[] a, int i) {
int left = leftChild(i);
int right = rightChild(i);
int largest = 0;
if (left < heap_size && a[i] < a[left]) {
largest = left;
} else {
largest = i;
}
if (right < heap_size && a[right] > a[largest]) {
largest = right;
}
if (largest == i) {
return;
} else {
int temp = a[i];
a[i] = a[largest];
a[largest] = temp;
max_heapify(a, largest);
}
}
上一步驟理解了,那麼之後的操作就很簡單了。
建好最小值堆後,每次取堆頂元素和數組最後一個元素(最後第n個元素,n爲交換次數)交換,剩下的元素採用維持堆性質的操作,
循環至最後堆只剩下一個元素即可,代碼如下:
public static void heapSort(int[] a) {
build_max_heap(a);
for (int i = a.length - 1; i >= 2; i--) {
int temp = a[1];
a[1] = a[i];
a[i] = temp;
heap_size--;
max_heapify(a, 1);
}
}
時間複雜度爲O(N*logN)。
至此,堆排序講解完畢!希望大家可以明白堆排序的原理了!
附上完整可運行代碼:
public class HeapSort {
public static int heap_size;
// 雙親編號
public static int parent(int i) {
return i / 2;
}
// 左孩子編號
public static int leftChild(int i) {
return 2 * i;
}
// 右孩子編號
public static int rightChild(int i) {
return 2 * i + 1;
}
/**
* 堆排序:首先使用建立最大堆的算法建立好最大堆,然後將堆頂元素(最大值)與最後一個值交換,同時使得堆的長度減小1
* ,調用保持最大堆性質的算法調整,使得堆頂元素成爲最大值,此時最後一個元素已被排除在外、
*/
public static void heapSort(int[] a) {
build_max_heap(a);
for (int i = a.length - 1; i >= 2; i--) {
int temp = a[1];
a[1] = a[i];
a[i] = temp;
heap_size--;
max_heapify(a, 1);
}
}
/**
* 建立最大堆。在數據中,a.length/2+1一直到最後的元素都是葉子元素,也就是平凡最大堆,因此從其前一個元素開始,一直到
* 第一個元素,重複調用max_heapify函數,使其保持最大堆的性質
*
* @param a
*/
public static void build_max_heap(int[] a) {
for (int i = a.length / 2; i >= 1; i--) {
max_heapify(a, i);
}
}
/**
* 保持最大堆的性質
*
* @param a
* ,堆中的數組元素
* @param i
* ,對以該元素爲根元素的堆進行調整,假設前提:左右子樹都是最大堆
*
* 由於左右孩子都是最大堆,首先比較根元素與左右孩子,找出最大值,假如不是根元素,則調整兩個元素的值;
* 由於左孩子(右孩子)的值與根元素交換,有可能打破左子樹(右子樹)的最大堆性質,因此繼續調用,直至葉子元素。
*/
public static void max_heapify(int[] a, int i) {
int left = leftChild(i);
int right = rightChild(i);
int largest = 0;
if (left < heap_size && a[i] < a[left]) {
largest = left;
} else {
largest = i;
}
if (right < heap_size && a[right] > a[largest]) {
largest = right;
}
if (largest == i) {
return;
} else {
int temp = a[i];
a[i] = a[largest];
a[largest] = temp;
max_heapify(a, largest);
}
}
public static void main(String[] args) {
int a[] = { 0, 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 };
heap_size = a.length;
heapSort(a);
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
}
}