算法 - 排序 - 插入排序 (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);
}