十大排序一图总览

算法 最佳时间复杂度 平均时间复杂度 最差时间复杂度 空间复杂度
冒泡排序 O(n) O($n^2$) O($n^2$​) O(1)
插入排序 O(n) O($n^2$) O($n^2$​) O(1)
归并排序 O(nlogn) O(nlogn) O(nlogn) O(n)
选择排序 O($n^2$) O($n^2$) O($n^2$​) O(1)
希尔排序 O(n) O(nlogn)比nlogn算法慢,
比$n^2$算法快
O(nlogn) O(1)
快速排序 O(nlogn) O(nlogn) O($n^2$) O(logn)
堆排序 O(nlogn) O(nlogn) O(nlogn) O(1)
计数排序 O(n) O(max(n, n_elem)) n_elem是元素值 O(max(n, n_elem)) O(n_elem + n)
桶排序 O(n+k) O(n+k) O($n^2$​) O(n)
基数排序 O(nk) O(nk) O(nk) O(n+k)

在基于元素比较的排序中,前三个是稳定排序(桶排序也算稳定算法吧?中间用的插入排序),接下来的四个是不稳定排序。

计数排序、桶排序 不是基于元素比较的排序算法。

PS:希尔排序比O($n^2$)的算法快,比O(nlong)算法慢 。对于大多数已经排好序的数组,希尔排序很快,但它不适用于大量数据排序。 适合大量数据排序的有快速排序、归并排序、堆排序等,若要求是稳定排序的,则只有归并排序。

此图来自 拓跋阿秀:https://github.com/forthespada/InterviewGuide

img

