直接插入排序(Straight Insertion Sort):将一个数据插入到已经排序的数据中,从而得到一个新的有序数据。
图例:
1.直接插入排序
代码实现:
- 直接插入
void InsertSort_1(int *arr, int left, int right)
{
for (int i = left + 1; i < right; i++)
{
int end = i;
while (end > 0 && arr[end] < arr[end - 1])
{
Swap(&arr[end], &arr[end - 1]);//交换
end--;
}
}
}
- 上述代码进行修改,减少交换次数
void InsertSort_2(int *arr, int left, int right)
{
for (int i = left + 1; i < right; i++)
{
int tmp = arr[i];
int end = i;
while (end > 0 && tmp < arr[end - 1])
{
arr[end] = arr[end - 1];
end--;
}
arr[end] = tmp;//end记录的是最后一次需要改变的位置
}
}
- 设置哨兵位:避免数组下标越界
void InsertSort_3(int *arr, int left, int right)
{
for (int i = left + 1; i < right; i++)
{
arr[0] = arr[i];//arr[0]为哨兵
int end = i;
while (arr[0] < arr[end - 1])
{
arr[end] = arr[end - 1];
end--;
}
arr[end] = arr[0];
}
}
2.折半插入排序
折半插入:利用“折半查找”来找到插入位置。
代码实现:
void BinInsertSort(int *arr, int left, int right)
{
for (int i = left + 1; i < right; i++)
{
int tmp = arr[i];
int end = i;
int start = left;
int mid;
while (start <= end)//寻找插入位置
{
mid = (start + end) >> 1;
if (tmp > arr[mid])
start = mid + 1;
else
end = mid - 1;
}
for (int j = i; j > end + 1; j--)//移动数据
arr[j] = arr[j - 1];
arr[end + 1] = tmp;
}
}
3. 2路插入排序
基本原理:以第一个元素作为比较元素,后续元素比首元素大,往前插入,比首元素小,往后插入。采用循环数组的方式实现。
void TwoWayInsertSort(int *arr, int left, int right)
{
int n = right - left;
int *tmp = (int *)malloc(sizeof(int) * n);//开辟数组
assert(tmp != NULL);
int front = 0, rear = 1;//头,尾指针
tmp[0] = arr[left];//首元素
for (int i = left + 1; i < right; i++)
{
if (arr[i] <= tmp[front])//比首元素还小
{
front = (front - 1 + n) % n;//更改首元素位置
tmp[front] = arr[i];
}
else if(arr[i] >= tmp[rear - 1])//比尾元素还要大
{
tmp[rear++] = arr[i];
}
else// 插入位置在中间
{
int j = rear - 1;
while (arr[i] < tmp[j])//寻找插入位置
{
tmp[(j + 1) % n] = tmp[j];
j = (j - 1 + n) % n;
}
tmp[(j + 1)%n] = arr[i];//插入
rear++;
}
}
int k = front;
for (int i = left; i < right; i++)//赋给原数组
{
arr[i] = tmp[k];
k = (k + 1) % n;
}
free(tmp);//释放临时空间,避免内存泄漏
tmp = NULL;
}
三种插入排序总结:
- 时间复杂度:均为O(N^2)
- 空间复杂度:直接插入排序:O(1),折半插入排序:O(1),2路插入排序:O(n)
- 稳定性:均为稳定排序
- 元素越接近有序,算法时间效率越高