内部排序

文章目录

  • 内部排序
    • 一、概述
      • (1)排序定义
      • (2)稳定性
      • (3)内部排序和外部排序
      • (4)两种基本操作
      • (5)数据类型定义
    • 二、分类
      • (1)插入排序
      • (2)交换排序
      • (3)选择排序
      • (4)归并排序
    • 三、总结
      • (1)按平均的时间性能来分:
      • (2)按空间性能分(排序过程中所需辅助空间大小)
      • (3)按排序方法的稳定性分:
    • 文章索引

一、概述

(1)排序定义

设n个记录{R1,R2,…,Rn},其关键字序列{K1,K2,…,Kn},重新确定1,2,…,n的一种排列p1,p2,…pn, 使得Kp1≤Kp2≤…≤Kpn,从而将{R1,R2,…,Rn}重新排列为{Rp1,Rp2,…,Rpn}的操作。

(2)稳定性

稳定排序和不稳定排序
关键字不是主关键字,排序结果不唯一;
对关键字相同的记录(设Ki=Kj (i≠j)),若排序前i<j

  • 稳定的排序方法:排序后的序列中Ri仍领先于Rj
  • 不稳定的排序方法:排序后的序列中Rj领先于Ri

(3)内部排序和外部排序

  • 内部排序:整个排序过程不需要访问外存便能完成
  • 外部排序:参加排序的记录数量很大,内存无法一次性容纳全部记录,则在排序过程中需对外存进行访问,不断地在内、外存之间进行数据交换。

(4)两种基本操作

比较两个关键字的大小
移动记录,和存储方式有关

(5)数据类型定义

typedef struct {KeyType key;  //关键字项//记录中的其它数据项
}RecordType;   //记录类型
typedef struct {RecordType r[MAXSIZE+1]; //r[0]未用int length; //待排序记录的个数
}SqList;   //顺序表类型

二、分类

(1)插入排序

