C++音视频

代码改变世界

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 :: 管理 ::

前言

最近心血来潮,对十大排序算法做了一个简单性能测试对比,下图是一张理论性能对比图(已找不到出处)。尽管有些算法的时间复杂度一样,比如冒泡排序,选择排序和插入排序的平均时间复杂度都是 O(n^2),但是由于它们常数项不一样,使得它们实际性能差别很大。

测试说明

  • windows 10系统
  • i7-11700K@3.60GHz 64GB RAM
  • 时间统计未区分 CPU 用户时间和 CPU 系统时间,时间仅为开始和结束时间差,忽略了系统调度带来的误差
  • 比较交换统计代码未体现在下面给出的代码中,文末会附上完整代码
  • 如有优化的,先单独测试,最后汇总对比

1. 冒泡排序 bubble

冒泡排序代码如下

void sort_bubble(std::vector<int> &arr) {
    int length = arr.size();
    for (int i = 0; i < length - 1; i++) {
        for (int j = 0; j < length - i - 1; j++) {  //轮询 length-1 次
            if (arr[j] > arr[j + 1]) {
                int temp = arr[j + 1];
                arr[j + 1] = arr[j];
                arr[j] = temp;
            }
        }
    }
}

冒泡排序的算法很简单,两个for循环,总的轮询次数和比较次数都是:(n-1) + (n-2) + ... + 1 = n*(n-1)/2。不论输入数据情况如何,顺序的,逆序的,部分顺序的,轮询和比较的次数一样多。
针对这些情况,可以对冒泡排序做一些优化,常用方法有三种:

  • 外层循环优化:设置一个标记用于标记本次是否发生交互,如果本次轮询没有发生交换,说明待排序数组已经是顺序的,不需要再排序,直接退出即可
  • 内层循环优化:记录最后一次发生数据交换的位置(数组下标),说明这个位置之后的数组都是有序的,下一次轮询只轮询到这个位置即可
  • 双向冒泡排序:又称鸡尾酒排序(cocktail sort),向右轮询时将最大值往右边移,再向左轮询将最小值往左移

以下代码是集合了上述三种优化方法的代码

void sort_bubble2(std::vector<int> &arr) {
    int length = arr.size();
    int lastExchange = 0;  //最后一次发生交换的位置
    int left = 0, right = arr.size() - 1;
    while (left < right) {
        bool exchange = false;
        for (int i = left; i < right; i++) {  //往右轮询
            if (arr[i] > arr[i + 1]) {
                int temp = arr[i + 1];
                arr[i + 1] = arr[i];
                arr[i] = temp;
                exchange = true;
                lastExchange = i;
            }
        }
        if (exchange == false) {  //如果没有发生数据交换,说明数组已有序
            return;
        }
        exchange = false;
        right = lastExchange;
        for (int i = right; i > left; i--) {  //往左轮询
            if (arr[i] < arr[i - 1]) {
                int temp = arr[i - 1];
                arr[i - 1] = arr[i];
                arr[i] = temp;
                exchange = true;
                lastExchange = i;
            }
        }
        if (exchange == false) {
            return;
        }
        left = lastExchange;
    }
}

测试对比:

数据长度 数据方式 排序算法 时间S 比较次数 交换次数 排序算法 时间S 比较次数 交换次数
1000 顺序
逆序
随机[0-1000]
全0
0->max->1
sort_bubble 0.004
0.011
0.009
0.004
0.008
499500
499500
499500
499500
499500
0
499500
248232
0
249001
sort_bubble2 0.000
0.011
0.007
0.000
0.006
999
499500
333797
999
332669
0
499500
248232
0
249001
10000 顺序
逆序
随机[0-10000]
全0
0->max->1
sort_bubble 0.355
1.079
0.772
0.327
0.682
49995000
49995000
49995000
49995000
49995000
0
49995000
25022825
0
24990001
sort_bubble2 0.001
1.054
0.643
0.000
0.590
9999
49995000
33301272
9999
33326669
0
49995000
25022825
0
24990001
100000 顺序
逆序
随机[0-100000]
全0
0->max->1
sort_bubble 32.537
103.080
77.567
32.879
69.141
4999950000
4999950000
4999950000
4999950000
4999950000
0
4999950000
2505074044
0
2499900001
sort_bubble2 0.001
104.881
64.226
0.001
58.965
99999
4999950000
3338285231
99999
33326669
0
4999950000
2505074044
0
2499900001