稳定排序:冒泡排序(O(n^2)/O(1) ),插入排序(O(n^2 / O(1)),归并排序O(nlogn)

不稳定排序:选择排序(O(n^2) / O(1)),快速排序(O(nlogn / O(logn)),希尔排序O(n^(1.3-2)),堆排序

计数排序,桶排序,基数排序

插入排序(变种:希尔排序)

希尔排序是插入排序的一个变种。采用插入排序的方法,先让数组中任意间隔为 h 的元素有序,刚开始 h 的大小可以是 h = n / 2,接着让 h = n / 4,让 h 一直缩小,当 h = 1 时,也就是此时数组中任意间隔为1的元素有序,此时的数组就是有序的了

明显,插入排序时间复杂度为O(n^2),SC:O(1),是稳定排序。

对比 插入排序与希尔排序代码,希尔排序相对插入排序只是 多了一个分组的for循环。所以其复杂度为O(n^(1.3-2) )(没有快排快,但比O(n^2)的算法快很多), SC:O(1),非稳定排序。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// 插入排序: 将一个数与该数之前的数依次进行比较,插入到该插入的位置
// nums = {4, 1, 2, 6, 5, 3}
// 1 4 2 6 5 3 
// 1 2 4 6 5 3 
// 1 2 4 5 6 3 
// 1 2 3 4 5 6 
// 1 2 3 4 5 6 
void insert_sort_core(vector<int>& nums, int i, int gap=1) {
    int elem = nums[i];
    int j = i - gap;
    while (j >= 0 && elem < nums[j]) {
        nums[j+gap] = nums[j]; 
        j--;
    }
    nums[j+gap] = elem;    // 插入到该插入的位置
}
void insert_sort(vector<int>& nums) {
    int n = nums.size();
    for (int i = 1; i < n; ++ i) {
        // if (nums[i] >= nums[i-1]) {
        //     // 如果nums[i]大于等于前面排好序的最大的数nums[i-1],则直接插入
        //     continue;
        // }
        // 上面这一段可以不要, 与 shell_sort 的写法形成对比(将核心部分分离开)
        insert_sort_core(nums, i);  // gap默认参数为1
        print(nums);
    } 
} 

// 希尔排序每趟排序结果:
// 4 1 2 6 5 3 
// 4 1 2 6 5 3 
// 4 1 2 6 5 3 
// 1 4 2 6 5 3 
// 1 2 4 6 5 3 
// 1 2 4 6 5 3 
// 1 2 4 5 6 3 
// 1 2 3 4 5 6 
// 1 2 3 4 5 6 
void shell_sort_core(vector<int>& nums, int i, int gap=1) {
    int elem = nums[i];     // 待插入的元素
    int j = i - gap;        // 与待插入元素比较的元素的位置
    while (j >= 0 && elem < nums[j]) {
        nums[j + gap] = nums[j];    // 元素后移
        j -= gap;
    }
    nums[j + gap] = elem;  // 插入到该插入的位置
}
void shell_sort(vector<int>& nums) {
    int n = nums.size();
    // 分组, 最开始的时候分组大小是 数组长度的一半
    for (int gap = n / 2; gap > 0; gap /= 2) {
        // 对各个分组进行插入排序
        for (int i = gap; i < n; ++ i) {
            // 将nums[i] 插入到正确的位置上
            shell_sort_core(nums, i, gap);
            print(nums);
        }
        
    }
}

快速排序

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <iostream>
#include <vector>
using namespace std;
void print(vector<int>& nums);

void quick_sort(int low, int high, vector<int>&nums) {
    if (low >= high) return ;
    int first = low, last = high, key = nums[first]; // key是 用于比较的基准数据
    while (first < last) {
        /// 从右往左找到第一个小于k 的
        while (first < last && nums[last] >= key) {
            last --;
        }
        if (first < last) nums[first++] = nums[last];
        // 从左往右找到第一个大于k 的
        while (first < last && nums[first] <= key) {
            first ++;
        }
        if (first < last) nums[last--] = nums[first];
    }
    nums[first] = key;	// 放置key
    print(nums);
    quick_sort(low, first-1, nums);
    quick_sort(first+1, high, nums);
}

// 每一趟打印的结果
// 3 1 2 4 5 6 
// 2 1 3 4 5 6 
// 1 2 3 4 5 6 
// 1 2 3 4 5 6 
// 1 2 3 4 5 6 
void print(vector<int>& nums) {
    for (auto& num: nums) {
        cout << num << " ";
    }
    cout << endl;
}

int main() {
    vector<int> vec = {4, 1, 2, 6, 5, 3};
    int n = vec.size();
    quick_sort(0, n-1, vec);
    for (auto num: vec) {
        cout << num << " ";
    }
    return 0;
}

归并排序

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
// nums = {4, 1, 2, 6, 5, 3}
// 1 4 2 6 5 3 
// 1 2 4 6 5 3 
// 1 2 4 5 6 3 
// 1 2 4 3 5 6 
// 1 2 3 4 5 6 
// 1 2 3 4 5 6
void merge_sort_iteration(vector<int>& nums) {
    int n = nums.size();
    vector<int> temp(n, 0);
    for (int k = 1; k < n; k <<= 1) {
        for (int start = 0; start < n; start += (k<<1)) {
            int low = start, mid = min(start + k, n), high = min(start + (k<<1), n);
            int index = low, start1 = low, end1 = mid, start2 = mid, end2 = high;
            // [start1, end1) 和 [start2, end2) 都是前闭后开
            while (start1 < end1 && start2 < end2) {
                temp[index++] = nums[start1] < nums[start2] ? nums[start1++] : nums[start2++];
            }
            while (start1 < end1) {
                temp[index++] = nums[start1++];
            }
            while (start2 < end2) {
                temp[index++] = nums[start2++];
            }
        }
        swap(nums, temp);
        print(nums);
    }
}
void merge_sort_core(vector<int>& nums, vector<int>& nums_copy, int low, int high) {
    if (low >= high) return ;
    int len = high - low, mid = low + len / 2;
    int start1 = low, end1 = mid;
    int start2 = mid + 1, end2 = high;
    merge_sort_core(nums, nums_copy, start1, end1);
    merge_sort_core(nums, nums_copy, start2, end2);
    int index = low;
    // [start1, end1], [start2, end2] 都是 闭区间
    while (start1 <= end1 && start2 <= end2) {
        nums_copy[index++] = nums[start1] < nums[start2] ? nums[start1++] : nums[start2++];
    }
    while (start1 <= end1) {
        nums_copy[index++] = nums[start1++];
    }
    while (start2 <= end2) {
        nums_copy[index++] = nums[start2++];
    }

    for (index = low; index <= high; ++index) {	
        nums[index] = nums_copy[index];
    }
    cout << "low = " << low << " high = " << high << endl;
    print(nums);
}
// low = 0 high = 1
// 1 4 2 6 5 3 
// low = 0 high = 2
// 1 2 4 6 5 3 
// low = 3 high = 4
// 1 2 4 5 6 3 
// low = 3 high = 5
// 1 2 4 3 5 6 
// low = 0 high = 5
// 1 2 3 4 5 6 
// 1 2 3 4 5 6 
void merge_sort_core_2(vector<int>& nums, vector<int>& nums_copy, int low, int high) {
    if (low >= high) return ;
    int len = high - low, mid = low + len / 2;
    int start1 = low, end1 = mid;
    int start2 = mid + 1, end2 = high;
    merge_sort_core(nums_copy, nums, start1, end1);
    merge_sort_core(nums_copy, nums, start2, end2);
    // merge_sort_core(nums, nums_copy, start1, end1);
    // merge_sort_core(nums, nums_copy, start2, end2);
    int index = low;
    // [start1, end1], [start2, end2] 都是 闭区间
    while (start1 <= end1 && start2 <= end2) {
        nums_copy[index++] = nums[start1] < nums[start2] ? nums[start1++] : nums[start2++];
    }
    while (start1 <= end1) {
        nums_copy[index++] = nums[start1++];
    }
    while (start2 <= end2) {
        nums_copy[index++] = nums[start2++];
    }

    // for (index = low; index <= high; ++index) {
    //     nums[index] = nums_copy[index];
    // }
    cout << "low = " << low << " high = " << high << endl;
    print(nums_copy);
}
void merge_sort(vector<int>& nums) {
    int n = nums.size();
    // merge_sort_core()
    vector<int> nums_copy(n, 0); // merge_sort_core() 时使用这个
    merge_sort_core(nums, nums_copy, 0, n-1);

    // merge_sort_core_2()
    // vector<int> nums_copy(nums);    // merge_sort_core_2() 时 使用这个
    // merge_sort_core_2(nums, nums_copy, 0, n-1);
    // nums.assign(nums_copy.begin(), nums_copy.end());    // merge_sort_core_2() 时 加上这个
}

堆排序

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// 对有一定顺序的堆, 当前第i个节点取根左右的最大值(此操作称为 heapity)
void heapify(vector<int>& nums, int n, int i) {
    int l = i * 2 + 1, r = i * 2 + 2;
    int max = i;
    if (l < n && nums[l] > nums[max]) max = l;
    if (r < n && nums[r] > nums[max]) max = r;
    if (max != i) {
        swap(nums[max], nums[i]);
        heapify(nums, n, max);
    }
}

// 建立大根堆 (从树的倒数第二层的第一个节点开始, 对每个节点做 heapify操作, 然后向上走)
void heapify_build(vector<int>& nums, int n) {
    int temp = (n - 2) / 2;
    for (int i = temp; i>=0; i--) {
        heapify(nums, n, i);
    }
    print(nums);
}

// nums = {4, 1, 2, 6, 5, 3}
// 6 5 3 1 4 2 
// =====大根堆建立完毕======
// 5 4 3 1 2 6 
// 4 2 3 1 5 6 
// 3 2 1 4 5 6 
// 2 1 3 4 5 6 
// 1 2 3 4 5 6 
// 1 2 3 4 5 6 
// ====小根堆结果打印======
// 1 2 3 4 5 6 
// 建立大根堆之后,每次交换最后一个节点和根节点(最大值)
// 对交换后的根节点继续进行heapify(此时堆的最后一位是最大值,因此不用管他, n变n-1)
void heapify_sort(vector<int>& nums, int n) {
    heapify_build(nums, n);
    cout << "=====大根堆建立完毕======" << endl;
    for (int i=0; i<n; i++) {
        swap(nums.front(), nums[n-i-1]);
        heapify(nums, n-i-1, 0);
        print(nums);
    }
    cout << "====小根堆结果打印======" << endl;
}

计数排序

适用于小范围整数(1-100)的排序不适用于字符串排序。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <algorithm>    // maxelement()
// 计数排序
void count_sort(vector<int>& vecRaw, vector<int>& vecObj) {
    // 确保待排容器非空
    if (vecObj.size() == 0) return;

    // 使用 vecRaw 的最大值 + 1 作为计数容器 vecCount的大小
    int vecCountLen = 1 + (*max_element(vecRaw.begin(), vecRaw.end()));
    vector<int> vecCount(vecCountLen, 0);

    // 统计每个键值出现的次数
    for (int i=0; i<vecRaw.size(); i++) {
        vecCount[vecRaw[i]] ++;
    }

    // 后面的键值出现的`位置` 是 前面所有键值出现的次数之和
    for (int i=1; i<vecCountLen; i++) {
        vecCount[i] += vecCount[i-1];
    }

    // 将键值放到目标位置
    for (int i=vecRaw.size(); i>0; i--) { // 此处逆序是为了保证相同键值的稳定性
        vecObj[--vecCount[vecRaw[i-1]]] = vecRaw[i-1];
    }
}

桶排序

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
 * @brief 桶排序
 *  思想步骤:
 *  1) 首先找到最大元素和最小元素 O(n)
 *  2) 将所有元素依次 放置到若干个桶中;
 *  3) 对每个非空桶进行排序(插入排序)
 *  4) 将非空桶的元素放到原来的数组中
 * @param nums 
 * @param bucketSize 
 */
