【C++ | 算法】:有关插入排序的记录

219 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第20天,点击查看活动详情

1、写在前面

大家好,今天文章的内容是:

  • 插入排序

2、内容

算法思想

算法思想

在每一轮循环中,将一个待排序的值按其关键字大小插入到前面已排好序的子序列中,直到全部记录插入完成。

示意举例:

下面举一个例子:

现在有一个未排序的序列:

image.png

这时我们将序列中的第一个值视为已排序完毕:

image.png

接着从第二个元素开始,我们会认为当前元素之前的部分是已经排完序的序列,接着我们需要将当前元素与已经排序完毕的序列进行逐一比较。对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

以下是排序过程中的示意图:

image.png

image.png

image.png

image.png

代码实现:

// C++
void InsertSort(int arr[], int n) {
    int i, j, temp;
    // 将各个元素插入到已经排好序的序列中
    for(i = 1; i<n; i++) {
        // 当前 arr[i] 小于前驱元素
        if(arr[i] < arr[i-1]) {
            // 用 temp 暂存 arr[i]
            temp = arr[i];
            // 检查所有前面已经排好序的元素,将所有大于temp的元素都往后移一位
            for(j = i-1; j >= 0 && arr[j] > temp; --j) {
                arr[j+1] = arr[j];
            }
            // 将temp复制到插入位置中
            arr[j+1] = temp;
        }
    }
}

效率分析:

  • 空间复杂度:O(1)O(1)
  • 时间复杂度:
    • 最好时间复杂度:O(n)O(n),共n-1趟处理,每一趟只需要对比关键字1次,不用移动元素
    • 最坏时间复杂度:O(n2)O(n^2),共n-1趟处理,每一趟都需要从尾移到到头(全部逆序)
  • 算法稳定性:稳定

算法优化

思路:先用折半查找法,找到应该插入的位置,再移动元素

  • low > high时,折半查找停止,将[low, i-1]内的元素全部右移,并将arr[0]复制到low所指位置
  • arr[mid] == arr[0]时,为了保证算法的 “稳定性”,应继续在mid所指位置右边寻找插入位置。

代码实现

void InsertSort(int arr[], int n) {
    int i,j,low,high,mid;
    // 依次将 arr[2]~arr[n] 插入到前面已排完序的序列
    for(i=2; i<=n; i++) {
        // 将arr[i] 暂存到 arr[0] (哨兵)
        arr[0] = arr[i];
        // 设置折半查找的范围
        low = 1; high = i-1;
        // 折半查找(默认递增有序)
        while(low <= high) {
            // 取中间点
            mid = (low+high) / 2;
            // 查找左半子表
            if(arr[mid] > arr[0]) high = mid - 1;
            // 查找右半子表
            else low = mid + 1;
        }
        // 统一后移元素,空出插入位置
        for( j = i-1; j >= high+1; --j) {
            arr[j+1] = arr[j];
        }
        // 将之前暂存的元素插入到指定位置arr[high+1]中(这里的 high+1 就相当于 low )
        arr[high+1] = arr[0];
    }
}

3、写在最后

好了,文章的内容就到这里,感谢观看。