算法 - 排序 - 插入排序 (Insertion Sort)

本文介绍了插入排序的基本思想、性能分析,并提供了直接插入排序、折半插入排序和希尔排序的C++代码实现。重点讲解了直接插入排序与折半插入排序的区别,以及希尔排序的增量序列选择对时间复杂度的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

算法 - 排序 - 插入排序 (Insertion Sort)

返回分类:全部文章 >> 基础知识

返回上级:算法 - 查找与排序 (Searching and Sorting)

本文将用C++实现插入排序算法。

在查看本文之前,需要一些程序语言的基础。

常用插入排序:

  • 直接插入排序 (Direct Insertion Sort) ;

  • 折半插入排序 (Binary Insertion Sort) ,也称二分插入排序;

  • 希尔排序 (Shell Sort) ,也称缩小增量排序 (Diminishing Increment Sort) 。

所有排序方法中,统一使用如下库与结构:

// Author: https://blog.youkuaiyun.com/DarkRabbit
// Sorted Dependency

#pragma once

#include <algorithm>
#include <vector>
#include <stack>

template<typename T>
struct MyItem
{
    int key;
    T data;

    MyItem(){ key = -1; }
    MyItem(const int& k) : key(k){ }
    MyItem(const int& k, const T& d) : key(k), data(d){ }
};

数据表统一使用了std::vector<MyItem<T>>,如果你使用静态数组MyItem<T>[]或指针数组MyItem<T>*,那么还要传入元素数量size

示例所用数据:

  • 元素数量为size = 15

  • 关键字为key = { 123, 122, 565, 22, 3, 64, 73, 44, 287, 6, 9, 83, 25, 42, 13 }



1 插入排序简述 (Introduction)

插入排序基本思想:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列。

常用插入排序:

  • 直接插入排序 (Direct Insertion Sort) ;

  • 折半插入排序 (Binary Insertion Sort) ,又称二分插入排序;

  • 希尔排序 (Shell Sort) ,又称缩小增量排序 (Diminishing Increment Sort) 。

折半插入排序与直接插入排序只是在寻找位置有所不同,这是因为前面的元素已经排过序了。

折半插入排序再插入第 i 个元素时,需要经过 ⌈ log ⁡ 2 i ⌉ + 1 \lceil \log_2i \rceil + 1 log2i+1 次关键字比较。

直接插入与折半插入过程:

  • 插入到第 i 个关键字时,逆向依次与已经排过序的关键字比较:
    • 找到比它小的关键字的位置 ilow ,关键字位置在 ilow 到 i-1 之间的元素整体右移 1 位,然后将它插入到 ilow+1;
    • 超出表的范围,则它最小,它之前元素整体右移 1 位,将它插入到0位置。

希尔排序过程:定义一个增量gap

  • 将数据表分成长度为gap的几个块;

  • 对每个块的对应下标元素(间隔是gap)组成的子表进行直接插入排序;

  • 利用公式改变gap(希尔增量是减半,Hibbard增量为 {2k-1, 2k-1-1, …, 7, 3, 1});

  • 循环过程,直到不可再分。

具体插入过程:

key = { 123, 122, 565, 22, 3, 64, 73, 44, 287, 6, 9, 83, 25, 42, 13 }

直接插入排序与折半插入排序:
    插入开始位置 i = 1 , 即 key_1 = 122
        它之前的所有元素为 { 123 } ,
        比较它们,122 < 123 ,则 { 123 } 右移 1位,将 122 放在 key_0 的位置

    i = 2 ,即 key_2 = 565 不动
    i = 3 ,即 key_3 = 22
        它之前的所有元素为 { 122, 123, 565 }
        比较它们,超出表的范围
        则它最小,将它们右移一位,然后将 22 放在 key_0 的位置
        现在表 { 22, 122, 123, 565 }
    重复过程

详细过程:
 1: 122 123 565  22   3  64  73  44 287   6   9  83  25  42  13
 2: 122 123 565  22   3  64  73  44 287   6   9  83  25  42  13
 3:  22 122 123 565   3  64  73  44 287   6   9  83  25  42  13
 4:   3  22 122 123 565  64  73  44 287   6   9  83  25  42  13
 5:   3  22  64 122 123 565  73  44 287   6   9  83  25  42  13
 6:   3  22  64  73 122 123 565  44 287   6   9  83  25  42  13
 7:   3  22  44  64  73 122 123 565 287   6   9  83  25  42  13
 8:   3  22  44  64  73 122 123 287 565   6   9  83  25  42  13
 9:   3   6  22  44  64  73 122 123 287 565   9  83  25  42  13
10:   3   6   9  22  44  64  73 122 123 287 565  83  25  42  13
11:   3   6   9  22  44  64  73  83 122 123 287 565  25  42  13
12:   3   6   9  22  25  44  64  73  83 122 123 287 565  42  13
13:   3   6   9  22  25  42  44  64  73  83 122 123 287 565  13
14:   3   6   9  13  22  25  42  44  64  73  83 122 123 287 565

key = { 123, 122, 565, 22, 3, 64, 73, 44, 287, 6, 9, 83, 25, 42, 13 }

希尔排序详细过程:

1: gap=7
    ( 1):  44 122 565  22   3  64  73 123 287   6   9  83  25  42  13
    ( 2):  44 122 565  22   3  64  73 123 287   6   9  83  25  42  13
    ( 3):  44 122   6  22   3  64  73 123 287 565   9  83  25  42  13
    ( 4):  44 122   6   9   3  64  73 123 287 565  22  83  25  42  13
    ( 5):  44 122   6   9   3  64  73 123 287 565  22  83  25  42  13
    ( 6):  44 122   6   9   3  25  73 123 287 565  22  83  64  42  13
    ( 7):  44 122   6   9   3  25  42 123 287 565  22  83  64  73  13
    ( 8):  13 122   6   9   3  25  42  44 287 565  22  83  64  73 123