void bucket_sort(vector<int>& nums, int bucketSize) {
    if (nums.size() == 0) return;
    int minVal = *min_element(nums.begin(), nums.end());
    int maxVal = *max_element(nums.begin(), nums.end());

    // 桶的初始化
    int DEFAULT_BUCKET_SIZE = 5;    // 设置桶的默认数量为5
    bucketSize = bucketSize | DEFAULT_BUCKET_SIZE;     // 桶的实际大小
    int bucketCount = (maxVal - minVal) / bucketSize + 1;  // 桶的个数
    // 创建bucketCount个桶
    vector<vector<int> > buckets(bucketCount, vector<int>());
    
    // 利用 映射函数 将元素分配到各个桶中
    for (auto& num: nums) {
        buckets[(num - minVal) / bucketSize].push_back(num);
    }
    int index = 0;
    for (int i=0; i<bucketCount; i++) {
        insert_sort(buckets[i]);    // 对每个桶进行排序, 使用了插入排序
        for (int j=0; j<buckets[i].size(); j++) {
            nums[index++] = buckets[i][j];
        }
    }    
}

基数排序

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
 * @brief 基数排序
 * 步骤:1)取得数组的最大数, 并获取它的 `位数`
 *      2)nums为原始数组, 从 最低位 开始取 `每个位` 组成`radix数组`;
 *      3)对radix进行计数排序(利用计数排序适用于小范围树的特点)
 * @param nums 
 * @param n 
 */
