C#排序算法的比较

首先通过图表比较不同排序算法的时间复杂度和稳定性。


排序方法


平均时间


最坏情况


最好情况


辅助空间


稳定性

直接插入排序
O(n2)


O(n2)


O(n)


O(1)

冒泡排序
O(n2)


O(n2)


O(n)


O(1)

简单选择排序
O(n2)


O(n2)


O(n2)


O(1)

希尔排序 -
O(nlog2n)~O(n2)


O(nlog2n)~O(n2)


O(1)

快速排序
O(nlog2n)


O(n2)


O(nlog2n)


O(log2n)

堆排序
O(nlog2n)


O(nlog2n)


O(nlog2n)


O(1)

2-路归并排序
O(nlog2n)


O(nlog2n)


O(nlog2n)


O(n)

基数排序 O(d(n + rd)) O(d(n + rd)) O(d(n + rd)) O(rd)

注:1. 算法的时间复杂度一般情况下指最坏情况下的渐近时间复杂度。

2. 排序算法的稳定性会对多关键字排序产生影响。

下面通过C#代码说明不同的排序算法

插入排序

时间复杂度:平均情况—O(n2) 最坏情况—O(n2) 辅助空间:O(1) 稳定性:稳定

插入排序是在一个已经有序的小序列的基础上,一次插入一个元素。当然,刚开始这个有序的小序列只有1个元素,就是第一个元素。比较是从有序序列的末尾开始,也就是想要插入的元素和已经有序的最大者开始比起,如果比它大则直接插入在其后面,否则一直往前找直到找到它该插入的位置。如果碰见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳定的。

C# 代码   复制


void InsertSort(SqList &L) {

  // 对顺序表L作直接插入排序。

  int i,j;

  for (i=2; i<=L.length; ++i)

    if (LT(L.r[i].key, L.r[i-1].key)) {

      // "<"时,需将L.r[i]插入有序子表

      L.r[0] = L.r[i];                 // 复制为哨兵

      for (j=i-1;  LT(L.r[0].key, L.r[j].key);  --j)

        L.r[j+1] = L.r[j];             // 记录后移

      L.r[j+1] = L.r[0];               // 插入到正确位置

    }

} // InsertSort   

希尔排序(shell)

时间复杂度:理想情况—O(nlog2n) 最坏情况—O(n2) 稳定性:不稳定

希尔排序是按照不同步长对元素进行插入排序,当刚开始元素很无序的时候,步长最大,所以插入排序的元素个数很少,速度很快;当元素基本有序了,步长很小,插入排序对于有序的序列效率很高。所以,希尔排序的时间复杂度会比o(n^2)好一些。由于多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序是不稳定的。

C# 代码   复制


void ShellInsert(SqList &L, int dk) {

  // 对顺序表L作一趟希尔插入排序。本算法对算法10.1作了以下修改:

  //     1. 前后记录位置的增量是dk,而不是1;

  //     2. r[0]只是暂存单元,不是哨兵。当j<=0时,插入位置已找到。

  int i,j;

  for (i=dk+1; i<=L.length; ++i)

    if (LT(L.r[i].key, L.r[i-dk].key)) { // 需将L.r[i]插入有序增量子表

      L.r[0] = L.r[i];                   // 暂存在L.r[0]

      for (j=i-dk; j>0 && LT(L.r[0].key, L.r[j].key); j-=dk)

        L.r[j+dk] = L.r[j];              // 记录后移,查找插入位置

      L.r[j+dk] = L.r[0];                // 插入

    }

} // ShellInsert



void ShellSort(SqList &L, int dlta[], int t) {

   // 按增量序列dlta[0..t-1]对顺序表L作希尔排序。

   for (int k=0;k<t;k++)

      ShellInsert(L, dlta[k]);  // 一趟增量为dlta[k]的插入排序

} // ShellSort  

冒泡排序