是主要借助插入操作完成排序的一类方法

  • 直接插入排序

    • 思想

      将无序序列的一个或几个记录按关键字的大小,插入到已排好序的有序序列中,使得有序序列的长度增加,而无序序列的长度减少。

    • 实现过程

      初始时认为第1个记录已排好序;
      从待排序的序列中取出第1个记录,按关键字的大小,插入到已排好序的有序序列中,使之成为长度加1的有序序列,而待排序的序列长度减1;
      重复执行上一步操作,直至待排序的记录都插入到有序序列中为止,即最终待排序的序列长度为0,而有序序列中包含了所有记录。

      • 第i(2≤i≤n)趟直接插入排序

        在有序表r[1…i-1]中插入记录r[i],使得子序列r[1…i]成为长度为i的有序表

      • 监视哨

        在有序序列中查找插入位置:采用顺序查找算法。在有序序列中从后向前进行查找,边查找边后移关键字大于r[i].key的记录。为避免每次都要检查数组下标是否出界,在r[0]处设监视哨。

      • 算法

        • 算法实现

          void InsertSort(SqList &L)
          { //用直接插入排序方法对L指示的顺序表进行排序
          for (i=2; i<=L.length; i++)
          if(LT(L.r[i].key,L.r[i-1].key))
          {L.r[0]=L.r[i];   //设监视哨       L.r[i]=L.r[i-1];for(j=i-2;LT(L.r[0].key,L.r[j].key);j--) L.r[j+1]=L.r[j]; //记录后移L.r[j+1]=L.r[0];
          }
          }
          
        • 效率分析

          • 空间复杂度:O(1),需要一个监视哨的辅助空间
          • 稳定性:是稳定的排序方法
          • 时间复杂度:O(n2)
        • 优缺点

          优点:
          在待排序序列是“正序”或接近“正序”时该算法的时间复杂度可提高至O(n)的数量级;
          由于该算法简单,所以在待排序序列长度n较小时(待排序的记录个数比较少),其时间效率也是较高的
          缺点:时间效率整体不高

        • 改进方法

          减少记录的移动次数
          减少关键字之间的比较次数

  • 其它插入排序方法

    • 折半插入排序

      稳定,适用于待排序记录数量很大的情况。

      • 思想

        在有序表R[1…i-1]中插入R[i]时,由于R[1…i-1]是一个按关键字有序的序列,因此可以用折半查找来确定R[i]的插入位置

      • 实现过程

        Step1:用折半查找确定R[i]在R[1…i-1]中的插入位置
        Step2:最后一个记录到插入位置的记录依次后移一个位置
        Step3:插入记录R[i]

        • 算法实现

          void BinInsertSort(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(LT(L.r[0].key,L.r[m].key)) high=m-1;  else low=m+1;  }//while
          for(j=i-1;j>=high+1;j--) L.r[j+1]=L.r[j]; //记录后移
          L.r[high+1]=L.r[0];     //插入第i个记录
          }//for
          }//BinInsertSort
          
        • 效率分析

          • 空间复杂度:O(1),需要一个监视哨的辅助空间
          • 稳定性:是稳定的排序方法
          • 时间复杂度:O(n2)
    • 表插入排序方法

      若希望在排序过程中不移动记录,只能改变存储结构,使用静态链表存储待排序序列,进行表插入排序
      设静态链表中已排好序的子序列以循环链表表示,并设数组中下标为0的分量为头结点,令其关键字为排序过程中不可能达到的最大值MAXINT。

    • 希尔排序

      特点: 相隔某个增量的记录组成一个子序列。
      希尔排序又称“缩小增量排序”

      • 思想

        先将待排序记录序列按指定的增量间隔分割成若干子序列分别进行直接插入排序
        不断缩小增量间隔,重复以上操作
        待整个序列中记录“基本有序” 时,最后再对全体记录进行一次直接插入排序。

      • 实现过程

        • 算法实现

          • 一趟希尔排序

            void ShellInsert(SqList &L,int dk)
            {//对L指示的顺序表进行一趟希尔排序,增量为dk
            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];
            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指示的顺序表进行希尔排序
            int k;
            for (k=0; k<t; ++k ) ShellInsert(L,dlta[k]);
            } //Shellsort
            
        • 效率分析

          • 空间复杂度:O(1),需要一个暂存记录的辅助空间

          • 稳定性:不稳定

          • 时间复杂度

            其运行时间取决于增量序列 ,是增量和记录数的函数