// nums = {44, 3, 38, 5, 795, 47, 15, 26, 132, 27, 2, 46, 4, 19, 50, 48}
// 对(右数起)第1排序的结果如下:
// 50 132 2 3 44 4 5 795 15 26 46 47 27 38 48 19 
// 对(右数起)第2排序的结果如下:
// 2 3 4 5 15 19 26 27 132 38 44 46 47 48 50 795 
// 对(右数起)第3排序的结果如下:
// 2 3 4 5 15 19 26 27 38 44 46 47 48 50 132 795 
// 2 3 4 5 15 19 26 27 38 44 46 47 48 50 132 795
void radix_sort(vector<int>& nums, int n) {
    int d = maxbit(nums, n);
    int radix = 1;
    for (int i=1; i<=d; i++) {  // 进行d次排序

        // 以下是计数排序的代码 
        vector<int> count(10, 0);   // 计数器,记录桶中元素的个数
        vector<int> tmp(n);
        int k;
        for (int j=0; j<n; j++) {
            count[(nums[j] / radix) % 10] ++;   // 桶的记录数
        }
        for (int j=1; j<10; j++) {
            count[j] += count[j-1]; 
        }
        for (int j=n; j>0; j--) {
            tmp[--count[(nums[j-1] / radix) % 10]] = nums[j-1];
        }

        for(int j=0; j<n; j++) {
            nums[j] = tmp[j];
        }
        radix *= 10;
        cout << "对(右数起)第" << i << "位排序的结果如下:" << endl; 
        print(nums);
    }
}

十大排序全部代码如下

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
/*** 
 * @Date: 2022-03-09 09:19:09
 * @Author: yufeng
 * @GitHub: https://github.com/fzhiy
 * @Email: fzhiy270@163.com
 * @LastEditTime: 2022-03-09 14:22:59
 */