从数据可以看出

  • 交换次数的多少直接影响bubble排序时间
  • 冒泡排序效率很低,一百万的数据排序时间将超多3000秒,所以未测试
  • 优化过的冒泡排序除了在顺序,全0(几乎不会发生这种情况)几乎不耗时外,在随机数组情况下,效率有小幅提升,但是不明显,约为64.226/77.567 = 82.8%
  • 逆序情况下(几乎不会出现),算法优不优化,效果一样

2. 选择排序 selection

首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

void sort_selection(std::vector<int>& arr) {
    int length = arr.size();
    for (int i = 0; i < length - 1; i++) {
        int minIndex = i;
        for (int j = i + 1; j < length; j++) {   //对比查找最小值的索引
            if (arr[j] < arr[minIndex]) {
                minIndex = j;
            }
        }
        if (minIndex != i) {                    //将最小值放到已排序序列末尾
            int temp = arr[i];
            arr[i] = arr[minIndex];
            arr[minIndex] = temp;
        }
    }
}

选择排序优化:每次遍历挑选出最小值和最大值,最小值放到左边有序序列末尾,最大值放到右边有序序列起始。这样便利次数会少一半。

void sort_selection2(std::vector<int>& arr) {
    for (int left = 0, right = arr.size() - 1; left < right; left++, right--) {
        int minIndex = left;
        int maxIndex = right;
        for (int i = left; i <= right; i++) {
            if (arr[i] < arr[minIndex]) {
                minIndex = i;
            }
            if (arr[i] > arr[maxIndex]) {
                maxIndex = i;
            }
        }
        if (minIndex != left) {
            int temp = arr[left];
            arr[left] = arr[minIndex];
            arr[minIndex] = temp;
        }
        if (maxIndex == left) {     //先交换left,需要考虑最大值就在left的情况
            maxIndex = minIndex;
        }
        if (maxIndex != right) {
            int temp = arr[right];
            arr[right] = arr[maxIndex];
            arr[maxIndex] = temp;
        }
    }
}

测试数据

数据长度 数据方式 排序算法 时间S 比较次数 交换次数 排序算法 时间S 比较次数 交换次数
1000 顺序
逆序
随机[0-1000]
全0
0->max->1
sort_selection 0.002
0.002
0.003
0.002
0.002
499500
499500
499500
499500
499500
0
500
996
0
995
sort_selection2 0.002
0.002
0.002
0.002
0.002
501000
501000
501000
501000
501000
0
500
991
0
991
10000 顺序
逆序
随机[0-10000]
全0
0->max->1
sort_selection 0.236
0.256
0.246
0.228
0.238
49995000
49995000
49995000
49995000
49995000
0
5000
9991
0
9990
sort_selection2 0.277
0.233
0.221
0.215
0.225
5001000
5001000
5001000
5001000
5001000
0
5000
9985
0
9986
100000 顺序
逆序
随机[0-100000]
全0
0->max->1
sort_selection 22.333
22.939
22.767
22.473
22.156
4999950000
4999950000
4999950000
4999950000
4999950000
0
50000
99992
0
99991
sort_selection2 21.705
22.738
21.593
21.793
22.403
5000100000
5000100000
5000100000
5000100000
5000100000
0
50000
99975
0
99981
  • 输入数据情况对选择排序时间影响不大,排序时间只受数据量的影响
  • 优化后效果不明显

3. 插入排序 insertion

void sort_insertion(std::vector<int>& arr) {
    for (int i = 1; i < arr.size(); i++) {
        int preIndex = i - 1;
        int current = arr[i];
        while (preIndex >= 0 && current < arr[preIndex]) {
            arr[preIndex + 1] = arr[preIndex];
            preIndex = preIndex - 1;
        }
        arr[preIndex + 1] = current;
    }
}

优化思路1:前半部分已经完成排序是有序序列,则可以通过二分查找法,查找下一个数据的插入点,但是数据的移动个数没有变,可以尝试使用memcpy来代替轮询移动。
优化思路2:希尔排序

4. 希尔排序 shell