(2)交换排序

  • 冒泡排序

    • 思想

      一旦进行比较的两个记录的关键字逆序(即顺序与排序要求相反),则交换它们的位置。

    • 冒泡排序过程

      • 第1趟:

        从第1到第n个记录,若相邻的两个记录逆序,则交换之;否则继续比较下面两个相邻记录……结果使关键字最大的记录被安置到第n个位置上。

      • 第i趟

        从第1到第n-i+1个记录,依次比较两个相邻记录的关键字,逆序时交换之, 使其中关键字最大的记录被交换到第n-i+1个位置上。

      • 结束条件

        在一趟排序过程中未出现交换记录的操作,则整个序列有序。

    • 算法实现

      void BubbleSort(SqList &L)
      { //对L指示的顺序表进行冒泡排序
      for(i=1;i<L.length;i++)
      { //最多进行n-1趟flag=TRUE;     // flag:标志for(j=1;j<=L.length-i;j++)if( LT(L.r[j+1].key,L.r[j].key))               { flag=FALSE;                         temp=L.r[j+1];L.r[j+1]=L.r[j];L.r[j]=temp;}if(flag) return;
      }//for
      }//BubbleSort
      
    • 效率分析

      • 空间复杂度:O(1),需要一个供交换用的辅助空间
      • 稳定性:是稳定的排序方法
      • 时间复杂度:O(n2)
  • 快速排序

    • 思想

      在待排序序列中任选一个记录作为枢轴,通过一趟排序将待排序记录分割成独立的两部分,其中前一部分记录的关键字均小于等于枢轴的关键字,后一部分记录的关键字都大于等于枢轴的关键字;
      分别对这两个子序列按照同样方法再进行快速排序(划分),直到分割出的每个子序列只包含一个记录为止;
      此时整个序列达到有序

    • 一趟快速排序

      • 思想

        通常选取待排序序列的第一个记录作为枢轴(或称支点),然后将关键字比枢轴关键字小的记录都放到它前面,将关键字比枢轴关键字大的记录都放到它后面。因此一趟快速排序之后,以枢轴最后的位置为分界线,将待排序序列分成独立的两部分。其中前一部分记录的关键字都不大于后一部分记录的关键字。

      • 过程

        选取待排序序列的第一个记录作为枢轴(或称支点),将其关键字暂存在变量pivotkey中;
        设置两个指针(实际上是整型数,指示数组下标)low和high,其初值位置分别为待排序序列的下界和上界位置;
        先从high所指位置开始向左搜索,找到第一个关键字小于pivotkey的记录,将它和枢轴记录互换位置;
        接着从low所指位置开始向右搜索,找到第一个关键字大于pivotkey的记录,将它和枢轴记录互换位置;
        重复以上两步,直至low=high为止。

      • 实现

        int Partition(SqList &L,int low,int high)
        { 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[low]<->L.r[high];
        }//while
        return low;    //返回枢轴所在位置
        }// Partition
        
        • 改进
          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];        }//whileL.r[low]=L.r[0];return low;                    }// Partition
        
    • 递归调用

      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 );  //对高端子序列递归排序
      }
      }// QSortvoid QuickSort ( SqList &L )
      { //对L指示的顺序表进行快速排序
      QSort ( L, 1, L.length );
      } // QuickSort
      
    • 效率分析

      • 空间复杂度:O(logn),需要一个栈空间

        若每趟排序都能将记录序列均匀分割成长度相近的两个子序列,则栈的最大深度为 (包括最外层参数进栈)
        若每趟排序后枢轴都偏向子序列的一端,则栈的最大深度为n。

        • 改进

          在一趟排序后比较分割出的2个子序列的长度,然后先对长度短的子序列进行下一趟快排,这样栈的最大深度可降为O(logn)

      • 稳定性:不稳定

      • 时间复杂度:O(nlogn)

        就平均时间而言,快速排序性能最好
        若经过每一趟快速排序得到的两个子序列的长度基本相等,则快速排序的时间效率最高: O(nlogn)
        若初始序列按关键字有序或基本有序(正序或逆序),则枢轴总是子序列中关键字最小或最大的记录,这样每趟快排划分出的两个子序列都有一个接近空序列,此时快速排序将蜕化为冒泡排序:O(n2)

        • 改进

          使用“三者取中法”来选取枢轴记录,即:比较r(low).key、r(high).key和r((low+hight)/2).key,然后取三者之中关键字居中的那个记录作为枢轴。

