排序大全

来源:互联网 发布:电销数据购买 编辑:程序博客网 时间:2024/06/06 13:23

一、排序的分类



插入排序算法复杂度为O(n²)因而,插入排序不适合对于数据量比较大的排序应用。

插入排序的空间复杂度为O(1)。

稳定性:稳定。

/void Insertsort(int arr[],size_t size)
// {
// for(size_t j=1;j<size;j++)
// {
// int i=j;
// while(i>0&&(arr[i]<arr[i-1]))
// {
// int key=arr[i];
// arr[i]=arr[i-1];
// arr[i-1]=key;
// i--;
// }
// }

希尔排序

基本思想:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。


图解(假设为升序):

O(N)<希尔排序的时间复杂度<O(N^2),希尔排序,当N大时,平均的时间复杂度,大约在N^1.25--1.6N^1.25之间。

希尔排序的空间复杂度为O(1)

void ShellSort(T* a,size_t n)//希尔排序  

{  

    assert(a);  

    int gap = n;//gap为所给增量  

    while(gap > 1)  

    {  

        //实验证明,gap=gap/3是比较优的,+1则是为了最后一次对全体数据进行插入排序  

        gap = gap/3 + 1;  

        for (size_t i = gap; i < n; ++i)  

        {  

            int end = i - gap;  

            T tmp = a[i];  

  

            while(end >= 0)  

            {  

                if (Compare()(tmp,a[end]))  

                {  

                    a[end+gap] = a[end];  

                    end -= gap;  

                }  

                else  

                {  

                    break;  

                }  

            }  

            a[end+gap] = tmp;  

        }  

    }  

选择排序


选择排序的比较次数O(n^2),比较次数与关键字的初始状态无关,总的比较次数:

N=(n-1)+(n-2)+...+1=n*(n-1)/2。 

交换次数O(n),最好情况是,已经有序,交换0次;最坏情况是,逆序,交换n-1次。

故此,选择排序的时间复杂度为O(N^2)

选择排序的空间复杂度为O(1)

  1. //C++风格的选择排序  
  2. void SelectSort2(int* a,size_t n)  
  3. {  
  4.     assert(a);  
  5.     int minIndex = 0;  
  6.     for (size_t i = 0; i < n - 1; ++i)  
  7.     {  
  8.         minIndex = i;      //未排序区间最小数据的位置下标  
  9.         size_t pos = i + 1;//未排序区间的第一个数据下标  
  10.   
  11.         while(pos < n)//选出未排序区间最小的数据  
  12.         {  
  13.             if (a[pos] < a[minIndex])  
  14.             {  
  15.                 minIndex = pos;  
  16.             }  
  17.             ++pos;  
  18.         }  
  19.         swap(a[i],a[minIndex]);//将所选数据放到正确位置  
  20.     }  
  21. }  
    1. //选择排序的优化:每次既选出最大的数,也选出最小的数  
    2. void SelectSort3(int* a,size_t n)  
    3. {  
    4.     assert(a);  
    5.     int left = 0;//未排序区间的左下标  
    6.     int right = n - 1;//未排序区间的右下标  
    7.   
    8.     while (left < right)  
    9.     {  
    10.         int minIndex = left;//未排序区间最小数据的位置下标  
    11.         int maxIndex = right;//未排序区间最大数据的位置下标  
    12.   
    13.         //选出最大和最小数据的下标  
    14.         for (int i = left; i <= right; ++i)  
    15.         {  
    16.             if (a[i] < a[minIndex])  
    17.             {  
    18.                 minIndex = i;  
    19.             }  
    20.             if (a[i] > a[maxIndex])  
    21.             {  
    22.                 maxIndex = i;  
    23.             }  
    24.         }  
    25.         //修正:最大值在最小位置或最小值在最大位置  
    26.         swap(a[maxIndex],a[right]);//将最大数据放到区间最右侧  
    27.         if (minIndex == right)  
    28.         {  
    29.             minIndex = maxIndex;  
    30.         }  
    31.         swap(a[minIndex],a[left]);//将最小数据放在区间最左侧  
    32.   
    33.         left++;//缩小区间范围  
    34.         right--;//缩小区间范围  
      1.     }  
      2. }  

      堆排序的基本思想

            堆积排序(Heapsort)是指利用这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆性质:即子结点的键值或索引总是小于(或者大于)它的父节点。


    35. 堆排序的时间复杂度为O(N*lgN)

      堆排序的空间复杂度为O(1)


  • //void  Headadjust(int* array,int size,int parent)
    //{
    // int child=parent*2+1;
    // while(child<size)
    // {
    //
    // if((child+1)<size&&array[child]<array[child+1])
    //          {
    //        child=child+1;
    //       }
    //
    //
    // if (array[child]>array[parent])
    // {
    // std::swap(array[child],array[parent]);
    // parent=child;
    // child=parent*2+1;
    // }
    // else 
    // return ;
    // }
    //
    //}
    //
    //int  head(int* array,int size)
    //{
    // for(int root=((size-2)/2);root>=0;root--)
    // {
    // Headadjust(array, size,root);
    // }
    //
    // int end=size-1;
    // while(end>0)
    // {
    // std::swap(array[0],array[end]);
    // Headadjust(array, end,0);
    // --end;
    // }
    // return 0;
    //}

    快速排序

  • 说到底,快速排序就是冒泡排序的一种改进,冒泡排序是通过每一趟冒泡将最大值(最小值)放到恰当位置,而快速排序則是每趟排序从待排序区间选一个基准值(也称作枢纽值),将比它小的数据全放在其左边,将比它大的值放在其右边然后递归其左右子区间对其排序,一层层递归下去,某区直到间只剩一个数据时,停止递归,此子区间已经算是有序,继而向其上层区间返回,一层层向上返回,当首次枢纽值的左右区间均已有序时,整个排序就算完成。



    //k快速排序




    //Vesion1
    //int  MID(int* array,int left,int right)
    //{
    // int begin=left;
    // int end=right-1;
    // int key=array[end];
    // while(begin<end)
    // {
    // while(begin<end&&array[begin]<=key)
    // begin++;
    //
    // while(begin<end&&array[end]>=key)
    // end--;
    //
    // if(begin<end)
    // {
    // std::swap(array[begin],array[end]);
    // }
    //
    // /*if(begin!=(right-1))
    // {
    // std::swap(array[begin],array[right-1]);
    // }*/
    // }
    // return end;
    //}
    //void Quicksort(int array[],int left,int right)
    //{
    // if(left<right)
    // {
    // int b=left;
    //// int e=right-1;
    //// int key=array[e];
    //// while(b<e)
    //// {
    //// while(b<e&&array[b]<=key)
    //// b++;
    //// if(b<e)
    //// array[e--]=array[b];
    ////
    //// while(b<e&&array[e]>=key)
    //// e--;
    //// if(b<e)
    //// array[b++]=array[e];
    ////
    //// }
    //// array[b]=key;
    //// Quicksort(array,left,b);
    //// Quicksort(array,b+1,right);
    //// }
    ////}
    //int GetmidIndex(int array[],int left,int right)
    //{
    //
    // int mid=(left+right)/2;
    // if(array[left]<array[right])
    // {
    // if(array[left]>array[mid])
    // return left;
    // else if(array[right]<array[mid])
    // return right;
    // else
    // return mid;
    // }
    // else
    // {
    // if(array[right]>array[mid])
    // return right;
    // else if(array[left]<array[mid])
    // return left;
    // else
    // return mid;
    // }
    //}
    //int  Quicksort(int array[],int left,int right)
    //{
    //  
    //    int mid = GetmidIndex(array, left, right);
    //    if (mid != right)
    //    {
    //            swap(array[mid], array[right]);//将中间值交换到此范围最右边,提高效率
    //    }
    //    int key = array[right];//一般将最
    //
    // int begin=left;
    // int end=right;
    //    key=array[end];
    // while(begin<end)
    // {
    //
    //    while(begin<end&&array[begin]<=key)
    // begin++;
    //
    // while(begin<end&&array[end]>=key)
    // end--;
    //
    // if(begin<end)
    // std::swap(array[begin],array[end]);   
    // }
    // if (array[right] <array[begin])
    //    {
    //        swap(array[begin], array[right]);
    //        return begin;
    //    }
    //    else
    //        return right;
    //
    //}
    //void QuickSort1(int *array, int left, int right)//递归
    // 
    //{
    //    
    //        if (left < right)
    //        {
    //            int boundary = Quicksort(array, left, right);//关键字所在位置的下标
    //            QuickSort1(array, left, boundary-1);
    //            QuickSort1(array, boundary + 1, right);
    //        }
    // }
    //
    //void sort(int* array,int left,int right)
    //{
    // stack<int> s;
    // if(left<right)
    // {
    // int mid=Quicksort(array,left, right);
    // if(left<mid-1)
    // {
    // s.push(left);
    //    s.push(mid-1);
    // }
    // if(mid+1<right)
    // {
    // s.push(mid+1);
    // s.push(right);
    // }
    // while(!s.empty())
    // {
    // int end=s.top();
    // s.pop();
    // int begin=s.top();
    // s.pop();
    // mid=Quicksort(array,begin, end);
    // if(begin<mid-1)
    // {
    // s.push(begin);
    // s.push(mid-1);
    // }
    // if(mid+1<end)
    // {
    // s.push(mid+1);
    // s.push(end);
    // }
    //
    // }
    // }
    //}


    一、归并排序

    1、基本思想

       归并排序(Merge sort,台湾译作:合并排序)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

    算法//
     void Merge(int* array,int left,int right,int* temp)
     {
    int mid=(left+right)/2;
    int b1=left;
         int end1=mid;
    int b2=mid+1;
    int end2=right;
    int index=left;
    while(b1<=end1&&b2<=end2)
    {
    if(array[b1]<=array[b2])
    {
    temp[index++]=array[b1++];
    }
    else
    {
    temp[index++]=array[b2++];
    }
    }
    while(b1<=end1)
    {
    temp[index++]=array[b1++];
    }
    while(b2<=end2)
    {
    temp[index++]=array[b2++];
    }
     }

    //void  _sort(int* array,int left,int right,int* temp)
    // {
    // if(left<right)
    // {
    // int mid=(left+right)/2;
    // _sort(array,left,mid,temp);
    // _sort(array,mid+1,right,temp);
    // Merge(array,left,right,temp);
    // memcpy(array+left,temp+left,(right-left+1)*sizeof(array[0]));
    // }
    //
    // }

    原理

    1>按照类似快速排序的方法递归地将待排序序列依次划分为两个区间,区间只剩一个数停止划分;
    2>如果一个区间只剩一个数,我们可将其看做有序区间,然后对左右两个小区间进行归并,归并后仍要保持区间的有序性;
    3>同2>提到的方法我们每次将两个有序的子区间归并为一个大的有序区间,并返回给上一层递归;

    4>直到所有划分的区间归并为一个有序序列,归并排序就算完成。



    一、计数排序

    1、基本思想

         给定一组要排序的序列,找出这组序列中的最大值,然后开辟一个最大值加1大小的数组,将这个数组里面的元素全部置零,然后用这个数组统计出要排序的序列中各个元素出现的次数。等到统计完成的时候,排序就已经完成了。

    图解(假设升序)




    2、算法执行步骤:

    1>找出待排序序列里的最大值max;

    2>开辟一个大小为max+1的临时数组tmp ,将数组元素的所有初值赋为0;

    3>遍历待排序序列,将序列中出现值作为下标,对tmp 数组的对应位置数据进行++;

    4>上述操作完成后,tmp中统计了每个数据在待排序列中出现的次数;

    3>最后,将tmp数组里值不为0的所有下标拷进原序列(注意同一个下标可能有多个重复值,都要进行拷贝,不能遗漏),排序就算完成。


    3、计数排序的优化

          从上面的例子中可以看出,3是待排序序列的最小值,在tmp数组中3 之前的位置并没有什么卵用,那么基于节省空间的角度进行考虑,我们可以开辟更小的临时数组统计次数,同样能完成排序。

        比如要对1000,999,1008这三数进行排序,按照普通的方法前998个空间都被浪费掉了,所以这时候我们对它进行优化。找出要排序的这组元素中的最大值和最小值,这样就确定了这组元素的范围,然后开辟这个范围加1大小的数组,然后再将要排序的元素映射到这个新开辟的数组中就可以了。

    结论:计数排序适用于数据比较集中的序列排序


    4、时间复杂度&&空间复杂度

        计数排序是一种非比较的排序方法,它的时间复杂度是O(N+K)空间复杂度是0(K),其中K是要排序的数组的范围。可以看出计数排序是一种以空间呢换取时间的方法。如果当K>N*logN的时候,计数排序就不是好的选择了,因为基于比较排序的算法的下限是O(N*logN)。 


    5、代码实现

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. #pragma once  
    2. #include <iostream>  
    3. #include <assert.h>  
    4. using namespace std;  
    5.   
    6. void PrintArray(int* a,size_t n)  
    7. {  
    8.     for (size_t i = 0; i < n; ++i)  
    9.     {  
    10.         cout<<a[i]<<" ";  
    11.     }  
    12.     cout<<endl;  
    13. }  
    14.   
    15.   
    16. void CountSort(int* a,size_t n)  
    17. {  
    18.     //查找待排序序列的最大值  
    19.     int max = a[0];  
    20.     for (size_t i = 0; i < n-1; ++i)  
    21.     {  
    22.         if (max < a[i+1])  
    23.         {  
    24.             max = a[i+1];  
    25.         }  
    26.     }  
    27.   
    28.     //开辟临时数组用来统计数据出现次数  
    29.     int* tmp = new int[max+1];  
    30.     //对临时数组初值赋为0  
    31.     memset(tmp,0,sizeof(int)*(max+1));  
    32.     //统计数据出现次数  
    33.     for (size_t i = 0; i < n; ++i)  
    34.     {  
    35.         tmp[a[i]]++;  
    36.     }  
    37.   
    38.     //将数据考回原数组  
    39.     int index = 0;  
    40.     for (int i = 0; i <= max; ++i)  
    41.     {  
    42.         while(tmp[i]--)  
    43.         {  
    44.             a[index] = i;  
    45.             ++index;  
    46.         }  
    47.     }  
    48.     delete []tmp;  
    49. }  
    50.   
    51. //计数排序的优化  
    52. void CountSort1(int* a,size_t n)  
    53. {  
    54.     //找到待排序序列的最大值和最小值  
    55.     int max = a[0];  
    56.     int min = a[0];  
    57.     for (size_t i = 0; i < n-1; ++i)  
    58.     {  
    59.         if (max < a[i+1])  
    60.         {  
    61.             max = a[i+1];  
    62.         }  
    63.         if (min > a[i+1])  
    64.         {  
    65.             min = a[i+1];  
    66.         }  
    67.     }  
    68.   
    69.     //开辟适当空间的数组  
    70.     int range = max - min + 1;  
    71.     int* tmp = new int[range];  
    72.     memset(tmp,0,sizeof(int)*(range));  
    73.     for (size_t i = 0; i < n; ++i)  
    74.     {  
    75.         tmp[a[i]-min]++;  
    76.     }  
    77.   
    78.     //将数据考回原数组  
    79.     int index = 0;  
    80.     for (int i = 0; i < range; ++i)  
    81.     {  
    82.         while(tmp[i]--)  
    83.         {  
    84.             a[index] = i+min;  
    85.             ++index;  
    86.         }  
    87.     }  
    88.     delete []tmp;  
    89. }  
    90.   
    91. void TestCountSort()  
    92. {  
    93.     int a[] = {2,5,4,9,3,6,8,7,1,0};  
    94.     int a1[] = {73,20,93,43,55,14,28,65,39,81};  
    95.     size_t sz = sizeof(a)/sizeof(a[0]);  
    96.     size_t sz1 = sizeof(a1)/sizeof(a1[0]);  
    97.   
    98.     CountSort(a,sz);  
    99.     CountSort1(a1,sz1);  
    100.     PrintArray(a,sz);  
    101.     PrintArray(a1,sz1);  
    102. }  


    运行结果:



    二、基数排序

    1、基数排序的分类
    LSD-- Least Significant Digit first(从低位向高位排)
    MSD-- Most Significant Digit first(从高位向低位排)

    由于原理差不多,此文只针对LSD进行分析。


    2、基本思想

        基数排序又称"桶子法",它从低位开始将待排序的数按照这一位的值放到相应的编号为0到9的桶中。等到低位排完之后得到一个序列,再将这个序列按照次低位的大小进入相应的桶中。以此类推,直到将所有的位都排完之后,这组数就已经有序了。


    图解(假设升序)



    3、算法执行步骤

    1》统计待排序序列中最大的数是几位数;

    2》开辟一个与原数组大小相同的临时数组tmp;

    3》用一个count数组统计原数组中某一位(从个位向高位统计)相同的数据出现的次数;

    4》用一个start数组计算原数组中某一位(从个位向高位计算)相同的数据出现的位置;

    5》将桶中数据从小到大(由顶至底)用tmp数组收集起来;

    6》循环3》4》5》直到所有位都被统计并计算过,然后用tmp收集起来;

    7》将tmp数组的数据拷回原数组,排序完成。


    图解:



    4、时间复杂度&&空间复杂度

        基数排序是一种非比较排序,它的时间复杂度是O(N*digit)其中digit是这组待排序中的最大的数的数量级。基数排序的空间复杂度就是在分配元素时,使用的桶空间,所以基数排序的空间复杂度是O(N)。它是一种稳定的排序方法。


    5、代码实现

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. #pragma once  
    2. #include <iostream>  
    3. #include <assert.h>  
    4. using namespace std;  
    5.   
    6. int GetMaxDigit(int* a,size_t n)//得到数组最大值的位数  
    7. {  
    8.     int digit = 1;  
    9.     int base = 10;  
    10.   
    11.     for (size_t i = 0; i < n; ++i)  
    12.     {  
    13.         //每次与几位数的最小数据(10、100、1000……)进行比较,  
    14.         //优点是相同位数的数据只统计一次  
    15.         while(a[i] >= base)  
    16.         {  
    17.             ++digit;  
    18.             base *= 10;  
    19.         }  
    20.     }  
    21.       
    22.     return digit;  
    23. }  
    24.   
    25. void LSDSort(int* a,size_t n)  
    26. {  
    27.     assert(a);  
    28.     int base = 1;  
    29.     int digit = GetMaxDigit(a,n);//统计位数  
    30.     int* tmp = new int[n];//开辟临时数组  
    31.   
    32.     while(digit--)  
    33.     {  
    34.         int count[10] = {0};  
    35.         //统计某位相同的数据出现次数  
    36.         for (size_t i = 0; i < n; ++i)  
    37.         {  
    38.             int index = a[i]/base %10;  
    39.             count[index]++;  
    40.         }  
    41.   
    42.         int start[10] = {0};  
    43.         //统计个位相同的数在数组a中出现的位置  
    44.         for (size_t i = 1; i < n; ++i)  
    45.         {  
    46.             start[i] = count[i-1] + start[i-1];  
    47.         }  
    48.   
    49.         memset(tmp,0,sizeof(int)*n);//为tmp数组初始化  
    50.         //从桶中收集数据(从小到大)  
    51.         for (size_t i = 0; i < n; ++i)  
    52.         {  
    53.             int index = a[i]/base %10;  
    54.             tmp[start[index]++] = a[i];  
    55.         }  
    56.   
    57.         //将数据拷回原数组  
    58.         memcpy(a,tmp,sizeof(int)*n);  
    59.         base *= 10;  
    60.     }  
    61.     delete []tmp;  
    62. }  
    63.   
    64. void PrintArray(int* a,size_t n)  
    65. {  
    66.     for (size_t i = 0; i < n; ++i)  
    67.     {  
    68.         cout<<a[i]<<" ";  
    69.     }  
    70.     cout<<endl;  
    71. }  
    72. void TestLDSort()  
    73. {  
    74.     int a[] = {73,20,93,43,55,14,28,65,39,81};  
    75.     size_t sz = sizeof(a)/sizeof(a[0]);  
    76.   
    77.     LSDSort(a,sz);  
    78.     PrintArray(a,sz);  
    79. }  


    运行结果:




     

     
     





  • 原创粉丝点击