希尔排序,也被称为递减增量排序,是插入排序的一种改进版本。将插入排序对比交换间隔 1 改成 gap 就是希尔排序。

void sort_shell(std::vector<int>& arr) {
    for (int gap = arr.size() / 2; gap > 0; gap = gap / 2) {  //使用gap进行递减插入排序,当 gap == 1 时,完全是插入排序
        for (int i = gap; i < arr.size(); i++) {
            int preIndex = i - gap;
            int current = arr[i];
            while (preIndex >= 0 && current < arr[preIndex]) {
                arr[preIndex + gap] = arr[preIndex];
                preIndex = preIndex - gap;
            }
            arr[preIndex + gap] = current;
        }
    }
}

希尔排序的核心是gap序列的设置,我选取了几个gap序列测试,使用以下代码测试

static inline void sort_insertion_gap(std::vector<int>& arr, int gap) {
    for (int i = gap; i < arr.size(); i++) {
        int preIndex = i - gap;
        int current = arr[i];
        while (preIndex >= 0 && current < arr[preIndex]) {
            arr[preIndex + gap] = arr[preIndex];
            preIndex = preIndex - gap;
        }
        arr[preIndex + gap] = current;
    }
}
void sort_shell_gap(std::vector<int>& arr, float divisor) {
    int gap = 0;
    for (gap = int(arr.size()*1.0 / divisor); gap > 0; gap = int(gap * 1.0 / divisor)) {
        sort_insertion_gap(arr, gap);
    }
    if (gap != 1) {
        sort_insertion_gap(arr, 1);
    }
}
数据长度 数据方式 排序算法 时间S 比较次数 交换次数 排序算法 时间S 比较次数 交换次数 排序算法 时间S 排序算法 时间S 排序算法 时间S
10000 顺序
逆序
随机[0-1000]
全0
0->max->1
sort_shell 0.001
0.001
0.003
0.001
0.001
120005
172578
262568
120005
145769
0
62560
147599
0
30748
gap_2.2 0.001
0.001
0.002
0.001
0.001
110843
147003
205750
110843
143453
0
44816
100214
0
36788
gap_3 0.001
0.001
0.003
0.001
0.001
gap_4 0.000
0.002
0.003
0.000
0.001
gap_5 0.001
0.001
0.004
0.001
0.001
100000 顺序
逆序
随机[0-10000]
全0
0->max->1
sort_shell 0.012
0.018
0.041
0.013
0.015
1500006
2244585
4244870
1500006
1849170
0
844560
2795529
0
399136
gap_2.2 0.011
0.015
0.034
0.012
0.016
1408345
1873314
2671875
1408345
1836048
0
551604
1317032
0
469577
gap_3 0.009
0.015
0.040
0.009
0.012
gap_4 0.007
0.015
0.041
0.007
0.012
gap_5 0.006
0.015
0.116
0.006
0.012
1000000 顺序
逆序
随机[0-100000]
全0
0->max->1
sort_shell 0.160
0.216
0.611
0.148
0.183
18000007
26357530
68376922
18000007
22371988
0
9357504
50878221
0
4871952
gap_2.2 0.149
0.185
0.374
0.140
0.189
17083348
22856174
33672822
17083348
22421397
0
6639212
17120198
0
5756891
gap_3 0.107
0.185
0.527
0.102
0.149
gap_4 0.084
0.181
0.615
0.078
0.130
gap_5 0.073
0.179
3.561
0.071
0.129
10000000 顺序
逆序
随机[0-100000]
全0
0->max->1
sort_shell 1.779
2.582
8.960
1.739
2.185
220000008
317626219
1069948411
220000008
270962910
0
107626176
854961948
0
55962560
gap_2.2 1.644
2.175
4.507
1.576
2.133
200833345
268000919
405669941
200833345
264788606
0
75831416
210155083
0
68143804
gap_3 1.183
2.093
9.045
1.146
1.668
gap_4 0.942
0.002
8.609
0.922
1.539
gap_5 0.883
2.112
39.075
0.861
1.555
  • gap 的选择对排序算法效率影响很大
  • 从这几个组测试数据来看,gap 选择 2.2 综合效率最高

5. 归并排序 merge

