数据结构:各大排序算法整理

1.直接排序法

整个排序过程为n-1趟插入,即先将序列中第1个记录看成是一个有序子序列,然后从第2个记录开始,逐个进行插入,直至整个序列有序。
例:
21,25,49,25*,16,08(*表示后一个25)
在这里插入图片描述

直接插入排序算法:

void InsertSort(SqList &L){
    int i,j;
    for(i=2;i<=L.length;++i)
       if( L.r[i].key<L.r[i-1].key)  //将L.r[i]插入有序子表
         {  L.r[0]=L.r[i];                  // 复制为哨兵
             L.r[i]=L.r[i-1];
            for(j=i-2; L.r[0].key<L.r[j].key;--j)
	                L.r[j+1]=L.r[j];       // 记录后移 
            L.r[j+1]=L.r[0];             //插入到正确位置
         }
 }

时间复杂度为O(n2);
空间复杂度为O(1);

直接插入排序算法小节:
1.是一种稳定的排序方法。
2.算法简便,且容易实现。
3.既适用于记录序列的顺序存储结构,也适用于链式存储结构,只是在单链表上无需移动记录,只需要修改相应的指针。
4.更加适合于初始记录本身基本有序(正序)的情况;当初始记录无序,且n较大时,算法的时间复杂度较高,不宜采用。

2.折半插入排序

void BInsertSort ( SqList &L ){
    for ( i = 2;  i <= L.length ; ++i )
    {    L.r[0] = L.r[i]; low = 1 ; high = i-1 ;
         while ( low <= high )       
            {  m = ( low + high ) / 2 ; 
               if  ( L.r[0].key < L.r[m]. key )   high = m -1 ;  //插入点在前一子表
               else  low = m + 1;                                               //插入点在后一子表
             }
        for ( j=i-1; j>=high+1; - - j ) L.r[j+1] = L.r[j];   //记录后移
        L.r[high+1] = L.r[0];              //插入r[0]到正确位置
     }
  }  // BInsertSort

时间复杂度为O(n2);
空间复杂度为 O(1);

折半插入排序算法小节:
1.是一种稳定的排序方法。
2.因为要进行折半查找,所以只能用于顺序存储结构,不能用于链式存储结构。
3.适合于初始记录无序,n较大时的情况。

3.希尔排序

先将整个待排记录序列分割成若干子序列,分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。
在这里插入图片描述

//希尔排序算法(其中某一趟的排序操作)
void   ShellInsert(SqList &L,int dk) {
      //对顺序表L进行一趟增量为dk的Shell排序,dk为步长因子
 for (i=dk+1;i<=L.length; ++ i)
      if(r[i].key < r[i-dk].key) { //需要将r[i] 插入有序增量子表        
       r[0]=r[i]//暂存在r[0]
       for(j=i-dk; j>0 &&(r[0].key<r[j].key); j=j-dk)
	 r[j+dk]=r[j]//关键字较大的记录在子表中后移
        r[j+dk]=r[0]//在本趟结束时将r[i]插入到正确位置
       }
}
//希尔排序算法-主程序
void   ShellSort(SqList &L,int dlta[ ]int t){
        //按增量序列dlta[0…t-1]对顺序表L作Shell排序
   for(k=0;k<t;++k)
     ShellInsert(L,dlta[k]);
   //增量为dlta[k]的一趟插入排序
}  // ShellSort

时间复杂度是n和d的函数:
O(n1.25)~O(1.6n1.25)—经验公式
空间复杂度为O(1)

希尔排序算法小节:
1.一种不稳定的排序方法,因为在排序过程,当增量大于1时,关键字较小的记录是跳跃式移动的。
2.实质是一种分组插入排序方法。
3.增量序列可以有各种取法,但应该使增量序列中的值没有除1之外的因子,并且最后一个增量值必等于1。
4.记录总的比较次数和移动次数都比直接插入排序要少,n越大时效果越明显,故适用于初始记录无序、n较大的情况。

4.冒泡排序

基本思想:每趟不断将相邻的记录两两比较,并按“前小后大” 规则交换。
在这里插入图片描述