时间复杂度:平均情况—O(n2) 最坏情况—O(n2) 辅助空间:O(1) 稳定性:稳定

冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。所以,如果两个元素相等,我想你是不会再无聊地把他们俩交换一下的;如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来,这时候也不会交换,所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法。

C# 代码   复制


void BubbleSort(SeqList R) {

  int i,j;

  Boolean exchange; //交换标志

  for(i=1;i<n;i++){ exchange="FALSE;" j="n-1;j">=i;j--) //对当前无序区R[i..n]自下向上扫描

            if(R[j+1].key< R[j].key){//交换记录

                R[0]=R[j+1]; //R[0]不是哨兵,仅做暂存单元

                R[j+1]=R[j];

                R[j]=R[0];

                exchange=TRUE; //发生了交换,故将交换标志置为真

            }

            if(!exchange) //本趟排序未发生交换,提前终止算法

            return;

  } //endfor(外循环)

}

快速排序

时间复杂度:平均情况—O(nlog2n) 最坏情况—O(n2) 辅助空间:O(log2n) 稳定性:不稳定

快速排序有两个方向,左边的i下标一直往右走,当a[i] <= a[center_index],其中center_index是中枢元素的数组下标,一般取为数组第0个元素。而右边的j下标一直往左走,当a[j] > a[center_index]。如果i和j都走不动了,i <= j, 交换a[i]和a[j],重复上面的过程,直到i>j。 交换a[j]和a[center_index],完成一趟快速排序。在中枢元素和a[j]交换的时候,很有可能把前面的元素的稳定性打乱,比如序列为 5 3 3 4 3 8 9 10 11, 现在中枢元素5和3(第5个元素,下标从1开始计)交换就会把元素3的稳定性打乱,所以快速排序是一个不稳定的排序算法,不稳定发生在中枢元素和a[j]交换的时刻。

C# 代码   复制


int Partition(SqList &L, int low, int high) {

 // 交换顺序表L中子序列L.r[low..high]的记录,使枢轴记录到位,

   // 并返回其所在位置,此时,在它之前(后)的记录均不大(小)于它

   KeyType pivotkey;

   RedType temp;

   pivotkey = L.r[low].key;     // 用子表的第一个记录作枢轴记录

   while (low < high) {           // 从表的两端交替地向中间扫描

      while (low < high && L.r[high].key>=pivotkey) --high;

      temp=L.r[low];

      L.r[low]=L.r[high];

      L.r[high]=temp;           // 将比枢轴记录小的记录交换到低端

      while (low  < high && L.r[low].key < =pivotkey) ++low;

      temp=L.r[low];

      L.r[low]=L.r[high];

      L.r[high]=temp;           // 将比枢轴记录大的记录交换到高端

   }

   return low;                  // 返回枢轴所在位置

} // Partition


void QSort(SqList &L, int low, int high) {

  // 对顺序表L中的子序列L.r[low..high]进行快速排序

  int pivotloc;

  if (low  <  high) {                      // 长度大于1

    pivotloc = Partition(L, low, high);  // 将L.r[low..high]一分为二

    QSort(L, low, pivotloc-1); // 对低子表递归排序,pivotloc是枢轴位置

    QSort(L, pivotloc+1, high);          // 对高子表递归排序

  }

} // QSort



void QuickSort(SqList &L) {

   // 对顺序表L进行快速排序

   QSort(L, 1, L.length);

} // QuickSort 

 

选择排序

时间复杂度:平均情况—O(n2) 最坏情况—O(n2) 辅助空间:O(1) 稳定性:不稳定

选择排序是给每个位置选择当前元素最小的,比如给第一个位置选择最小的,在剩余元素里面给第二个元素选择第二小的,依次类推,直到第n-1个元素,第n个元素不用选择了,因为只剩下它一个最大的元素了。那么,在一趟选择,如果当前元素比一个元素小,而该小的元素又出现在一个和当前元素相等的元素后面,那么交换后稳定性就被破坏了。比较拗口,举个例子,序列5 8 5 2 9, 我们知道第一遍选择第1个元素5会和2交换,那么原序列中2个5的相对前后顺序就被破坏了,所以选择排序不是一个稳定的排序算法。