static inline void Merge(std::vector<int>& arr, int left, int mid, int right) {
    int n = right - left + 1;//临时数组存合并后的有序序列
    int* tmp = new int[n];
    int k = 0;
    int i = left;
    int j = mid + 1;
    while (i <= mid && j <= right) {
        if (arr[i] < arr[j]) {
            tmp[k++] = arr[i++];
        }
        else {
            tmp[k++] = arr[j++];
        }
    }
    while (i <= mid) {
        tmp[k++] = arr[i++];
    }
    while (j <= right) {
        tmp[k++] = arr[j++];
    }
    for (int m = 0; m < n; ++m) {
        arr[left + m] = tmp[m];
    }
    delete[] tmp;//删掉堆区的内存
}
static inline void sortMerge(std::vector<int>& arr, int left, int right) {
    if (left == right) {
        return;  //递归基是让数组中的每个数单独成为长度为1的区间
    }
    int mid = (left + right) / 2;
    sortMerge(arr, left, mid);
    sortMerge(arr, mid + 1, right);
    Merge(arr, left, mid, right);
}
void sort_merge(std::vector<int>& arr) {
    sortMerge(arr, 0, arr.size() - 1);
}

6. 快速排序 quick

static inline int partition(std::vector<int>& arr, int left, int right) {
    int pivot = arr[left]; /* 在最左边挖了一个坑,并临时保存坑中的元素,此时left指向这个坑 */
    while (left < right) {
        while (left < right && arr[right] >= pivot) {
            right--; 
        }
        arr[left] = arr[right]; /* 从右往左,找到第一个小于基准的值,与坑进行互换 */
        while (left < right && arr[left] <= pivot) {
            left++; 
        }
        arr[right] = arr[left]; /* 从左望右,找到第一个大于基数的值,与坑进行互换 */
    }
    arr[left] = pivot; /* 把基准填入最后一个坑 */
    return left;
}
static inline void quickSort(std::vector<int>& arr, int left, int right) {
    if (left >= right) {
        return;
    }
    int position = partition(arr, left, right);
    if (position > left) {
        quickSort(arr, left, position - 1);
    }
    if (position < right) {
        quickSort(arr, position + 1, right);
    }
}
void sort_quick(std::vector<int>& arr) {
    quickSort(arr, 0, arr.size() - 1);
}

以上代码的中函数 partition 中的 pivot 每次都选最左边的值,当数组是逆序或者大面积逆序的时候,效率很差。同时数据逆序时,递归调用层数太多,很有可能栈溢出。
稍微改动一点,每次在 left 和 right 中间选取一个随机位置,将这个位置与 left 进行交换,相当于 pivot 的值就是随机的,改动代码如下

static inline int partition_random(std::vector<int>& arr, int left, int right) {
    std::default_random_engine random(time(NULL));
    std::uniform_int_distribution<int> dis1(left, right);
    int mid = dis1(random);
    int temp = arr[left];
    arr[left] = arr[mid];
    arr[mid] = temp;
    int pivot = arr[left]; /* 在最左边挖了一个坑,并临时保存坑中的元素,此时left指向这个坑 */
    while (left < right) {
        while (left < right && arr[right] >= pivot) {
            right--;
        }
        arr[left] = arr[right]; /* 从右往左,找到第一个小于基准的值,与坑进行互换 */
        while (left < right && arr[left] <= pivot) {
            left++;
        }
        arr[right] = arr[left]; /* 从左望右,找到第一个大于基数的值,与坑进行互换 */
    }
    arr[left] = pivot; /* 把基准填入最后一个坑 */
    return left;
}

经测试发现,在输入数组本身为随机的情况下,partition_random 方法效率要差很多,原因是随机函数调用次数太多导致性能差,于是做一点点改进,换成使用获取ns时间来模拟随机值

static inline int partition_random_time(std::vector<int>& arr, int left, int right) {
    uint64_t t = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
    int mid = t % (right - left) + left;
    int temp = arr[left];
    arr[left] = arr[mid];
    arr[mid] = temp;
    int pivot = arr[left]; /* 在最左边挖了一个坑,并临时保存坑中的元素,此时left指向这个坑 */
    while (left < right) {
        while (left < right && arr[right] >= pivot) {
            right--;
        }
        arr[left] = arr[right]; /* 从右往左,找到第一个小于基准的值,与坑进行互换 */
        while (left < right && arr[left] <= pivot) {
            left++;
        }
        arr[right] = arr[left]; /* 从左望右,找到第一个大于基数的值,与坑进行互换 */
    }
    arr[left] = pivot; /* 把基准填入最后一个坑 */
    return left;
}

