• 排序法汇总


    1.基本概念

        主关键字唯一区分不同数据,否则为次关键字。主关键字排序具有唯一性,次关键字否。若相同次关键字的元素排序可能发生交换顺序,则称算法不稳定。常用排序算法优劣衡量指标:

         ❶时间性能好。即较少的关键字比较次数和元素移动次数。

         ❷空间性能好。即辅助缓存小。

         ❸稳定性。即相同次关键字元素,排序前后相对位置恒定


    2.排序方法汇总

    2.1直接插入(二分法)排序

        ❶原理:从第2个元素开始,假设前面的元素已经排序完毕,每次将后序一个元素插入至前面合适位置。
        ❷特点:稳定。适用基本有序或者排序或者元素个数少。
        ❸伪代码:

    viod insertSort (int data[],int n)
    int temp;
    int i,j;
    for i=1:n-1
       temp=data[i];
       j=i-1;
       while(j>-1 &&temp <A[j])
           A[j+1]=A[j];
           j--;
       A[j+1]=temp;
    end

             二分法直接插入。先寻找合适的插入位置。继而移动元素,然后插入。减少了比较次数,移动次数未变。

        

    template<typename T, unsigned int n>
    void insertSort(T (&data)[n]){
    	int temp, mid;
    	int low, high;
    	int i, j;
    	for (i = 1; i < n; i++){
    		temp = data[i];
    
    		if (data[0]>data[i])
    			low = 0;
    		else if (data[i] < data[i - 1]){
    			low = 0;
    			high = i - 1;
    			while (low<high){//寻找low,使得倒数第一个data[low]>temp  
    				mid = (low + high) / 2;
    				if (data[mid]>temp)
    					high = mid;
    				else
    					low = mid + 1;
    			}
    		}
    		else
    			low = i;
    
    		for (j = i - 1; j >= low; j--)//数据向后移动  
    			data[j + 1] = data[j];
    		data[low] = temp;// 插入数值
    	}
    }


    2.2 希尔排序:

        ❶原理:又称最小增量排序。希尔排序先将元素分为若干不重叠小组。每个小组分别直接排序。小组个数逐渐减小。最后在一个组排序完成。希尔排序基于直接排序在基本有序元素列的高效率考虑。
        ❷特点:不稳定。
        ❸伪代码:

    void shellSort(int data,int n,int d[],int m)
    
    
    int span,temp;
    int i,j,k,key;
    for(i=0;i<m;i++)//m个跨度,d[m]为步长,最后一个增量为1
    {
        span=d[m];
        for(k=0;k<span;k++)//至多span个分组,data[0]......data[span-1]分别为span个分组的起始位置。
           for(j=k+span;j<n;j+=span)//每个分组使用插入排序。
    	   temp=data[j];
               key=j-span;
    	   while(key>=0&&temp>data[key])
    	       data[key+span]=data[key];
    	       key-=span;
               data[key+span]=temp;	  
    }

       分析:shell排序时间性能优于直接选择排序。

             1)初始时候步长比较大,每组元素少,排序很快。

             2)后来基本有序,使用直接插入的时间也较快。

    2.3直接选择排序

         ❶原理:选取最小的元素作为第一个元素,继而寻找第2大的元素作为第2个元素。继而依次....
         ❷特点:不稳定。
         ❸伪代码:

    viod insertSort (int data[],int n)
      int min;
      int temp;
      for(int i=0;i<n-1;i++)
        min=i;
        for(int j=i+1;j<n;j++)//寻找i---n-1最小元素位置
          if(data[min]>data[j])
            min=j;
        if(min!=i)//将最小元素放在比较序列的最前面
    	temp=data[i];
    	data[i]=data[min];
    	data[min]=temp;

       

    2.4堆排序

          ❶堆:以大跟堆为例,满足3个性质。根节点最大;从大根堆至任何叶子节点的路径,非递增有序;任意非空左右子树均为大根堆。
          ❷原理:调整堆
          ❸特点:不稳定。不适宜元素少或者排列有序的情况,适合元素多且乱序情况。堆不稳定因为需要每次最后一个元素和堆顶元素交换。

    void adjustHeap(int A[],int n,int k)//调整堆,已知A[k+1]----A[n-1]已经为堆。调整根节点为A[k]的堆
    {
    	int j,temp;
    	bool flag=false;
    	temp=A[k];//暂存根节点值
    	j=2*k+1;
    	while(j<n&&!flag)
    	{
    		if(j<n-1&&A[j+1]>A[j])//j指向左右节点的关键字最大
    			j++;
    		if(temp>A[j])
    			flag=true;
    		else
    		{
    			A[k]=A[j];
    			k=j;//更新k
    			j=2*k+1;//更新j
    		}
    	}
    	A[k]=temp;//根节点赋值给当前子节点
    }
    void heapSort(int A[],int n)
    {
    	int temp;
    	for(int i=(n-2)/2;i>=0;i--)//从非叶子节点开始,从后到前调整建立堆
    	   adjustHeap(A,n,i);
    
    	for(int i=n;i>1;i--)//将根节点(最大值)与最后一个叶子节点的值交换,i-表示长度
    	 {
    	   temp=A[0];
    	   A[0]=A[i-1];
    	   A[i-1]=temp;
    	   adjustHeap(A,i-1,0);//调整长度序列为i-1。
    	 }
    }


    2.5冒泡排序

         ❶原理:两两比较大小,将较大的元素像右移动。因此先是最大元素移动至列尾,继而是次最大..........
         ❷特点:比直接插入和直接排序移动次数多。是最慢的一种方法。
         ❸伪代码:

    void bubbleSort(int data[],int n)
    {
         int temp=0;
    	 int flag=true;
    	 for(int i=1;i<n&&flag;i++)
    	     flag=false;
    	     for (int j=0;j<n-i;j++)
    	        if(data[j]>data[j+1])
    		      {
    			     flag=true;//代表该次循环中发生过交换。倘若没有发生,则不需要下一次循环。
    			     temp=data[j];
    			     data[j]=data[j+1];
    			     data[j+1]=temp;
    			   }
    }


    2.6快速排序

        ❶原理:又称为划分排序。选取基本元素,将小于基本元素向前移动。大于基本元素的向后移动。最后将基准元素左右2边分别作为子区间递归调用。
        ❷特点:需要附加栈空间。不稳定(涉及到元素交换)。平均速度最快。可以比较居中元素,前后元素。取三者中居中的作为基本元素,并将其移动至队前列。辅助空间O(log2n).
        ❸伪代码:
    void quickResort(int data[], int low, int high)
    {
    	int i = low;
    	int j = high;
    	int temp = data[i];//选取第一个元素为中间元素  
    	while (i < j){
    		while (i<j&&data[j] >= temp)
    			j--;
    		data[i] = data[j];
    		while (i<j&&data[j] < temp)
    			i++;
    		data[j] = data[i];
    	}
    	data[i] = temp;
    	if (low<i - 1) //左区间不止一个元素   
    		quickResort(data, low, i - 1);
    	if (i + 1<high) //右区间不止一个元素   
    		quickResort(data, i + 1, high);
    }




    2.7归并排序

         ❶原理:将2个有序序列合并为一个有序序列。从2个元素合并一直进行到最后。
         ❷特点:稳定。由于需要将2个数组合并,因此需要辅助数组。空间复杂的相对较高。
         ❸伪代码:

    mergeSort(int data[],int n,int low,int high){
          mid=(low+high)/2;
          if(mid-low>0)
    	 mergeSort(data,n,low,mid);
         if(high-mid>1)
    	 mergeSort(data,n,mid+1,high);
          
         int i=low,j=mid+1,k=0;
         int* temp=new int[high-low+1];
         while(i<=mid&&j<=high)
            if(data[i]<data[j])
                temp[k++]=data[i++];
            else
               	temp[k++]=data[j++];
        
        while(i<=mid)
          temp[k++]=data[i++];
        while(j<=high)
          temp[k++]=data[j++];
    	
       k=low;
       while(k<=high)
         data[k]=temp[k]
         k++;
    
    }

    2.8计数排序

          1.设n个数,范围为n。则时间复杂度为O(m+n),且需要辅助内存O(n),为稳定排序。一般而言,计数排序以空间换性能,且一般适用于正数数组排序。当且仅当n不是很大时,计数排序十分有效。

          2.思想:不是基于比较的排序:

               1)统计数组中每个值为i的元素出现的次数,存入数组counter[i]

               2)循环遍历counter[i],使得counter[i]=counter[i]+counter[i-1]。目的是统计待排序数组中,小于等于i的元素个数

               3)从后向前遍历data[j], 则输出的数组中sort[--counter[data[j]]]=data[j]。注意从后向前遍历data[]保证了计数排序的稳定性。

    void countSort(int data[], int n,int sorted[],int n){
        
    	int max=data[1]
    	for(int i=1;i<n;i++)
    	     if(max<data[i])
    		      max=data[i];
    
    	int * count=(int*)malloc((max+1)*sizeof(int));
    	if(!count)
    	    exit(1)
        
    	for(int i=0;i<n;i++)
    	    count[data[i]]++;
    	
    	for(int i=1;i<=max;i++)
    	   count[i]=count[i]+count[i-1];
    	   
    	for(int i=n-1;i>-1;i--)
    	    sorted[--count[data[i]]]=data[i];
    	   	
    	free(count);
    }


    2.9 桶排序

            思想:通过映射函数将n个数映射至m个桶中(比如划分区间),在单独对每个桶的数进行单独排序比如快速排序。时间复杂度为O(n+n*log(n/m)),最好为O(n)此时每个桶只有一个数,但是空间很大,最坏为log(nlogn),此时所有的数都被分到一个桶里面。

            桶排序的稳定性取决于单个桶的排序算法。若是快速排序则不稳定。

           计数排序是桶排序的特例,此时映射函数未f(x)=x,桶的个数为max+1


    2.10基数排序

          一种适用于关键值为整形的高效排序法。设数字为m位d进制数(如1234为4位10进制数)。则定义d个桶。对于总数为n的d进制位的数。最好最差平均时间均为O(m*n),辅助存储方面,若采用链式队列,则为O(n);考虑顺序队列,因为要考虑最差情况,则O(n*d),此时被n个数进入同一个队列,实现需要分配d*n个辅助存储单元.

           思想:先排最低位进桶,再排次地位进桶,依次按序号进桶。

                       桶使用队列存储,保证先进先出。为稳定算法。

    //m位d进制
    void radixSort(int data[],int length,int m,const int d){
    	queue<int>* q=new queue<int>[d];
    
    	int power;
    	for(int i=0;i<m;i++){
    		if(i==0)
    			power=1;
    		else
    			power=power*d;
    
    		int k=0;
    		for(int j=0;j<length;j++){
    			k=data[j]/power-(data[j]/(power*d))*d;
    			q[k].push(data[j]);
    		}
    
    		k=0;
    		for(int j=0;j<d;j++){
    			while(!q[j].empty()){
    				data[k++]=q[j].front();
    				q[j].pop();
    			}
    		}
    	}
    	delete []q;
    }



    3.排序方法比较

       

        ❶从时间角度。
             平均。直接插入,直接选择,冒泡均为O(n.^2)。堆,归并,快速为O (nlogn)。希尔排序基于2者之间。
             最好。直接插入,冒泡和直接插入为O(n)最好。快速,归并,堆次之为 O (nlogn)。
             最差。堆和归并仍然为 O (nlogn),其它为O(n.^2)
        ❷从空间的角度。
            归并最差,需要O(n)。快速为O(logn)(辅助栈空间)。其它均为O(1)。
        ❸从稳定角度
            只要直接插入,归并,冒泡,基数,计数 排序稳定。
        ❹总结:
            数目多且随机。内存容许要求稳定可以用归并;内存不容许或者不要求稳定,可以用堆排序。直接插入和冒泡在最好的情况下性能最好。当n比较大且随机分布时,快速排序效果好。当n很大时且关键字很小,使用基数排序。

           

           ❺.std::sort封装了快速排序算法,因此是不稳定的,如果要使用稳定排序,可以用std:stable_sort

           另外,二叉树排序法的时间复杂度,最坏为O(n.^2),平均为O(nlogn).空间复杂度为O(n)




      

  • 相关阅读:
    bat 笔记 一
    air 桌面应用发布后可以删除的文件
    as3 去掉字符串空白问题
    as3 air 获取文件夹下的所有文件
    egret 配置设置
    egret 精简游戏项目
    starling 第一天
    《笨办法学Python》 第2课手记
    《笨办法学Python》 第1课手记
    《笨办法学Python》 第0课手记
  • 原文地址:https://www.cnblogs.com/engineerLF/p/5393057.html
Copyright © 2020-2023  润新知