第八章排序

导读:本篇文章讲解 第八章排序,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

目录

 

排序概念

存储结构

插入排序

直接插入排序

带哨兵的情况

 算法特点

折半插入排序

 算法排序

 希尔排序

算法特点

交换排序

冒泡排序

改进后的冒泡排序

 快速排序

选择排序

简单选择排序

堆排序

堆的调整

 建初堆

堆排序

算法特点

归并排序

 基数排序

排序概念

将一组杂乱无章的数据按一定规律排列起来。即,将无序序列排成一个有序序列的运算

第八章排序

按存储介质可分为:

  • 内部排序:数据量不大,数据在内存,无需内外存交换数据
  • 外部排序:数据量较大、数据在外存(文件排序)

按主要操作可分为:

  • 比较排序:插入排序、交换排序、选择排序、归并排序
  • 基数排序:不比较元素的大小,仅仅根据元素本身的取值确定其有序位置 

按稳定性可分为:

  • 稳定排序:能够使任何数值相等的元素,排序以后相对次序不变
  • 非稳定排序:不是稳定排序的方法 

自然排序是指越有序的序列排序速度越快 

存储结构

#define MAXSIZE 20    //设记录不超过20个
typedef int KeyType;    //设关键字为整型量(int型)

Typedef struct{        //定义每个记录(数据元素)的结构
    KeyType key;        //关键字
    InfoType otherinfo;    //其它数据项
}RedType;

Typedef struct{                //定义顺序表的结构
    RedType r[MAXSIZE+1];    //存储顺序表的向量    r[0]一般作哨兵或缓冲区
    int length;                //顺序表的长度
}SqList;
    

插入排序

直接插入排序

①复制插入元素

②记录后移,查找插入位置

③插入到正确位置

带哨兵的情况

1.复制为哨兵        L.r[o] = L.r[i]

2. 记录后移,查找插入位置

3.哨兵的元素插入到正确位置

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];            //复制为哨兵
            for(j=i-1;L.r[0].key < L.r[j].key;--j){
                L.r[j+1] = L.r[j];        //记录后移
            }
            L.r[j+1] = L.r[0];            //插入到正确位置
        }
    }
}

 算法特点

①稳定排序

②算法简单,且容易实现

③适用于链式存储结构,只是在单链表上无需移动记录,只需修改相应的指针

④更适合于初始记录基本有序的情况,当初始记录无序,n较大时,此算法时间复杂度较高

折半插入排序

void BInsertSort(SqList &L){
    for(i=2;i <= L.length;++i){    //依次插入第2-第n个元素
        L.r[0] = L.r[i];    //当前插入元素存到“哨兵”位置
        low = 1;high = i-1;    //采用二分查找法查找插入位置
        while(low <= high){
            mid = (low+high)/2;
            if(L.r[0].key < L.r[mid].key)    high = mid-1;
            else low = mid+1;
        }
        for(j=i-1;j >= high+1;--j)    L.r[j+1] = L.r[j];    //移动元素
        L.r[high+1] = L.r[0];
    }
}
  • 折半查找比顺序查找块,所以折半插入排序就平均性能来说比直接插入排序要快

第八章排序

  • 折半插入排序的对象移动次数与直接插入排序相同,依赖于对象的初始排列 

        ①减少了比较次数,但没有减少移动次数

        ②平均性能优于直接插入排序

时间复杂度为O(n^2)        空间复杂度为O(1)        稳定的排序方法 

 算法排序

①稳定排序

②因为要进行折半查找,所以只能用于顺序结构,不能用于链式结构

③适合初始记录无序、n较大时的情况

 希尔排序

基本思想:先把整个待排序列分割成若干子序列,分别进行直接插入排序,待整个系列中的记录“基本有序”时,再对全体记录进行一次直接插入排序

void ShellInsert(SqList &L,int dk){
    //对顺序表L做一趟增量是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];            //将r[0]即原r[i],插入到正确位置
        }
}

void ShellSort(SqList &L,int dt[];int t){    //增量dk存放在dt[]数组中
    for(k=0;k<t;++k)
        ShellInsert(L.dt[k]);    //一趟增量为dt[t]的希尔插入排序
}

时间复杂度为O(n^1.3)        空间复杂度为O(1)

算法特点

①记录跳跃式地移动导致排序方法是不稳定的

②只能用于顺序结构,不能用于链式结构

③增量序列可以有各种取法,但应该使增量序列中的值没有除1之外的公因子,并且最后一个增量值必须等于1

④记录总的比较次数和移动次数都比直接插入排序要少,n越大时,效果越明显。所以适合初始记录无序,n较大时的情况

交换排序

冒泡排序

 n个元素总共需要n-1趟

第m趟需要比较n-m次

void bubble_sort(SqList &L){    //冒泡排序算法
    int m,i,j;    RedType x;    //交换时临时存储
    for(m=1;m <= n-1;m++){        //总共需m趟
        for(j=1;j <= n-m;j++)
            if(L.r[j].key > L.r[j+1].key){    //发生逆序
                x=L.r[j];    L.r[j]=L.r[j+1];    L.r[j+1]=x;    //交换
            }
    }
}

 优点:每趟结束时,不仅能挤出一个最大值到最后面位置,还能同时部分理顺其他元素