测试对比

数据长度 数据方式 排序算法 时间S 比较次数 交换次数 栈深 排序算法 时间S 比较次数 交换次数 栈深 排序算法 时间S 比较次数 交换次数 栈深
10000 顺序
逆序
全0
0->max->1
随机[0-10000]
随机[0-1000]
随机[0-100]
随机[0-10]
sort_quick 0.167
0.148
0.165
0.002
0.002
0.001
0.003
0.013
49995000
50000000
49995000
477279
201426
225631
539491
4156165
9999
9999
9999
30955
31227
28140
22158
14680
199980
199980
199980
1640
580
900
3140
24980
sort_quick_random 0.022
0.018
0.192
0.020
0.022
0.026
0.030
0.039
173230
177047
49995000
211550
206076
212510
536657
4243287
6718
12773
9999
32612
31089
28787
22346
14904
620
600
199980
600
640
800
3220
23720
sort_quick_random_time 0.001
0.001
0.150
0.002
0.001
0.001
0.002
0.015
159219
178019
49995000
209432
199248
230973
547243
4532485
7062
12540
9999
31622
31438
28148
21936
14724
580
640
199980
660
560
1000
3200
25240
100000 顺序
逆序
全0
0->max->1
随机[0-100000]
随机[0-10000]
随机[0-1000]
随机[0-100]
sort_quick 14.959
14.654
15.479
0.026
0.019
0.016
0.025
0.125
4999950000
5000000000
4999950000
7622561
2681076
2762938
6020716
41162538
99999
99999
99999
391120
389278
364649
299146
222137
1999980
1999980
1999980
3060
780
940
3520
30380
sort_quick_random 0.183
0.202
14.375
0.197
0.199
0.232
0.260
0.362
2018329
2226212
4999950000
2603392
2714558
2827954
5985350
41224175
62102
129462
99999
384938
385797
362389
299000
225317
680
760
1999980
720
900
1000
3700
29180
sort_quick_random_time 0.010
0.010
14.099
0.016
0.020
0.019
0.029
0.133
2034893
2252728
4999950000
2697028
2662789
2816588
6031719
43824040
70760
124837
99999
402245
389422
362149
297201
215492
780
840
1999980
780
760
1000
3460
30680
1000000 顺序
逆序
全0
0->max->1
随机[0-1000000]
随机[0-100000]
随机[0-10000]
随机[0-1000]
sort_quick -1.000
-1.000
-1.000
0.368
0.200
0.201
0.289
1.346
0
0
0
109786135
33897189
34206626
66110991
430278258
0
0
0
4576789
4614226
4403201
3728938
2975141
0
0
0
4980
1100
1160
3740
28400
sort_quick_random 1.712
2.023
-1.000
2.055
2.102
2.455
2.781
3.842
24297024
26128503
0
33585568
32921333
35084119
65943225
428986214
587196
1210851
0
4782607
4639564
4351762
3751578
2973398
920
960
0
980
1000
1280
3840
31240
sort_quick_random_time 0.113
0.123
-1.000
0.176
0.228
0.231
0.306
1.330
24855857
26576833
0
33544634
33142889
34799692
65457491
426499334
706298
1250884
0
4687857
4636489
4377918
3761835
3003727
1000
980
0
1080
1000
1160
3760
30080
  • 时间 -1.000 表示未测试,时间太长了,等不起
  • 栈深度单位是4字节,有些许误差,但不会太大
  • 使用随机pivot效果明显,在有序和部分有序情况对改善排序时间和递归栈深度都有改善

7. 堆排序 heap