2: gap=3
    ( 9):   9 122   6  13   3  25  42  44 287 565  22  83  64  73 123
    (10):   9   3   6  13 122  25  42  44 287 565  22  83  64  73 123
    (11):   9   3   6  13 122  25  42  44 287 565  22  83  64  73 123
    (12):   9   3   6  13 122  25  42  44 287 565  22  83  64  73 123
    (13):   9   3   6  13  44  25  42 122 287 565  22  83  64  73 123
    (14):   9   3   6  13  44  25  42 122 287 565  22  83  64  73 123
    (15):   9   3   6  13  44  25  42 122 287 565  22  83  64  73 123
    (16):   9   3   6  13  22  25  42  44 287 565 122  83  64  73 123
    (17):   9   3   6  13  22  25  42  44  83 565 122 287  64  73 123
    (18):   9   3   6  13  22  25  42  44  83  64 122 287 565  73 123
    (19):   9   3   6  13  22  25  42  44  83  64  73 287 565 122 123
    (20):   9   3   6  13  22  25  42  44  83  64  73 123 565 122 287
3: gap=1
    (21):   3   9   6  13  22  25  42  44  83  64  73 123 565 122 287
    (22):   3   6   9  13  22  25  42  44  83  64  73 123 565 122 287
    (23):   3   6   9  13  22  25  42  44  83  64  73 123 565 122 287
    (24):   3   6   9  13  22  25  42  44  83  64  73 123 565 122 287
    (25):   3   6   9  13  22  25  42  44  83  64  73 123 565 122 287
    (26):   3   6   9  13  22  25  42  44  83  64  73 123 565 122 287
    (27):   3   6   9  13  22  25  42  44  83  64  73 123 565 122 287
    (28):   3   6   9  13  22  25  42  44  83  64  73 123 565 122 287
    (29):   3   6   9  13  22  25  42  44  64  83  73 123 565 122 287
    (30):   3   6   9  13  22  25  42  44  64  73  83 123 565 122 287
    (31):   3   6   9  13  22  25  42  44  64  73  83 123 565 122 287
    (32):   3   6   9  13  22  25  42  44  64  73  83 123 565 122 287
    (33):   3   6   9  13  22  25  42  44  64  73  83 122 123 565 287
    (34):   3   6   9  13  22  25  42  44  64  73  83 122 123 287 565


2 性能分析 (Performance Analysis)

排序方法最好时间最差时间平均时间辅助空间稳定性
直接插入排序O(n)O(n2)O(n2)O(1)稳定
折半插入排序O(n log2n)O(n2)O(n2)O(1)稳定
希尔排序O(n log2n)O(n2)与增量算法有关O(1)不稳定

注意1:希尔排序时间复杂度和增量算法的选择有关,标准希尔增量是 O(n2),Hibbard增量时间复杂度 O(n1.5) 。

注意2:希尔增量的计算是一个复杂的问题,目前希尔增量的常用算法时间复杂度多为 O(n1.3) 到 O(n1.5) 。


3 直接插入排序C++代码 (Direct Insertion Sort C++ Code)

// Author: https://blog.youkuaiyun.com/DarkRabbit
// Direct Insertion Sort

template<typename T>
void DirectSort(std::vector<MyItem<T>>& list, 
                const int& start, 
                const int& end)
{
    for (int i = start + 1; i <= end; i++)
    {
        for (int j = i; 
             j > start && list[j].key < list[j - 1].key; 
             j--) // 比较
        {
            std::swap(list[j], list[j - 1]); // 左移
        }
    }
}

template<typename T>
void DirectSort(std::vector<MyItem<T>>& list)
{
    DirectSort(list, 0, list.size() - 1);
}

4 折半插入排序C++代码 (Binary Insertion Sort C++ Code)

// Author: https://blog.youkuaiyun.com/DarkRabbit
// Binary Insertion Sort
template<typename T>
void BinarySort(std::vector<MyItem<T>>& list)
{
    int left;
    int right;
    int mid;
    for (int i = 1; i < list.size(); i++)
    {
        left = 0;
        right = i - 1;
        while (left <= right) // 二分找位置
        {
            mid = (left + right) / 2;

            if (list[i].key < list[mid].key)
            {
                right = mid - 1;
            }
            else
            {
                left = mid + 1;
            }
        }

        for (int j = i - 1; j >= left; j--) // 右移
        {
            std::swap(list[j + 1], list[j]);
        }
    }
}

5 希尔排序C++代码 (Shell Sort C++ Code)

// Author: https://blog.youkuaiyun.com/DarkRabbit
// Shell Sort
template<typename T>
void ShellSort(std::vector<MyItem<T>>& list, int gap)
{
    if (list.empty())
    {
        return;
    }

    if (gap < 0 || gap >= list.size())
    {
        gap = list.size() / 2;
    }

    for (gap; gap != 0; gap /= 2) // 标准希尔增量
    {
        // 直接插入排序
        for (int i = gap; i < list.size(); i++)
        {
            for (int j = i; 
                 j >= gap && list[j].key < list[j - gap].key; 
                 j -= gap)
            {
                std::swap(list[j], list[j - gap]);
            }
        }
    }
}

template<typename T>
void ShellSort(std::vector<MyItem<T>>& list)
{
    ShellSort(list, list.size() / 2);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值