排序是算法中最基本的话题之一,而关于排序有许许多多的算法,它们各自有着自己的适应场景。这一篇主要讨论选择排序和插入排序。
选择排序:
首先找到数组中最小的元素和第一个元素交换位置,再在剩下的元素中找到最小的元素,将它和数组中第二个元素交换位置。如此往复,直到整个数组都处于有序状态。对于长度为N的数组,选择排序需要大约N^2/2次交换和N次比较。
选择排序有两个很鲜明的特点:
1.运行时间和输入无关;
2.数据移动是最少的。每次交换都会改变两个数组元素的值,因此选择排序用了N次交换--交换次数和数组的大小是线性关系。
下图显示了array{20,40,30,10,60,50}在选择排序过程中的数据交换过程。
插入排序:
通常人们整理桥牌的方法是一张一张的来,将每一张牌插入到其它已经有序的牌中的适当位置。在计算机中,为了给要插入的元素腾出空间,我们需要将其余元素在插入之前都移动一位。与选择排序一样,当前索引左边的元素都是有序的,但它们的最终位置还不确定,为了给更小的元素(假设从小到大排序)腾出空间,他们可能会被移动。但当当前索引到达数组右端时,数组就处于有序状态。和选择排序不同的是,插入排序的运行时间取决于数组中的初始顺序。例如,对一个很大且其中元素已经有序(或者接近有序)的数组排序将会比对随机顺序的数组或是逆序数组进行排序要快得多。
对于随机排序的长度为N且主键不重复的数组,平均情况下插入排序需要N^2/4次比较以及N^2/4交换。最坏情况下需要N^2/2次比较和N^2/2次交换,最好情况下需要N-1次比较和0次交换。
具体可以参考下面的示意图:
插入排序对于部分有序数组很有效,下面是几种典型的部分有序数组:
1.数组中每个元素距离它的最终位置都不远;
2.一个有序的大数组接一个小数组;
3.数组中只有几个元素的位置不正确。
最后给出C++实现的两种算法的实现代码:
//
// Created by Jack on 2017/11/18.
//
#include <iostream>
using namespace std;
template<typename T>
void select_sort(T array[], int length) {
for (int i = 0; i < length; ++i) {
int min = i;
for (int j = i + 1; j < length; ++j) {
if (array[j] < array[min]) { //升序只需要改动这里的小于号
min = j;
}
}
std::swap(array[i], array[min]);
}
}
template<typename T>
void insert_sort(T array[], int length) {
for (int i = 1, j; i < length; ++i) {
T temp = array[i];
for (j = i - 1; j >= 0 && array[j] >temp; --j) { //升序只需要改动这里的大于号
array[j + 1] = array[j];
}
array[j + 1] = temp;
}
}
int main() {
int array[] = {2, 4, 3, 1, 5};
//select_sort(array, 5);
insert_sort(array, 5);
for (auto &x:array) {
cout<<x<<" ";
}
cout<<endl;
return 0;
}