参考一:
估计还要问问:什么是堆,什么是堆排序?堆与计算机分配内存的堆相同吗?
很多资料给出:堆的定义是
(1)、n个关键字序列(Kl,K2,…,Kn)称为(Heap),当且仅当该序列满足如下性质(简称为堆性质):
ki≤K2i且ki≤K2i+1 或 Ki≥K2i且ki≥K2i+1 (1≤i≤ n) //ki相当于二叉树的非叶结点,K2i则是左孩子,k2i+1是右孩子
(2)若将此序列所存储的向量R[1..n]看做是一棵完全二叉树的存储结构,则堆实质上是满足如下性质的完全二叉树:
树中任一非叶结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。
(3)、根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最小者的堆称为小根堆,又称最小堆。根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最大者,称为大根堆,又称最大堆。
(4)、堆中任一子树亦是堆。
(5)、堆可以视为一棵完全的二叉树,完全二叉树的一个“优秀”的性质是,除了最底层之外,每一层都是满的,这使得堆可以利用数组来表示(普通的一般的二叉树通常用链表作为基本容器表示),每一个结点对应数组中的一个元素。
那么堆排序呢?
堆排序其实就是将一组无序的序列变成堆的过程、下面我们一起看看堆排序的算法。
2、堆排序
以最大堆为例,步骤为:
① 先将初始文件R[1..n]建成一个大根堆,此堆为初始的无序区
② 再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,由此得到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys≤R[n].key
③由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。然后再次将R[1..n-1]中关键字最大的记录R[1]和该区间的最后一个记录R[n-1]交换,由此得到新的无序区R[1..n-2]和有序区R[n-1..n],且仍满足关系R[1..n-2].keys≤R[n-1..n].keys,同样要将R[1..n-2]调整为堆。
……
直到无序区只有一个元素为止。
将一个无序的序列建成堆的过程实际上是一个反复筛选的过程。若将此序列看作是一棵完全二叉树,则最后一个非终端结点是[n/2](不大于n/2的整数),因此筛选过程只需从[n/2]开始。要建成一个大顶堆,即先选择一个值最大的元素并与序列中的最后一个元素交换,然后对序列前n-1元素进行筛选,从新调整为一个大顶堆,直到结束。
算法描述如下:
typedef int[] Elem;//采用顺序存储表示完全二叉树
void HeapAdjust(Elem &e,int s,int m)
{//e中s...m的元素除e[s]之外已经满足堆的定义(e[i] <=e[2*i]&&e[i] <=e[2*i+1]
//或e[i]> =e[2*i]&&e[i]> =e[2*i+1]),调整e[s]使的e[s...m]成为一个大顶堆。
selem=e[s];
for(i=2*s;i <=m;i*=2)//沿着值较大的元素(结点)向下筛选
{
if(i <m && e[i] <e[i+1])++i;//i为值较大的元素下标
if(selem> =e[i])break;
e[s]=e[i];
s=i;
}
e[s]=selem;
}
void HeapSort(Elem &e)
{//对顺序表排序
for(i=e.length/2;i> 0;--i)
HeapAdjust(e,i,e.length);
for(i=e.length;i> 1;--i)
{
temp=e[1]; //将堆中的最后一个元素与第一个元素交换
e[1]=e[i];
e[i]=temp;
HeapAdjust(H,1,i-1);//调整1..i-1的元素成为大顶堆
}
}
/////////////////////////////////////////////////////////////
使用堆排序注意的问题:::
1、序列是从1开始的。,注意定义,R[1..n],故要空出R[0]。
2、堆排序算法分为两部分:建立大顶堆和排序。
3、排序的最坏时间复杂度为O(nlogn)。堆序的平均性能较接近于最坏性能。
4、由于建初始堆所需的比较次数较多,所以堆排序不适宜于记录数较少的文件。
5、 堆排序是就地排序,辅助空间为O(1), 它是不稳定的排序方法。
问题:当序列空间为R[0,1....n];时,该怎么去使用堆排序呢?元素后移一位,这明显不是一个好方法。
参考二:
堆排序(C/C++):
2011-05-19 15:06:52| 分类: 代码分享 | 标签:堆排序 |字号大中小 订阅
n个关键字序列Kl,K2,…,Kn称为(Heap),当且仅当该序列满足如下性质(简称为堆性质):
(1) ki≤K2i且ki≤K2i+1 或(2)Ki≥K2i且ki≥K2i+1(1≤i≤ n) //ki相当于二叉树的非叶结点,K2i则是左孩子,k2i+1是右孩子
大根堆和小根堆:
根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最小者的堆称为小根堆,又称最小堆。
根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最大者,称为大根堆,又称最大堆
堆排序利用了大根堆(或小根堆)堆顶记录的关键字最大(或最小)这一特征,
使得在当前无序区中选取最大(或最小)关键字的记录变得简单。
(1)用大根堆排序的基本思想
① 先将初始文件R[1..n]建成一个大根堆,此堆为初始的无序区
② 再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,
由此得到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys≤R[n].key
③由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。
然后再次将R[1..n-1]中关键字最大的记录R[1]和该区间的最后一个记录R[n-1]交换,
由此得到新的无序区R[1..n-2]和有序区R[n-1..n],且仍满足关系R[1..n-2].keys≤R[n-1..n].keys,
同样要将R[1..n-2]调整为堆。
……
直到无序区只有一个元素为止。
代码:
参考三:
C++堆排序
#ifndef _HEAP_H
#define _HEAP_H
#include <memory.h>
class CHeap
{
public:
CHeap(int heapCapacity) : _mHeapCapacity(heapCapacity)
{
_mHeap = new int[_mHeapCapacity];
memset(_mHeap, 0, _mHeapCapacity);
}
~CHeap() { delete[] _mHeap; }
void BuildMaxHeap(const char *filePath);
void MaxHeapify(int i);
void HeapSort();
void PrintResult();
private:
int _mHeapSize;
int _mRealSize;
int _mHeapCapacity;
int *_mHeap;
int LeftChild(int i) { return 2 * i + 1; }
int RightChild(int i) { return 2 * (i + 1); }
void Swap(int &a, int &b) { int tmp = a; a = b; b = tmp; }
};
#endif
/*********************** Heap.cpp ***********************/
#include "Heap.h"
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
const char *filePath = "data.txt";
CHeap heap(20);
heap.BuildMaxHeap(filePath);
heap.HeapSort();
heap.PrintResult();
return 0;
}
void CHeap::BuildMaxHeap(const char *filePath)
{
ifstream infile(filePath);
if (infile)
{
//cout << "Open file succeed." << endl;
string line;
int i = 0;
while (infile >> line)
{
_mHeap[i] = atoi(line.c_str());
++i;
if (i > _mHeapCapacity)
break;
}
_mHeapSize = _mRealSize = i;
infile.close(); // close the file descriptor
for (i = (_mHeapSize - 2) / 2; i >= 0; --i)
{
MaxHeapify(i);
}
}
}
void CHeap::MaxHeapify(int i)
{
int left = LeftChild(i);
int right = RightChild(i);
int max = i;
if (left < _mHeapSize && _mHeap[max] < _mHeap[left])
{
max = left;
}
if (right < _mHeapSize && _mHeap[max] < _mHeap[right])
{
max = right;
}
if (max != i)
{
Swap(_mHeap[i], _mHeap[max]);
MaxHeapify(max);
}
}
void CHeap::HeapSort()
{
for (int i = _mHeapSize - 1; i > 0; --i)
{
Swap(_mHeap[0], _mHeap[i]);
--_mHeapSize;
MaxHeapify(0);
}
}
void CHeap::PrintResult()
{
cout << "Total number of digits is: " << _mRealSize << endl;
for (int i = 0; i < _mRealSize; ++i)
{
cout << _mHeap[i] << endl;
}
}
/////////////////// data.txt //////////////////
23
34
56
...
...
堆排序的算法原理很简单,但实际应用却很广泛。

756

被折叠的 条评论
为什么被折叠?



