zoukankan      html  css  js  c++  java
  • 排序比较与总结

    之前一共实现了6种比较常见的排序算法,分别是:

    选择排序插入排序冒泡排序归并排序快速排序堆排序

     效率:

    衡量一个算法的效率包括空间和时间,有时候还要考虑稳定性。

    前3种排序的方法效率较低,实现也比较简单,适合规模比较小的排序,个人认为适合排序总量在10000以下的随机数组。

    后3种排序的方法效率较高,实现稍微复杂一点,但也还好,适合规模较大的排序。


    时间方面,前3种排序的复杂度都是O(N^2),后3种排序的复杂度都是O(N*LogN),即呈指数级减少(因为基本思路都是递归的方式分治)。当然了,这是平均情况。

    空间方面,即是否需要额外的空间,只有归并排序需要一个数组长度相同的空间来存储排序的结果,即O(N)。快速排序的需要O(log2N)。其余排序都不需要额外的空间。

    稳定性方面,只有插入排序和归并排序是稳定的。稳定性保证的是数组中值相等的数据在排序时顺序不变,这在纯int型数组时没什么意义,但如果是复杂数据结构的排序,如果改变了顺序则可能影响数据结构中其他字段的排序。

    疑问:谁能告诉我为什么快速排序的空间复杂度是O(log2N)?

    特点:

    每种排序都有自己的特点,要不然也不会留传了这么久。以下是个人看法:

    冒泡排序:比较SB,只适合教学,效率低,但易实现。但能保证稳定。

    选择排序:比冒泡排序好一点,好的地方是交换次数少了,但仍然很SB。而且不稳定。

    插入排序:有点像打扑克牌时的排序,但插入时会让数组的移动变多,如果是链表则效率很高。且能保证稳定。

    归并排序:典型地递归分治,缺点是需要额外的空间来存储结果。但能保证稳定。

    快速排序:跟归并排序很像,但区别是归并排序切分的中点是数组索引,快速排序切分的中点是第一个数据的值。相同的是,都要碰运气。但不稳定。

    堆排序:思路很特别,花了好几个班车上的时间片,另外用了扑克牌演示每一步的过程才弄明白流程。但不稳定。


    运行时间比较:

    这里就专门写个测试程序来测试一下这6种排序算法的运行时间倒底区别有多大。

    随机生成100,000个数:

    const int N = 200000;
    int O = 0;
    
    int* GenRandom()
    {
    	srand( (unsigned)time( NULL ) );
    
    	int* a = new int[N];
    	for (int i = 0; i < N; i++)
    	{
    
    		a[i] = rand()*rand() ;
    	}
    	return a;
    }


    用下面的代码来计算时间:

    SYSTEMTIME StartTime = {0};
    FILETIME StartFileTime = {0};
    SYSTEMTIME EndTime= {0};
    FILETIME SEndFileTime= {0};
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	int* a = GenRandom();
    	GetLocalTime(&StartTime);
    	printf("timeBefore %d:%d:%d 
    ", StartTime.wMinute, StartTime.wSecond, StartTime.wMilliseconds);
    
    	BubbleSort(a);
    	/*SelectionSort(a);
    	InsertSort(a);
    	MergeSort(a,0,N-1);
    	QuickSort(a,0,N-1);
    	HeapSort(a,0,N);*/
    
    	GetLocalTime(&EndTime);
    	printf("timeAfter %d:%d:%d 
    ", EndTime.wMinute, EndTime.wSecond, EndTime.wMilliseconds);
    
    	return 0;
    }


    依次得到的结果如下:

    BubbleSort:1分37秒818,是的,你没看错。。

    SelectionSort :12秒338。

    InsertSort:1分11秒23。

    MergeSort:1秒598

    QuickSort:0秒036

    HeapSort:0秒081


    说多了都是泪....这才是100,000个数,假设要是千万级的,差别就更大了,可能冒泡需要几个小时。。而快速和堆排序都表现的相当优秀。

    这也难怪快速排序叫做快速排序。


    实现代码:

    以防我的代码实现的有问题,影响了测试效果,特将代码列与此,如果有不足之处请各位指教和指正:

    // Defines the entry point for the console application.
    //
    
    #include "stdafx.h"
    #include "windows.h"
    #include "time.h"
    
    const int N = 100000;
    int O = 0;
    
    int* GenRandom()
    {
    	srand( (unsigned)time( NULL ) );
    
    	int* a = new int[N];
    	for (int i = 0; i < N; i++)
    	{
    
    		a[i] = rand()*rand();
    	}
    	return a;
    }
    
    void swap(int& a, int& b)
    {
    	int temp = 0;
    	temp = a;
    	a = b;
    	b = temp;
    }
    
    //small -> large
    void SelectionSort(int* ua)
    {
    	//round times,遍历N次
    	for (int i = 0; i < N-1; i++)
    	{
    		int nMinIndex = i;	//最小值的索引
    		//每次确定一个值,从第一个值开始。。。第二次从第二个值开始
    		for (int j = i + 1; j < N; j++)
    		{
    			if( ua[nMinIndex] >= ua[j] )
    			{
    				nMinIndex = j;
    			}
    			O++;
    		}
    		swap(ua[i], ua[nMinIndex] );
    	}
    }
    
    //small -> large
    void InsertSort(int* ua)
    {
    	//round times  
        for (int i = 1; i <= N; i++)  
        {  
            for (int j = i; j > 0; j--)  
            {  
                if( ua[j] < ua[j-1] )  
                {  
                    swap(ua[j], ua[j-1] );  
                }  
            }  
        }  
    }
    
    //small -> large
    void BubbleSort(int* ua)  
    {  
    	O = 0;  
    	//round times  
    	for (int i = 0; i < N; i++)  
    	{  
    		/*printf("round %d 
    ", i);  
    		for (int i = 0; i < N; i++)  
    		{  
    		printf("a[%d]=%d 
    ",i, *(ua+i));  
    		}  */
    
    		for (int j = 0; j < (N-i-1); j++)  
    		{  
    			if(ua[j] > ua[j+1])  
    			{  
    				swap(ua[j], ua[j+1] );  
    			}  
    			O++;  
    		}  
    
    	}  
    }  
    
    void Merge(int* ua, int nStart, int nMid, int nEnd)
    {
    	int a[N];
    	int i = nStart;
    	int j = nMid + 1;
    
    	for (int k = nStart; k <= nEnd; k++)
    	{
    		a[k] = ua[k];
    	}
    
    	for (int k = nStart; k <= nEnd; k++)
    	{
    		if(i > nMid)
    		{
    			ua[k] = a[j++];
    		}
    		else if(j > nEnd)
    		{
    			ua[k] = a[i++];
    		}
    		else if( a[j] < a[i])
    		{
    			ua[k] = a[j++];
    		}
    		else
    		{
    			ua[k] = a[i++];
    		}
    
    		/*printf("round %d 
    ", k); 
    		for (int k = nStart; k < nEnd; k++)  
    		{  
    		printf("a[%d]=%d 
    ", k, *(ua + k));  
    		}  */
    	}
    }
    //small -> large
    void MergeSort(int* ua, int nStart, int nEnd)
    {
    	//递归退出条件
    	if(nStart >= nEnd)
    	{
    		return;
    	}
    	int nMid = nStart + (nEnd - nStart) / 2;
    
    	MergeSort(ua, nStart, nMid);
    	MergeSort(ua, nMid+1, nEnd);
    	Merge(ua, nStart, nMid, nEnd);
    }
    
    int QuickPartition(int* ua, int nStart, int nEnd)
    {
    	int i = nStart;
    	int j = nEnd + 1;
    
    	//中点值
    	int nFlagValue = ua[nStart];
    
    	while(1)
    	{
    		//找到左边大于中点的值,记录索引
    		while( ua[++i] < nFlagValue )
    		{
    			if( i == nEnd)
    			{
    				break;
    			}
    		}
    		//找到右边小于中点的值,记录索引
    		while( ua[--j] > nFlagValue )
    		{
    			if( j == nStart)
    			{
    				break;
    			}
    		}
    		//两边向中间靠拢的过程中相遇则退出
    		if( i >= j)
    		{
    			break;
    		}
    		//交换两边的值
    		swap( ua[i], ua[j] );
    	}
    	//将右边最后一个小于中点值的数与中点值交换位置,
    	//保证中点值的左边都小于中点值,右边都大于中点值
    	swap( ua[nStart], ua[j] );
    
    	//返回将右边最后一个小于中点值的数的索引,做为右边部分的中点值。
    	return j;
    }
    void QuickSort(int* ua, int nStart, int nEnd)
    {
    	//递归退出条件
    	if(nStart >= nEnd)
    	{
    		return;
    	}
    	int nMid = QuickPartition(ua, nStart, nEnd);
    	QuickSort(ua, nStart, nMid-1);
    	QuickSort(ua, nMid+1, 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;  
    		}
    	}  
    	/*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();
    	GetLocalTime(&StartTime);
    	printf("timeBefore %d:%d:%d 
    ", StartTime.wMinute, StartTime.wSecond, StartTime.wMilliseconds);
    
    	//BubbleSort(a);
    	//SelectionSort(a);
    	//InsertSort(a);
    	//MergeSort(a,0,N-1);
    	//QuickSort(a,0,N-1);
    	HeapSort(a,0,N);
    
    	GetLocalTime(&EndTime);
    	printf("timeAfter %d:%d:%d 
    ", EndTime.wMinute, EndTime.wSecond, EndTime.wMilliseconds);
    	printf("times %d 
    ", O);
    
    	return 0;
    }
    



  • 相关阅读:
    什么样的代码称得上是好代码?
    九年程序人生 总结分享
    Docker入门 第一课 --.Net Core 使用Docker全程记录
    阿里云 Windows Server 2012 r2 部署asp.net mvc网站 平坑之旅
    Visual studio 2015 Community 安装过程中遇到问题的终极解决
    Activiti6.0 spring5 工作流引擎 java SSM流程审批 项目框架
    java 进销存 库存管理 销售报表 商户管理 springmvc SSM crm 项目
    Leetcode名企之路
    24. 两两交换链表中的节点
    21. 合并两个有序链表
  • 原文地址:https://www.cnblogs.com/riskyer/p/3217673.html
Copyright © 2011-2022 走看看