zoukankan      html  css  js  c++  java
  • 数据结构与算法分析-排序

    作者:xiabodan 出处:http://blog.csdn.net/xiabodan 

    排序算法(Sorting Algorithm)是计算机算法的一个组成部分。也是程序=算法+数据结构中的一部分(算法)。

    实验平台:raspberry 2 B + Ubuntu Mate 

    插入排序

    外循环i由1到N-1,内循环由j由i到1,每次内循环都将A【j】插入到序列A【0】-A【i】的正确位置,这样就保证了每次外循环之后序列A【0】-A【i】都是已经排好序的,外循环不过将自己插入在A【0】-A【i】之间,且不影响原来的序列正确性。和冒泡算法一样都要经过O(N^2)次比較和交换,例如以下图


    //插入排序
    //stable
    //O(N^2) comparisons and swaps
    //Adaptive: O(n) time when nearly sorted
    //Very low overhead
    void insertion(elementtype A[],int n)
    {
    	int p = 0 ;
    	int j = 0 ;
    	for(p=1;p<n;p++ )
    	{
    		elementtype tem = A[p] ; 
    		for(j=p;j>0&&A[j-1]>tem;j--)
    		{
    			 A[j] = A[j-1];
    		}
    		A[j] = tem;
    	}
    			
    }
    

    希尔排序

    希尔排序有时能够成为增量缩小排序,选择一个大小为hk的区间,对相隔hk个元素进行插入排序,逐渐减小hk区间。直至hk变为1

    //希尔排序
    //O(N^3/2)   unstable
    //Adaptive: O(N.lg(N)) time when nearly sorted
    //Very low overhead
    void shell(elementtype A[],int n)
    {
    	int i,j,inc;
    	elementtype tem;
    
    	for(inc=N/2;inc>0;inc /=2)
    	{
    		for(i=inc;i<N;i++)
    		{
    			tem = A[i];
    			for(j=i;j>=inc;j-=inc)
    			{
    				if(tem<A[j-inc])
    					A[j] = A[j-inc];
    				else
    					break;
    			}
    			A[j] = tem;
    		}
    	}
    }

    冒泡排序

    冒泡排序比較暴力,外循环i从A【0】-A【N-1】,内循环从A【N-1】-A【i】,同一时候内循环从末尾開始一直向i靠近。并找出当中最小的元素冒出来到A【i+1】,故称为冒泡排序。十分暴力,但非常稳定,须要1+2+3+4+.......+N = N*(N+1) = O(N^2)次比較和交换,效率非常低。可是当某次内循环检測到没有发生交换后,说明A【N-1】-A【i】都是依照顺序排列的。不须要排序了。因此假设当给定数据是已经接近排序好的时候,冒泡算法时间复杂度仅为O(N)。

    //冒泡排序
    //O(N^2)   stable
    //Adaptive: O(N) time when nearly sorted
    //Very low overhead
    void bubble(elementtype A[],int n)
    {
    	int flag = 1;
    	int i,j;
    	for(i=0;i<n;i++)
    	{
    		flag = 0;
    		for(j=n-1;j>i;j--)
    		{
    			if(A[j]<A[j-1])
    			{
    				flag = 1;
    				swap(A+j,A+j-1 );
    			}
    				
    		}
    		if(flag == 0) break;
    	}
    }


    选择排序

    选择排序,外循环i从A【0】-A【N-1】。内循环从A【i+1】-A【N-1】。内循环负责找出A【i+1】-A【N-1】中比A【i】小的元素,并用一个k去标记它的位置,在内循环结束的时候将A【i】与A【k】互换。那么A【i+1】-A【N-1】中比A【i】小的元素A【k】就被放在了A【i】这个位置。也就是选择一个最小的放在A【i】这个位置。

    选择排序的比較次数是O(N^2)。可是交换次数却唯独O(N)。由于内循环每次不过标记最小元素,并没有实时的去交换元素。

    //选择排序
    //Not stable
    //O(1) extra space
    //Θ(n2) comparisons
    //Θ(n) swaps
    //Not adaptive
    void selection(elementtype A[],int n)
    {
    	int i,j;
    	int k;
    	for(i=0;i<n;i++)
    	{
    		k = i;
    		for(j=i+1;j<n;j++)
    		{
    			if(A[j]<A[k])
    			{	
    				k = j;
    			}
    		}
    
    		
    		swap(A+i,A+k);
    	}
    }
    


    高速排序

    高速排序是一个典型的分治策略,就是将非常大的集合A划分为非常小的模块。逐一处理。首先须要选择一个枢纽元素pivot,然后将整个集合A分为两半,一办是都比pivot小的元素,集合Al放在左边。一组是比pivot大的元素都放在右边。叫做集合Ar。然后再对左右边的子集Al与Ar分别採取相同的方法递归。直至集合Al和Ar都唯独一个元素,在实际应用中,一般不会让Al和Ar都递归到1个元素。一种要的方法是当Al和Ar小于10(阈值CUT)后,就不在採用快排算法。而是换为插入排序算法效率更高。因为本文測试中不利于大数据的排序。所以只将阈值CUT设置为10。见下图

    另外一个问题是枢纽元素pivot的选择,选择枢纽元素pivot的方法有非常多。一种经常使用的就是三数中值算法。也就是选取集合A首尾以及中位元素,然后选取这三个数的中位数作为枢纽元素pivot。


    //高速排序
    //not Stable
    //O(lg(n)) extra space (see discussion)
    //O(n2) time, but typically O(n·lg(n)) time
    //Not adaptive
    #define CUT 3
    elementtype median3(elementtype A[],int left ,int right)
    {
    	int center = (left +right) / 2;
    	if(A[left]>A[center])
    		swap(&A[left],&A[center]);
    	if(A[left]>A[right])
    		swap(&A[left],&A[right]);
    	if(A[center]>A[right])
    		swap(&A[center],&A[right]);
    
    	swap(&A[center],&A[right-1]);
    
    	return A[right-1];
    }
    void Qsort(elementtype A[],int left, int right)
    {
    	int i,j;
    	elementtype pivot;
    
    	if(left + CUT<= right)
    	{
    		pivot = median3(A,left,right); //select middle element as pivot
    		i = left;j = right-1;
    		for(;;)
    		{
    			while(A[++i]<pivot){}
    
    			while(A[--j]>pivot){}
    			if(i<j)
    				swap(&A[i],&A[j]);
    			else
    				break;
    		}
    		swap(&A[i],&A[right-1]);
    
    		Qsort(A,left,i-1);
    		Qsort(A,i+1,right);
    	}
    	else
    		insertion(A+left,right-left+1);
    }
    void quick1(elementtype A[],int n)
    {
    	Qsort(A,0,n-1);
    }
    


    归并排序

    归并排序的思想也是(divide-and-conquer)分治策略。将集合A递归均分。直至没组仅仅剩下一个元素时,在開始组合。组合原理例如以下图非常形象,就不多解释。很多其它关于归并算法能够參见博客http://geeksquiz.com/merge-sort/

    //归并排序
    //Stable
    //(n) extra space for arrays (as shown)
    //(lg(n)) extra space for linked lists
    //(n·lg(n)) time
    //Not adaptive
    //Does not require random access to data
    void Merge(elementtype A[],elementtype TA[],int lpos,int rpos,int rightend)
    {
    	int leftend = rpos-1;
    	int numelement = rightend -lpos + 1;
    	int tpos = lpos;
    
    	while(lpos<=leftend && rpos<=rightend)
    		if(A[lpos] <= A[rpos])
    			TA[tpos++] = A[lpos++];
    		else
    			TA[tpos++] = A[rpos++];
    
    	while(lpos<=leftend)
    		TA[tpos++] = A[lpos++];
    	while(rpos<=rightend)
    		TA[tpos++] = A[rpos++];
    
    	int i = 0;
    	for(i=0;i<numelement;i++,rightend--)
    	{
    		A[rightend] = TA[rightend];
    	}
    		
    }
    void MSort(elementtype A[],elementtype TA,int left,int right)
    {
    	int center ;
    	if(left < right)
    	{
    		center = (left+right)/2;
    		MSort(A,TA,left,center);
    		MSort(A,TA,center+1;right);
    		Merge(A,TA,left,center+1,right);
    	}
    }
    void mergesort(elementtype A[],int n)
    {
    	elementtype *TA;
    	TA = (elementtype*)malloc(sizeof(elementtype)); //just malloc once 
    	if(NULL != TA)
    	{
    		MSort(A,TA,0,n-1);
    		free(TA);
    	}
    	else
    		printf("error: TA can't be empty!
    ");
    }



    头文件里包括其它的一个变量和打印、交换函数
    typedef   int elementtype;
    #define N 10

    //other function used for debug
    void print(elementtype A[],int n)  
    {
    	int i = 0;
    	printf("after sorting
    ");
    	for(i=0;i<n;i++)
    	{
    		printf(" %d 
    ",A[i]);
    	}
    }
    void swap(elementtype *a,elementtype *b)
    {
    	elementtype tem = *a;
    	*a	= *b;
    	*b	= tem;
    }



    未完待续.......


    总结:

         稳定的排序:    插入排序。       冒泡排序,      归并排序
         时间复杂度  :        O(n2)  O(n2)   O(n·lg(n))

         不稳定的排序:选择排序,      希尔排序,       堆排。             快排
         时间复杂度  :        O(n2)  O(n3/2)  O(n·lg(n))  O(n·lg(n))
          
          

         冒泡和插入是慢慢找到最大或最小的放在第一个去;选择直接找到最大或者最小放到第一个。归并、快排都用了devide-merge-conquer(分治策略),期间还是会用到前面提到的那几种最主要的算法;堆排序用了选择排序的思想。桶排序用了空间换时间的方法;万变不离其宗。

    參考:

         数据结构与算法分析-C语言描写叙述[M],机械工业出版社

         博客园 vamei的博客:http://www.cnblogs.com/vamei/archive/2013/03/12/2948847.html

             天津城市学院一个精品课程:http://sjjp.tjuci.edu.cn/sjjg/datastructure/ds/web/paixu/paixu8.1.1.1.htm

    国外一个排序站点有动画,分析,为代码: http://www.sorting-algorithms.com/

    一个在国内不算太有名的国外站点,里面的内容貌似不止算法:
    Programming problems and Competitions :: HackerRank
    一个俄罗斯的ACM竞赛站点,不定期有算法比赛:
    Codeforces
    据说是某个Top2大学为后台的算法站点,比較简单,并且一直在定期更新算法新手教程,有定期比赛:
    hihoCoder
    hdu主办的ACM算法竞赛站点。定期有比赛:
    Welcome to BestCoder
    宇宙级题库:
    UOJ - Universal Online Judge

    北大:poj.org/
    杭电:acm.hdu.edu.cn/
    华中科技大学:acm.hust.edu.cn/vjudge/

  • 相关阅读:
    ionic2项目中实现md5加密
    ionic2中使用极光IM的WebSDK实现即时聊天
    react-native清除android项目缓存的命令
    在react-native项目中使用iconfont自定义图标库
    ionic2中使用videogular2实现m3u8文件播放
    vue-video-player集成videojs-contrib-hls实现.m3u8文件播放
    react组件生命周期
    在vue2中隐藏elementUI的tab栏
    Spark2.1.0——Spark初体验
    Spark2.1.0——运行环境准备
  • 原文地址:https://www.cnblogs.com/gavanwanggw/p/7258568.html
Copyright © 2011-2022 走看看