【数据结构】内部排序


前言

  排序是计算机程序设计中一种重要的操作,它的功能是将一个数据元素(或记录)的任意序列,重新排列成一个关键字有序的序列。排序是数据结构最后一个章节,相对来讲比较容易【主要是因为c++有模板】
  一个排序是稳定的指的是相同大小的两个数,在排序之后它们原先的顺序不变,不稳定的指的是排序之后它们原先的顺序改变。
  内部排序是指待排序记录存放在计算机随机存储器中进行的排序过程,外部排序指的是待排序记录的数量很大,以致内存一次不能容纳全部记录,在排序时需要对外存进行排序的过程。
  排序按照所需要的工作量区分,可以分为3类:简单排序(O( n 2 n^2 n2))、先进的排序(O(nlogn))和基数排序(O(dn))。

插入排序

直接插入排序

  最简单的排序方法,基本操作是将一个记录插入到已排好序的有序表中,从而得到一个新的表。其时间复杂度为O( n 2 n^2 n2)。

其他插入排序

折半插入排序

  如果当前的表已经是一个有序表,则可以通过"折半查找"的方法来进行。虽然减少了关键字的比较次数,但是记录的移动次数不变,因此时间复杂度仍然是O( n 2 n^2 n2)。

2-路插入排序

  该方法时在折半插入排序的基础上再改进,目的是减少排序过程中移动记录的次数,但需要增加n个记录的辅助空间。具体操作是:将辅助空间当作是一个循环变量。同时设置原先表中第一个数值为哨兵,如果后续的数值比哨兵大,则插入到后面,否则插入到前面。同时,数值在插入的时候也是遵循直接插入法的原则,但是移动的次数会比原来减少。

表插入排序

  表插入排序一个不同之处在于它使用指针的改变来替代关键字的移动,但是关键字的比较次数没有改变,因此复杂度仍然为O( n 2 n^2 n2)。为了实现有序表的折半查找,还需要对记录进行重新排列。具体的操作如下:如果第i个小关键字的节点是数组中下标为p且p>i的分量,则互换SL.r[i]和SL.r[p],同时令交换后SLr[i]中的指针域修改为p。

希尔排序

  希尔排序也是插入排序的一种,但是时间效率上会有较大的改进。它的基本思想是:先将整个待排记录序列分割为若干子序列分别进行直接插入排序,待整个序列中的记录基本有序时,再对全体记录做一次直接插入排序

快速排序

  快速排序是对冒泡排序的一种改进,其基本思想是:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。具体做法是:设两个指针low和high,设枢轴的关键字为p,则首先从high所指位置起向前搜索找到第一个关键字小于p的记录和枢轴记录交换,然后从low往后找,找到第一个关键字比p的记录和枢轴记录交换,重复这两步直到low=high。
参考代码如下:

//快速排序
void quick_sort(int s[], int l, int r)
{
    if (l < r)
    {
        //Swap(s[l], s[(l + r) / 2]); //将中间的这个数和第一个数交换 参见注1
        int i = l, j = r, x = s[l];
        while (i < j)
        {
            while(i < j && s[j] >= x) // 从右向左找第一个小于x的数
                j--;  
            if(i < j) 
                s[i++] = s[j];
            
            while(i < j && s[i] < x) // 从左向右找第一个大于等于x的数
                i++;  
            if(i < j) 
                s[j--] = s[i];
        }
        s[i] = x;
        quick_sort(s, l, i - 1); // 递归调用 
        quick_sort(s, i + 1, r);
    }
}

选择排序

  选择排序的基本思想是:每一趟在n-i+1个记录中选取关键字最小的记录作为有序序列中第i个记录,其中最简单的是简单选择排序:令i=1~(n-1),进行n-1次的选择操作,所需要的复杂度为O( n 2 n^2 n2)。但是可以根据a1>a2;a2>a3,则a1>a3这一思想改进选择排序。

树形选择排序

  又称锦标赛排序。首先对n个记录的关键字俩俩进行比较,然后再其中叫嚣着之间再重复进行比较,如此反复直到选出最小关键字为止,该过程可以用一棵二叉树来表示。但是这种方法会需要较多的辅助存储空间。

堆排序

堆排序只需一个记录大小的辅助空间,每个待排序的记录仅占有一个。堆可分为大顶堆和小顶堆,对应的定义如下:

  1. 大顶堆: 每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;
  2. 小顶堆: 每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;

一个堆的建立是自堆顶到叶子的调整过程,称为筛选。堆排序堆记录数较少的文件并不值得提倡,但是堆较大的文件效率还是比较高,在最坏的情况下,其时间复杂度是O(nlogn)。

归并排序

归并排序是一种稳定的算法,核心操作时将一维数组中前后相邻的两个有序序列归并为一个有序序列,参考代码如下:

void Merge(vector<int> &Array, int front, int mid, int end) {

    vector<int> LeftSubArray(Array.begin() + front, Array.begin() + mid + 1);
    vector<int> RightSubArray(Array.begin() + mid + 1, Array.begin() + end + 1);
    int idxLeft = 0, idxRight = 0;
    LeftSubArray.insert(LeftSubArray.end(), numeric_limits<int>::max());
    RightSubArray.insert(RightSubArray.end(), numeric_limits<int>::max());
    // Pick min of LeftSubArray[idxLeft] and RightSubArray[idxRight], and put into Array[i]
    for (int i = front; i <= end; i++) {
        if (LeftSubArray[idxLeft] < RightSubArray[idxRight]) {
            Array[i] = LeftSubArray[idxLeft];
            idxLeft++;
        } else {
            Array[i] = RightSubArray[idxRight];
            idxRight++;
        }
    }
}

void MergeSort(vector<int> &Array, int front, int end) {
    if (front >= end)
        return;
    int mid = (front + end) / 2;
    MergeSort(Array, front, mid);
    MergeSort(Array, mid + 1, end);
    Merge(Array, front, mid, end);
}

基数排序

  基数排序不需要进行记录关键字间的比较,它是一种借助多关键字排序的思想来对单逻辑关键字进行排序。
  基数排序有两种排序方法,一种是通过先比较最高位关键字,排序后得到的每一个子序列再通过次高位关键字来排序的方法,称为最高位优先(MSD);另一种是从最次位关键字进行排序,再对高一位的关键字进行排序的方法,称为最低位优先(LSD)
  基数排序一个典型例子就是数值排序。首先先比较各个数值个位的大小,一轮比较过后重新排列,比较十位的大小,依次类推,直到最后所有的数字都排序完成。

各种排序方法的比较

在这里插入图片描述

总结

  在排序中用的最多的方法是快速排序,归并排序在递归算法中也会提及,而其他的排序算法后面用的形况并不多。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值