插入排序
思想:插入排序的思想是将一个无需的数列中的数据挨个插入到一个已经有序的数列中,使得插入后原本有序的数列依然有序,最终完成整个数列的排序。在实际的排序过程中,“已经有序的数列”指的是数列中被拍好序的部分。考虑对下图的数组进行插入排序:
首先考虑6这个元素,因为第一个元素8只有它自己,是已经排好序的。
考虑6时将6这个元素插入到有序部分“8”中,6应该在8的前面,因此这一步结束后数组为
接下来考虑2这个元素,此时数组中有序部分为“8 6”,将2插入到有序部分中的合适位置,结果为
下一步再考虑3这个元素,此时数组中有序部分为“2 6 8”,将3插入到其中的合适位置结果为
以上过程是插入排序的思想,算法共两层循环,外层循环负责遍历数组的元素,内层循环负责将外层循环选中的第i个元素在有序部分中找到合适的位置。在找位置进行插入的过程中有两种做法,考虑上面倒数第二幅图:一种是将3和8交换,再将3和6交换,完成对3的插入排序;另一种是将3暂存,8比3大则将8后移一个位置,继续判断6也比3大,那么将6后移一个位置,继续判断2不大于3,因此将3插入到2后面。两种方法中,第一种每次交换相当于3次赋值操作,而第二种方法则只是一次赋值,因此效率会高一些。
最后,相比与选择排序,由于选择排序的内层循环每次要找到剩余元素的极值索引,因此需要内层循环完全执行才能结束,而插入排序的内层循环,一旦找到了合适的位置就会提前结束,因此效率会更高。实际上,对于一个近乎有序的数组,插入排序的效率是非常高的。
希尔排序
希尔排序也是一种插入排序算法,是上文的插入排序算法的改进后的版本,该算法的复杂度突破了O(n^2)。
思想:将待排序的数组分组进行插入排序,通过一个增量将数组元素划分为若干组分别进行排序。逐步减小增量,继续按组进行分组排序,直至增量为1。还是看图比较容易懂啊,gap为增量!
代码实现
sortTestHelper.h
// Created by 开机烫手 on 2018/4/19.
//
#ifndef SORT_SORTTESTHELPER_H
#define SORT_SORTTESTHELPER_H
#include <iostream>
#include <ctime>
#include <cassert>
using namespace std;
namespace SortTestHelper {
int *generateRandomArray(int n, int RangeL, int RangeR) {
assert(RangeL <= RangeR);
int *arr = new int[n];
srand(time(NULL));
for (int i = 0; i < n; i++) {
arr[i] = rand() % (RangeR - RangeL + 1) + RangeL;
}
return arr;
}
template<typename T>
void printArray(T arr[], int n) {
for (int i = 0; i < n; i++) {
cout << arr[i] << ' ';
}
cout << endl;
}
template<typename T>
bool isSorted(T arr[], int n) {
for (int i = 0; i < n - 1; i++) {
if (arr[i] > arr[i + 1])
return false;
}
return true;
}
template<typename T>
void testSort(string name, void(*sort)(T [], int n), T arr[], int n) {
clock_t startTime = clock();
sort(arr, n);
clock_t endTime = clock();
assert(isSorted(arr, n));
cout << name << ": " << double(endTime - startTime) / CLOCKS_PER_SEC << " s" << endl;
}
int *copyIntArray(int a[], int n) {
int *arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = a[i];
}
return arr;
}
};
#endif //SORT_SORTTESTHELPER_H
main.cpp
#include <iostream>
#include "sortTestHelper.h"
using namespace std;
template<typename T>
void InsertSort(T k[], int len) {
T temp; int j;
for (int i = 1; i < len; i++) {
if (k[i] < k[i - 1]) {
temp = k[i];
for (j = i; j > 0 && k[j - 1] > temp; j--) {
k[j] = k[j - 1];
}
k[j] = temp;
}
}
}
template<typename T>
void ShellSort(T k[], int len) {
for (int gap = len / 2; gap > 0; gap /= 2) {
for (int i = gap; i < len; i++) {
int j = i;
T temp = k[j];
if (k[j] < k[j - gap]) {
while (j - gap >= 0 && temp < k[j - gap]) {
k[j] = k[j - gap];
j -= gap;
}
k[j] = temp;
}
}
}
}
int main() {
int n = 50000;
int *arr = SortTestHelper::generateRandomArray(n, 0, n);
int *arr2 = SortTestHelper::copyIntArray(arr, n);
SortTestHelper::testSort("Insert Sort", InsertSort, arr, n);
SortTestHelper::testSort("Shell Sort", ShellSort, arr2, n);
delete[] arr;
delete[] arr2;
return 0;
}
输出:
Insert Sort: 3.024 s
Shell Sort: 0.019 s