【算法导论】排序 (二):堆排序
2012-05-25 07:29
253 查看
四、(1)堆排序
第一次听堆排序是在107lab听忠哥讲的,但是没讲怎么实现。那时刚看了数据结构的二叉树,还以为要通过指针建立二叉树的方式来实现,觉得挺难的。
其实堆排序实现没有想象中的那么难。
“堆”这个词最初是在堆排序中提出来的,但后来就逐渐指”废料收集储存区“,就像程序设计语言Lisp和Java中所提供的设施那样。我们这里的堆数据结构不是废料收集存储区。
堆排序的运行时间与归并排序一样为O(n lg n), 但是一种原地(in place)排序。
(二叉)堆数据结构是一种数组对象,它可以被视为一棵完全二叉树。
对于一个数组arr[ ]={16,14,10,8,7,9,3,2,4,1}的存储数据结构如下图:
在这个结构中,对于每一个根节点i ,要保证它都比他的子节点大
我们可以用一个数组A【1...length【A】】来表示这个完全二叉树结构。 其中A【1】为根节点1
首先问题是求父节点、左儿子、右儿子的坐标,通过观察我们可以用宏或者内联函数实现:
[cpp] view
plaincopy
// 根据某节点下标i, 计算父节点、左儿子、右儿子的下标
inline int Parent(int i) { return i>>1; }
inline int Left(int i) { return i<<1; }
inline int Right(int i) { return (i<<1)|1; } //位运算乘2后,结果是偶数所以最后一位一定是0, 所以|1将会把最后一位变成1,从而实现加1的效果
无论是《C++ primer》还是《Effective C++》,都讲过宏的缺陷,用内联函数是个更好的选择。位运算做乘除的速度更快。
至于算法演示过程在《算法导论》上讲得很详细,不再赘述。
堆排序过程需要以下三个函数:
1、Max-Heapify(A,i) : 保持堆的性质,让A【i】在最大堆中“下降”,使以i节点为根的字数成为最大树
2、Buid-Max-Heap(A) : 自底向上将数组A【1...n】变成一个最大堆
3、Heap-Sort(A): 进行堆排序
C++代码实现:(数组A是下标1开始的)
[cpp] view
plaincopy
/* 算法导论 第6章 堆排序 */
#include<cstdio>
#include<algorithm>
using namespace std;
// 根据某节点下标i, 计算父节点、左儿子、右儿子的下标
inline int Parent(int i) { return i>>1; }
inline int Left(int i) { return i<<1; }
inline int Right(int i) { return (i<<1)|1; } //位运算乘2后,结果是偶数所以最后一位一定是0, 所以|1将会把最后一位变成1,从而实现加1的效果
inline void Swap(int &a,int &b) { if(a!=b){a^=b; b^=a; a^=b;} }
/*
堆排序的基本过程函数:
MaxHeap(A,len,i): 其运行时间为O(lgN),是保持最大堆性质的关键
BuidMaxHeap(A): 过程,以线性时间运行,可以在无需的输入数组基础上构造出最大堆
HeapSort(A): 对一个数组原地进行排序
*/
// 保持堆的性质,使以i为根的子树成为最大堆
void MaxHeap(int *A,int heap_size,int i){
int l,r,max;
l = Left(i);
r = Right(i);
if(l<=heap_size && A[l]>A[i])
max=l;
else
max=i;
if(r<=heap_size && A[r]>A[max])
max=r;
if(max!=i){
Swap(A[i],A[max]);
MaxHeap(A,heap_size,max);
}
}
// 建堆,自底向上用MaxHeap将整个数组变成一个最大堆
void BuidMaxHeap(int *A,int heap_size){
for(int i=heap_size>>1; i>=1; --i){
MaxHeap(A,heap_size,i);
}
}
// 堆排序
void HeapSort(int *A,int heap_size){
BuidMaxHeap(A,heap_size);
int len=heap_size;
for(int i=heap_size; i>=2; --i){
Swap(A[1], A[i]);
--len;
MaxHeap(A,len,1);
}
}
(2)优先级队列
C++ STL中的priority_queue就是用这种方法来实现的。
1、Heap-MaxiNum(A): 取出堆中的最大值
2、Heap-Extract-Max(A): 删除堆中的最大值并返回它的值
3、Heap-Increase-Key(A,i,key):将节点i的值增加到key,这里key要比i节点原来的数大。
4、Max-Heap-Insert(A, key): 插入一个新元素key到堆中,要用到3的函数
C++实现:
[cpp] view
plaincopy
// 优先级队列
int HeapMaxNum(int *A){
return A[1];
}
int HeapExtractMax(int *A,int heap_size){
if(heap_size>=1){
int max=A[1];
A[1] = A[heap_size];
--heap_size;
MaxHeap(A,heap_size,1);
return max;
}
}
bool HeapIncreaseKey(int *A,int i,int key){
if(key<A[i])
return false;
A[i] = key;
while(i>1 && A[Parent(i)]<A[i]){
Swap(A[i],A[Parent(i)]);
i = Parent(i);
}
return true;
}
void MaxHeapInsert(int *A,int key,int heap_size){
++heap_size;
A[heap_size] = -2147484646;
HeapIncreaseKey(A,heap_size,key);
}
—— 生命的意义,在于赋予它意义。
原创 http://blog.csdn.net/shuangde800 , By
D_Double
第一次听堆排序是在107lab听忠哥讲的,但是没讲怎么实现。那时刚看了数据结构的二叉树,还以为要通过指针建立二叉树的方式来实现,觉得挺难的。
其实堆排序实现没有想象中的那么难。
“堆”这个词最初是在堆排序中提出来的,但后来就逐渐指”废料收集储存区“,就像程序设计语言Lisp和Java中所提供的设施那样。我们这里的堆数据结构不是废料收集存储区。
堆排序的运行时间与归并排序一样为O(n lg n), 但是一种原地(in place)排序。
(二叉)堆数据结构是一种数组对象,它可以被视为一棵完全二叉树。
对于一个数组arr[ ]={16,14,10,8,7,9,3,2,4,1}的存储数据结构如下图:
在这个结构中,对于每一个根节点i ,要保证它都比他的子节点大
我们可以用一个数组A【1...length【A】】来表示这个完全二叉树结构。 其中A【1】为根节点1
首先问题是求父节点、左儿子、右儿子的坐标,通过观察我们可以用宏或者内联函数实现:
[cpp] view
plaincopy
// 根据某节点下标i, 计算父节点、左儿子、右儿子的下标
inline int Parent(int i) { return i>>1; }
inline int Left(int i) { return i<<1; }
inline int Right(int i) { return (i<<1)|1; } //位运算乘2后,结果是偶数所以最后一位一定是0, 所以|1将会把最后一位变成1,从而实现加1的效果
无论是《C++ primer》还是《Effective C++》,都讲过宏的缺陷,用内联函数是个更好的选择。位运算做乘除的速度更快。
至于算法演示过程在《算法导论》上讲得很详细,不再赘述。
堆排序过程需要以下三个函数:
1、Max-Heapify(A,i) : 保持堆的性质,让A【i】在最大堆中“下降”,使以i节点为根的字数成为最大树
2、Buid-Max-Heap(A) : 自底向上将数组A【1...n】变成一个最大堆
3、Heap-Sort(A): 进行堆排序
C++代码实现:(数组A是下标1开始的)
[cpp] view
plaincopy
/* 算法导论 第6章 堆排序 */
#include<cstdio>
#include<algorithm>
using namespace std;
// 根据某节点下标i, 计算父节点、左儿子、右儿子的下标
inline int Parent(int i) { return i>>1; }
inline int Left(int i) { return i<<1; }
inline int Right(int i) { return (i<<1)|1; } //位运算乘2后,结果是偶数所以最后一位一定是0, 所以|1将会把最后一位变成1,从而实现加1的效果
inline void Swap(int &a,int &b) { if(a!=b){a^=b; b^=a; a^=b;} }
/*
堆排序的基本过程函数:
MaxHeap(A,len,i): 其运行时间为O(lgN),是保持最大堆性质的关键
BuidMaxHeap(A): 过程,以线性时间运行,可以在无需的输入数组基础上构造出最大堆
HeapSort(A): 对一个数组原地进行排序
*/
// 保持堆的性质,使以i为根的子树成为最大堆
void MaxHeap(int *A,int heap_size,int i){
int l,r,max;
l = Left(i);
r = Right(i);
if(l<=heap_size && A[l]>A[i])
max=l;
else
max=i;
if(r<=heap_size && A[r]>A[max])
max=r;
if(max!=i){
Swap(A[i],A[max]);
MaxHeap(A,heap_size,max);
}
}
// 建堆,自底向上用MaxHeap将整个数组变成一个最大堆
void BuidMaxHeap(int *A,int heap_size){
for(int i=heap_size>>1; i>=1; --i){
MaxHeap(A,heap_size,i);
}
}
// 堆排序
void HeapSort(int *A,int heap_size){
BuidMaxHeap(A,heap_size);
int len=heap_size;
for(int i=heap_size; i>=2; --i){
Swap(A[1], A[i]);
--len;
MaxHeap(A,len,1);
}
}
(2)优先级队列
C++ STL中的priority_queue就是用这种方法来实现的。
1、Heap-MaxiNum(A): 取出堆中的最大值
2、Heap-Extract-Max(A): 删除堆中的最大值并返回它的值
3、Heap-Increase-Key(A,i,key):将节点i的值增加到key,这里key要比i节点原来的数大。
4、Max-Heap-Insert(A, key): 插入一个新元素key到堆中,要用到3的函数
C++实现:
[cpp] view
plaincopy
// 优先级队列
int HeapMaxNum(int *A){
return A[1];
}
int HeapExtractMax(int *A,int heap_size){
if(heap_size>=1){
int max=A[1];
A[1] = A[heap_size];
--heap_size;
MaxHeap(A,heap_size,1);
return max;
}
}
bool HeapIncreaseKey(int *A,int i,int key){
if(key<A[i])
return false;
A[i] = key;
while(i>1 && A[Parent(i)]<A[i]){
Swap(A[i],A[Parent(i)]);
i = Parent(i);
}
return true;
}
void MaxHeapInsert(int *A,int key,int heap_size){
++heap_size;
A[heap_size] = -2147484646;
HeapIncreaseKey(A,heap_size,key);
}
—— 生命的意义,在于赋予它意义。
原创 http://blog.csdn.net/shuangde800 , By
D_Double
相关文章推荐
- MIT:算法导论——4.1.排序和顺序统计量_堆排序
- 算法导论之插入排序,选择排序,归并排序,冒泡排序,希尔排序,堆排序,快速排序的c语言实现
- 插入排序,合并排序,堆排序,快速排序,计数排序的实现(算法导论)
- 排序及相关一(算法导论二章)
- [算法导论] 快速排序
- 冒泡排序、选择排序、堆排序、快速排序、插入排序算法复杂度分析与算法实现(自己总结与转)
- 算法导论-8-3-排序不同长度的数据项
- 面试--算法排序(5)(堆排序)
- 【数据结构与算法】内部排序之三:堆排序(含完整源码)
- 算法导论:快速排序代码
- [算法导论]非比较排序及数据结构
- 算法导论2.4 合并排序求逆序数
- 第十六周项目1-验证算法选择排序之堆排序
- 【算法导论】插入排序
- 2011-03-06 算法导论 第8章 线性时间排序
- 算法 ----- 排序NB二人组 堆排序 归并排序
- 快速排序、归并排序、堆排序三种算法性能比较
- 10种算法原理(冒泡排序,选择排序,快速排序,堆排序,希尔排序,桶排序等)
- 算法导论之快速排序
- 有意思的排序算法-堆排序