#include <iostream>
#include <vector>
using namespace std;

void print(vector<int>& nums);

// 冒泡排序
// 若相邻元素不是目标排序的方式(升序或降序), 则交换这两个元素
// nums = {4, 1, 2, 6, 5, 3}
// 1 2 4 5 3 6 
// 1 2 4 3 5 6 
// 1 2 3 4 5 6 
// 1 2 3 4 5 6
void bubble_sort(vector<int>& nums) {
    int n = nums.size();
    for (int i=0; i<n-1; i++) { // n-1趟循环
        bool flag = false;  // false表示不存在相邻元素交换
        for (int j=0; j<n-i-1; j++) {
            if (nums[j] > nums[j+1]) {
                flag = true;    // 这一趟存在依次相邻元素交换
                swap(nums[j], nums[j+1]);
            }
        }
        if (!flag) {    
            // 这一趟不存在元素交换,说明所有元素都是有序的(优化)
            break;
        }
        // 打印这一趟排序的结果
        print(nums);
    }
}

// 选择排序
// 1 4 2 6 5 3 
// 1 2 4 6 5 3 
// 1 2 3 6 5 4 
// 1 2 3 4 5 6 
// 1 2 3 4 5 6 
// 1 2 3 4 5 6 
// 1 2 3 4 5 6 
void select_sort(vector<int>& nums) {
    int n = nums.size();
    for (int i=0; i<n; i++) {
        int minIndex = i;
        for (int j=i+1; j<n; j++) {
            if (nums[j] < nums[minIndex]) {
                minIndex = j;
            }
        }
        swap(nums[minIndex], nums[i]);
        print(nums);
    }
}

// 快速排序 每一趟打印的结果
// nums = {4, 1, 2, 6, 5, 3}
// 3 1 2 4 5 6 
// 2 1 3 4 5 6 
// 1 2 3 4 5 6 
// 1 2 3 4 5 6 
// 1 2 3 4 5 6 
void quick_sort(int low, int high, vector<int>&nums) {
    if (low >= high) return ;
    int first = low, last = high, key = nums[first];
    while (first < last) {
        /// 从右往左找到第一个小于k 的
        while (first < last && nums[last] >= key) {
            last --;
        }
        if (first < last) nums[first++] = nums[last];
        // 从左往右找到第一个大于k 的
        while (first < last && nums[first] <= key) {
            first ++;
        }
        if (first < last) nums[last--] = nums[first];
    }
    nums[first] = key;
    print(nums);
    quick_sort(low, first-1, nums);
    quick_sort(first+1, high, nums);
}

// 插入排序: 将一个数与该数之前的数依次进行比较,插入到该插入的位置
// nums = {4, 1, 2, 6, 5, 3}
// 1 4 2 6 5 3 
// 1 2 4 6 5 3 
// 1 2 4 5 6 3 
// 1 2 3 4 5 6 
// 1 2 3 4 5 6 
void insert_sort_core(vector<int>& nums, int i, int gap=1) {
    int elem = nums[i];
    int j = i - gap;
    while (j >= 0 && elem < nums[j]) {
        nums[j+gap] = nums[j]; 
        j--;
    }
    nums[j+gap] = elem;    // 插入到该插入的位置
}
void insert_sort(vector<int>& nums) {
    int n = nums.size();
    for (int i = 1; i < n; ++ i) {
        // if (nums[i] >= nums[i-1]) {
        //     // 如果nums[i]大于等于前面排好序的最大的数nums[i-1],则直接插入
        //     continue;
        // }
        // 上面这一段可以不要, 与 shell_sort 的写法形成对比(将核心部分分离开)
        insert_sort_core(nums, i);  // gap默认参数为1
        print(nums);
    } 
} 

