堆排序及C++实现

本文介绍了使用C++实现小根和大根二叉堆进行堆排序的方法,虽然在实践中堆排序的效率不敌快速排序,但其能在O(logn)时间内找到最大值或最小值,这使得堆排序在寻找极值和调度算法中有重要应用。
摘要由CSDN通过智能技术生成

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);  
    }  
  
}  



                
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值