• 堆排序


    引子:

    堆排序是第3个比较有意思的排序(相对于 归并排序,快速排序),效率很高,且不用额外的空间。

    堆的定义是:它用一个数组来存储一个完全二叉树(叶子节点只出现在最下层的树)。

    例如如下的数组的10个数据:

    int a[] = {3,4,9,7,5,6,10,8,2,1};


    其实是被看作为:


    不难观察,规律是从 a[0] 到 a[9] 按在树中层次依次排列。

    3的子节点是4和9,即a[0]的子节点是a[1]和a[2]。

    4的子节点是7和5,即a[1]的子节点是a[3]和a[4]。

    9的子节点是6和10,即a[2]的子节点是a[5]和a[6]。

    7的子节点是8和2,即a[3]的子节点是a[7]和a[8]。
    5的子节点是1,即a[4]的子节点是a[9]。

    以此类推,可以得出

    a[n]的子节点是a[2n+1]和a[2n+2] (如果有子节点的话)。


    排序思路:

    堆分两种:

    • 最大堆,即堆顶是数组中的最大数。
    • 最小堆,即堆顶是数组中的最小数。
    大顶堆有一个重要的性质就是父子点永远大于或等于两个子节点。小顶堆则相反。

    假设排序的目标是从小排到大。
    堆排序的第一步是建立最大堆,代码如下:

    	for (int i = nEnd/2 -1; i >= 0 ; i--)
    	{
    		HeapAdjust( ua, i, nEnd);
    	}

    主要目的就是让堆中的最大值跑到堆顶去。

    void HeapAdjust(int* ua, int nStart, int nEnd)
    {
    	int nMaxIndex = 0;
    
    	while ( ( 2*nStart + 1) < nEnd )  
    	{  
    		nMaxIndex = 2*nStart + 1;  
    		if ( ( 2*nStart + 2) < nEnd)  
    		{  
    			//比较左子树和右子树,记录较大值的Index  
    			if (ua[2*nStart + 1] < ua[2*nStart + 2])  
    			{  
    				nMaxIndex++;  
    			}  
    		}  
    
    		//如果父结点大于子节点,则退出,否则交换
    		if (ua[nStart] > ua[nMaxIndex])  
    		{  
    			break;
    		}  
    		else
    		{
    			swap( ua[nStart], ua[nMaxIndex] );
    			//堆被破坏,继续递归调整(注意,这里是递归的关键)
    			nStart = nMaxIndex;  
    		}
    	}  
    }

    这样数组经过调整后就变成了:
    之前:
    {3,4,9,7,5,6,10,8,2,1};

    之后:
    {10,8,9,7,5,6,3,4,2,1};

    这样最大值10就跑到堆顶了。


    堆排序的第二步是:每次把堆顶的数与堆的最后一个数交换,再让最后一个数脱离堆,然后继续建最大堆。然后如此循环,直到只剩下堆顶的数。

    比如第一轮把堆顶的10与1交换,此时1在堆顶,让10从堆中脱离,让10放在数组的末尾。此时堆还有9个数,最后1个数10留在末尾。


    第二轮先建堆,让9在堆顶,然后让9和2交换,此时2在堆顶,让9从堆中脱离,主9放在数组的倒数第2个位置,此时堆还有8个数,最后两个数是9,10。


    如此不堆的递归,直到堆越来越小,数组越来越大,并且是排序的状态。最后堆就没有了。

    代码如下:

    	for (int i = nEnd-1; i > 0; i--)
    	{
    		swap(ua[0], ua[i]);
    		HeapAdjust(ua, 0, i);
    	}

    完整的代码如下:
    #include "stdafx.h"
    #include "windows.h"
    #include "time.h"
    
    
    const int N = 10;
    int O = 0;
    
    int* GenRandom()
    {
    srand( (unsigned)time( NULL ) );
    
    
    int* a = new int[N];
    for (int i = 0; i < N; i++)
    {
        a[i] = rand() ;
    }
        return a;
    }
    
    
    void swap(int& a, int& b)
    {
        int temp = 0;
        temp = a;
        a = b;
        b = temp;
    }
    
    void HeapAdjust(int* ua, int nStart, int nEnd)
    {
    	int nMaxIndex = 0;
    
    	while ( ( 2*nStart + 1) < nEnd )  
    	{  
    		nMaxIndex = 2*nStart + 1;  
    		if ( ( 2*nStart + 2) < nEnd)  
    		{  
    			//比较左子树和右子树,记录较大值的Index  
    			if (ua[2*nStart + 1] < ua[2*nStart + 2])  
    			{  
    				nMaxIndex++;  
    			}  
    		}  
    
    		//如果父结点大于子节点,则退出,否则交换
    		if (ua[nStart] > ua[nMaxIndex])  
    		{  
    			break;
    		}  
    		else
    		{
    			swap( ua[nStart], ua[nMaxIndex] );
    			//堆被破坏,继续递归调整  
    			nStart = nMaxIndex;  
    		}
    	}  
    	for (int i = 0; i < N; i++)
    	{
    		printf("%d ",ua[i]);
    	}
    	printf("
    ");
    	//printf("%d ", O++);
    }
    
    void HeapSort(int* ua, int nStart, int nEnd)
    {
    	for (int i = nEnd/2 -1; i >= 0 ; i--)
    	{
    		HeapAdjust( ua, i, nEnd);
    	}
    
    	for (int i = nEnd-1; i > 0; i--)
    	{
    		swap(ua[0], ua[i]);
    		HeapAdjust(ua, 0, i);
    	}
    }

    SYSTEMTIME StartTime = {0};
    FILETIME StartFileTime = {0};
    SYSTEMTIME EndTime= {0};
    FILETIME SEndFileTime= {0};
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	//int* a = GenRandom();
    	int a[] = {3,4,9,7,5,6,10,8,2,1};
    	GetLocalTime(&StartTime);
    	printf("timeBefore %d:%d:%d 
    ", StartTime.wMinute, StartTime.wSecond, StartTime.wMilliseconds);
    
    	HeapSort(a, 0, N);
    
    	GetLocalTime(&EndTime);
    	printf("timeAfter %d:%d:%d 
    ", EndTime.wMinute, EndTime.wSecond, EndTime.wMilliseconds);
    	printf("times %d 
    ", O);
    	return 0;
    }
    



             

  • 相关阅读:
    redhat下设置网桥br0
    RackSpace推开源云计算平台OpenStack震动业界
    centos5.2 64位yum国内源之首选 上海交大(未验证)
    image config
    编程的四种境界
    怎样学好C语言
    利用SSL加密HTTP通道加强IIS安全性
    Sql Server 日期函数
    如何启用Oracle 11g的默认用户Scott
    ASP.NET自定义错误处理页面的添加
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3217678.html
Copyright © 2020-2023  润新知