// 希尔排序每趟排序结果:
// 4 1 2 6 5 3 
// 4 1 2 6 5 3 
// 4 1 2 6 5 3 
// 1 4 2 6 5 3 
// 1 2 4 6 5 3 
// 1 2 4 6 5 3 
// 1 2 4 5 6 3 
// 1 2 3 4 5 6 
// 1 2 3 4 5 6 
void shell_sort_core(vector<int>& nums, int i, int gap=1) {
    int elem = nums[i];     // 待插入的元素
    int j = i - gap;        // 与待插入元素比较的元素的位置
    while (j >= 0 && elem < nums[j]) {
        nums[j + gap] = nums[j];    // 元素后移
        j -= gap;
    }
    nums[j + gap] = elem;  // 插入到该插入的位置
}
void shell_sort(vector<int>& nums) {
    int n = nums.size();
    // 分组, 最开始的时候分组大小是 数组长度的一半
    for (int gap = n / 2; gap > 0; gap /= 2) {
        // 对各个分组进行插入排序
        for (int i = gap; i < n; ++ i) {
            // 将nums[i] 插入到正确的位置上
            shell_sort_core(nums, i, gap);
            print(nums);
        }
        
    }
}

void merge_sort_iteration(vector<int>& nums) {
    int n = nums.size();
    vector<int> temp(n, 0);
    for (int k = 1; k < n; k <<= 1) {
        for (int start = 0; start < n; start += (k<<1)) {
            int low = start, mid = min(start + k, n), high = min(start + (k<<1), n);
            int index = low, start1 = low, end1 = mid, start2 = mid, end2 = high;
            // [start1, end1) 和 [start2, end2) 都是前闭后开
            while (start1 < end1 && start2 < end2) {
                temp[index++] = nums[start1] < nums[start2] ? nums[start1++] : nums[start2++];
            }
            while (start1 < end1) {
                temp[index++] = nums[start1++];
            }
            while (start2 < end2) {
                temp[index++] = nums[start2++];
            }
        }
        swap(nums, temp);
        print(nums);
    }
}
// nums = {4, 1, 2, 6, 5, 3}
// 1 4 2 6 5 3 
// 1 2 4 6 5 3 
// 1 2 4 5 6 3 
// 1 2 4 3 5 6 
// 1 2 3 4 5 6 
// 1 2 3 4 5 6
void merge_sort_core(vector<int>& nums, vector<int>& nums_copy, int low, int high) {
    if (low >= high) return ;
    int len = high - low, mid = low + len / 2;
    int start1 = low, end1 = mid;
    int start2 = mid + 1, end2 = high;
    merge_sort_core(nums, nums_copy, start1, end1);
    merge_sort_core(nums, nums_copy, start2, end2);
    int index = low;
    // [start1, end1], [start2, end2] 都是 闭区间
    while (start1 <= end1 && start2 <= end2) {
        nums_copy[index++] = nums[start1] < nums[start2] ? nums[start1++] : nums[start2++];
    }
    while (start1 <= end1) {
        nums_copy[index++] = nums[start1++];
    }
    while (start2 <= end2) {
        nums_copy[index++] = nums[start2++];
    }

    for (index = low; index <= high; ++index) {
        nums[index] = nums_copy[index];
    }
    cout << "low = " << low << " high = " << high << endl;
    print(nums);
}
// low = 0 high = 1
// 1 4 2 6 5 3 
// low = 0 high = 2
// 1 2 4 6 5 3 
// low = 3 high = 4
// 1 2 4 5 6 3 
// low = 3 high = 5
// 1 2 4 3 5 6 
// low = 0 high = 5
// 1 2 3 4 5 6 
// 1 2 3 4 5 6 
void merge_sort_core_2(vector<int>& nums, vector<int>& nums_copy, int low, int high) {
    if (low >= high) return ;
    int len = high - low, mid = low + len / 2;
    int start1 = low, end1 = mid;
    int start2 = mid + 1, end2 = high;
    merge_sort_core(nums_copy, nums, start1, end1);
    merge_sort_core(nums_copy, nums, start2, end2);
    // merge_sort_core(nums, nums_copy, start1, end1);
    // merge_sort_core(nums, nums_copy, start2, end2);
    int index = low;
    while (start1 <= end1 && start2 <= end2) {
        nums_copy[index++] = nums[start1] < nums[start2] ? nums[start1++] : nums[start2++];
    }
    while (start1 <= end1) {
        nums_copy[index++] = nums[start1++];
    }
    while (start2 <= end2) {
        nums_copy[index++] = nums[start2++];
    }

    // for (index = low; index <= high; ++index) {
    //     nums[index] = nums_copy[index];
    // }
    cout << "low = " << low << " high = " << high << endl;
    print(nums_copy);
}
void merge_sort(vector<int>& nums) {
    int n = nums.size();
    merge_sort_iteration(nums);

    // merge_sort_core()
    // vector<int> nums_copy(n, 0); // merge_sort_core() 时使用这个
    // merge_sort_core(nums, nums_copy, 0, n-1);

    // merge_sort_core_2()
    // vector<int> nums_copy(nums);    // merge_sort_core_2() 时 使用这个
    // merge_sort_core_2(nums, nums_copy, 0, n-1);
    // nums.assign(nums_copy.begin(), nums_copy.end());    // merge_sort_core_2() 时 加上这个
}

