数据结构 第十章内部排序

第十章 内部排序

知识点
  1. 掌握排序的基本概念和各种排序方法的特点,并能加以灵活应用

    1. 插入排序
      1. 基本思想:
        1. 每步将一个待排序的对象,按其关键码大小,插入到前面已经排好序的一组对象的适当位置上,直到对象全部插入为止。
        2. 即边插入边排序,保证子序列中随时都是排好序的
      2. 分类
        1. 直接插入排序(基于顺序查找)
          1. 排序过程
            1. 整个排序过程为n-1趟插入,
            2. 先将序列中第1个记录看成是一个有序子序列,
            3. 然后从第2个记录开始,逐个进行插入,
            4. 直至整个序列有序。
          2. 算法分析
            1. 设对象个数为n,则执行n-1趟,比较次数和移动次数与初始排列有关
            2. 最好情况下每趟只需比较 1 次,不移动 总比较次数为 n-1
            3. 最坏情况下:第 i 趟比较i 次,移动i+1次
            4. 若出现各种可能排列的概率相同,则可取最好情况和最坏情况的平均情况,平均情况比较次数和移动次数为n2/4
            5. 时间复杂度为 O(n2),空间复杂度为 O(1),是一种稳定的排序方法
    2. 折半插入排序(基于折半查找)->减少关键字间的比较次数
      1. 在插入 r[i] 时,利用折半查找法寻找 r[i] 的插入位置
      2. 算法分析
        1. 折半查找比顺序查找快,所以折半插入排序就平均性能来说比直接插入排序要快
        2. 它所需要的关键码比较次数与待排序对象序列的初始排列无关,仅依赖于对象个数。
        3. 在插入第 i 个对象时,需要经过 [log2 i] +1 次关键码比较,才能确定它应插入的位置
        4. 当 n 较大时,总关键码比较次数比直接插入排序的最坏情况要好得多,但比其最好情况要差
        5. 在对象的初始排列已经按关键码排好序或接近有序时,直接插入排序比折半插入排序执行的关键码比较次数要少
        6. 折半插入排序的对象移动次数与直接插入排序相同,依赖于对象的初始排列
        7. 减少了比较次数,但没有减少移动次数,平均性能优于直接插入排序
        8. 时间复杂度为 O(n2),空间复杂度为 O(1),是一种稳定的排序方法
      3. 希尔排序(基于逐躺缩小增量)
        1. 算法思想的出发点:
          1. 直接插入排序在基本有序时,效率较高
          2. 在待排序的记录个数较少时,效率较高
        2. 基本思想:
          1. 先将整个待排记录序列分割成若干子序列,分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。
        3. 技巧:
          1. 子序列的构成不是简单地“逐段分割”
          2. 将相隔某个增量dk的记录组成一个子序列
          3. 让增量dk逐趟缩短(例如依次取5,3,1)
          4. 直到dk=1为止。
        4. 优点:
          1. 小元素跳跃式前移
          2. 最后一趟增量为1时,序列已基本有序
          3. 平均性能优于直接插入排序
        5. 算法分析
          1. 时间复杂度是n和d的函数
          2. 空间复杂度为 O(1)
          3. 是一种不稳定的排序方法
  2. 熟练掌握直接插入排序、折半插入排序、起泡排序、直接选择排序、快速排序的排序算法及其性能分析

    1. 直接插入排序

      1. 算法
      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]; //插入到正确位置
          }
      }
      
      1. 算法分析
        1. 设对象个数为n,则执行n-1趟,比较次数和移动次数与初始排列有关
        2. 最好情况下每趟只需比较 1 次,不移动 总比较次数为 n-1
        3. 最坏情况下:第 i 趟比较i 次,移动i+1次
        4. 若出现各种可能排列的概率相同,则可取最好情况和最坏情况的平均情况,平均情况比较次数和移动次数为n2/4
        5. 时间复杂度为 O(n2),空间复杂度为 O(1),是一种稳定的排序方法
    2. 折半插入排序

      1. 算法
      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];
          }
      }  // BInsertSort
      
      1. 算法分析
        1. 折半查找比顺序查找快,所以折半插入排序就平均性能来说比直接插入排序要快
        2. 它所需要的关键码比较次数与待排序对象序列的初始排列无关,仅依赖于对象个数。
        3. 在插入第 i 个对象时,需要经过 [log2 i] +1 次关键码比较,才能确定它应插入的位置
        4. 当 n 较大时,总关键码比较次数比直接插入排序的最坏情况要好得多,但比其最好情况要差
        5. 在对象的初始排列已经按关键码排好序或接近有序时,直接插入排序比折半插入排序执行的关键码比较次数要少
        6. 折半插入排序的对象移动次数与直接插入排序相同,依赖于对象的初始排列
        7. 减少了比较次数,但没有减少移动次数,平均性能优于直接插入排序
        8. 时间复杂度为 O(n2),空间复杂度为 O(1),是一种稳定的排序方法
    3. 起泡排序

      1. 基本思想
        1. 基本思想:每趟不断将记录两两比较,并按“前小后大” 规则交换
      2. 优点
        1. 优点:每趟结束时,不仅能挤出一个最大值到最后面位置,还能同时部分理顺其他元素;一旦下趟没有交换,还可提前结束排序
      3. 算法
      void main() 			 
      {	int a[11];		/*a[0]不用,之用a[1]~a[10]*/
          int i,j,t;
          printf("\nInput 10 numbers: \n");
          for(i=1;i<=10;i++)	scanf("%d",&a[i]);	printf("\n");
          for(j=1;j<=9;j++)
              for(i=1;i<=10-j;i++)
              if(a[i]>a[i+1])	{t=a[i];a[i]=a[i+1];a[i+1]=t;}      //交换
          for(i=1;i<=10;i++)	printf("%d ",a[i]);   
      }
      
      1. 算法分析
        1. 设对象个数为n,比较次数和移动次数与初始排列有关
        2. 最好情况:只需 1趟排序,比较次数为 n-1,不移动
        3. 最坏情况:需 n-1趟排序,第i趟比较n-i次,移动3(n-i)次
        4. 时间复杂度为 O(n2),空间复杂度为 O(1),是一种稳定的排序方法
    4. 快速排序

      1. 基本思想
        1. 任取一个元素 (如第一个) 为中心
        2. 所有比它小的元素一律前放,比它大的元素一律后放,形成左右两个子表;
        3. 对各子表重新选择中心元素并依此规则调整,直到每个子表的元素只剩一个
      2. 算法
      void main (){   
          QSort ( L, 1, L.length ); 
          }
      
      void QSort ( SqList &L,int low,  int  high ){  
          if  ( low < high ) {  
              pivotloc = Partition(L, low, high ) ;
              Qsort (L, low, pivotloc-1) ; 
              Qsort (L, pivotloc+1, 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;
      }
      
      1. 算法分析
        1. 可以证明,平均计算时间是O(nlog2n)。
        2. 实验结果表明:就平均计算时间而言,快速排序是我们所讨论的所有内排序方法中最好的一个。
        3. 快速排序是递归的,需要有一个栈存放每层递归调用时参数(新的low和high)。
        4. 最大递归调用层次数与递归树的深度一致,因此,要求存储开销为 O(log2n) 。
        5. 最好:划分后,左侧右侧子序列的长度相同
        6. 最坏:从小到大排好序,递归树成为单支树,每次划分只得到一个比上一次少一个对象的子序列,必须经过 n-1 趟才能把所有对象定位,而且第 i 趟需要经过 n-i 次关键码比较才能找到第 i 个对象的安放位置
        7. 时间效率:O(nlog2n) —每趟确定的元素呈指数增加
        8. 空间效率:O(log2n)—递归要用到栈空间
        9. 稳 定 性: 不稳定 —可选任一元素为支点。
    5. 直接选择排序

      1. 基本思想
        1. 每一趟在后面 n-i +1个中选出关键码最小的对象, 作为有序序列的第 i 个记录
      2. 算法
        void SelectSort(SqList &K)
        { 
            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];            
            }  
        }
        
      3. 算法分析
        1. 最好情况:0
        2. 最坏情况3(n-1)
        3. 比较次数:(n^2-n)/2
        4. 时间复杂度:O(n²),空间复杂度:O(1),稳定
  3. 掌握希尔排序、归并排序、堆排序、基数排序的方法及其性能分析

    1. 希尔排序
      1. 算法
        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
        
        void   ShellInsert(SqList &L,int dk) {
            
        for(i=dk+1;i<=L.length; ++ i)
            if(r[i].key < r[i-dk].key) {         
            r[0]=r[i]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]}
        }
        
      2. 算法分析
        1. 时间复杂度是n和d的函数
        2. 空间复杂度为 O(1)
        3. 是一种不稳定的排序方法
    2. 归并排序
      1. 归并
        1. 将两个或两个以上的有序表组合成一个新有序表
      2. 排序过程
        1. 初始序列看成n个有序子序列,每个子序列长度为1
        2. 两两合并,得到【n/2】个长度为2或1的有序子序列
        3. 再两两合并,重复直至得到一个长度为n的有序序列为止
      3. 算法分析
        1. 时间效率:O(nlog2n)
        2. 空间效率:O(n)
        3. 稳 定 性:稳定
    3. 堆排序
      1. 基本思想
        1. 将无序序列建成一个堆
        2. 输出堆顶的最小(大)值
        3. 使剩余的n-1个元素又调整成一个堆,则可得到n个元素的次小(大)值
        4. 重复执行,得到一个有序序列
      2. 算法分析
        1. 时间效率:O(nlog2n)
        2. 空间效率:O(1)
        3. 稳 定 性:不稳定
        4. 适用于n 较大的情况
    4. 基数排序
      1. 基数排序(Radix Sorting) 又称为桶排序或数字排序:按待排序记录的关键字的组成成分(或“位”)进行排序。
      2. 基数排序和前面的各种内部排序方法完全不同,不需要进行关键字的比较和记录的移动。借助于多关键字排序思想实现单逻辑关键字的排序。
      3. 时间效率:O(d( n+rd))
      4. 空间效率:O(n+rd)
      5. 稳 定 性:稳定
习题

10.1 设待排序文件的初始排序码序列为{24,5,27,69,56,16,27*,41,8,35},试分别写出使用以下排序算法排序时,排序码序列在各趟结束时的状态。(P229-2)
(1)直接插入排序
(2)希尔排序
(3)冒泡排序
(4)快速排序
(5)直接选择排序
(6)堆排序

10.2 判别以下序列是否为堆(小顶堆或大顶堆)。如果不是,则把它调整为堆(要求记录交换次数最少)。
  (1)(100, 86, 48, 73, 35, 39, 42, 57, 66, 21);
  (2)(12, 70, 33, 65, 24, 56, 48, 92, 86, 33);
  (3)(103, 97, 56, 38, 66, 23, 42, 12, 30, 52, 06, 20);
(4)(05, 56, 20, 23, 40, 38, 29, 61, 35, 76, 28, 100)。

10.3 试以单链表为存储结构实现简单选择排序的算法。

10.4试列出3种平均时间复杂度为O(nlog2n)的排序算法,并给出每种排序算法的稳定性。

10.5针对数据规模,不同的排序算法各有优势,你会如何选择它们?

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值