C# 代码   复制


void SelectSort(SqList &L) {

  // 对顺序表L作简单选择排序。

  int i,j;

  for (i=1; i < L.length; ++i) { // 选择第i小的记录,并交换到位

    j = SelectMinKey(L, i);  // 在L.r[i..L.length]中选择key最小的记录

    if (i!=j) {                // L.r[i]←→L.r[j];   与第i个记录交换

      RedType temp;

      temp=L.r[i];

      L.r[i]=L.r[j];

      L.r[j]=temp;

    }

  }

} // SelectSort    

 

堆排序

时间复杂度:平均情况—O(nlog2n) 最坏情况—O(nlog2n) 辅助空间:O(1) 稳定性:不稳定

我们知道堆的结构是节点i的孩子为2*i和2*i+1节点,大顶堆要求父节点大于等于其2个子节点,小顶堆要求父节点小于等于其2个子节点。在一个长为n的序列,堆排序的过程是从第n/2开始和其子节点共3个值选择最大(大顶堆)或者最小(小顶堆),这3个元素之间的选择当然不会破坏稳定性。但当为n/2-1, n/2-2, ...1这些个父节点选择元素时,就会破坏稳定性。有可能第n/2个父节点交换把后面一个元素交换过去了,而第n/2-1个父节点把后面一个相同的元素没有交换,那么这2个相同的元素之间的稳定性就被破坏了。所以,堆排序不是稳定的排序算法

C# 代码   复制


void HeapAdjust(HeapType &H, int s, int m) {

  // 已知H.r[s..m]中记录的关键字除H.r[s].key之外均满足堆的定义,

  // 本函数调整H.r[s]的关键字,使H.r[s..m]成为一个大顶堆

  // (对其中记录的关键字而言)

  int j;

  RedType rc;

  rc = H.r[s];

  for (j=2*s; j < =m; j*=2) {   // 沿key较大的孩子结点向下筛选

    if (j < m && H.r[j].key < H.r[j+1].key) ++j; // j为key较大的记录的下标

    if (rc.key >= H.r[j].key) break;         // rc应插入在位置s上

    H.r[s] = H.r[j];  s = j;

  }

  H.r[s] = rc;  // 插入

} // HeapAdjust



void HeapSort(HeapType &H) {

   // 对顺序表H进行堆排序。

   int i;

   RedType temp;

   for (i=H.length/2; i>0; --i)  // 把H.r[1..H.length]建成大顶堆

      HeapAdjust ( H, i, H.length );

      for (i=H.length; i>1; --i) {

         temp=H.r[i];

         H.r[i]=H.r[1];

         H.r[1]=temp;  // 将堆顶记录和当前未经排序子序列Hr[1..i]中

                       // 最后一个记录相互交换

         HeapAdjust(H, 1, i-1);  // 将H.r[1..i-1] 重新调整为大顶堆

      }

} // HeapSort    

归并排序

时间复杂度:平均情况—O(nlog2n) 最坏情况—O(nlog2n) 辅助空间:O(n) 稳定性:稳定

归并排序是把序列递归地分成短序列,递归出口是短序列只有1个元素(认为直接有序)或者2个序列(1次比较和交换),然后把各个有序的段序列合并成一个有序的长序列,不断合并直到原序列全部排好序。可以发现,在1个或2个元素时,1个元素不会交换,2个元素如果大小相等也没有人故意交换,这不会破坏稳定性。那么,在短的有序序列合并的过程中,稳定是是否受到破坏?没有,合并过程中我们可以保证如果两个当前元素相等时,我们把处在前面的序列的元素保存在结果序列的前面,这样就保证了稳定性。所以,归并排序也是稳定的排序算法。

