插入排序
最简单的算法之一就是插入排序,插入排序的时间复杂度是O(N2),并且这个界是精确的,如果输入是反序的,即可达到该边界,如果输入数据已经预先排序,那么运行时间是O(N)。平均情况是O(N2)。
template<typename comparable>
void insertionSort(vector<comparable>& a) {
for (int i = 1; i < a.size(); ++i) {
int temp = a[i];
int j = i;
for (; j >=1 && temp < a[j - 1]; --j) {
a[j] = a[j - 1];
}
a[j] = temp;
}
}
希尔排序
希尔排序是插入排序的升级版,插入排序是每隔一个进行插入,而希尔排序是隔gap个间隔进行排序,其中gap是数组长度的一半,并且每次减半。其时间复杂度计算十分复杂,这里只说明结果。当使用谢尔增量时最坏时间复杂度是O(N2),当使用Hibbard增量时,最坏时间是O(N3/2)。不同的谢尔增量速度不同。
template<typename comparable>
void shellSort(vector<comparable>& a) {
int len = a.size();
for (int gap = len / 2; gap >= 1; gap /= 2) { //增量下的多个插入排序
for (int j = gap; j < len; j += gap) { //插入排序
int temp = a[j];
int k = j;
for (; k >= gap&&temp < a[k - gap]; k -= gap) {
a[k] = a[k - gap];
}
a[k] = temp;
}
}
}
堆排序
堆排序是在 优先队列(堆) 的基础上
inline int leftchild(int i) {
return 2 * i + 1;
}
template<typename comparable>
void percDown(vector<comparable>& a, int i, int n) { //将二叉树从根部向下搜索移动
int child;
comparable temp = a[i];
for (; leftchild(i) < n; i = child) {
child = leftchild(i);
if (child != n - 1 && a[child] < a[child + 1]) {
child++;
}
if (temp < a[child]) {
a[i] = a[child];
}
else break;
}
a[i] = temp;
}
template<typename comparable>
void heapsort(vector<comparable> &a) {
for (int i = a.size() / 2; i >= 0; --i) //首先需要将数组变成最大堆
percDown(a, i, a.size());
for (int j = a.size() - 1; j > 0; --j) { //每次选择一个大数放到数组的最后
swap(a[0], a[j]);
percDown(a, 0, j);
}
}
归并排序
归并排序就是一个递归的调用,将一个数组分拆成两部分,先左部分排序,再右半部分排序,最后将两部分合起来再排序。
//合并左右两部分
template<typename comparable>
void merge(vector<comparable>& a, vector<comparable> &tempArray, int left, int center, int right) {
int rightStart = center + 1;
int numSize = right - left + 1;
int index = left;
while (left <= center && rightStart <= right) {
if (a[left] < a[rightStart])
tempArray[index++] = a[left++];
else
tempArray[index++] = a[rightStart++];
}
while (left <= center)
tempArray[index++] = a[left++];
while (rightStart <= right)
tempArray[index++] = a[rightStart++];
for (int i = 0; i < numSize; ++i,right--) {
a[right] = tempArray[right];
}
}
template<typename comparable>
void mergeSort(vector<comparable> &a, vector<comparable> &tempArray, int left, int right) {
if (left < right) {
int center = left + (right - left) / 2;
mergeSort(a, tempArray, left, center); //先对左部分排序
mergeSort(a, tempArray, center + 1, right);//再对右部分排序
merge(a, tempArray, left, center, right);//最后将两部分合起来
}
}
template<typename comparable>
void mergeSort(vector<comparable> &a) {
vector<comparable>tempArray(a.size());
mergeSort(a, tempArray, 0, a.size() - 1);
}
快速排序
注意快排不要使用第一个元素作为枢纽元,这样速度会很慢,最好的是选择中位数作为枢纽元。
//选择中位数作为枢纽元
template<typename comparable>
const comparable &median3(vector<comparable> &a, int left, int right) {
int center = left + (right - left) / 2;
if (a[center] < a[left])
swap(a[left], a[center]);
if (a[right] < a[left])
swap(a[left], a[right]);
if (a[right] < a[center])
swap(a[center], a[right]);
if(left+1<right)
swap(a[center], a[right]);
return a[right];
}
template<typename comparable>
void quickSort(vector<comparable> &a, int left, int right) {
if (left < right) {
comparable pivot = median3(a, left, right);
int i = left, j = right - 1;
while (i<j) {
while (a[++i]<pivot) {}
while (pivot<a[--j]) {}
if (i < j)
swap(a[i], a[j]);
else break;
}
swap(a[i], a[right - 1]);
quickSort(a, left, i - 1);
quickSort(a, i + 1, right);
}
}
template<typename comparable>
void quickSort(vector<comparable> &a) {
quickSort(a, 0, a.size() - 1);
}
算法总结
不同算法在数据量不同时其效率也不一样,快排在数据量大于10时是最快的。