改进后的冒泡排序

void bubble_sort(SqList &L){    //改进后的冒泡排序算法
    int m,i,j; flag=1; RedType x;    //flag作为是否有交换的标记
    for(m=1;m <= n-1&&flag==1;m++){        //总共需m趟
        flag = 0;
        for(j=1;j <= m;j++)
            if(L.r[j].key > L.r[j+1].key){    //发生逆序
                flag = 1;    //发生交换,flag置为1,若本趟没有发生交换,flag保持为0
                x=L.r[j];    L.r[j]=L.r[j+1];    L.r[j+1]=x;    //交换
            }
    }
}

最好情况(正序):比较次数为n-1        移动次数为0

最坏情况(逆序):比较次数为 (n^2-n) / 2        移动次数为 3(n^2-n) / 2  

 快速排序

不适于对原本有序或基本有序的记录序列进行排序(无论正序还是逆序)

基本思想

  • 任取一个元素为中心(pivot为枢轴、中心点)
  • 所有比它小的元素一律前放,比它大的元素一律后放,形成左右两个子表;
  • 对各子表重新选择中心元素并依此规则调整
  • 直到每个子表的元素只剩一个
void main(){
    QSort(L,1,L.length);
}

//QSort()的时间复杂度为O(log2n)
void QSort(SqList &L,int low,int high){    //对顺序表L快速排序
    if(low < high){    //长度大于1
        pivotloc = Partition(L,low,high);
            //将L.r[low……high]一分为2,pivotloc为枢轴元素排好序的位置
        QSort(L,low,pivotloc-1);    //对低子表递归排序
        QSort(L,pivotloc+1,high);    //对高子表递归排序
    }
}

//Partition()的时间复杂度为O(n)
int Partition(SqList &L,int low,int high){
    L.r[0] = L.r[low];
    pivotloc = L.r[low].key;
    while(low < high){
        while(low < high && L.r[high].key >= pivotloc)    --high;
        L.r[low] = L.r[high];
        while(low < high && L.r[high].key <= pivotloc)    ++low;
        L.r[high] = L.r[low];
    }
    L.r[low] = L.r[0];
    return low;
}

①划分元素的选取是影响时间性能的关键

②输入数据次序越乱,所选划分元素值的随机性越好,排序速度越快,快速排序不是自然排序方法

③最坏情况下,快速排序的时间复杂性总是O(n^2)

选择排序

简单选择排序

基本思想:在待排序的数据中选出最大(小)的元素放在其最终的位置

n个元素需要n-1趟操作才能形成最后的排序

void SelectSort(SqList &L){
    //对顺序表L做简单选择排序
    for(i=1;i<L.length;++i){
        k = i;
        for(j=i+1;j <= L.length;++j)
            if(L.r[j],key<L.r[k].key)    k=j;    //k指向此趟排序中关键字最小的记录
        if(k!=i){
            t=L.r[i];    L.r[i]=L.r[k];    L.r[k]=t;
        }
    }
}

记录移动次数        最好情况:O        最坏情况:3(n-1)       

比较次数:n(n-1) / 2

 可用于链式存储结构

堆排序

堆的实质是完全二叉树:二叉树中任一非叶子结点均大于(小于)它的孩子结点

第八章排序

第八章排序

 若在输出堆顶的最小值(最大值)后,使得剩余n-1个元素的序列重新又建成一个堆,则得到n个元素的次小值(次大值)。之后便能得到一个有序序列,这个过程称为堆排序

堆的调整

1.输出根并以最后一个元素代替之

2.比较其左右孩子值的大小,并与其中最小者交换(小根堆)

void HeapAdjust(SqList &L,int s,int 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 >= L.r[j].key)    break;
        L.r[s] = L.r[j];    s=j;        //rc应插入在位置s上
    }
    L.r[s] = rc;
}

 建初堆

void CreateHeap(SqList &L){
    n = L.length;
    for(i=n/2;i>0;--i)    //从最后一个非叶子结点向前
        HeapAdjust(L,i,n);
}
        

堆排序

void HeapSort(SqList &L){
    CreateHeap(L);            //将无序序列L.r[1,2……n-1]建成大根堆
    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);    //将L.r[1,2……n-1]重新调整为大根堆
    }
}

算法特点

①不稳定排序

②只能用于顺序结构,不能用于链式结构

③初始建堆所需的比较次数较多,因此记录数较少时不宜使用。当记录较多时较为高效

归并排序

将两个或两个以上的有序子序列“归并”为一个有序序列

将两个位置相邻的有序子序列R[1……m]和R[m+1……n]归并为一个有序序列R[1……n]

第八章排序

 基数排序

设置若干个箱子,将关键字为k的记录放入第k个箱子,然后再按序号将非空的连接

例子:数字是有范围的,均由0-9这十个数字组成,则只需设置十个箱子,相继按个、十、百进行排序

第八章排序

第八章排序

第八章排序

 时间效率:O(k*(n+m))        k:关键字个数(按照几个标准进行划分)        m:关键字取值范围为m个值

空间效率:O(n+m)

第八章排序

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/83247.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!