C# 代码   复制

void Merge (RedType SR[], RedType TR[], int i, int m, int n) {

   // 将有序的SR[i..m]和SR[m+1..n]归并为有序的TR[i..n]

   int j,k;

   for (j=m+1, k=i;  i < =m && j < =n;  ++k) {

      // 将SR中记录由小到大地并入TR

      if LQ(SR[i].key,SR[j].key) TR[k] = SR[i++];

      else TR[k] = SR[j++];

   }

   if (i < =m)  // TR[k..n] = SR[i..m];  将剩余的SR[i..m]复制到TR

      while (k < =n && i < =m) TR[k++]=SR[i++];

   if (j < =n)  // 将剩余的SR[j..n]复制到TR

      while (k < =n &&j  < =n) TR[k++]=SR[j++];

} // Merge



void MSort(RedType SR[], RedType TR1[], int s, int t) {

   // 将SR[s..t]归并排序为TR1[s..t]。

   int m;

   RedType TR2[20];

   if (s==t) TR1[t] = SR[s];

   else {

      m=(s+t)/2;            // 将SR[s..t]平分为SR[s..m]和SR[m+1..t]

      MSort(SR,TR2,s,m);    // 递归地将SR[s..m]归并为有序的TR2[s..m]

      MSort(SR,TR2,m+1,t);  // 将SR[m+1..t]归并为有序的TR2[m+1..t]

      Merge(TR2,TR1,s,m,t); // 将TR2[s..m]和TR2[m+1..t]归并到TR1[s..t]

   }

} // MSort



void MergeSort(SqList &L) {

  // 对顺序表L作归并排序。

  MSort(L.r, L.r, 1, L.length);

} // MergeSort  
时间: 2024-09-24 19:20:01

C#排序算法的比较的相关文章

经典排序算法 - 冒泡排序Bubble sort

 原文出自于 http://www.cnblogs.com/kkun/archive/2011/11/23/bubble_sort.html 经典排序算法 - 冒泡排序Bubble sort 原理是临近的数字两两进行比较,按照从小到大或者从大到小的顺序进行交换, 这样一趟过去后,最大或最小的数字被交换到了最后一位, 然后再从头开始进行两两比较交换,直到倒数第二位时结束,其余类似看例子 例子为从小到大排序, 原始待排序数组| 6 | 2 | 4 | 1 | 5 | 9 | 第一趟排序(外循环) 第

排序算法比较及其应用

一.将各种数据排序 只要实现了Comparable接口的数据类型就可以被排序. 但要使算法能够灵活地用不同字段进行排序,则是后续需要考虑的问题. 1.指针排序 在Java中,指针操作是隐式的,排序算法操作的总是数据引用,而不是数据本身. 2.键不可变 如果在排序后,用例还可以改变键值,那么数组很可能就不是有序的了.类似,优先队列也会乱套. Java中,可以用不可变数据类型作为键来避免这个问题,如String,Integer,Double和File都是不可变的. 3.廉价交换 使用引用的另一个好处

选择排序 —— 排序算法系列

假设我们有如下一个数组: 使用选择排序算法对这个数组进行排序,步骤如下: 第 1 次 在下标0到6之间找到最小的数字,我们可以发现最小的数字是15,它在下标为4的位置上: 把下标4上面的数字跟下标0上面的数字互换,得到排序如下图的数组: 第 2 次 在下标1到6之间找到最小的数字,我们可以发现最小的数字是33,它在下标为5的位置上: 把下标5上面的数字跟下标1上面的数字互换,得到排序如下图的数组: 第 3 次 在下标2到6之间找到最小的数字,我们可以发现最小的数字是48,它在下标为5的位置上:

排序算法Java版,以及各自的复杂度,以及由堆排序产生的top K问题