(3)选择排序

  • 简单选择排序

    • 思想

      每一趟在i…n共n-i+1(i=1,2,……n-1)个记录中选取关键字最小的记录使之成为有序序列的第i个记录。

    • 过程

      • 第1趟

        在第1到第n共n个记录中通过n-1次关键字之间的比较,选取关键字最小的记录,若它不是第1个记录,则交换之;

      • 第i趟

        在第i到第n共n-i+1个记录中通过n-i次关键字之间的比较,选取关键字最小的记录,若它不是第i个记录,则交换之;

    • 算法

      void SelectSort ( SqList &L )
      {  //对L指示的顺序表进行简单选择排序
      for(i=1; i<L.length; ++i)
      { minloc=i;for(j=i+1;j<=L.length;j++)if(LT(L.r[j].key, L.r[minloc].key)) minloc=j;if (i!=minloc) {L.r[i] <-> L.r[minloc];}
      }//for
      } // SelectSort
      
    • 分析

      • 空间复杂度:O(1),需要一个供交换用的辅助空间
      • 稳定性:不稳定
      • 时间复杂度:O(n2)
  • 树形选择排序

    • 思想

      借鉴锦标赛赛制安排的思想

    • 分析

      • 空间复杂度:O(n),用于存储中间结点的辅助空间
      • 稳定性:不稳定
      • 时间复杂度:O(nlogn)
  • 堆排序

      • 大顶堆
      • 小顶堆
    • 算法

      • 筛选算法

        void HeapAdjust(SqList &H, int s, int m)
        { /*已知H->r[s..m]中记录的关键字除H->r[s].key之外均满足堆的定义,本函数调整H->r[s]的关键字,使H->r[s..m]成为一个大顶堆*/
        rc= H.r[s];   //rc暂存子树根结点的元素值
        for(j=2*s; j<=m; j*=2)
        { if (j<m && LT(H.r[j].key, H.r[j+1].key)) ++j;
        if (!LT(rc.key, H.r[j].key)) break;
        H.r[s]= H.r[j]; s=j;
        }
        H.r[s]=rc;   //把rc放入到最终应该在的位置
        }// HeapAdjust
        
      • 堆排序算法

        void HeapSort(SqList &H)
        { // 对H指向的顺序表进行堆排序
        for(i=H.length/2; i>0; --i ) HeapAdjust(H, i, H.length);
        for(i=H.length; i>1; --i )
        { H.r[1]<->H.r[i];HeapAdjust(H,1,i-1);
        }//for
        }// HeapSort
        
    • 效率分析

      • 空间复杂度:O(1),需要一个暂存记录的辅助空间
      • 稳定性:不稳定
      • 时间复杂度:O(nlogn)

(4)归并排序

  • 思想

    通常采用2-路归并算法
    将两个或两个以上有序子序列“归并”为一个有序序列

  • 过程

    初始时可看成n个有序子序列,每个子序列长度为1
    两两归并得到n/2个长度为2或1的有序子序列
    再两两归并……直至得到一个长度为n的有序序列为止

  • 效率分析

    • 空间复杂度:O(n)
    • 稳定性:稳定
    • 时间复杂度:O(nlogn)

三、总结

(1)按平均的时间性能来分:

  • (1)时间复杂度为O(nlog2n):快速排序、堆排序和归并排序;
  • (2)时间复杂度为O(n2):直接插入排序、折半插入排序、起泡排序和简单选择排序;
  • (3)时间复杂度为O(d*n): 基数排序
  • (4)当待排记录按关键字有序,直接插入排序和起泡排序能达到O(n); 而对快速排序是最不好的情况,时间性能蜕化为O(n2)。

(2)按空间性能分(排序过程中所需辅助空间大小)

  • (1) 空间复杂度是O(1) :直接插入、起泡、简单选择和堆排序
  • (2) 空间复杂度是O(log2n):快速排序
  • (3) 空间复杂度是O(n) :归并排序

(3)按排序方法的稳定性分:

  • 希尔排序、快速排序、简单选择和堆排序不稳定

文章索引

  • 【知识索引】【数据结构(C语言)】
  • 【数据结构(C语言)】数据结构-表
  • 【数据结构(C语言)】数据结构-树
  • 【数据结构(C语言)】数据结构-图
  • 【数据结构(C语言)】数据结构-查找
  • 【数据结构(C语言)】数据结构-内部排序

