堆排序算法(Heapsort)
- MAX-HEAPIFY过程,其运行时间为О(lg n),是保持最大堆性质的关键
- BUILD-MAX-HEAP过程,以线性时间运行,可以在无序的输入数组基础上构造出最大堆
- HEAPSORT过程,运行时间О(n lg n),对一个数组原地进行排序
Heapsort过程
1、建最大堆
2、将最大元素A[1] 与 A[i] 交换,堆大小heap-size减一
3、重建堆
4、重复2、3操作,直至堆只剩一个元素
HEAPSORT(A)
BUILD-MAX-HEAP(A)
for i <- lenght[A] downto 2
do exchange(A[1], A[i])
heap-size[A] <- heapsize[A] - 1
MAX-HEAPIFY(A, 1)
堆
PARENT(i)
return ⌊i / 2⌋
LEFT(i)
return 2i
RIGHT(i)
return 2i + 1
保持堆性质
MAX-HEAPIFY(A, i)
l <- LEFT(i)
r <- RIGHT(i)
if l <= heap-size[A] and A[l] > A[i]
then largest <- l
else largest <- i
if r <= heap-size[A] and A[r] > A[largest]
then largest <- r
if largest ≠ i
then exchange A[i] <-> A[largest]
MAX-HEAPIFY(A, largest)
建堆
对于数组A[1 .. n]
子数组A[ (⌊n/2⌋ + 1) .. n ]中的元素都是树中的叶子,可看作是只含一个元素的堆。
只需对子数组A[ 1 .. (⌊n / 2⌋) ]做MAX-HEAPIFY操作
BUILD-MAX-HEAP(A)
heap-size[A] <- length[A]
for i <- ⌊ length[A] / 2 ⌋ downto 1
do MAX-HEAPIFY(A, i)
#include <iostream>
#include <stdlib.h>
#include <time.h>
int heap_size = 0;
void print(const int *beg, const int *end);
void heapsort(int *ia, size_t size);
void main()
{
const size_t num = 100000;
int a[num];
srand((unsigned) time(NULL));
for(size_t i = 0; i < num; ++i)
a[i] = rand() % 10000;
clock_t start = clock();
heapsort(a, num);
clock_t end = clock();
double time = (double) (end - start) / 1000;
std::cout << "花费时间" << time << "秒" << std::endl;
system("pause");
}
/*
其他函数
*/
void swap(int& i, int& j)
{
int temp = i;
i = j;
j = temp;
}
void print(const int *beg, const int *end)
{
while(beg != end)
std::cout << *beg++ << " ";
std::cout << std::endl;
}
/*
堆排序所用到的函数
*/
void max_heapify(int *ia, int i)
{
int w = 2 * i + 1;
while(w < heap_size)
{
if(w + 1 < heap_size)
if(ia[w + 1] > ia[w])
++w;
if(ia[i] > ia[w])
return;
swap(ia[i], ia[w]);
i = w;
w = 2 * i + 1;
}
}
void build_max_heap(int *ia, size_t size)
{
heap_size = size;
for(int i = size / 2 - 1; i >= 0; --i)
max_heapify(ia, i);
}
void heapsort(int *ia, size_t size)
{
build_max_heap(ia, size);
for(size_t i = size - 1; i >= 1; --i)
{
swap(ia[0], ia[i]);
--heap_size;
max_heapify(ia, 0);
}
}
使用STL heap 有关的函数
make_heap(), pop_heap(), push_heap(), sort_heap(), is_heap;
is_heap() :
原型如下 :
1. bool is_heap(iterator start, iterator end);
->判断迭代器[start, end] 区间类的元素是否构成一个堆. 是返回true ,否则返回 false.
2. bool is_heap(iterator start, iterator end, StrictWeakOrdering cmp);
->判断迭代器[start, end] 区间类的元素在cmp条件下是否构成一个堆. 是返回true ,否则返回 false.
make_heap() :
原型如下 :
1.void make_heap( random_access_iterator start, random_access_iterator end );
2.void make_heap( random_access_iterator start, random_access_iterator end, StrictWeakOrdering cmp);
->以 迭代器[start , end] 区间内的元素生成一个堆. 默认使用 元素类型 的 < 操作符 进行判断堆的类型, 因此生成的是大顶堆 .
->当使用了 版本2时, 系统使用 用户定义的 cmp 函数来构建一个堆
->值得注意的是, make_heap 改变了 迭代器所指向的 容器 的值.
pop_heap() :
原型如下 :
1. void pop_heap( random_access_iterator start, random_access_iterator end );
2. void pop_heap( random_access_iterator start, random_access_iterator end, StrictWeakOrdering cmp);
->pop_heap() 并不是真的把最大(最小)的元素从堆中弹出来. 而是重新排序堆. 它把首元素和末元素交换,然后将[first,last-1)的数据再做成一个堆。
此时, 原来的 首元素 位于迭代器 end-1 的位置, 它已不再属于堆的一员!
->如果使用了 版本2 , 在交换了 首元素和末元素后 ,使用 cmp 规则 重新构建一个堆.
push_heap() :
原型如下 :
1.void push_heap( random_access_iterator start, random_access_iterator end );
2. void push_heap( random_access_iterator start, random_access_iterator end, StrictWeakOrdering cmp);
-> 算法假设迭代器区间[start, end-1)内的元素已经是一个有效堆, 然后把 end-1 迭代器所指元素加入堆.
-> 如果使用了 cmp 参数, 将使用 cmp 规则构建堆.
sort_heap() :
原型如下 :
1. void sort_heap (random_access_iterator start, random_access_iterator end);
2. void sort_heap (random_access_iterator start, random_access_iterator end, StrictWeakOrdering cmp);
-> 堆结构被完全破坏, 相当于对元素进行排序, 效果和排序算法类似.
-> 如果使用了 cmp 参数, 将使用 cmp 规则排序堆.
#include <iostream>
#include <algorithm>
#include <vector>
#include <stdlib.h>
#include <time.h>
using namespace std;
void print(vector<int>::const_iterator beg, vector<int>::const_iterator end);
void main()
{
vector<int> ivec;
const size_t num = 100000000;
srand((unsigned) time(NULL));
for(size_t i = 0; i < num; ++i)
ivec.push_back(rand() % 100000);
clock_t start = clock();
make_heap(ivec.begin(), ivec.end());
sort_heap(ivec.begin(), ivec.end());
clock_t end = clock();
double time = (double) (end - start) / 1000;
cout << time << "秒" << endl;
system("pause");
}