void bubble_sort(SqList &L){
   m=L.length-1;flag=1;   //flag记录某一趟排序是否发生交换
   while((m>0)&&(flag==1))
   {  flag=0; //flag置为0,若本趟没发生交换,则不执行下一趟
      for(j=1;j<=m;j++)
         if(L.r[j].key>L.r[j+1].key)
          {  flag=1;
             x=L.r[j];L.r[j]=L.r[j+1];L.r[j+1]=x;    //交换
           }//endif
      m--;
    }//endwhile
 }        

时间复杂度为O(n2);
空间复杂度为O(1);

冒泡排序算法小节:
1.是一种稳定的排序方法。
2.既适用于记录序列的顺序存储结构,也适用于链式存储结构。
3.移动记录次数较多,算法平均时间性能比直接插入排序差。当初始记录无序,且n较大时,算法的时间复杂度较高,不宜采用。

5.快速排序

一趟快速排序的具体步骤如下:
(1)选择待排序表中的第一个记录作为枢轴,将枢轴记录暂存于r[0]的位置。附设两个指针low和high,初始时分别指向表中的下界和上界(第一趟时low=1;high=L.length)。
(2)从表的最右侧(high的位置)依次向左搜索,找到第一个关键字小于枢轴关键字pivotkey的记录,将其移动到low处,即当low<high时,若high所指记录的关键字大于等于pivotkey,则向左移动high(–high);否则将high所指向记录与枢轴记录交换。
在这里插入图片描述

int Partition ( SqList &L,int low,  int  high ) //一趟快速排序
{  L.r[0] = L.r[low];   pivotkey = L.r[low].key;
   while  ( low < high ) 
    { while ( low < high && L.r[high].key >= pivotkey )  --high;
                 L.r[low] = L.r[high];
      while ( low < high && L.r[low].key <= pivotkey )  ++low;
                 L.r[high] = L.r[low];
     }
    L.r[low]=L.r[0]; 
    return low;
}

void QSort ( SqList &L,int low,  int  high ) 
{  //调用前置初值:low=1;high=L.length
if  ( low < high ) 
    {  pivotloc = Partition(L, low, high ) ;
        Qsort (L, low, pivotloc-1) ; 
        Qsort (L, pivotloc+1, high )}
}

void QuickSort (SqList &L )
 {   
 QSort ( L, 1, L.length );
 }

平均时间复杂度O(nlog2n)。

快速排序算法小结:
1.由于存在记录的非顺次移动,所以此方法是不稳定的。
2.适合于顺序表,很难用于链式结构,因为在排序过程中需要定位表的下界和上界。
3.当n比较大时,在平均情况快速排序是所有内部排序方法中速度最快的一种,所以其适合于初始记录无序、n较大的情况。

6.简单选择排序

基本思想:每一趟在后面 n-i +1个中选出关键字最小的记录, 按顺序放在已经排好序的记录序列的最后(即已经有序序列的第 i 个记录)。
在这里插入图片描述
算法代码:

void SelectSort(SqList &L)
 { 
    for (i=1; i<L.length; ++i)
    {   //在L.r[i..L.length] 中选择key最小的记录
        k=i;     
        for( j=i+1;j<=L.length ; j++)
              if ( L.r[j].key <L.r[k].key) k=j; 
        if(k!=i)
	   L.r[i]←→L.r[k];            
    }  
}

时间复杂度:O(n²)
空间复杂度:O(1)

简单选择排序算法小结
1.可用于链式存储结构。
2.移动记录次数较少,当每一记录占用的空间较好时,此方法快于直接插入排序。

7.堆排序

基本思想:
(1)将序列r[1…n] 建初堆,交换r[1]和r[n],则r[n]为关键字最大的记录。
(2)将r[1…n-1]重新调整为堆,交换r[1]和r[n-1] ,则r[n-1]为关键字次大的记录。
(3)循环n-1次,直到交换了r[1]和r[2]为止,得到了一个非递减的有序序列r[1…n]。

无序序列建成堆 :在这里插入图片描述
堆的调整:将根结点r[1]与左、右子树根结点比较,并与小者交换;重复直至叶子结点,得到新的堆。在这里插入图片描述
堆排序算法-筛选调整

