1. 选择排序
时间复杂度 O(n^2)
主要过程:
选择排序就是挨着选,选到小的放前面
想选小的,就要有比较的对象,
每次默认被选择数组的第一个数是最小值,然后依次和后面的数进行比较,获取剩余数组中最小值的下标,
然后与被选择数组的第一个数交换,完成一次选择
template <typename T>
void selectSort(T arr[], int n) {
for(int i =0 ; i < n; i++) {
int minIndex = i;
for (int j = i+1; j < n; j++) {
if(arr[minIndex] > arr[j])
minIndex = j;
}
swap(arr[i], arr[minIndex]);
}
}
number = 10000
selectSort:0.117953s
number = 100000
selectSort:11.019439s
从上面的运行结果来看,计算量增加10倍,时间增加了100倍, 符合O(N^2)的时间复杂度
2. 插入排序
时间复杂度 O(n^2)
主要过程:
一点一点的排序,就是
先排前两个数,使其有序
再排前三个数,使其有序
再排前四个数,使其有序
...
...
再排最后.. 就全都有序了
在实现的时候注意的点是,可以直接从下标1开始循环,因为只有一个数的话,本身就是有序的,
再就是,以从小到大排来说,如果监测到前面的数已经比当前的数小了,
那就可以跳出当前循环了,毕竟前面的原本就是有序的
template <typename T>
void insertSort(T arr[], int n) {
for(int i = 1; i < n; i++) {
// i ~ 0 就是需要进行排序的前几个
for( int j = i; j > 0; j--) {
if( arr[j] < arr[j-1] ) {
swap(arr[j], arr[j-1]);
} else {
break;
}
}
}
}
///////////////////
number = 10000
selectSort:0.127038s
insertSort:0.145065s
上面的排序有优化的空间,因为一次交换等于三次赋值,所以尽量不用交换
而是记录下每次新加入的数需要放到的位置,然后后面的数全部向后移动一位
最后把新加入的数放到记录下的位置
template <typename T>
void insertSort(T arr[], int n) {
for(int i = 1; i < n; i++) {
T e = arr[i];
int j;
for(j = i; j > 0; j--) {
if( arr[j-1] > e ) {
arr[j] = arr[j-1];
} else {
break;
}
}
arr[j] = e;
}
}
//并且为了美观,可以把if的条件提到for中
template <typename T>
void insertSort(T arr[], int n) {
for(int i = 1; i < n; i++) {
T e = arr[i];
int j;
for(j = i; j > 0 && arr[j-1] > e ; j--) {
arr[j] = arr[j-1];
}
arr[j] = e;
}
}
///////////////////
number = 10000
selectSort:0.116369s
insertSort:0.076550s
插入排序在处理近乎有序的数列中,速度趋向于O(N), 因为他只要判断前面是有序的,就进行下一次的比较
所以在处理那些近乎有序的数列时,可以优先考虑
3. 冒泡排序
时间复杂度为O(n^2)
主要过程:
第一个数和第二个数比较,大的话就交换,第二个数在和第三个数进行比较,大的话交换,这样依次交换过去,最大的数最终会放在数组的最后.
然后再进行第一个跟第二个数比较,大的话就交换,第二个数在和第三个数进行比较,大的话交换,知道把倒数第二大的数放到倒数第二个,
。。。
。。。
最后全部比完之后,从后到前,从大到小,也就有序了
template <typename T>
void bubbleSort(T arr[], int n) {
for(int i = 0; i < n; i++) {
for(int j = 1; j < n-i; j++ ) {
if(arr[j] < arr[j-1])
{
swap(arr[j], arr[j-1]);
}
}
}
}
///////
number = 10000
selectSort:0.117713s
insertSort:0.072229s
bubbleSort:0.306428s
4. 希尔排序
时间复杂度:O(N*logN)
希尔排序是比较特殊的排序,是插入排序的改良的排序算法,他的步长是经过调整的,
主要过程:
比如数组为 6 5 3 1 8 7 2 4
初始步长为3的情况下6 5 3作为前三个数是不需要调整的,从1开始,1向前跳三位,来到6的这个位置,进行比较,1比6小,
所以1跟6的位置进行交换,这样1就到了位置0, 再往前三位就越界了,所以交换的过程停止
数组 1 5 3 6 8 7 2 4
下面在处理8这个数,8向前跳三位是和5进行比较的,发现跟5比较,8>5,直接停止交换过程,进行下一个数的比较
就来到了7这个数的位置,7再向前跳三位,发现7>3的,再次停止
到了2这个位置,2向前跳三位,跟6比较,2<6,所以2和6交换,2再向前跳三位,此时发现,2应该和1进行比较,2>1,所以2停在原位置,
数组 1 5 3 2 8 7 6 4
接下来处理4这个数,4向前跳三位,来到了8这个数的位置,4<8,交换,4再向前跳三位比较,发现4<5,进行交换,4来到了位置1,不能在向前跳了,
这样这个步长为3的插入排序就结束了
步长为3之后,还有步长为2,最后来到步长为1,也就是一个经典的插入排序
希尔排序最终都会以步长为1的排序结束
希尔排序的关键就在于步长的选择,步长选择越优,时间复杂度越低,步长选择越劣,就越趋近于时间复杂度O(n^2)的这样的级别
template<typename T>
void shellSort(T arr[], int n)
{
int h = 1;
while( h < n/3 )
h = 3 * h + 1;
// 计算 increment sequence: 1, 4, 13, 40, 121, 364, 1093...
while( h >= 1 ){
// h-sort the array
for( int i = h ; i < n ; i ++ ){
// 对 arr[i], arr[i-h], arr[i-2*h], arr[i-3*h]... 使用插入排序
T e = arr[i];
int j;
for( j = i ; j >= h && e < arr[j-h] ; j -= h )
arr[j] = arr[j-h];
arr[j] = e;
}
h /= 3;
}
}
——————————–完整代码———————————–
#ifndef SORTTESTHELPER_H
#define SORTTESTHELPER_H
#include <iostream>
#include <ctime>
#include <cassert>
#include<iomanip>
using namespace std;
namespace SortTextHelper {
// 生成有n个元素的随机数组,元素的范围[rangeL, rangeR]
int *generateRangeArray(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+1] < arr[i] ) {
return false;
}
}
return true;
}
template <typename T>
void testSort(const char *sortName,void (*sort)(T arr[], int ), T arr[], int n) {
clock_t startTime = clock();
sort(arr, n);
clock_t endTime = clock();
assert(isSorted(arr,n));
cout << sortName << ":" << setiosflags(ios::fixed) << double(endTime-startTime) / CLOCKS_PER_SEC << "s" << endl;
}
int * copyIntArray(int a[], int n) {
int *arr = new int[n];
copy(a, a+n, arr);
return arr;
}
}
#endif //SORTTESTHELPER_H
#include <iostream>
#include "SortTextHelper.h"
using namespace std;
template <typename T>
void selectSort(T arr[], int n) {
for(int i =0 ; i < n; i++) {
int minIndex = i;
for (int j = i+1; j < n; j++) {
if(arr[minIndex] > arr[j])
minIndex = j;
}
swap(arr[i], arr[minIndex]);
}
}
template <typename T>
void insertSort(T arr[], int n) {
for(int i = 1; i < n; i++) {
T e = arr[i];
int j;
for(j = i; j > 0 && arr[j-1] > e; j--) {
arr[j] = arr[j-1];
}
arr[j] = e;
}
}
template <typename T>
void bubbleSort(T arr[], int n) {
for(int i = 0; i < n; i++) {
bool bSwapped = false;
for(int j = 1; j < n-i; j++ ) {
if(arr[j] < arr[j-1])
{
swap(arr[j], arr[j-1]);
bSwapped = true;
}
}
if(bSwapped == false) {
break;
}
}
}
template<typename T>
void shellSort(T arr[], int n){
int h = 1;
while( h < n/3 )
h = 3 * h + 1;
// 计算 increment sequence: 1, 4, 13, 40, 121, 364, 1093...
while( h >= 1 ){
// h-sort the array
for( int i = h ; i < n ; i ++ ){
// 对 arr[i], arr[i-h], arr[i-2*h], arr[i-3*h]... 使用插入排序
T e = arr[i];
int j;
for( j = i ; j >= h && e < arr[j-h] ; j -= h )
arr[j] = arr[j-h];
arr[j] = e;
}
h /= 3;
}
}
int main(int argc, char *argv[])
{
int n = 10000;
cout << "number = " << n << endl;
int *arr = SortTextHelper::generateRangeArray(n, 0, n);
int *arr2 = SortTextHelper::copyIntArray(arr, n);
int *arr3 = SortTextHelper::copyIntArray(arr, n);
int *arr4 = SortTextHelper::copyIntArray(arr, n);
SortTextHelper::testSort("selectSort", selectSort, arr, n);
SortTextHelper::testSort("insertSort", insertSort, arr2, n);
SortTextHelper::testSort("bubbleSort", bubbleSort, arr3, n);
SortTextHelper::testSort("shellSort ", shellSort, arr4, n);
//SortTextHelper::printArray(arr, n);
delete[] arr;
delete[] arr2;
delete[] arr3;
delete[] arr4;
return 0;
}