1. 使用其中一种堆结构, 二叉堆也可以实现排序。
背景:
(1)
完全二叉树——若设二叉树的高度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h层有
叶子结点,并且叶子结点都是从左到右依次排布,这就是
完全二叉树。(二叉堆使用完全二叉树的结构实现)
(2)
满二叉树——除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树。
本文主要使用C++实现了小根和大根二叉堆,
时间复杂度为
•
向上调整
/
向下调整
–
每层是常数级别
,
共
logn
层
,
因此
O(
logn
)
•
插入
/
删除
–
只调用一次向上或向下调整
,
因此都是
O(
logn
)
•
建堆
–
高度为
h
的结点有
n/
2
h+1
个
,
总时间为
2. C++实现代码如下:
<pre name="code" class="cpp">#include <iostream>
#include <vector>
using namespace std;
const int MAXN = 11111;
int n, a[MAXN], ans = 0;
//以下两个是自定义校验函数,mincmp是小根堆所需要的,而maxcmp就是大根堆所需要的了,
inline bool mincmp(const int &x, const int &y)
{ return x < y; }
inline bool maxcmp(const int &x, const int &y)
{ return x > y; }
//定义,第一个关键字为堆的大小,第二关键字为自定义校验类型
template <int N, bool cmp(const int&, const int&)>
class Heap
{
public:
int n;//n表示当前元素的个数,同时也是最后一个元素的下一个指针
vector<int> a;
public:
inline Heap()
{
clear();
a = vector<int>(N);
}
inline void clear()
{
n = 0;
}//清空
inline void push(int x)//插入操作,向上调整 ,将最后节点的值即即将插入的值和父节点比较
{
if(n+1>a.size())
{
a.resize(a.size()*2);
}
int hole = n++;//(0-n)
for(; hole > 0 && cmp(x, a[hole >> 1]); hole = hole >> 1)//把这个元素和根节点比较并交换
{
a[hole] = a[(hole) >> 1];
}
a[hole] = x;
}
inline void heapsort()
{
int m = n;
//int i = 0;
while( n>0)
{ //i++;
int head_value = a[0];
deletenode(0);
a[n] = head_value;
cout<<"pos"<<n<<" value"<<a[n]<<endl;
}
n = m;
}
inline void deletenode(int pos)//删除操作, 向下调整 ,将最后一个节点的值和子节点较优者比较交换
{
int tmp;//(0-n-1)
int hole = pos;
int lc = (hole<<1)+1;
//int rc = lc+1;
//int x = a[0];//如果需要在弹出的同时返回总根节点,把void改成int,并加上下面的两行被注释部分
while(lc+1 < n)
{
tmp = (cmp(a[lc+1], a[lc]) )? (lc+1) : lc;
if(cmp(a[tmp], a[n-1]))
{
a[hole] = a[tmp];
lc = (lc<<1)+1;
//rc = lc+1;
hole =tmp;
}
else
{
break;
}
}
a[hole] = a[n-1];
n--;
//return x;
}
inline void pop()//弹出操作
{
deletenode(0);
}
inline void popAll()
{
int m = n;
for(int i = 0; i < m; i++)
{
printf("%d ", top());
pop();
}
}
inline int top()
{
return a[0];
}//返回堆顶元素
inline bool empty()
{
return !n;
}//判断堆是否为空
inline void print()
{
for (int i = 0; i < n; i++)
{
printf("%d\n",a[i]);
}
}
};
Heap< MAXN, maxcmp > h;//定义一个堆,这里定义的是大根堆,如果要使用小根堆,把第二关键字换成上面的mincmp就可以了
int main()
{
printf("how many nums do you want to input?\n");
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
printf("input the num %d\n", i);
scanf("%d", a + i);
h.push(a[i]);//压入堆中
}
printf("the heap is......\n");
h.print();
//printf("from head to child......\n");
//h.popAll();
printf("from head to child......\n");
h.heapsort();
h.print();
system("pause");
return 0;
}
3. 堆排序的意义
在实践中,由于堆排序不如快速排序,所以很少被用来进行排序。
她的真正意义在于最快的找到最大值或者最小值。在堆结构中插入一个值重新构造堆结构,或者当取走一个值时重新构造堆结构。复杂度为lgn, 即高度值。
所以堆的相关操作常被用于调度算法中。
</pre><pre class="cpp" name="code"><pre name="code" class="cpp">//调整节点 大根堆
template<class T>
void AdjustHeapNode(T a[],int i,int n){ //调整节点i,数组共有N个节点
if (n==1||i>(n-2)/2) //i为叶子节点 (n-2)/2 最后一个非叶子节点的位置
return;
int iLeft=2*i+1;
int iRight=2*i+2;
if (iRight<=n-1) //说明i有左右两个子节点 三个节点找最大值
{
if (a[i]>=a[iLeft]&&a[i]>=a[iRight]) // i 最大 不用调整
return;
if (a[i]<a[iLeft]&&a[iRight]<=a[iLeft]) // iLeft 最大
{
T temp=a[iLeft];
a[iLeft]=a[i];
a[i]=temp;
AdjustHeapNode(a,iLeft,n);
return;
}
if (a[i]<a[iRight]&&a[iLeft]<=a[iRight]) // iRight 最大
{
T temp=a[iRight];
a[iRight]=a[i];
a[i]=temp;
AdjustHeapNode(a,iRight,n);
return;
}
}else{ // 说明i只有左节点 二个节点找最大值
//iLeft为最后一个节点
if (a[i]>=a[iLeft])
return;
else
{
T temp=a[iLeft];
a[iLeft]=a[i];
a[i]=temp;
AdjustHeapNode(a,iLeft,n);
return;
}
}
}
//建立堆
template<class T>
void CreateHeap(T a[],int n)
{
int iFirst=(n-1)/2; //第一个非叶子节点
for (;iFirst>=0;iFirst--)
{
AdjustHeapNode(a,iFirst,n);
}
}
//堆排序
template<class T>
void HeapSort(T a[],int n)
{
CreateHeap(a,n);
T temp;
for (int i=0;i<n-1;i++)
{
temp=a[n-1-i];
a[n-1-i]=a[0];
a[0]=temp;
AdjustHeapNode(a,0,n-1-i);
}
}