void  HeapAdjust(SqList &L,int s,int n){
 //设r[s+1,..,m]已是堆,将r[s..m]调整为以r[s]为根的大根堆
     rc=L.r[s];
     for(j=2*s;j<=m;j*=2){ //沿key较大的孩子结点向下筛选
          if(j<m && L.r[j].key<L.r[j+1].key) ++j;
          if(rc.key>=L.r[j].key)  break;
          L.r[s]=L.r[j;
          s=j;
     }
     L.r[s]=rc;
}

堆排序算法-建初堆

void  CreateHeap(SqList &L){
      //将无序序列L.r[1..n]建成初堆
      n=L.Length;
      for(i=n/2;i>0;--i)
            HeapAdjust(L,i,n);
}

堆排序算法

void  HeapSort(SqList &L){
      CreateHeap(SqList &L);
      for(i=L.length;i>1;--i){
           x=L.r[1]; L.r[1]=L.r[i]; L.r[i]=x;
           HeapAdjust(L,1,i-1);
      }
}

时间效率:O(nlog2n)
空间效率:O(1)

稳 定 性:不稳定
适用场合:顺序存储结构,且n 较大的情况

8.归并排序

排序过程:
1.将初始序列看成n个有序子序列,其中每个子序列长度为1。
2.两两合并,得到n/2个长度为2或1的有序子序列。
3.再两两合并,重复直至得到一个长度为n的有序序列为止。
在这里插入图片描述

void MSort(RedType R[],RedTypeT[],int low,int high)
{//R[low..high]归并排序后放入T[low..high]中
   if(low==high)  T[low]=R[low];
   else
   {
     mid=(low+high)/2;//将当前序列一分为二,求出分裂点mid
     MSort(R,S,low,mid);//对子序列R[low..high]递归归并排序,结果放入S[low..high]
     MSort(R,S,mid+1,high);//对子序列R[mid+1..high]递归归并排序,结果放入S[mid+1..high]
     Merge(S,T,low,mid,high);//将S[low..mid]和S[mid+1..high]归并到T[low..high]
   }
}

void MergeSort(SqList &L)
{//对顺序表L做归并排序
MSort(L.r,L.r,1,L.length);
}

时间效率:O(nlog2n)
空间效率:O(n)

稳 定 性:稳定
适 用 性:可用于链式结构,且不需要附加存储空间,但递归实现时仍要开辟相应的递归工作栈。

排序算法比较图:
在这里插入图片描述

排序算法选择规则:

n较大时:
(1)分布随机,稳定性不做要求,则采用快速排序;
(2)内存允许,要求排序稳定时,则采用归并排序;
(3)可能会出现正序或逆序,稳定性不做要求,则采用堆排序或归并排序。
n较小时:
(1)基本有序,要求稳定,则采用直接插入排序;
(2)分布随机,稳定性不做要求,则采用直接选择排序,若排序码不接近逆序,也可以采用直接插入排序。

以上是根据老师上课整理的笔记。

  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
南邮数据结构实验中,我们实现了各种内排序算法,并进行了性能比较。为了更直观地展示比较结果,我们使用了Excel进行数据记录和可视化分析。 首先,我们按照实验要求,分别实现了冒泡排序、插入排序、选择排序、快速排序、堆排序和归并排序等多种内排序算法。每种算法在相同的输入数据集上进行了测试,并记录了它们的执行时间。 接下来,我们将实验结果整理并输入Excel中。Excel的表格功能使得我们可以更直观地观察数据,并进行各种计算和比较。我们将每个算法的执行时间输入到不同的列中,每个数据集的执行时间占据一行。 在Excel中,我们可以使用各种功能和图表来进行性能比较。例如,我们可以使用线性图表来比较不同算法在不同数据集上的执行时间。通过对图表的观察,我们可以直观地了解各种算法的性能表现,并比较它们的优劣。 此外,我们还可以使用Excel的排序功能对数据进行排序,以便更好地进行比较和分析。我们可以按照执行时间的大小对算法进行排序,并观察它们的排名。通过排序和比较,我们可以更清楚地看到哪些算法在不同数据集上表现较好。 总之,使用Excel进行南邮数据结构实验中各种内排序算法的性能比较,能够使得数据更加直观、易于分析。通过Excel的表格和图表功能,我们可以对不同算法的执行时间进行比较,并找出性能较好的算法。这样的比较结果对我们在实际应用中选择合适的排序算法具有重要的参考价值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值