【数据结构(C语言)】数据结构-内部排序相关推荐

  1. 用数据结构c语言写成绩排序,C语言数据结构 快速排序实例详解

    C语言数据结构 快速排序实例详解 一.快速排序简介 快速排序采用分治的思想,第一趟先将一串数字分为两部分,第一部分的数值都比第二部分要小,然后按照这种方法,依次对两边的数据进行排序. 二.代码实现 # ...

  2. 【数据结构与算法】内部排序之一:插入排序和希尔排序的N中实现(不断优化,附完整源码)...

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/20043459   前言 本来想将所有的内部排序总结为一篇博文,但是随着研究的深入,还是放弃 ...

  3. 数据结构实验8:内部排序

    实验8                                                                            姓名: 学号: 班级: 8.1 实验目的 ...

  4. 【数据结构与算法】内部排序之三:堆排序(含完整源码)

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/20227303 前言 堆排序.快速排序.归并排序(下篇会写这两种排序算法)的平均时间复杂度都 ...

  5. c语言编程文件中删除数据结构,C语言数据结构实战(一)顺序表的插入与删除

    今天学习了思成老师的数据结构实战教程 写了一个顺序表 插入和删除的操作 把源码共享给大家 一共包括list.c stu.h main.c list.h   .h文件是头文件 需要引入 具体的功能我都已 ...

  6. 栈的应用行编辑数据结构c语言,数据结构题典022:栈的应用——行编辑程序(C语言版)...

    编写程序实现从终端接收用户输入的数据,并存入用户数据区.输入#表示上一字符无效,输入@表示当前输入行整行无效. /* * line editor by using stack. * * fduan, ...

  7. C语言实现各个排序算法(直接插入排序,折半插入排序,希尔排序,冒泡排序,简单选择排序)

    数据结构开发总结报告 --内部排序综合设计程序的编程实现 #include<stdio.h> #define MaxSize 20 #include<windows.h> ty ...

  8. 数据结构排序算法 内部排序(冒泡、鸡尾酒、选择、简单插入、二分插入、快排、希尔、归并、堆排)C语言实现

    文章目录 排序 冒泡排序 鸡尾酒排序 选择排序: 简单插入排序: 二分插入排序 快速排序: 希尔排序: 归并排序: 堆排序: 排序 点击以下图片查看大图: 冒泡排序 1.比较相邻的元素,如果前一个比后 ...

  9. 内部排序算法比较-数据结构C语言课设

    名称:内部排序算法比较 内容:在教科书中,各种内部排序算法的时间复杂的分析结果只给出了算法执行时间的阶,或大概执行时间.试通过随机数据比较各种算法的关键字比较次数和关键字移动次数,以取得直观感受. 任 ...

最新文章

  1. 查询选修c语言课程的学生学号和姓名,数据库实验(学生信息表)
  2. LUA ipairs遍历的问题
  3. .bam.bai的意义_业务活动监视器(BAM)2.0带来的革命
  4. linux下用c语言打印hellword,用C语言写一个Helloworld_实现第一步编译运行
  5. Java中对象和引用的理解
  6. 1.15-1.16 sqoop action
  7. 打开u盘提示不在计算机中,U盘不被电脑识别怎么办 U盘在电脑上打不开解决方法...
  8. Linux内存管理:memblock(引导期间管理内存区域)
  9. LFW数据集—人脸对齐
  10. android 遥控器 地址码,RK3128平台android系统修改添加遥控器键值码值
  11. Linux安全合规性检查和加固
  12. 机械原理习题-(附答案)
  13. 重庆航天职业技术学院计算机系在哪个校区,重庆航天职业技术学院江北校区怎么样...
  14. 1468 - 平方矩阵 Python
  15. 计算机网络技术的就业前景
  16. 23岁那年你正处在哪个状态?现在呢?
  17. AutoFac基本使用-笔记
  18. ASP无限分类数据库版
  19. makefile中一些符号的含义
  20. 如何透过上层div点击下层的元素

热门文章

  1. 既然Tengine比Nginx更强大,为什么Tengine没有取代Nginx呢?
  2. Java中String的用法
  3. mapbox 加载json数据 和数据中颜色 和高度 并根据数值加载颜色
  4. 动画会震一下css,csshake.css强大的CSS3元素抖动动画库
  5. mysql 直接删表空间文件_oracle删除(释放)数据文件/表空间流程
  6. mwt是什么意思网络用语_网络用语奶茶婊是什么意思(奶茶婊具备的3个表现)...
  7. 如何设置计算机的网络参数,如何正确设置电脑的IP地址和DNS等参数[图文]
  8. 卷积神经网络训练准确率突然下降_从MobileNet看轻量级神经网络的发展
  9. Svn装到eclipse
  10. c语言程序设计教程课后选择题答案,C语言程序设计教程课后习题包括答案.docx...