static inline void heapify(std::vector<int>& arr, int i, int length) {
    int left = 2 * i + 1;
    int right = 2 * i + 2;
    int largest = i;
    if (left < length && arr[left] > arr[largest]) {
        largest = left;
    }
    if (right < length && arr[right] > arr[largest]) {
        largest = right;
    }
    if (largest != i) {
        int temp = arr[i];
        arr[i] = arr[largest];
        arr[largest] = temp;
        heapify(arr, largest, length);
    }
}
void sort_heap(std::vector<int>& arr) {
    /* 构建大顶堆 */
    for (int i = arr.size() / 2 - 1; i >= 0; i--) {
        heapify(arr, i, arr.size());
    }
    for (int i = arr.size() - 1; i > 0; i--) {
        int temp = arr[0];
        arr[0] = arr[i];
        arr[i] = temp;
        heapify(arr, 0, i);
    }
}

8. 计数排序 counting

计数排序的思想是:对每一个输入元素x,确定小于x的元素个数,利用这一信息,就可以直接把x放到它在输出数组中的位置上了。

void sort_counting(std::vector<int>& arr) {
    int max = arr[0];
    for (int i = 0; i < arr.size(); i++) {
        if (arr[i] > max) {
            max = arr[i];
        }
    }
    std::vector<int> count(max + 1, 0);
    std::vector<int> tArr(arr);
    for (auto x : arr) {
        count[x]++;
    }
    for (int i = 1; i <= max; i++) {
        count[i] += count[i - 1];
    }
    for (int i = arr.size() - 1; i >= 0; i--) {
        arr[count[tArr[i]] - 1] = tArr[i];
        count[tArr[i]]--;
    }
}

9. 桶排序 bucket

void sort_bucket(std::vector<int>& arr) {
    int max = arr[0];
    int min = arr[0];
    for (int i = 0; i < arr.size(); i++) {
        if (arr[i] > max) {
            max = arr[i];
        }
        if (arr[i] < min) {
            min = arr[i];
        }
    }
    int bucketCount = 10;
    int bucketSize = arr.size();
    int gap = (max - min) / bucketCount + 1;
    std::vector<std::vector<int>> bucket(bucketCount + 1);
    for (int i = 0; i < arr.size(); i++) {
        int index = (arr[i] / gap);
        bucket[index].push_back(arr[i]);
    }
    int k = 0;
    for (int i = 0; i < bucket.size(); i++) {
        sort_shell(bucket[i]);  //调用希尔排序
        for (int j = 0; j < bucket[i].size(); j++) {
            arr[k++] = bucket[i][j];
        }
    }
}

10. 基数排序 radix

void sort_radix(std::vector<int>& arr) {
    /* 获取位数 */
    int max = arr[0];
    for (int i = 0; i < arr.size(); i++) {
        if (arr[i] > max) {
            max = arr[i];
        }
    }
    int d = 1;
    int p = 10;
    while (max >= p) {
        max /= p;
        d++;
    }
    std::vector<int> temp(arr.size(), 0);
    std::vector<int> count(p);
    for (int radix = 1, i = 0; i <= d; i++, radix *= p) {
        for (int j = 0; j < p; j++) {   /* 清空计数器 */
            count[j] = 0;  
        }
        for (int j = 0; j < arr.size(); j++) {  /* 统计每个桶中的记录 */
            int index = (arr[j] / radix) % p;
            count[index]++;
        }
        for (int j = 1; j < p; j++) {
            count[j] = count[j] + count[j - 1];
        }
        for (int j = arr.size() - 1; j >= 0; j--) {
            int index = (arr[j] / radix) % p;
            temp[count[index] - 1] = arr[j];
            count[index]--;
        }
        for (int j = 0; j < arr.size(); j++) {
            arr[j] = temp[j];
        }
    }
}

十大排序测试对比

  • 测试耗时单位是秒
  • 冒泡排序使用 sort_bubble2, 希尔排序使用 sort_shell_gap_2.2, 快速排序使用 sort_quick_random_time
