zoukankan      html  css  js  c++  java
  • 排序算法总结----比较类排序

    概述:排序算法可分为比较性的排序,以及运算性的排序;这里详细介绍这些排序的原理,性能,实现,以及应用场合。

    前面是维基百科的介绍,这里介绍几个比较典型的算法。

    理论

    交换排序

    选择排序

    插入排序

    归并排序

    分布排序

    并发排序

    混合排序

    其他

    以下介绍典型的算法:::::----》》》》

    比较排序

    一:快速排序(交换排序2号)

    1:原理

    采用了分治思想,在序列A[p...r]中选取一个元素,当然这里是用了p或者r处的元素(规格一致);找到该元素的,满足前面的值都比它小,后面的都比它大;同理让子序列递归下去。

    2:性能

    最坏时间复杂度:θ(n²)---出现在序列是顺序的,导致极度不平衡划分,1:0效果划分。

    最好时间复杂度:θ(nlog2(n))---出现在每次划分都是在中间处;

    中间状态:比如划分比例是a:b;则复杂度就是a/(b+a);b/(b+a)取大的(假设为a/(b+a)),则复杂度为θ(nlog(b+a)/a(n))其实也可以是θ(nlog(n))

    平均复杂度也就是θ(nlog(n));

    空间复杂度为:O(logn)----递归引起;

    3:应用

    实际排序中比较好,适合对平均时间复杂度有要求,要求原址排序,且隐含因子比较小;应用场景多。百万数量级以下合适

    4:实现----c++代码实现如下

    void exchang(int * sort,int i,int j)
    {
        int temp=0;
        temp=sort[i];
        sort[i]=sort[j];
        sort[j]=temp;
    }
    int partition(int *sort ,int p,int r)
    {
        int x=sort[r];
        int i=p-1;
        for(int j=p;j<r;j++)//p--->r-1
        if(sort[j]<=x)
        {
            i++;
            if(i!=j)
                exchang(sort,i,j);
        }
        if((i+1)<r)
            exchang(sort,i+1,r);    
        return i+1;
    }
    //栈溢出,原因是它递归太深了
    void QuickSort(int *sort ,int sn,int en)
    {
        if(sn<en)
        {
            int q=partition(sort,sn,en);
            QuickSort(sort,sn,q-1);
            QuickSort(sort,q+1,en);
        }
    }

    第二:插入排序

     4:实现

            /// <summary>
            /// 插入排序
            /// </summary>
            /// <param name="unsorted"></param>
            static void insertion_sort(int[] unsorted)
            {
                for (int i = 1; i < unsorted.Length; i++)
                {
                    if (unsorted[i - 1] > unsorted[i])
                    {
                        int temp = unsorted[i];
                        int j = i;
                        while (j > 0 && unsorted[j - 1] > temp)
                        {
                            unsorted[j] = unsorted[j - 1];
                            j--;
                        }
                        unsorted[j] = temp;
                    }
                }
            }

    第三:归并排序

     4:实现

    void merge(int * a,int p,int q,int r)
    {
        int n1=q-p+1;
        int n2=r-q;
        int * left=new int[n1+1];
        int * right=new int[n2+1];
        for(int i=0;i<=n1;i++)
            left[i]=a[p+i];
        for(int i=0;i<=n2;i++)
            right[i]=a[q+i+1];
        left[n1]=INT_MAX;
        right[n2]=INT_MAX;
        int n=0,m=0;
        for(int k=p;k<=r;k++)
        {
            if(left[n]<=right[m])
            {
                a[k]=left[n];
                n++;
            }
            else
            {
                a[k]=right[m];
                m++;
            }
        }
        delete [] left;
        delete [] right;
    }
    void mergesort(int * a,int p,int r)
    {
        if(p<r)
        {
            int q=(p+r)/2;
            mergesort(a,p,q);
            mergesort(a,q+1,r);
            merge(a,p,q,r);
        }
    }
      

    第四:堆排序

    1:原理

    2:性能

    3:应用

    4:实现

    //数据交换 
    void Exchang(int * sort,int i,int j)
    {
        int temp=0;
        temp=sort[i];
        sort[i]=sort[j];
        sort[j]=temp;
    }
    //修改堆
    void MaxHeapIfy(int *ops,int i,int heapsize)
    {
        int l=2*i+1;//left
        int r=2*(i+1);
    
        int max;
        if((l<=heapsize-1)&&ops[l]>ops[i])
            max=l;
        else
            max=i;
        if((r<=heapsize-1)&&ops[r]>ops[max])
            max=r;
        if(max!=i)
        {
            exchang(ops,i,max);
            maxheapify(ops,max,heapsize);
        }
    
    }
    //构建堆
    void BuildMaxHeap(int *ops,int length)
    {
        for(int i=(length-2)/2;i>=0;i--)
            maxheapify(ops,i,length);
    }
    //树也有递归,但是树本身不会那么深。
    void HeapSort(int *ops,int length)
    {
        buildmaxheap(ops,length);
        for(int i=length-1;i>=1;i--)
        {
            exchang(ops,0,i);
            length--;
            maxheapify(ops,0,length);
        }
    }

    第五:选择排序

    1:原理

    通过比较,先找出最大值或者最小值,找到后就将其放到队首,这样下来就是顺序的了。

    2:性能

    最差时间复杂度О(n²)

    最优时间复杂度О(n²)

    平均时间复杂度О(n²)

    空间复杂度:1

    3:应用

    n小时比较合适,算法简单,是比较后排序;使用场合少

    4:c++实现

    void SelectionSort(int* unsorted,int length)
    {
        for (int i = 0; i < length; i++)
        {
            int min = unsorted[i], min_index = i;
            for (int j = i; j < length; j++)
            {
                if (unsorted[j] < min)//选择最小值处
                {
                    min = unsorted[j];
                    min_index = j;
                }
            }
            if (min_index != i)//选出后交换
            {
                int temp = unsorted[i];
                unsorted[i] = unsorted[min_index];
                unsorted[min_index] = temp;
            }
        }
    }

    第六:冒泡排序(交换排序1号)

    1:原理

    通过比较,不断冒充最大值或者最小值,它和选择排序相似,选择排序是先找最小值,再交换;而冒泡是比较一次就会交换,从效率上将,冒泡排序不如选择排序。

    2:性能

    最差时间复杂度O(n^2)

    最优时间复杂度O(n)为什么是这个值,因为可以往后冒泡,产生的结果就可以达到这个效果。。。。

    平均时间复杂度O(n^2)

    3:应用

    应用类似插入算法

    4:实现

    1)往前冒泡

    void BubbleSort(int *unsorted,int length)
    {
        for (int i = 0; i <length ; i++)
        {
            for (int j = i; j < length; j++)
            {
                if (unsorted[i] > unsorted[j])
                {
                    int temp = unsorted[i];
                    unsorted[i] = unsorted[j];
                    unsorted[j] = temp;
                }
            }
        }
    }

    2)往后冒泡(C#)

    public void bubbleSort(int arr[]) {
        boolean didSwap;
        for(int i = 0, len = arr.length; i < len - 1; i++) {
            didSwap = false;
            for(int j = 0; j < len - i - 1; j++) {
                if(arr[j + 1] < arr[j]) {
                    swap(arr, j, j + 1);
                    didSwap = true;
                }
            }
            if(didSwap == false)
                return;
        }    
    }

    第七:希尔排序

    1:原理

    通过分组排序,比如以某个增量d,也就是分成d组,在d组内中进行插入排序;不断让d减小,使的最后为1,也就是到达了直接排序效果;和插入比较有何体现呢?插入最坏是O(n²),最好是O(n)。但是在n小时相差不大,这就是用分组减小n,而后期的组数少了,但是排序好了,则会走向好的情况,故而总体是效率高了。

    2:性能

    最坏时间复杂度:O(ns),s大致是1~2;

    平均时间复杂度:O(nlog(n));

    最好时间复杂度:O(n)

    注:n较大时目前一般是n1.25到1.6n1.25之间。

    3:应用

    它优于插入算法,不稳定。

    4:实现

    //
    //采取2的k次幂-1是最佳的,k表示第几趟
    //最后一个步长是1
    //k是趟数,这个可以自己设定。。。。
    //
    
    //通俗版本,比较简单
    void ShellSort(int* a, int length)  
    {  
        int gap; //增量,,组 
        for(gap = 3; gap >0; gap--)  //3组。。。自己也可以设定大一点组,
        {  
            for(int i=0; i<gap; i++)  //显然这种分组的步长是1
            {  
                for(int j = i+gap; j<length; j=j+gap)  //
                {  
                    if(a[j]<a[j-gap])  
                    {  
                        int temp = a[j];  
                        int k = j-gap;  
                        while(k>=0&&a[k]>temp)  
                        {  
                            a[k+gap] = a[k];  
                            k = k-gap;  
                        }  
                        a[k+gap] = temp;  
                    }  
                }  
            }  
        }  
    }
    //通用版本,,比较灵活
    void ShellSort(int a[], int n , int d[] ,int numOfD)
    {
    
        int i,j,k;
        int val;
        int span;    //增量
        for(int m=0; m<numOfD; m++)        //m趟
        {
            span=d[m];
            for(k=0; k<span; k++) //span个小组 
            {
                //组内进行直接插入排序 ,区别在于每次不是增加1,而是增加span
                for(i=k; i<n-span; i+=span)
                {
                    val=a[j+span];
                    j=i;
                    while(j>-1 && val<a[j])
                    {
                        a[j+span]=a[j];
                        j=j-span;
                    }
    
                    a[j+span]=val;
                }
            }
        }
    }

    第八:鸡尾酒排序

    1:原理

    在冒泡基础上改进,让单向冒泡变成双向冒泡;结束时是两个冒泡起点重合时。

    2:性能

    最坏情况还是和冒泡一致;n的2次方

    但是好的和插入相似了。为n;它比直接冒泡要好;

    空间复杂度:1

    3:应用

    性能和直接插入算法相似,故而和插入算法应用相似。

    4:实现

    void CocktailSort(int * list, int list_length) 
    {
        int bottom = 0;
        int top = list_length - 1;
        int bound = 0; //优化循环次数,记录已经排序的边界,减少循环次数
        while(bottom!=top) //表示已全部冒泡完了,就是已经成为顺序了
        {
            for(int i = bottom; i < top; i = i + 1)
            {//顺冒泡
                if(list[i] > list[i+1]) 
                {
                    swap(list[i], list[i+1]); 
               
                    bound = i;
                }
            }
            top = bound;
            for(int i = top; i > bottom; i = i - 1)
            {//反顺冒泡
                if(list[i] < list[i-1])
                {
                    swap(list[i], list[i-1]);
                    bound = i;
                }
            }
            bottom = bound;
        }
    }

    第九:地精排序

    1:原理

    在冒泡基础上改进的,它的效果和插入排序相似,号称是简单的排序算法,算法实现非常短。

    2:性能

    性能和插入排序一样

    3:应用

    应用和插入排序一样

    4:实现

    void GnomeSort(int* unsorted,int length)
    {
        int i = 0;
        while (i < length)//到达最后一个表示完成了
        {
            if (i == 0 || unsorted[i - 1] <= unsorted[i])
            {//表示能前进的条件,i=0或者是没有交换
                i++;
            }
            else
            {//发生交换了
                int tmp = unsorted[i];
                unsorted[i] = unsorted[i - 1];
                unsorted[i - 1] = tmp;
                i--;
            }
        }
    }

    第十:奇偶排序

    1:原理

    选取奇数,和它相邻比较,排序;同样对偶数也这样,最后达到没有交换了,此时就是顺序的了

    2:性能

    平均时间复杂度:O(n2);

    最好时间复杂度:O(n);

    最差时间复杂度:O(n2);

    可以看到有点像插入排序了;不稳定

    3:应用

    和插入排序应用相似。。。

    4:实现

    void OddEvenSort(int * szArray,int length)
    {  
        bool sorted = false;  
        while (!sorted)  
        {  
            sorted = true;  
            for (int i = 1; i < length - 1; i += 2)  
            {  //偶数排
                if (szArray[i]>szArray[i + 1])  
                {  
                    int tmp = szArray[i];  
                    szArray[i] = szArray[i+1];  
                    szArray[i+1] = tmp;    
                    sorted = false;  
                }  
            }   
            for (int i = 0; i < length - 1; i+= 2)  
            {  //奇数排
                if (szArray[i]>szArray[i + 1])  
                {  
                    int tmp = szArray[i];  
                    szArray[i] = szArray[i+1];  
                    szArray[i+1] = tmp;    
                    sorted = false;   
                } 
            }  
        }  
    }

    第十一:梳排序

    1:原理

    它是基于冒泡和插入排序的结合品,选取了适合的比率,进行冒泡。

    2:性能

    最差时间复杂度Omega(n^2)
          最优时间复杂度O(n)

    平均时间复杂度Omega(n^2/2^p),p是比率;

    空间复杂度:1。。。。不稳定

    3:应用

    类似插入算法应用

    4:实现

    void CombSort(int *arr, int size) 
    {
     
      double shrink_factor = 1.247330950103979;//1.3比率
      int gap = size;
      int swapped = 1;
      int swap;
      int i;
     
      while ((gap > 1) || swapped) 
      {
        if (gap > 1) 
        {
            gap = gap / shrink_factor;//计算间距
        }
     //直到gap为1时表示已经再距离上已经是到达了
        //此时就和冒泡一致了,外循环加内循环了,直到不再交换为止。。。
        swapped = 0; 
        i = 0;
     
        while ((gap + i) < size) 
        {
          if(arr[i]-arr[i+gap] > 0) 
          {
            swap = arr[i];
            arr[i] = arr[i+gap];
            arr[i+gap] = swap;
            swapped = 1;
          }
          ++i;
        }
      }
    }

    下一遍----->运算类的排序<-------下一篇

  • 相关阅读:
    HDU 1863 畅通工程(Kruskal)
    HDU 1879 继续畅通工程(Kruskra)
    HDU 1102 Constructing Roads(Kruskal)
    POJ 3150 Cellular Automaton(矩阵快速幂)
    POJ 3070 Fibonacci(矩阵快速幂)
    ZOJ 1648 Circuit Board(计算几何)
    ZOJ 3498 Javabeans
    ZOJ 3490 String Successor(模拟)
    Java实现 LeetCode 749 隔离病毒(DFS嵌套)
    Java实现 LeetCode 749 隔离病毒(DFS嵌套)
  • 原文地址:https://www.cnblogs.com/miner007/p/3793549.html
Copyright © 2011-2022 走看看