常用的排序算法包括: 冒泡排序:每次在无序队列里将相邻两个数依次进行比较,将小数调换到前面, 逐次比较,直至将最大的数移到最后.最将剩下的N-1个数继续比较,将次大数移至倒数第二.依此规律,直至比较结束.时间复杂度:O(n^2) 选择排序:每次在无序队列中"选择"出最大值,放到有序队列的最后,并从无序队列中去除该值(具体实现略有区别).时间复杂度:O(n^2) 直接插入排序:始终定义第一个元素为有序的,将元素逐个插入到有序排列之中,其特点是要不断的 移动数据,空出一个适当的位置,把待插

排序算法总结

各种排序算法总结  排序算法  插入排序 冒泡排序  选择排序  归并排序  快速排序 堆排序  计数排序  基数排序  桶排序  思想  构建有序序列 两两交换 每次找一个最小值 分治法思想 分治法思想 最小堆.最大堆 数字本身的属性  对数据选择多种基数  函数的映射关系.Hash  数据结构  数组  数组  数组  数组 不定   数组 数组 数组  数组  最差时间复杂度 O(n^2)   O(n^2)   O(n^2)   O(n*lgn)  O(n^2).改进O(n*lgn)  O

七大常见排序算法总结

文档版本 开发工具 测试平台 工程名字 日期 作者 备注 V1.0 2016.04.06 lutianfei none V1.1 2016.07.16 lutianfei 增加了归并排序说明 V2.0 2016.07.19 lutianfei 完善了排序算法的总结 排序另一种分法 外排序:需要在内外存之间多次交换数据才能进行 内排序: 插入类排序 直接插入排序 希尔排序 选择类排序 简单选择排序 堆排序 交换类排序 冒泡排序 快速排序 归并类排序 归并排序 排序方法 平均情况 最好情况 最坏情况

数据结构——各排序算法的比较

1.从时间复杂度比较  从平均时间复杂度来考虑,直接插入排序.冒泡排序.直接选择排序是三种简单的排序方法,时间复杂度都为O(n2),而快速排序.堆排序.二路归并排序的时间复杂度都为O(nlog2n),希尔排序的复杂度介于这两者之间.若从最好的时间复杂度考虑,则直接插入排序和冒泡排序的时间复杂度最好,为O(n),其它的最好情形同平均情形相同.若从最坏的时间复杂度考虑,则快速排序的为O(n2),直接插入排序.冒泡排序.希尔排序同平均情形相同,但系数大约增加一倍,所以运行速度将降低一半,最坏情形对直接

八种排序算法

最近一段时间自己在研究各种排序算法,于是自己写了一个八种排序算法的集合: /************************************************************************* > Copyright (c)2014 stay hungry,stay foolish !!! > File Name: sort.cpp > Author: kanty > Mail: [email protected] > Created Time:

排序算法 之 快速排序

快速排序是基于分治思想的一种排序算法,就像该方法的名字一样,速度比较快,所以叫做快速排序:它的平均时间复杂度为O(N*logN),最坏时间复杂度为O(n2),由于快速排序在序列元素数量多的时候速度比较快,所以很多语言内置的排序方法也是用快速排序实现的.快速排序也有很多优化的版本,比如在排序时基数的选择等等-下面就说一下一般的快速排序的实现. 基本思想: 快速排序的基本思想就是,先从待排序的序列中任选一个元素作为基数,然后将序列中的其他小于基数的元素放在基数的左边,大于或等于基数的元素放在基数的右

排序算法的JS实现

排序算法是基础算法,虽然关键在于算法的思想而不是语言,但还是决定借助算法可视化工具结合自己常用的语言实现一下 1.冒泡排序 基本思路:依次比较两两相邻的两个数,前面数比后面数小,不变.前面数比后面数大,交换顺序.一轮下来,最后的一个数是最大的数. 外循环每增加一次,内循环减少一次. 图形展示: function bubbleSort(arr){ for (var i = 0; i < arr.length; i++) { for (var j = 0; j< arr.length-i-1; j