// 对有一定顺序的堆, 当前第i个节点取根左右的最大值(此操作称为 heapity)
void heapify(vector<int>& nums, int n, int i) {
    int l = i * 2 + 1, r = i * 2 + 2;
    int max = i;
    if (l < n && nums[l] > nums[max]) max = l;
    if (r < n && nums[r] > nums[max]) max = r;
    if (max != i) {
        swap(nums[max], nums[i]);
        heapify(nums, n, max);
    }
}

// 建立大根堆 (从树的倒数第二层的第一个节点开始, 对每个节点做 heapify操作, 然后向上走)
void heapify_build(vector<int>& nums, int n) {
    int temp = (n - 2) / 2;
    for (int i = temp; i>=0; i--) {
        heapify(nums, n, i);
    }
    print(nums);
}

// nums = {4, 1, 2, 6, 5, 3}
// 6 5 3 1 4 2 
// =====大根堆建立完毕======
// 5 4 3 1 2 6 
// 4 2 3 1 5 6 
// 3 2 1 4 5 6 
// 2 1 3 4 5 6 
// 1 2 3 4 5 6 
// 1 2 3 4 5 6 
// ====小根堆结果打印======
// 1 2 3 4 5 6 
// 建立大根堆之后,每次交换最后一个节点和根节点(最大值)
// 对交换后的根节点继续进行heapify(此时堆的最后一位是最大值,因此不用管他, n变n-1)
void heapify_sort(vector<int>& nums, int n) {
    heapify_build(nums, n);
    cout << "=====大根堆建立完毕======" << endl;
    for (int i=0; i<n; i++) {
        swap(nums.front(), nums[n-i-1]);
        heapify(nums, n-i-1, 0);
        print(nums);
    }
    cout << "====小根堆结果打印======" << endl;
}

void print(vector<int>& nums) {
    for (auto& num: nums) {
        cout << num << " ";
    }
    cout << endl;
}


#include <algorithm>    // maxelement()
// 计数排序
void count_sort(vector<int>& vecRaw, vector<int>& vecObj) {
    // 确保待排容器非空
    if (vecObj.size() == 0) return;

    // 使用 vecRaw 的最大值 + 1 作为计数容器 vecCount的大小
    int vecCountLen = 1 + (*max_element(vecRaw.begin(), vecRaw.end()));
    vector<int> vecCount(vecCountLen, 0);

    // 统计每个键值出现的次数
    for (int i=0; i<vecRaw.size(); i++) {
        vecCount[vecRaw[i]] ++;
    }

    // 后面的键值出现的`位置` 是 前面所有键值出现的次数之和
    for (int i=1; i<vecCountLen; i++) {
        vecCount[i] += vecCount[i-1];
    }

    // 将键值放到目标位置
    for (int i=vecRaw.size(); i>0; i--) { // 此处逆序是为了保证相同键值的稳定性
        vecObj[--vecCount[vecRaw[i-1]]] = vecRaw[i-1];
    }
}

/**
 * @brief 桶排序
 *  思想步骤:
 *  1) 首先找到最大元素和最小元素 O(n)
 *  2) 将所有元素依次 放置到若干个桶中;
 *  3) 对每个非空桶进行排序(插入排序)
 *  4) 将非空桶的元素放到原来的数组中
 * @param nums 
 * @param bucketSize 
 */
