我們知道簡單選擇排序
的時間複雜度為o(n^2),熟悉各種排序演算法的朋友都知道,這個時間複雜度是很大的,所以怎樣減小簡單選擇排序的時間複雜度呢?簡單選擇排序主要操作是進行關鍵字的比較,所以怎樣減少比較次數就是改進的關鍵。簡單選擇排序中第i趟需要進行n-i
次比較,如果我們用到前面已排好的序列a[1...i-1]
是否可以減少比較次數呢?答案是可以的。舉個例子來說吧,a、b、c進行比賽,b戰勝了a,c戰勝了b,那麼顯然c可以戰勝a,c和a就不用比了。正是基於這種思想,有人提出了樹形選擇排序
:對n個記錄進行兩兩比較,然後在([n/2]向上取整)個較小者之間在進行兩兩比較,如此重複,直到選出最小記錄。但是這種排序演算法需要的輔助空間比較多,所以威洛姆斯(j . willioms)在2023年提出了另一種選擇排序
,這就是下面要談的堆排序
。www.gaimor.cn
首先堆heap是一種資料結構,是一棵完全二叉樹且滿足性質:所有非葉子結點的值均不大於或均不小於其左、右孩子結點的值.
堆排序的基本思想是利用heap這種資料結構(可看成乙個完全二叉樹),使在排序中比較的次數明顯減少。
堆排序的時間複雜度為o(n*log(n)), 非穩定排序,原地排序(空間複雜度o(1))。
wwwww.gaimor.cn堆排序的關鍵在於建堆和調整堆,下面簡單介紹一下建堆的過程:
第1趟將索引0至n-1
處的全部資料建大頂(或小頂)堆,就可以選出這組資料的最大值(或最小值)。將該堆的根節點與這組資料的最後乙個節點交換,就使的這組資料中最大(最小)值排在了最後。
第2趟將索引0至n-2
處的全部資料建大頂(或小頂)堆,就可以選出這組資料的最大值(或最小值)。將該堆的根節點與這組資料的倒數第二個節點交換,就使的這組資料中最大(最小)值排在了倒數第2位。
第k趟將索引0至n-k
處的全部資料建大頂(或小頂)堆,就可以選出這組資料的最大值(或最小值)。將該堆的根節點與這組資料的倒數第k個節點交換,就使的這組資料中最大(最小)值排在了倒數第k位。www.acgred.cn
其實整個堆排序過程中, 我們只需重複做兩件事:
因而堆排序整體的時間複雜度為o(n*log n).
下面通過一組資料說明堆排序的方法:
9, 79, 46, 30, 58, 49
1: 先將待排序的數視作完全二叉樹(按層次遍歷順序進行編號, 從0開始),如下圖:
2:完全二叉樹的最後乙個非葉子節點,也就是最後乙個節點的父節點。最後乙個節點的索引為陣列長度len-1,那麼最後乙個非葉子節點的索引應該是為(len-1)/2.也就是從索引為2的節點開始,如果其子節點的值大於其本身的值。則把他和較大子節點進行交換,即將索引2處節點和索引5處元素交換。交換後的結果如圖:
建堆從最後乙個非葉子節點開始即可
3:向前處理前乙個節點,也就是處理索引為1的節點,此時79>30,79>58,因此無需交換。
4:向前處理前乙個節點,也就是處理索引為0的節點,此時9 < 79,9 < 49, 因此需交換。應該拿索引為0的節點與索引為1的節點交換,因為79>49. 如圖:
5:如果某個節點和它的某個子節點交換後,該子節點又有子節點,系統還需要再次對該子節點進行判斷。如上圖因為1處,3處,4處中,1處的值大於3,4出的值,所以還需交換。
牢記:將每次堆排序得到的最大元素與當前規模的陣列最後乙個元素交換。
1、由於是完全二叉樹, 故有:
parent(i)
return i / 2
left(i)
return 2 * i
right(i)
2 * i + 1
2、heapify
以最大堆為例,偽**:
max-heapify(a, i)
l = lift(i)
r = right(i)
if l <= a.heapsize and a[l] > a[i]
largest = l
else largest = i
if r <= a.heapsize and a[r] > a[largest]
largest = r
if largest != i
exchage a[i] with a[largest]
max-heapify(a, largest)
3、build heap
以最大堆為例,偽**:
build-max-heap(a)
a.heap-size = a.length
for a.length / 2 downto 1
max-heapify(a, i)
4、heapsort
以最大堆為例,偽**:
heapsort(a)
build-max-heap(a)
for i = a.length downto 2
exchange a[1] with a[i]
a.heap-size = a.heap-size - 1
max-heapify(a, 1)
#include
#include
#include
#include
using
namespace
std;
void adjust(int arr, int len, int index)
}void heapsort(int arr, int size)
for(int i = size - 1; i >= 1; i--)
}int main()
; heapsort(array, 8);
for(auto it: array)
當陣列中有相等元素時,堆排序演算法對這些元素的處理方法不止一種,故是不穩定的。
#include
#include
#include
void swap(void* x, void* y, size_t sz)
void makeheap(void* x, int i, int n, size_t sz, int(*cmp)(const
void*, const
void*))
}void buildheap(void* x, int n, size_t sz, int(*cmp)(const
void*, const
void*))
void heapsort(void* x, int n, size_t sz, int(*cmp)(const
void*, const
void*))
}void p(int* x,int n)
printf("\n");
}int less(const
void* a, const
void* b)
int greater(const
void* a, const
void* b)
int main();
// 降序全排列
heapsort(x, 8, sizeof(int), less);
p(x, 8);
// 公升序全排列
heapsort(x, 8, sizeof(int), greater);
p(x, 8);
// 最大的4個元素,在陣列末尾
heapsort(x, 4, sizeof(int), less);
p(x, 8);
}
堆排序原理及其實現 C
我們知道簡單選擇排序的時間複雜度為o n 2 熟悉各種排序演算法的朋友都知道,這個時間複雜度是很大的,所以怎樣減小簡單選擇排序的時間複雜度呢?簡單選擇排序主要操作是進行關鍵字的比較,所以怎樣減少比較次數就是改進的關鍵。簡單選擇排序中第i趟需要進行n i次比較,如果我們用到前面已排好的序列a 1.i ...
堆排序原理及其實現 C
我們知道簡單選擇排序的時間複雜度為o n 2 熟悉各種排序演算法的朋友都知道,這個時間複雜度是很大的,所以怎樣減小簡單選擇排序的時間複雜度呢?簡單選擇排序主要操作是進行關鍵字的比較,所以怎樣減少比較次數就是改進的關鍵。簡單選擇排序中第i趟需要進行n i次比較,如果我們用到前面已排好的序列a 1.i ...
堆排序原理及其實現 c
老生常談,開篇介紹,定義先行 堆排序 英語 heapsort 是指利用堆這種資料結構所設計的一種排序演算法。堆是乙個近似完全二叉樹的結構,並同時滿足堆積的性質 即子結點的鍵值或索引總是小於 或者大於 它的父節點 實現 include using namespace std 交換 void swap ...