数据长度 数据方式 bubble selection insertion shell merge quick heap counting bucket radix
10000
顺序
逆序
全0
0->max->1
随机[0-10000]
随机[0-1000]
随机[0-100]
随机[0-10]
0.000
0.684
0.000
0.343
0.403
0.393
0.393
0.383
0.237
0.250
0.233
0.227
0.228
0.230
0.227
0.225
0.000
0.360
0.000
0.164
0.163
0.161
0.161
0.147
0.001
0.001
0.001
0.001
0.003
0.002
0.001
0.002
0.003
0.003
0.003
0.003
0.003
0.003
0.003
0.003
0.001
0.001
0.142
0.001
0.002
0.002
0.002
0.012
0.004
0.004
0.001
0.003
0.004
0.004
0.004
0.003
0.000
0.000
0.000
0.001
0.000
0.000
0.000
0.000
0.001
0.001
0.002
0.001
0.003
0.002
0.002
0.001
0.001
0.002
0.000
0.001
0.001
0.001
0.001
0.001
100000
十万
顺序
逆序
全0
0->max->1
随机[0-100000]
随机[0-10000]
随机[0-1000]
随机[0-100]
0.001
61.222
0.001
33.859
39.825
39.829
39.564
43.278
22.599
23.436
22.225
23.018
22.535
22.526
22.863
23.781
0.001
32.724
0.001
16.201
16.158
16.257
17.074
16.449
0.011
0.015
0.012
0.017
0.030
0.027
0.026
0.020
0.028
0.028
0.028
0.030
0.037
0.038
0.038
0.036
0.010
0.011
14.263
0.016
0.019
0.019
0.028
0.133
0.046
0.042
0.004
0.046
0.049
0.047
0.050
0.049
0.002
0.002
0.001
0.002
0.002
0.002
0.002
0.001
0.015
0.019
0.017
0.017
0.031
0.028
0.027
0.021
0.013
0.015
0.004
0.013
0.015
0.012
0.011
0.009
1000000
百万
顺序
逆序
全0
0->max->1
随机[0-1000000]
随机[0-100000]
随机[0-10000]
随机[0-1000]
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
0.142
0.183
0.142
0.182
0.383
0.363
0.324
0.275
0.324
0.306
0.308
0.311
0.414
0.416
0.405
0.393
0.117
0.125
-
0.178
0.221
0.224
0.311
1.331
0.579
0.551
0.038
0.581
0.630
0.640
0.623
0.598
0.025
0.023
0.018
0.021
0.033
0.022
0.019
0.018
0.176
0.235
0.200
0.207
0.462
0.449
0.410
0.300
0.150
0.175
0.046
0.154
0.171
0.151
0.127
0.111
10000000
千万
顺序
逆序
全0
0->max->1
随机[0-10000000]
随机[0-1000000]
随机[0-100000]
随机[0-10000]
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
1.600
2.139
1.609
2.127
4.542
4.398
4.005
3.499
3.375
3.342
3.350
3.354
4.628
4.604
4.514
4.391
1.310
1.373
-
2.030
2.562
2.588
3.459
13.738
6.910
6.637
0.363
6.972
9.015
9.015
8.941
9.001
0.251
0.237
0.181
0.212
0.886
0.303
0.232
0.197
2.011
2.669
2.329
2.366
6.426
6.201
5.962
5.135
1.746
1.945
0.463
1.725
1.723
1.717
1.515
1.316
100000000
亿
顺序
逆序
全0
0->max->1
随机[0-100000000]
随机[0-10000000]
随机[0-1000000]
随机[0-100000]
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
17.698
23.631
17.656
23.425
52.693
51.197
47.068
41.920
36.396
36.297
36.145
36.865
51.035
50.766
49.950
48.687
14.377
14.977
-
22.245
28.941
29.133
37.513
142.467
80.115
77.762
3.617
81.797
143.594
136.161
137.860
131.183
2.521
2.323
1.832
2.140
10.130
8.420
3.221
2.612
23.266
30.549
25.508
27.192
95.108
93.309
90.055
80.646
19.521
21.508
4.673
19.327
21.460
19.427
17.248
15.114
  • bubble,selection,insertion 三兄弟在决赛刚开始没多久就骂骂咧咧的退出了比赛
  • 使用递减 gap 序列插入排序的希尔排序,表现很亮眼
  • 归并排序时间复杂度,只与输入数据量大小有关,与数据是否有序关系不大
  • 快速排序在输入序列值相同较多的情况下,时间复杂度高,输入全0也出现了退赛情况
  • 堆排序
  • 计数排序,桶排序,基数排序都很快,但是内存需求也很高,在可以使用空间换时间的情况下,是不错的选择
  • 选择使用什么排序算法需要根据待排序数据情况做合适的取舍
posted on 2023-04-05 18:38  shunxiang  阅读(414)  评论(0)    收藏  举报