void bucket_sort(vector<int>& nums, int bucketSize) {
    if (nums.size() == 0) return;
    int minVal = *min_element(nums.begin(), nums.end());
    int maxVal = *max_element(nums.begin(), nums.end());

    // 桶的初始化
    int DEFAULT_BUCKET_SIZE = 5;    // 设置桶的默认数量为5
    bucketSize = bucketSize | DEFAULT_BUCKET_SIZE;     // 桶的实际大小
    int bucketCount = (maxVal - minVal) / bucketSize + 1;  // 桶的个数
    // 创建bucketCount个桶
    vector<vector<int> > buckets(bucketCount, vector<int>());
    
    // 利用 映射函数 将元素分配到各个桶中
    for (auto& num: nums) {
        buckets[(num - minVal) / bucketSize].push_back(num);
    }
    int index = 0;
    for (int i=0; i<bucketCount; i++) {
        insert_sort(buckets[i]);    // 对每个桶进行排序, 使用了插入排序
        for (int j=0; j<buckets[i].size(); j++) {
            nums[index++] = buckets[i][j];
        }
    }    
}

// 基数排序的辅助函数,用于求数组元素的最大位数
int maxbit(vector<int>& nums, int n) {
    int maxElem = *max_element(nums.begin(), nums.end());

    int d = 1, p = 10;  // d表示元素的位数
    while (maxElem >= p) {
        maxElem /= 10;
        ++ d;
    }
    return d;
}
/**
 * @brief 基数排序
 * 步骤:1)取得数组的最大数, 并获取它的 `位数`
 *      2)nums为原始数组, 从 最低位 开始取 `每个位` 组成`radix数组`;
 *      3)对radix进行计数排序(利用计数排序适用于小范围树的特点)
 * @param nums 
 * @param n 
 */
// nums = {44, 3, 38, 5, 795, 47, 15, 26, 132, 27, 2, 46, 4, 19, 50, 48}
// 对(右数起)第1排序的结果如下:
// 50 132 2 3 44 4 5 795 15 26 46 47 27 38 48 19 
// 对(右数起)第2排序的结果如下:
// 2 3 4 5 15 19 26 27 132 38 44 46 47 48 50 795 
// 对(右数起)第3排序的结果如下:
// 2 3 4 5 15 19 26 27 38 44 46 47 48 50 132 795 
// 2 3 4 5 15 19 26 27 38 44 46 47 48 50 132 795
void radix_sort(vector<int>& nums, int n) {
    int d = maxbit(nums, n);
    int radix = 1;
    for (int i=1; i<=d; i++) {  // 进行d次排序

        // 以下是计数排序的代码 
        vector<int> count(10, 0);   // 计数器,记录桶中元素的个数
        vector<int> tmp(n);
        int k;
        for (int j=0; j<n; j++) {
            count[(nums[j] / radix) % 10] ++;   // 桶的记录数
        }
        for (int j=1; j<10; j++) {
            count[j] += count[j-1]; 
        }
        for (int j=n; j>0; j--) {
            tmp[--count[(nums[j-1] / radix) % 10]] = nums[j-1];
        }

        for(int j=0; j<n; j++) {
            nums[j] = tmp[j];
        }
        radix *= 10;
        cout << "对(右数起)第" << i << "位排序的结果如下:" << endl; 
        print(nums);
    }
}

int main() {
    vector<int> nums = {4, 1, 2, 6, 5, 3};
    int n = nums.size();
    // 稳定排序:
    bubble_sort(nums);                  // 冒泡排序
    // insert_sort(nums);               // 插入排序
    // merge_sort(nums);                // 归并排序

    // 不稳定排序:
    // select_sort(nums);               // 选择排序
    // quick_sort(0, n-1, nums);        // 快速排序
    // shell_sort(nums);                // 希尔排序
    // heapify_sort(nums, n);           // 堆排序(通过构建大根堆然后做调整获得小根堆)

    vector<int> vecRaw = {0,5,7,9,6,3,4,5,2,8,6,9,2,1};
    // vector<int> nums = {44, 3, 38, 5, 795, 47, 15, 26, 132, 27, 2, 46, 4, 19, 50, 48};
    vector<int> vecObj(vecRaw.size(), 1);

    // count_sort(vecRaw, vecObj);      // 计数排序
    // bucket_sort(vecRaw, 0);          // 桶排序
    // radix_sort(nums, nums.size());   // 基数排序
    print(nums);

    return 0;
}