跳转至

5、排序算法

1、bead sort 珠排序

​ Bead sort是一种基于重力排序的算法,也称为gravity sort或者gravity algorithm。它是一种简单且易于实现的排序算法,特别适用于排序数字范围较小的数组。

​ Bead sort的核心思想是将待排序的数组中的每个元素表示为一列小珠子,然后将这些小珠子放置在一系列竖直的杆子上,珠子会根据自身重力向下滑动,最终会落到最下方的位置,从而形成有序的结果。这个过程可以用重力来模拟,因此Bead sort也被称为gravity sort。

​ Bead sort的时间复杂度为O(n),其中n是待排序的元素个数。但是,由于需要使用额外的空间来表示每个元素的小珠子,因此Bead sort的空间复杂度比较高,是O(nk),其中k是元素值的范围。此外,Bead sort也不稳定,不能用于需要稳定排序的场合。

​ 虽然Bead sort不如一些其他排序算法那样高效,但它的实现相对简单,因此在某些特定的场合下还是很有用的。

普通版本的代码

C++
 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
#include <iostream>
#include <vector>

using namespace std;

void beadSort(vector<int>& arr) {
    int n = arr.size();
    int maxElem = arr[0];

    // 找到数组中最大的元素
    for (int i = 1; i < n; i++) {
        if (arr[i] > maxElem) {
            maxElem = arr[i];
        }
    }

    // 初始化一个二维数组来表示小珠子的位置
    vector<vector<int>> beads(maxElem, vector<int>(n));

    // 将每个元素表示为一列小珠子,并将它们放置在杆子上
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < arr[i]; j++) {
            beads[j][i] = 1;
        }
    }

    // 让小珠子根据重力向下滑动,直到落到最下面的位置
    for (int i = 0; i < n; i++) {
        int sum = 0;
        for (int j = 0; j < maxElem; j++) {
            if (beads[j][i] == 1) {
                sum++;
                beads[j][i] = 0;
            } else if (sum > 0) {
                beads[j][i] = 1;
                sum--;
            }
        }
    }

    // 从杆子底部向上读取每个元素的值,得到有序的结果
    for (int i = 0; i < n; i++) {
        int sum = 0;
        for (int j = maxElem - 1; j >= 0; j--) {
            if (beads[j][i] == 1) {
                sum++;
            } else if (sum > 0) {
                arr[i] = sum;
                break;
            }
        }
    }
}

int main() {
    vector<int> arr = {5, 3, 1, 4, 2};
    beadSort(arr);

    for (int i = 0; i < arr.size(); i++) {
        cout << arr[i] << " ";
    }
    cout << endl;

    return 0;
}

专家版本的代码

C++
 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
// C++ program to implement gravity/bead sort
#include <cstdio>
#include <cstring>

#define BEAD(i, j) beads[i * max + j]

// function to perform the above algorithm
void beadSort(int *a, int len) {
    // Find the maximum element
    int max = a[0];
    for (int i = 1; i < len; i++)
        if (a[i] > max)
            max = a[i];

    // allocating memory
    unsigned char *beads = new unsigned char[max * len];
    memset(beads, 0, static_cast<size_t>(max) * len);

    // mark the beads
    for (int i = 0; i < len; i++)
        for (int j = 0; j < a[i]; j++) BEAD(i, j) = 1;

    for (int j = 0; j < max; j++) {
        // count how many beads are on each post
        int sum = 0;
        for (int i = 0; i < len; i++) {
            sum += BEAD(i, j);
            BEAD(i, j) = 0;
        }

        // Move beads down
        for (int i = len - sum; i < len; i++) BEAD(i, j) = 1;
    }

    // Put sorted values in array using beads
    for (int i = 0; i < len; i++) {
        int j;
        for (j = 0; j < max && BEAD(i, j); j++) {
        }

        a[i] = j;
    }
    delete[] beads;
}

// driver function to test the algorithm
int main() {
    int a[] = {5, 3, 1, 7, 4, 1, 1, 20};
    int len = sizeof(a) / sizeof(a[0]);

    beadSort(a, len);

    for (int i = 0; i < len; i++) printf("%d ", a[i]);

    return 0;
}

2、Bitonic Sort 双调排序

​ Bitonic Sort是一种并行排序算法,常用于GPU和其他并行计算平台上。它基于比较交换排序算法,可以排序大小为2的幂次方的数组。

​ Bitonic Sort的核心思想是将待排序的数组分解成一系列的“比特位”(bitonic sequence),并对这些比特位进行排序。比特位是指数组中一段连续的元素序列,这个序列要么单调递增,要么单调递减。在排序的过程中,Bitonic Sort通过交换相邻元素的位置来实现排序,直到所有的比特位都有序为止。

​ Bitonic Sort的时间复杂度为O(n*log2(n)),其中n是待排序的元素个数。由于Bitonic Sort的实现比较复杂,因此它一般用于需要高效处理大规模数据集的并行计算场合。

以下是Bitonic Sort的具体步骤:

  1. 将输入的数组大小n进行分解,得到一系列的比特位序列,每个比特位序列的长度为2的幂次方。
  2. 对每个比特位序列进行排序,排序方式为:
  3. 从小到大排序:比特位序列要么单调递增,要么单调递减,如果单调递减,先将其反转,然后再按照单调递增排序。
  4. 从大到小排序:比特位序列要么单调递减,要么单调递增,如果单调递增,先将其反转,然后再按照单调递减排序。
  5. 将所有比特位序列拼接起来,得到排序后的数组。

普通版本的代码

C++
 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
void bitonicSort(vector<int>& arr, int low, int count, bool isAscending) {
    if (count > 1) {
        int k = count / 2;

        // 比特位单调递增排序
        bitonicSort(arr, low, k, true);

        // 比特位单调递减排序
        bitonicSort(arr, low + k, k, false);

        // 比特位合并
        bitonicMerge(arr, low, count, isAscending);
    }
}

void bitonicMerge(vector<int>& arr, int low, int count, bool isAscending) {
    if (count > 1) {
        int k = count / 2;
        for (int i = low; i < low + k; i++) {
            if (isAscending == (arr[i] > arr[i + k])) {
                swap(arr[i], arr[i + k]);
            }
        }
        bitonicMerge(arr, low, k, isAscending);
        bitonicMerge(arr, low + k, k, isAscending);
    }
}

void bitonicSort(vector<int>& arr) {
    int n = arr.size();
    bitonicSort(arr, 0, n, true);
}

​ 以上代码实现了Bitonic Sort算法,使用递归方式对输入的数组进行排序,具体实现可以参考注释。

专家版的代码

C++
 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
// Source : https://www.geeksforgeeks.org/bitonic-sort/

/* C++ Program for Bitonic Sort. Note that this program
   works only when size of input is a power of 2. */

#include <algorithm>
#include <iostream>

/*The parameter dir indicates the sorting direction, ASCENDING
   or DESCENDING; if (a[i] > a[j]) agrees with the direction,
   then a[i] and a[j] are interchanged.*/
void compAndSwap(int a[], int i, int j, int dir) {
    if (dir == (a[i] > a[j]))
        std::swap(a[i], a[j]);
}

/*It recursively sorts a bitonic sequence in ascending order,
  if dir = 1, and in descending order otherwise (means dir=0).
  The sequence to be sorted starts at index position low,
  the parameter cnt is the number of elements to be sorted.*/
void bitonicMerge(int a[], int low, int cnt, int dir) {
    if (cnt > 1) {
        int k = cnt / 2;
        for (int i = low; i < low + k; i++) compAndSwap(a, i, i + k, dir);
        bitonicMerge(a, low, k, dir);
        bitonicMerge(a, low + k, k, dir);
    }
}

/* This function first produces a bitonic sequence by recursively
    sorting its two halves in opposite sorting orders, and then
    calls bitonicMerge to make them in the same order */
void bitonicSort(int a[], int low, int cnt, int dir) {
    if (cnt > 1) {
        int k = cnt / 2;

        // sort in ascending order since dir here is 1
        bitonicSort(a, low, k, 1);

        // sort in descending order since dir here is 0
        bitonicSort(a, low + k, k, 0);

        // Will merge wole sequence in ascending order
        // since dir=1.
        bitonicMerge(a, low, cnt, dir);
    }
}

/* Caller of bitonicSort for sorting the entire array of
   length N in ASCENDING order */
void sort(int a[], int N, int up) { bitonicSort(a, 0, N, up); }

// Driver code
int main() {
    int a[] = {3, 7, 4, 8, 6, 2, 1, 5};
    int N = sizeof(a) / sizeof(a[0]);

    int up = 1;  // means sort in ascending order
    sort(a, N, up);

    std::cout << "Sorted array: \n";
    for (int i = 0; i < N; i++) std::cout << a[i] << " ";
    return 0;
}

3、Bogo sort

​ Bogo Sort是一种非常简单但是非常低效的排序算法,其核心思想是通过随机打乱数组元素的顺序来进行排序。具体来说,算法会不断地对数组进行随机打乱,直到数组元素的顺序变得有序为止。由于Bogo Sort是一种随机算法,因此其时间复杂度是随机的,最坏情况下可以达到O(n*n!),因此Bogo Sort只是一种玩笑般的排序算法,不适用于实际应用场景。

以下是Bogo Sort的具体步骤:

  1. 判断输入的数组是否已经有序,如果有序则直接返回。
  2. 否则,随机打乱数组的顺序。
  3. 判断打乱后的数组是否有序,如果有序则结束算法;否则重复步骤2和3。

普通版本的代码

C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#include <algorithm>
#include <random>

using namespace std;

bool isSorted(vector<int>& arr) {
    for (int i = 1; i < arr.size(); i++) {
        if (arr[i] < arr[i - 1]) {
            return false;
        }
    }
    return true;
}

void bogoSort(vector<int>& arr) {
    while (!isSorted(arr)) {
        random_shuffle(arr.begin(), arr.end());
    }
}

​ 以上代码实现了Bogo Sort算法,使用随机打乱数组元素的顺序来进行排序,直到数组元素的顺序变得有序为止。由于Bogo Sort的时间复杂度是随机的,因此其实现非常简单,但是非常低效,不适用于实际应用场景。

专家版的代码

C++
  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
/**
 * @file
 * @brief Implementation of [Bogosort algorithm](https://en.wikipedia.org/wiki/Bogosort)
 *
 * @details
 *      In computer science, bogosort (also known as permutation sort, stupid sort, slowsort, 
 *      shotgun sort, random sort, monkey sort, bobosort or shuffle sort) is a highly inefficient 
 *      sorting algorithm based on the generate and test paradigm. Two versions of this algorithm 
 *      exist: a deterministic version that enumerates all permutations until it hits a sorted one,
 *      and a randomized version that randomly permutes its input.Randomized version is implemented here. 
 *
 * ### Algorithm
 * Shuffle the array untill array is sorted.
 *
 * @author [Deep Raval](https://github.com/imdeep2905)
 */
#include <iostream>
#include <algorithm>
#include <array>
#include <cassert>


/**
 * @namespace sorting
 * @brief Sorting algorithms
 */
namespace sorting {
/**
 * Function to shuffle the elements of an array. (for reference)
 * @tparam T typename of the array
 * @tparam N length of array
 * @param arr array to shuffle
 * @returns new array with elements shuffled from a given array
 */
template <typename T, size_t N>
std::array <T, N> shuffle (std::array <T, N> arr) {
    for (int i = 0; i < N; i++) {
        // Swaps i'th  index with random index (less than array size)
        std::swap(arr[i], arr[std::rand() % N]);
    }
    return arr;
}
/**
 * Implement randomized Bogosort algorithm and sort the elements of a given array.
 * @tparam T typename of the array
 * @tparam N length of array
 * @param arr array to sort
 * @returns new array with elements sorted from a given array
 */
template <typename T, size_t N>
std::array <T, N> randomized_bogosort (std::array <T, N> arr) {
    // Untill array is not sorted
    while (!std::is_sorted(arr.begin(), arr.end())) {
        std::random_shuffle(arr.begin(), arr.end());// Shuffle the array
    }
    return arr;
}

}  // namespace sorting

/**
 * Function to display array on screen 
 * @tparam T typename of the array
 * @tparam N length of array
 * @param arr array to display
 */
template <typename T, size_t N>
void show_array (const std::array <T, N> &arr) {
    for (int x : arr) {
        std::cout << x << ' ';
    }
    std::cout << '\n';
}

/**
 * Function to test above algorithm
 */
void test() {
    // Test 1
    std::array <int, 5> arr1;
    for (int &x : arr1) {
        x = std::rand() % 100;
    }
    std::cout << "Original Array : ";
    show_array(arr1);
    arr1 = sorting::randomized_bogosort(arr1);
    std::cout << "Sorted Array : ";
    show_array(arr1);
    assert(std::is_sorted(arr1.begin(), arr1.end()));
    // Test 2
    std::array <int, 5> arr2;
    for (int &x : arr2) {
        x = std::rand() % 100;
    }
    std::cout << "Original Array : ";
    show_array(arr2);
    arr2 = sorting::randomized_bogosort(arr2);
    std::cout << "Sorted Array : ";
    show_array(arr2);
    assert(std::is_sorted(arr2.begin(), arr2.end()));
}

/** Driver Code */
int main() {
    // Testing
    test();
    // Example Usage
    std::array <int, 5> arr = {3, 7, 10, 4, 1}; // Defining array which we want to sort
    std::cout << "Original Array : ";
    show_array(arr);
    arr = sorting::randomized_bogosort(arr); // Callling bogo sort on it
    std::cout << "Sorted Array : ";
    show_array(arr); // Printing sorted array
    return 0;
}

4、Bubble Sort 冒泡排序

​ Bubble Sort(冒泡排序)是一种简单的排序算法,其核心思想是不断地交换相邻的两个元素,直到整个数组有序为止。具体来说,算法会从左到右遍历数组,比较相邻的两个元素的大小,如果它们的顺序不正确,则交换它们的位置。通过不断进行这样的操作,最终可以将整个数组排序。冒泡排序的时间复杂度为O(n2),因此对于大规模数据的排序来说效率非常低,但对于小规模数据的排序则是一种较为简单实用的算法。

以下是Bubble Sort的具体步骤:

  1. 从左到右遍历数组,比较相邻的两个元素的大小,如果它们的顺序不正确,则交换它们的位置。
  2. 重复步骤1,直到整个数组有序为止。

普通版本的代码

C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
void bubbleSort(vector<int>& arr) {
    int n = arr.size();
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                swap(arr[j], arr[j + 1]);
            }
        }
    }
}

以上代码实现了Bubble Sort算法,使用不断交换相邻的两个元素的方式来进行排序,直到整个数组有序为止。由于Bubble Sort的时间复杂度为O(n2),因此对于大规模数据的排序来说效率较低,但对于小规模数据的排序则是一种较为简单实用的算法。

专家版的代码

C++
 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
/**
 * @file
 * @brief Bubble sort algorithm
 *
 * The working principle of the Bubble sort algorithm:

Bubble sort algorithm is the bubble sorting algorithm. The most important reason
for calling the bubble is that the largest number is thrown at the end of this
algorithm. This is all about the logic. In each iteration, the largest number is
expired and when iterations are completed, the sorting takes place.

What is Swap?

Swap in the software means that two variables are displaced.
An additional variable is required for this operation. x = 5, y = 10.
We want x = 10, y = 5. Here we create the most variable to do it.

int z;
z = x;
x = y;
y = z;

The above process is a typical displacement process.
When x assigns the value to x, the old value of x is lost.
That's why we created a variable z to create the first value of the value of x,
and finally, we have assigned to y.

Bubble Sort Algorithm Analysis (Best Case - Worst Case - Average Case)

Bubble Sort Worst Case Performance is O (n²). Why is that? Because if you
remember Big O Notation, we were calculating the complexity of the algorithms in
the nested loops. The n * (n - 1) product gives us O (n²) performance. In the
worst case all the steps of the cycle will occur. Bubble Sort (Avarage Case)
Performance. Bubble Sort is not an optimal algorithm. in average, O (n²)
performance is taken. Bubble Sort Best Case Performance. O (n). However, you
can't get the best status in the code we shared above. This happens on the
optimized bubble sort algorithm. It's right down there.
*/

#include <iostream>
#include <vector>

int main() {
    int n;
    bool swap_check = true;
    std::cout << "Enter the amount of numbers to sort: ";
    std::cin >> n;
    std::vector<int> numbers;
    std::cout << "Enter " << n << " numbers: ";
    int num;

    // Input
    for (int i = 0; i < n; i++) {
        std::cin >> num;
        numbers.push_back(num);
    }

    // Bubble Sorting
    for (int i = 0; (i < n) && (swap_check); i++) {
        swap_check = false;
        for (int j = 0; j < n - 1 - i; j++) {
            if (numbers[j] > numbers[j + 1]) {
                swap_check = true;
                std::swap(numbers[j],
                          numbers[j + 1]);  // by changing swap location.
                                            // I mean, j. If the number is
                                            // greater than j + 1, then it
                                            // means the location.
            }
        }
    }

    // Output
    std::cout << "\nSorted Array : ";
    for (int i = 0; i < numbers.size(); i++) {
        if (i != numbers.size() - 1) {
            std::cout << numbers[i] << ", ";
        } else {
            std::cout << numbers[i] << std::endl;
        }
    }
    return 0;
}

5、Bucket Sort 桶排序

​ Bucket Sort(桶排序)是一种线性排序算法,其核心思想是将元素按照一定的规则划分到不同的桶中,然后对每个桶内的元素进行排序,最后将所有桶内的元素按照顺序依次合并起来。桶排序的时间复杂度取决于划分桶的规则,如果划分得当,时间复杂度可以达到O(n),但如果划分不当,时间复杂度也可能达到O(n2)。

以下是Bucket Sort的具体步骤:

  1. 创建一个空的桶列表。
  2. 遍历输入数组,将每个元素插入到对应的桶中。
  3. 对每个桶中的元素进行排序。
  4. 合并所有桶内的元素,并返回排序后的数组。

普通版本的代码

C++
 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
void bucketSort(vector<int>& arr, int bucketSize) {
    if (arr.empty()) {
        return;
    }

    int minValue = arr[0];
    int maxValue = arr[0];
    for (int i = 1; i < arr.size(); i++) {
        if (arr[i] < minValue) {
            minValue = arr[i];
        } else if (arr[i] > maxValue) {
            maxValue = arr[i];
        }
    }

    int bucketCount = (maxValue - minValue) / bucketSize + 1;
    vector<vector<int>> buckets(bucketCount);

    for (int i = 0; i < arr.size(); i++) {
        int bucketIndex = (arr[i] - minValue) / bucketSize;
        buckets[bucketIndex].push_back(arr[i]);
    }

    arr.clear();
    for (int i = 0; i < bucketCount; i++) {
        sort(buckets[i].begin(), buckets[i].end());
        for (int j = 0; j < buckets[i].size(); j++) {
            arr.push_back(buckets[i][j]);
        }
    }
}

​ 以上代码实现了Bucket Sort算法,使用将元素按照一定的规则划分到不同的桶中,然后对每个桶内的元素进行排序,最后将所有桶内的元素按照顺序依次合并起来的方式来进行排序。由于Bucket Sort的时间复杂度取决于划分桶的规则,因此算法的实现较为复杂,但如果划分得当,时间复杂度可以达到O(n)。

专家版的代码

C++
 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
// C++ program to sort an array using bucket sort
#include <algorithm>
#include <iostream>
#include <vector>

// Function to sort arr[] of size n using bucket sort
void bucketSort(float arr[], int n) {
    // 1) Create n empty buckets
    std::vector<float> *b = new std::vector<float>[n];

    // 2) Put array elements in different buckets
    for (int i = 0; i < n; i++) {
        int bi = n * arr[i];  // Index in bucket
        b[bi].push_back(arr[i]);
    }

    // 3) Sort individual buckets
    for (int i = 0; i < n; i++) std::sort(b[i].begin(), b[i].end());

    // 4) Concatenate all buckets into arr[]
    int index = 0;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < b[i].size(); j++) arr[index++] = b[i][j];
    delete[] b;
}

/* Driver program to test above funtion */
int main() {
    float arr[] = {0.897, 0.565, 0.656, 0.1234, 0.665, 0.3434};
    int n = sizeof(arr) / sizeof(arr[0]);
    bucketSort(arr, n);

    std::cout << "Sorted array is \n";
    for (int i = 0; i < n; i++) std::cout << arr[i] << " ";
    return 0;
}

6、Cocktail Selection Sort 双向选择排序/鸡尾酒排序

​ Cocktail Selection Sort(双向选择排序),也称为鸡尾酒排序或定向冒泡排序,是一种改进的冒泡排序算法。与传统的冒泡排序不同,它不仅从左到右地遍历数组进行比较和交换,而且还会从右到左地遍历数组。这样可以使得排序算法更加高效,因为它可以在同一次遍历中同时进行从左到右和从右到左的比较和交换操作。

以下是Cocktail Selection Sort的具体步骤:

  1. 从左到右遍历数组,选出最小的元素,将其放在数组的左边。
  2. 从右到左遍历数组,选出最大的元素,将其放在数组的右边。
  3. 重复步骤1和步骤2,直到整个数组有序为止。

普通版本的代码

C++
 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
void cocktailSelectionSort(vector<int>& arr) {
    int left = 0;
    int right = arr.size() - 1;

    while (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;
            }
        }

        swap(arr[left], arr[minIndex]);

        if (maxIndex == left) {
            maxIndex = minIndex;
        }

        swap(arr[right], arr[maxIndex]);

        left++;
        right--;
    }
}

​ 以上代码实现了Cocktail Selection Sort算法,使用从左到右和从右到左遍历数组选出最小和最大的元素,并将它们放在数组的两端的方式来进行排序,直到整个数组有序为止。与传统的冒泡排序不同,Cocktail Selection Sort算法同时进行从左到右和从右到左的比较和交换操作,因此可以使得排序算法更加高效。

专家版的代码

C++
  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
// Returns Sorted elements after performing Cocktail Selection Sort
// It is a Sorting algorithm which chooses the minimum and maximum element in an
// array simultaneously, and swaps it with the lowest and highest available
// position iteratively or recursively

#include <algorithm>
#include <iostream>
#include <vector>

// Iterative Version

void CocktailSelectionSort(std::vector<int> *vec, int low, int high) {
    while (low <= high) {
        int minimum = (*vec)[low];
        int minimumindex = low;
        int maximum = (*vec)[high];
        int maximumindex = high;

        for (int i = low; i <= high; i++) {
            if ((*vec)[i] >= maximum) {
                maximum = (*vec)[i];
                maximumindex = i;
            }
            if ((*vec)[i] <= minimum) {
                minimum = (*vec)[i];
                minimumindex = i;
            }
        }
        if (low != maximumindex || high != minimumindex) {
            std::swap((*vec)[low], (*vec)[minimumindex]);
            std::swap((*vec)[high], (*vec)[maximumindex]);
        } else {
            std::swap((*vec)[low], (*vec)[high]);
        }

        low++;
        high--;
    }
}

// Recursive Version

void CocktailSelectionSort_v2(std::vector<int> *vec, int low, int high) {
    if (low >= high)
        return;

    int minimum = (*vec)[low];
    int minimumindex = low;
    int maximum = (*vec)[high];
    int maximumindex = high;

    for (int i = low; i <= high; i++) {
        if ((*vec)[i] >= maximum) {
            maximum = (*vec)[i];
            maximumindex = i;
        }
        if ((*vec)[i] <= minimum) {
            minimum = (*vec)[i];
            minimumindex = i;
        }
    }
    if (low != maximumindex || high != minimumindex) {
        std::swap((*vec)[low], (*vec)[minimumindex]);
        std::swap((*vec)[high], (*vec)[maximumindex]);
    } else {
        std::swap((*vec)[low], (*vec)[high]);
    }

    CocktailSelectionSort(vec, low + 1, high - 1);
}

// main function, select any one of iterative or recursive version

int main() {
    int n;
    std::cout << "Enter number of elements\n";
    std::cin >> n;
    std::vector<int> v(n);
    std::cout << "Enter all the elements\n";
    for (int i = 0; i < n; ++i) {
        std::cin >> v[i];
    }

    int method;
    std::cout << "Enter method: \n\t0: iterative\n\t1: recursive:\t";
    std::cin >> method;

    if (method == 0) {
        CocktailSelectionSort(&v, 0, n - 1);
    } else if (method == 1) {
        CocktailSelectionSort_v2(&v, 0, n - 1);
    } else {
        std::cerr << "Unknown method" << std::endl;
        return -1;
    }
    std::cout << "Sorted elements are\n";
    for (int i = 0; i < n; ++i) {
        std::cout << v[i] << " ";
    }

    return 0;
}

7、Comb Sort

​ Comb Sort是一种基于交换的排序算法,类似于冒泡排序和快速排序。它的基本思想是通过一系列的间隔来比较和交换数组中相距较远的元素,从而逐渐缩小间隔并最终完成排序。与冒泡排序不同的是,Comb Sort算法使用的间隔可以大于1,这样可以加快排序速度。

以下是Comb Sort的具体步骤:

  1. 初始化间隔gap为数组长度。
  2. 对于每个gap,遍历数组,比较每个元素与当前gap个位置之后的元素的大小关系。如果前一个元素大于后一个元素,交换这两个元素的位置。
  3. 缩小gap,直到gap为1,完成排序。

普通版本的代码

C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
void combSort(vector<int>& arr) {
    int n = arr.size();
    int gap = n;
    bool swapped = true;

    while (gap > 1 || swapped) {
        gap = gap * 10 / 13;
        if (gap < 1) {
            gap = 1;
        }

        swapped = false;
        for (int i = 0; i < n - gap; i++) {
            if (arr[i] > arr[i + gap]) {
                swap(arr[i], arr[i + gap]);
                swapped = true;
            }
        }
    }
}

​ 以上代码实现了Comb Sort算法,使用一系列的间隔来比较和交换数组中相距较远的元素,从而逐渐缩小间隔并最终完成排序。在排序过程中,每次缩小间隔gap的大小,直到gap为1为止。由于Comb Sort算法使用的间隔可以大于1,因此可以加快排序速度。

专家版的代码

C++
  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
/**
 *
 * \file
 * \brief [Comb Sort Algorithm
 * (Comb Sort)](https://en.wikipedia.org/wiki/Comb_sort)
 *
 * \author
 *
 * \details
 * - A better version of bubble sort algorithm
 * - Bubble sort compares adjacent values whereas comb sort uses gap larger
 *   than 1
 * - Best case Time complexity O(n)
 *   Worst case Time complexity O(n^2)
 *
 */

#include <algorithm>
#include <cassert>
#include <iostream>

/**
 *
 * Find the next gap by shrinking the current gap by shrink factor of 1.3
 * @param gap current gap
 * @return new gap
 *
 */
int FindNextGap(int gap) {
    gap = (gap * 10) / 13;

    return std::max(1, gap);
}

/** Function to sort array
 *
 * @param arr array to be sorted
 * @param l start index of array
 * @param r end index of array
 *
 */
void CombSort(int *arr, int l, int r) {
    /**
     *
     * initial gap will be maximum and the maximum possible value is
     * the size of the array that is n and which is equal to r in this
     * case so to avoid passing an extra parameter n that is the size of
     * the array we are using r to initialize the initial gap.
     *
     */
    int gap = r;

    /// Initialize swapped as true to make sure that loop runs
    bool swapped = true;

    /// Keep running until gap = 1 or none elements were swapped
    while (gap != 1 || swapped) {
        /// Find next gap
        gap = FindNextGap(gap);

        swapped = false;

        /// Compare all elements with current gap
        for (int i = l; i < r - gap; ++i) {
            if (arr[i] > arr[i + gap]) {
                std::swap(arr[i], arr[i + gap]);
                swapped = true;
            }
        }
    }
}

void tests() {
    /// Test 1
    int arr1[10] = {34, 56, 6, 23, 76, 34, 76, 343, 4, 76};
    CombSort(arr1, 0, 10);
    assert(std::is_sorted(arr1, arr1 + 10));
    std::cout << "Test 1 passed\n";

    /// Test 2
    int arr2[8] = {-6, 56, -45, 56, 0, -1, 8, 8};
    CombSort(arr2, 0, 8);
    assert(std::is_sorted(arr2, arr2 + 8));
    std::cout << "Test 2 Passed\n";
}

/** Main function */
int main() {
    /// Running predefined tests
    tests();

    /// For user interaction
    int n;
    std::cin >> n;
    int *arr = new int[n];
    for (int i = 0; i < n; ++i) std::cin >> arr[i];
    CombSort(arr, 0, n);
    for (int i = 0; i < n; ++i) std::cout << arr[i] << ' ';
    delete[] arr;
    return 0;
}

8、Count Inversions

​ Count Inversions排序,也称为Merge Sort With Inversions Counting,是一种使用Count Inversions算法计算逆序对数量的排序算法。在排序过程中,算法会对数组进行分割、排序和合并,同时计算数组中逆序对的数量。

以下是Count Inversions排序的具体步骤:

  1. 使用分治法将数组分成两半,分别对左半部分和右半部分进行排序。
  2. 对于左半部分和右半部分,使用Count Inversions算法计算逆序对数量。
  3. 对于左半部分和右半部分,使用归并排序进行合并,并计算左半部分和右半部分之间的逆序对数量。
  4. 返回左半部分和右半部分之间的逆序对数量之和,作为数组中逆序对的总数量。

普通版本的代码

C++
 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
long long merge(vector<int>& arr, int start, int mid, int end) {
    int leftIndex = start;
    int rightIndex = mid + 1;
    vector<int> temp(end - start + 1);
    long long count = 0;
    int index = 0;

    while (leftIndex <= mid && rightIndex <= end) {
        if (arr[leftIndex] <= arr[rightIndex]) {
            temp[index++] = arr[leftIndex++];
        } else {
            temp[index++] = arr[rightIndex++];
            count += mid - leftIndex + 1;
        }
    }

    while (leftIndex <= mid) {
        temp[index++] = arr[leftIndex++];
    }

    while (rightIndex <= end) {
        temp[index++] = arr[rightIndex++];
    }

    for (int i = start; i <= end; i++) {
        arr[i] = temp[i - start];
    }

    return count;
}

long long countInversions(vector<int>& arr, int start, int end) {
    if (start >= end) {
        return 0;
    }

    int mid = start + (end - start) / 2;

    long long count = 0;
    count += countInversions(arr, start, mid);
    count += countInversions(arr, mid + 1, end);
    count += merge(arr, start, mid, end);

    return count;
}

long long countInversionsSort(vector<int>& arr) {
    return countInversions(arr, 0, arr.size() - 1);
}

​ 以上代码实现了Count Inversions排序算法,使用分治法将数组分成两半,分别对左半部分和右半部分进行排序。对于左半部分和右半部分,使用Count Inversions算法计算逆序对数量。对于左半部分和右半部分,使用归并排序进行合并,并计算左半部分和右半部分之间的逆序对数量。最后,返回左半部分和右半部分之间的逆序对数量之和,作为数组中逆序对的总数量。时间复杂度为O(nlogn)。

专家版的代码

C++
  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
/**
 * @file
 * @brief Counting Inversions using [Merge
 Sort](https://en.wikipedia.org/wiki/Merge_sort)
 *
 * @details
 * Program to count the number of inversions in an array
 * using merge-sort.
 *
 * The count of inversions help to determine how close the array
 * is to be sorted in ASCENDING order.
 *
 * two elements a[i] and a[j] form an inversion if `a[i]` > `a[j]` and i < j
 *
 * Time Complexity --> `O(n.log n)`

 * Space Complexity --> `O(n)` ; additional array `temp[1..n]`
 * ### Algorithm

 *   1. The idea is similar to merge sort, divide the array into two equal or
 almost
 *      equal halves in each step until the base case is reached.
 *   2. Create a function merge that counts the number of inversions when two
 halves of
 *      the array are merged, create two indices i and j, i is the index for
 first half
 *      and j is an index of the second half. if `a[i]` is greater than `a[j]`,
 then there are (mid – i)
 *      inversions, Because left and right subarrays are sorted, so all the
 remaining elements
 *      in left-subarray (a[i+1], a[i+2] … a[mid]) will be greater than a[j].
 *   3. Create a recursive function to divide the array into halves and find the
 answer by summing
 *      the number of inversions is the first half, number of inversion in the
 second half and
 *      the number of inversions by merging the two.
 *   4. The base case of recursion is when there is only one element in the
 given half.
 *   5. Print the answer
 *
 * @author [Rakshit Raj](https://github.com/rakshitraj)
 */
#include <cassert>   /// for assert
#include <cstdint>   /// for typedef datatype uint64_t
#include <iostream>  /// for IO operations
#include <vector>    /// for std::vector

/**
 * @namespace sorting
 * @brief Sorting algorithms
 */
namespace sorting {
/**
 * @namespace inversion
 * @brief Functions for counting inversions using Merge Sort algorithm
 */
namespace inversion {

// Functions used --->
// int mergeSort(int* arr, int* temp, int left, int right);
// int merge(int* arr, int* temp, int left, int mid, int right);
// int countInversion(int* arr, const int size);
// void show(int* arr, const int size);

/**
 * @brief Function to merge two sub-arrays.
 *
 * @details
 * merge() function is called from mergeSort()
 * to merge the array after it split for sorting
 * by the mergeSort() funtion.
 *
 * In this case the merge fuction will also count and return
 * inversions detected when merging the sub arrays.
 *
 * @param arr    input array, data-menber of vector
 * @param temp   stores the resultant merged array
 * @param left   lower bound of `arr[]` and left-sub-array
 * @param mid    midpoint, upper bound of left sub-array,
 *               `(mid+1)` gives the lower bound of right-sub-array
 * @param right  upper bound of `arr[]` and right-sub-array
 * @returns number of inversions found in merge step
 */
template <typename T>
uint32_t merge(T* arr, T* temp, uint32_t left, uint32_t mid, uint32_t right) {
    uint32_t i = left;       /* i --> index of left sub-array */
    uint32_t j = mid + 1;    /* j --> index for right sub-array */
    uint32_t k = left;       /* k --> index for resultant array temp */
    uint32_t inv_count = 0;  // inversion count

    while ((i <= mid) && (j <= right)) {
        if (arr[i] <= arr[j]) {
            temp[k++] = arr[i++];
        } else {
            temp[k++] = arr[j++];
            inv_count +=
                (mid - i +
                 1);  // tricky; may vary depending on selection of sub-array
        }
    }
    // Add remaining elements from the larger subarray to the end of temp
    while (i <= mid) {
        temp[k++] = arr[i++];
    }
    while (j <= right) {
        temp[k++] = arr[j++];
    }
    // Copy temp[] to arr[]
    for (k = left; k <= right; k++) {
        arr[k] = temp[k];
    }
    return inv_count;
}

/**
 * @brief Implement merge Sort and count inverions while merging
 *
 * @details
 * The mergeSort() function implements Merge Sort, a
 * Divide and conquer algorithm, it divides the input
 * array into two halves and calls itself for each
 * sub-array and then calls the merge() function to
 * merge the two halves.
 *
 * @param arr   - array to be sorted
 * @param temp  - merged resultant array
 * @param left  - lower bound of array
 * @param right - upper bound of array
 * @returns number of inversions in array
 */
template <typename T>
uint32_t mergeSort(T* arr, T* temp, uint32_t left, uint32_t right) {
    uint32_t mid = 0, inv_count = 0;
    if (right > left) {
        // midpoint to split the array
        mid = (right + left) / 2;
        // Add inversions in left and right sub-arrays
        inv_count += mergeSort(arr, temp, left, mid);  // left sub-array
        inv_count += mergeSort(arr, temp, mid + 1, right);

        // inversions in the merge step
        inv_count += merge(arr, temp, left, mid, right);
    }
    return inv_count;
}

/**
 * @brief Function countInversion() returns the number of inversion
 * present in the input array. Inversions are an estimate of
 * how close or far off the array is to being sorted.
 *
 * @details
 * Number of inversions in a sorted array is 0.
 * Number of inversion in an array[1...n] sorted in
 * non-ascending order is n(n-1)/2, since each pair of elements
 * contitute an inversion.
 *
 * @param arr   - array, data member of std::vector<int>, input for counting
 * inversions
 * @param array_size    - number of elementa in the array
 * @returns number of inversions in input array, sorts the array
 */
template <class T>
uint32_t countInversion(T* arr, const uint32_t size) {
    std::vector<T> temp;
    temp.reserve(size);
    temp.assign(size, 0);
    return mergeSort(arr, temp.data(), 0, size - 1);
}

/**
 * @brief UTILITY function to print array.
 * @param arr[]   array to print
 * @param array_size    size of input array arr[]
 * @returns void
 *
 */
template <typename T>
void show(T* arr, const uint32_t array_size) {
    std::cout << "Printing array: \n";
    for (uint32_t i = 0; i < array_size; i++) {
        std::cout << " " << arr[i];
    }
    std::cout << "\n";
}

}  // namespace inversion
}  // namespace sorting

/**
 * @brief Test implementations
 * @returns void
 */
static void test() {
    // Test 1
    std::vector<uint64_t> arr1 = {
        100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84,
        83,  82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67,
        66,  65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50,
        49,  48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33,
        32,  31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16,
        15,  14, 13, 12, 11, 10, 9,  8,  7,  6,  5,  4,  3,  2,  1};
    uint32_t size1 = arr1.size();
    uint32_t inv_count1 = 4950;
    uint32_t result1 = sorting::inversion::countInversion(arr1.data(), size1);
    assert(inv_count1 == result1);
    // Test 2
    std::vector<int> arr2 = {22, 66, 75, 23, 11, 87, 2, 44, 98, 43};
    uint32_t size2 = arr2.size();
    uint32_t inv_count2 = 20;
    uint32_t result2 = sorting::inversion::countInversion(arr2.data(), size2);
    assert(inv_count2 == result2);
    // Test 3
    std::vector<double> arr3 = {33.1, 45.2, 65.4, 76.5, 1.0,
                                2.9,  5.4,  7.7,  88.9, 12.4};
    uint32_t size3 = arr3.size();
    uint32_t inv_count3 = 21;
    uint32_t result3 = sorting::inversion::countInversion(arr3.data(), size3);
    assert(inv_count3 == result3);
    // Test 4
    std::vector<char> arr4 = {'a', 'b', 'c', 'd', 'e'};
    uint32_t size4 = arr4.size();
    uint32_t inv_count4 = 0;
    uint32_t result4 = sorting::inversion::countInversion(arr4.data(), size4);
    assert(inv_count4 == result4);
}

// /**
//  * @brief Program Body contains all main funtionality
//  * @returns void
//  */
// template <typename T>
// static void body() {
//     // Input your own sequence
//     uint_t size;
//     T input;
//     std::cout << "Enter number of elements:";
//     std::cin >> size;
//
//     std::vector<T> arr;
//     arr.reserve(size);
//
//     std::cout << "Enter elements -->\n";
//     for (uint64_t i=1; i<=size; i++) {
//         std::cout << "Element "<< i <<" :";
//         std::cin >> input;
//         arr.push_back(input);
//     }
//
//     if (size != arr.size()) {
//         size = arr.size();
//     }
//
//     std::cout << "\n";
//     sorting::inversion::show(arr.data(), size);
//     std::cout << "\n";
//
//     // Counting inversions
//     std::cout << "\nThe number of inversions: "<<
//     sorting::inversion::countInversion(arr.data(), size) << "\n";
//
//     // Output sorted array
//     std::cout << "\nSorted array -->  \n";
//     sorting::inversion::show(arr.data(), size);
// }

/**
 * @brief Main function
 * @returns 0 on exit
 */
int main() {
    test();  // Run test implementations
    // body(); // test your own array
    return 0;
}

9、Counting Sort 计数排序

​ Counting Sort,也称计数排序,是一种非比较排序算法。它利用桶(bucket)原理,将输入的数据值转化为键存储在额外开辟的数组空间中,然后依次把桶中的计数值依次输出,从而完成排序。

以下是Counting Sort的具体步骤:

  1. 找出待排序的数组中最大和最小的元素。
  2. 统计数组中每个值为i的元素出现的次数,存储在数组C的第i项中。
  3. 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)。
  4. 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。

普通版本的代码

C++
 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
void countingSort(vector<int>& arr) {
    if (arr.empty()) {
        return;
    }

    int n = arr.size();
    int maxVal = *max_element(arr.begin(), arr.end());

    vector<int> count(maxVal + 1, 0);

    for (int i = 0; i < n; i++) {
        count[arr[i]]++;
    }

    for (int i = 1; i <= maxVal; i++) {
        count[i] += count[i - 1];
    }

    vector<int> temp(n);

    for (int i = n - 1; i >= 0; i--) {
        temp[--count[arr[i]]] = arr[i];
    }

    arr = temp;
}

​ 以上代码实现了Counting Sort算法,首先找出待排序的数组中最大和最小的元素,然后创建一个桶数组count,统计数组中每个值为i的元素出现的次数,存储在数组C的第i项中。对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)。反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。时间复杂度为O(n+k),其中k为桶的数量。当待排序元素的最大值和最小值差距过大时,需要消耗大量空间,不适合使用Counting Sort。

专家版的代码

C++
 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
#include <iostream>
using namespace std;

int Max(int Arr[], int N) {
    int max = Arr[0];
    for (int i = 1; i < N; i++)
        if (Arr[i] > max)
            max = Arr[i];
    return max;
}

int Min(int Arr[], int N) {
    int min = Arr[0];
    for (int i = 1; i < N; i++)
        if (Arr[i] < min)
            min = Arr[i];
    return min;
}

void Print(int Arr[], int N) {
    for (int i = 0; i < N; i++) cout << Arr[i] << ", ";
}

int *Counting_Sort(int Arr[], int N) {
    int max = Max(Arr, N);
    int min = Min(Arr, N);
    int *Sorted_Arr = new int[N];

    int *Count = new int[max - min + 1];

    for (int i = 0; i < N; i++) Count[Arr[i] - min]++;

    for (int i = 1; i < (max - min + 1); i++) Count[i] += Count[i - 1];

    for (int i = N - 1; i >= 0; i--) {
        Sorted_Arr[Count[Arr[i] - min] - 1] = Arr[i];
        Count[Arr[i] - min]--;
    }

    return Sorted_Arr;
}

int main() {
    int Arr[] = {47, 65, 20, 66, 25, 53, 64, 69, 72, 22,
                 74, 25, 53, 15, 42, 36, 4,  69, 86, 19},
        N = 20;
    int *Sorted_Arr;

    cout << "\n\tOrignal Array = ";
    Print(Arr, N);
    Sorted_Arr = Counting_Sort(Arr, N);
    cout << "\n\t Sorted Array = ";
    Print(Sorted_Arr, N);
    cout << endl;

    return 0;
}

10、Counting Sort String

​ Counting Sort String是Counting Sort算法的一种变体,它是用于对字符串进行排序的算法。相比于传统的Counting Sort算法,它将字符串视为字符数组来处理,对于每个字符出现的次数进行统计和计数,从而达到排序的目的。

以下是Counting Sort String的具体步骤:

  1. 找出待排序的字符串中最大和最小的字符。
  2. 统计字符串中每个字符出现的次数,存储在数组C的对应位置中。
  3. 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)。
  4. 反向填充目标字符串数组:将每个字符i放在新数组的第C(i)项,每放一个字符就将C(i)减去1。

普通版本的代码

C++
 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
void countingSortString(string& str) {
    if (str.empty()) {
        return;
    }

    int n = str.size();
    int maxVal = *max_element(str.begin(), str.end());
    vector<int> count(maxVal + 1, 0);

    for (int i = 0; i < n; i++) {
        count[str[i]]++;
    }

    for (int i = 1; i <= maxVal; i++) {
        count[i] += count[i - 1];
    }

    string temp(n, ' ');

    for (int i = n - 1; i >= 0; i--) {
        temp[--count[str[i]]] = str[i];
    }

    str = temp;
}

​ 以上代码实现了Counting Sort String算法,首先找出待排序的字符串中最大和最小的字符,然后创建一个桶数组count,统计字符串中每个字符出现的次数,存储在数组C的对应位置中。对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)。反向填充目标字符串数组:将每个字符i放在新数组的第C(i)项,每放一个字符就将C(i)减去1。时间复杂度为O(n+k),其中k为桶的数量。当待排序字符串中字符集较小时,Counting Sort String是一种高效的排序算法。

专家版的代码

C++
 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
// C++ Program for counting sort
#include <iostream>

using namespace std;

void countSort(string arr) {
    string output;

    int count[256], i;
    for (int i = 0; i < 256; i++) count[i] = 0;

    for (i = 0; arr[i]; ++i) ++count[arr[i]];

    for (i = 1; i < 256; ++i) count[i] += count[i - 1];

    for (i = 0; arr[i]; ++i) {
        output[count[arr[i]] - 1] = arr[i];
        --count[arr[i]];
    }

    for (i = 0; arr[i]; ++i) arr[i] = output[i];

    cout << "Sorted character array is " << arr;
}

int main() {
    string arr;
    cin >> arr;

    countSort(arr);

    return 0;
}

11、Cycle Sort

​ Cycle Sort是一种不稳定的排序算法,它在原数组上进行排序,通过最小化交换操作的数量来实现排序。它的主要思想是将数组中的每个元素放到它应该在的位置上,直到所有元素都在正确的位置上为止。对于n个元素的数组,最多只需要n-1次交换操作即可完成排序。

以下是Cycle Sort的具体步骤:

  1. 从第一个元素开始遍历数组,将该元素放到正确的位置上,即将它放到数组下标与它相等的位置上,同时计算出这个位置上的元素的个数,假设这个位置上的元素个数为k。
  2. 如果k等于1,则说明该元素已经在正确的位置上,继续遍历下一个元素;否则,从数组的下标为0的位置开始,找到一个元素它的值也应该是当前元素的值,然后将它与当前元素交换,交换完成后,更新该元素的个数k=k-1,继续重复这个过程,直到k=1为止。
  3. 继续从下一个位置开始遍历,重复步骤1和2,直到所有元素都被放到正确的位置上为止。

普通版本的代码

C++
 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
void cycleSort(vector<int>& arr) {
    int n = arr.size();
    for (int cycleStart = 0; cycleStart < n - 1; cycleStart++) {
        int item = arr[cycleStart];
        int pos = cycleStart;
        for (int i = cycleStart + 1; i < n; i++) {
            if (arr[i] < item) {
                pos++;
            }
        }
        if (pos == cycleStart) {
            continue;
        }
        while (item == arr[pos]) {
            pos++;
        }
        swap(item, arr[pos]);
        while (pos != cycleStart) {
            pos = cycleStart;
            for (int i = cycleStart + 1; i < n; i++) {
                if (arr[i] < item) {
                    pos++;
                }
            }
            while (item == arr[pos]) {
                pos++;
            }
            swap(item, arr[pos]);
        }
    }
}

​ 以上代码实现了Cycle Sort算法,首先从第一个元素开始遍历数组,将该元素放到正确的位置上,即将它放到数组下标与它相等的位置上,同时计算出这个位置上的元素的个数。如果该元素已经在正确的位置上,则继续遍历下一个元素;否则,从数组的下标为0的位置开始,找到一个元素它的值也应该是当前元素的值,然后将它与当前元素交换。重复这个过程,直到所有元素都被放到正确的位置上为止。时间复杂度为O(n2)。

专家版的代码

C++
  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
/**
 * @file
 * @brief Implementation of [Cycle
 * sort](https://en.wikipedia.org/wiki/Cycle_sort) algorithm
 * @details
 * Cycle Sort is a sorting algorithm that works in \f$O(n^2)\f$ time in the best
 * case and works in \f$O(n^2)\f$ in worst case. If a element is already at its
 * correct  position, do nothing. If a element is not at its correct position,
 * we then need to move it to its correct position by computing the correct
 * positions.Therefore, we should make sure the duplicate elements.
 * @author [TsungHan Ho](https://github.com/dalaoqi)
 */

#include <algorithm>  /// for std::is_sorted, std::swap
#include <cassert>    /// for assert
#include <iostream>   /// for io operations
#include <vector>     /// for std::vector

/**
 * @namespace sorting
 * @brief Sorting algorithms
 */
namespace sorting {
/**
 * @namespace cycle_sort
 * @brief Functions for [Cycle sort](https://en.wikipedia.org/wiki/Cycle_sort)
 * algorithm
 */
namespace cycle_sort {
/**
 * @brief The main function implements cycleSort
 * @tparam T type of array
 * @param in_arr array to be sorted
 * @returns void
 */
template <typename T>
std::vector<T> cycleSort(const std::vector<T> &in_arr) {
    std::vector<T> arr(in_arr);
    for (int cycle_start = 0; cycle_start <= arr.size() - 1; cycle_start++) {
        // initialize item
        T item = arr[cycle_start];

        // Count the number of elements smaller than item, this  number is the
        // correct index of item.
        int pos = cycle_start;
        for (int i = cycle_start + 1; i < arr.size(); i++) {
            if (arr[i] < item) {
                pos++;
            }
        }

        // item is already in correct position
        if (pos == cycle_start) {
            continue;
        }

        // duplicate  elements
        while (item == arr[pos]) pos += 1;
        if (pos == cycle_start) {
            continue;
        } else {
            std::swap(item, arr[pos]);
        }
        // Rest of the  elements
        while (pos != cycle_start) {
            pos = cycle_start;
            // Find position where we put the element
            for (size_t i = cycle_start + 1; i < arr.size(); i++) {
                if (arr[i] < item) {
                    pos += 1;
                }
            }
            // duplicate  elements
            while (item == arr[pos]) pos += 1;
            if (item == arr[pos]) {
                continue;
            } else {
                std::swap(item, arr[pos]);
            }
        }
    }
    return arr;
}
}  // namespace cycle_sort
}  // namespace sorting

/**
 * @brief Test implementations
 * @returns void
 */
static void test() {
    // Test 1
    // [4, 3, 2, 1] return [1, 2, 3, 4]
    std::vector<uint32_t> array1 = {4, 3, 2, 1};
    std::cout << "Test 1... ";
    std::vector<uint32_t> arr1 = sorting::cycle_sort::cycleSort(array1);
    assert(std::is_sorted(std::begin(arr1), std::end(arr1)));
    std::cout << "passed" << std::endl;

    // [4.3, -6.5, -7.4, 0, 2.7, 1.8] return [-7.4, -6.5, 0, 1.8, 2.7, 4.3]
    std::vector<double> array2 = {4.3, -6.5, -7.4, 0, 2.7, 1.8};
    std::cout << "Test 2... ";
    std::vector<double> arr2 = sorting::cycle_sort::cycleSort(array2);
    assert(std::is_sorted(std::begin(arr2), std::end(arr2)));
    std::cout << "passed" << std::endl;

    // Test 3
    // [3, 3, 3, 3] return [3, 3, 3, 3]
    std::vector<uint32_t> array3 = {3, 3, 3, 3};
    std::cout << "Test 3... ";
    std::vector<uint32_t> arr3 = sorting::cycle_sort::cycleSort(array3);
    assert(std::is_sorted(std::begin(arr3), std::end(arr3)));
    std::cout << "passed" << std::endl;

    // [9, 4, 6, 8, 14, 3] return [9, 4, 6, 8, 14, 3]
    std::vector<uint32_t> array4 = {3, 4, 6, 8, 9, 14};
    std::cout << "Test 4... ";
    std::vector<uint32_t> arr4 = sorting::cycle_sort::cycleSort(array4);
    assert(std::is_sorted(std::begin(arr4), std::end(arr4)));
    std::cout << "passed" << std::endl;
}

/**
 * @brief Main function
 * @returns 0 on exit
 */
int main() {
    test();  // execute the test
    return 0;
}

12、Dnf Sort

​ Dutch National Flag(DNF)Sort是一种三向切分快速排序的变种,它是由荷兰计算机科学家Edsger W. Dijkstra提出的。这个算法的目的是将一个包含三种不同元素的数组(例如0、1、2)排序,使得所有相同元素都聚在一起。

​ DNF Sort的主要思想是将数组分成三部分:小于等于第一个元素的部分,等于第一个元素的部分和大于第一个元素的部分。然后递归地对小于和大于部分进行排序,直到整个数组都被排序。这个算法的时间复杂度为O(n),其中n为数组的长度。

以下是DNF Sort的具体步骤:

  1. 初始化三个指针:low、mid和high。low指向数组的开头,mid指向数组的开头,high指向数组的末尾。
  2. 当mid指向的元素小于第一个元素时,将它和low指向的元素交换,并且将low和mid指针都向右移动一位。
  3. 当mid指向的元素大于第一个元素时,将它和high指向的元素交换,并且将high指针向左移动一位。
  4. 当mid指向的元素等于第一个元素时,将mid指针向右移动一位。
  5. 重复步骤2、3、4,直到mid指针遍历完整个数组为止。

普通版本的代码

C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
void dnfSort(vector<int>& arr) {
    int low = 0, mid = 0, high = arr.size() - 1;
    while (mid <= high) {
        if (arr[mid] == 0) {
            swap(arr[low], arr[mid]);
            low++;
            mid++;
        } else if (arr[mid] == 1) {
            mid++;
        } else {
            swap(arr[mid], arr[high]);
            high--;
        }
    }
}

​ 以上代码实现了DNF Sort算法,使用了三个指针:low、mid和high。在遍历数组的过程中,将数组分成三部分:小于等于第一个元素的部分,等于第一个元素的部分和大于第一个元素的部分。使用指针low指向小于等于第一个元素的部分的末尾,指针mid指向等于第一个元素的部分的末尾,指针high指向大于第一个元素的部分的开头。通过不断地交换元素,最终可以将数组中的所有元素按照顺序排列。时间复杂度为O(n)。

专家版的代码

C++
  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
/**
 * @file
 * @brief Implementation of the [DNF
 * sort](https://www.geeksforgeeks.org/sort-an-array-of-0s-1s-and-2s/)
 * implementation
 * @details
 * C++ program to sort an array with 0, 1 and 2 in a single pass(DNF sort).
 * Since one traversal of the array is there hence it works in O(n) time
 * complexity.
 * @author [Sujal Gupta](https://github.com/heysujal)
 */

#include <algorithm>  /// for std::is_sorted
#include <cassert>    /// for assert
#include <iostream>   /// for std::swap and io operations
#include <vector>     /// for std::vector

/**
 * @namespace sorting
 * @breif Sorting algorithms
 */
namespace sorting {
/**
 * @namespace dnf_sort
 * @brief Functions for the [DNF
 * sort](https://en.wikipedia.org/wiki/Dutch_national_flag_problem)
 * implementation
 */
namespace dnf_sort {
/**
 * @brief The main function implements DNF sort
 * @tparam T type of array
 * @param a array to be sorted,
 * @param arr_size size of array
 * @returns void
 */
template <typename T>
std::vector<T> dnfSort(const std::vector<T> &in_arr) {
    std::vector<T> arr(in_arr);
    uint64_t lo = 0;
    uint64_t hi = arr.size() - 1;
    uint64_t mid = 0;

    // Iterate till all the elements
    // are sorted
    while (mid <= hi) {
        switch (arr[mid]) {
            // If the element is 0
            case 0:
                std::swap(arr[lo++], arr[mid++]);
                break;

            // If the element is 1 .
            case 1:
                mid++;
                break;

            // If the element is 2
            case 2:
                std::swap(arr[mid], arr[hi--]);
                break;
        }
    }
    return arr;
}
}  // namespace dnf_sort
}  // namespace sorting

/**
 * @brief Self-test implementations
 * @returns void
 */
static void test() {
    // 1st test
    // [1, 0, 2, 1] return [0, 1, 1, 2]
    std::vector<uint64_t> array1 = {0, 1, 1, 2};
    std::cout << "Test 1... ";
    std::vector<uint64_t> arr1 = sorting::dnf_sort::dnfSort(array1);
    assert(std::is_sorted(std::begin(arr1), std::end(arr1)));
    std::cout << "passed" << std::endl;
    // 2nd test
    // [1, 0, 0, 1, 1, 0, 2, 1] return [0, 0, 0, 1, 1, 1, 1, 2]
    std::vector<uint64_t> array2 = {1, 0, 0, 1, 1, 0, 2, 1};
    std::cout << "Test 2... ";
    std::vector<uint64_t> arr2 = sorting::dnf_sort::dnfSort(array2);
    assert(std::is_sorted(std::begin(arr2), std::end(arr2)));
    std::cout << "passed" << std::endl;
    // 3rd test
    // [1, 1, 0, 0, 1, 2, 2, 0, 2, 1] return [0, 0, 0, 1, 1, 1, 1, 2, 2, 2]
    std::vector<uint64_t> array3 = {1, 1, 0, 0, 1, 2, 2, 0, 2, 1};
    std::cout << "Test 3... ";
    std::vector<uint64_t> arr3 = sorting::dnf_sort::dnfSort(array3);
    assert(std::is_sorted(std::begin(arr3), std::end(arr3)));
    std::cout << "passed" << std::endl;
    // 4th test
    // [2, 2, 2, 0, 0, 1, 1] return [0, 0, 1, 1, 2, 2, 2]
    std::vector<uint64_t> array4 = {2, 2, 2, 0, 0, 1, 1};
    std::cout << "Test 4... ";
    std::vector<uint64_t> arr4 = sorting::dnf_sort::dnfSort(array4);
    assert(std::is_sorted(std::begin(arr4), std::end(arr4)));
    std::cout << "passed" << std::endl;
}

/**
 * @brief Main function
 * @returns 0 on exit
 */
int main() {
    test();  // execute the test
    return 0;
}

13、Gnome Sort

​ Gnome Sort是一种简单的排序算法,它通过不断地比较相邻元素的大小来将数组排序。这个算法的名称源于它的工作方式,它会像一个园艺爱好者修剪一棵小树一样,将小的元素移到前面,大的元素移到后面,因此被称为Gnome Sort(gnome在英语中是指小精灵的意思)。

以下是Gnome Sort的具体步骤:

  1. 初始化一个指针pos,将其指向数组的第二个元素。
  2. 如果前面的元素大于当前元素,将它们交换,并将指针pos向前移动一位。
  3. 如果前面的元素小于或等于当前元素,将指针pos向前移动一位。
  4. 重复步骤2、3,直到pos指针遍历完整个数组为止。

普通版本的代码

C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
void gnomeSort(vector<int>& arr) {
    int pos = 1;
    while (pos < arr.size()) {
        if (arr[pos] < arr[pos - 1]) {
            swap(arr[pos], arr[pos - 1]);
            if (pos > 1) {
                pos--;
            }
        } else {
            pos++;
        }
    }
}

​ 以上代码实现了Gnome Sort算法,使用了一个指针pos。在遍历数组的过程中,如果当前元素比前面的元素小,就将它们交换,并将指针pos向前移动一位。否则,将指针pos向前移动一位。通过不断地比较相邻元素的大小,最终可以将数组按照升序排列。时间复杂度为O(n2)。

专家版的代码

C++
  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
/**
 * @file
 * @brief Implementation of [gnome
 * sort](https://en.wikipedia.org/wiki/Gnome_sort) algorithm.
 * @author [beqakd](https://github.com/beqakd)
 * @author [Krishna Vedala](https://github.com/kvedala)
 * @details
 * Gnome sort algorithm is not the best one but it is widely used.
 * The algorithm iteratively checks the order of pairs in the array. If they are
 * on right order it moves to the next successive pair, otherwise it swaps
 * elements. This operation is repeated until no more swaps are made thus
 * indicating the values to be in ascending order.
 *
 * The time Complexity of the algorithm is \f$O(n^2)\f$ and in some cases it
 * can be \f$O(n)\f$.
 */

#include <algorithm>  // for std::swap
#include <array>      // for std::array
#include <cassert>    // for assertions
#include <iostream>   // for io operations

/**
 * @namespace sorting
 * Sorting algorithms
 */
namespace sorting {
/**
 * This implementation is for a C-style array input that gets modified in place.
 * @param [in,out] arr our array of elements.
 * @param size size of given array
 */
template <typename T>
void gnomeSort(T *arr, int size) {
    // few easy cases
    if (size <= 1) {
        return;
    }

    int index = 0;  // initialize some variables.
    while (index < size) {
        // check for swap
        if ((index == 0) || (arr[index] >= arr[index - 1])) {
            index++;
        } else {
            std::swap(arr[index], arr[index - 1]);  // swap
            index--;
        }
    }
}

/**
 * This implementation is for a C++-style array input. The function argument is
 * a pass-by-value and hence a copy of the array gets created which is then
 * modified by the function and returned.
 * @tparam T type of data variables in the array
 * @tparam size size of the array
 * @param [in] arr our array of elements.
 * @return array with elements sorted
 */
template <typename T, size_t size>
std::array<T, size> gnomeSort(std::array<T, size> arr) {
    // few easy cases
    if (size <= 1) {
        return arr;
    }

    int index = 0;  // initialize loop index
    while (index < size) {
        // check for swap
        if ((index == 0) || (arr[index] >= arr[index - 1])) {
            index++;
        } else {
            std::swap(arr[index], arr[index - 1]);  // swap
            index--;
        }
    }
    return arr;
}
}  // namespace sorting

/**
 * Test function
 */
static void test() {
    // Example 1. Creating array of int,
    std::cout << "Test 1 - as a C-array...";
    const int size = 6;
    std::array<int, size> arr = {-22, 100, 150, 35, -10, 99};
    sorting::gnomeSort(arr.data(),
                       size);  // pass array data as a C-style array pointer
    assert(std::is_sorted(std::begin(arr), std::end(arr)));
    std::cout << " Passed\n";
    for (int i = 0; i < size; i++) {
        std::cout << arr[i] << ", ";
    }
    std::cout << std::endl;

    // Example 2. Creating array of doubles.
    std::cout << "\nTest 2 - as a std::array...";
    std::array<double, size> double_arr = {-100.2, 10.2, 20.0, 9.0, 7.5, 7.2};
    std::array<double, size> sorted_arr = sorting::gnomeSort(double_arr);
    assert(std::is_sorted(std::begin(sorted_arr), std::end(sorted_arr)));
    std::cout << " Passed\n";
    for (int i = 0; i < size; i++) {
        std::cout << double_arr[i] << ", ";
    }
    std::cout << std::endl;

    // Example 3. Creating random array of float.
    std::cout << "\nTest 3 - 200 random numbers as a std::array...";
    const int size2 = 200;
    std::array<float, size2> rand_arr{};

    for (auto &a : rand_arr) {
        // generate random numbers between -5.0 and 4.99
        a = float(std::rand() % 1000 - 500) / 100.f;
    }

    std::array<float, size2> float_arr = sorting::gnomeSort(rand_arr);
    assert(std::is_sorted(std::begin(float_arr), std::end(float_arr)));
    std::cout << " Passed\n";
    // for (int i = 0; i < size; i++) std::cout << double_arr[i] << ", ";
    std::cout << std::endl;
}

/**
 * Our main function with example of sort method.
 */
int main() {
    test();
    return 0;
}

14、Heap Sort 堆排序

​ Heap Sort(堆排序)是一种基于堆数据结构的排序算法,其时间复杂度为O(nlogn)。它的核心思想是将待排序数组构建成一个堆,然后重复执行以下操作,直到排序完成:

  1. 将堆顶元素(即数组中的最大值或最小值)与堆底元素交换。

  2. 将堆的大小减1,然后重新调整堆,使其满足堆的性质。

这个算法可以分为两个主要的步骤:建堆和排序。在建堆过程中,需要从底向上对每个非叶子节点进行调整,以使得其子树满足堆的性质。在排序过程中,每次取出堆顶元素并将其放置在数组的末尾,然后重新调整堆。

普通版本的代码

C++
 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
// 调整堆,使其满足堆的性质
void adjustHeap(vector<int>& arr, int i, int len) {
    int left = 2 * i + 1;
    int right = 2 * i + 2;
    int largest = i;
    if (left < len && arr[left] > arr[largest]) {
        largest = left;
    }
    if (right < len && arr[right] > arr[largest]) {
        largest = right;
    }
    if (largest != i) {
        swap(arr[i], arr[largest]);
        adjustHeap(arr, largest, len);
    }
}

// 堆排序
void heapSort(vector<int>& arr) {
    int len = arr.size();
    // 建堆
    for (int i = len / 2 - 1; i >= 0; i--) {
        adjustHeap(arr, i, len);
    }
    // 排序
    for (int i = len - 1; i >= 1; i--) {
        swap(arr[0], arr[i]);
        adjustHeap(arr, 0, i);
    }
}

​ 以上代码实现了Heap Sort算法,使用了一个递归函数adjustHeap来调整堆,并在排序过程中重复执行调整堆的操作。时间复杂度为O(nlogn)。由于其使用了额外的空间来存储堆,因此其空间复杂度为O(1)。虽然Heap Sort在时间复杂度上优于冒泡排序和插入排序,但由于其不稳定性和空间复杂度的限制,实际应用较少。

专家版的代码

C++
  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
/**
 * \file
 * \brief [Heap Sort Algorithm
 * (heap sort)](https://en.wikipedia.org/wiki/Heapsort) implementation
 *
 * \author [Ayaan Khan](http://github.com/ayaankhan98)
 *
 * \details
 *  Heap-sort is a comparison-based sorting algorithm.
 *  Heap-sort can be thought of as an improved selection sort:
 *  like selection sort, heap sort divides its input into a sorted
 *  and an unsorted region, and it iteratively shrinks the unsorted
 *  region by extracting the largest element from it and inserting
 *  it into the sorted region. Unlike selection sort,
 *  heap sort does not waste time with a linear-time scan of the
 *  unsorted region; rather, heap sort maintains the unsorted region
 *  in a heap data structure to more quickly find the largest element
 *  in each step.
 *
 *  Time Complexity - \f$O(n \log(n))\f$
 *
 */
#include <algorithm>
#include <cassert>
#include <iostream>

/**
 *
 * Utility function to print the array after
 * sorting.
 *
 * @param arr array to be printed
 * @param sz size of array
 *
 */
template <typename T>
void printArray(T *arr, int sz) {
    for (int i = 0; i < sz; i++) std::cout << arr[i] << "  ";
    std::cout << "\n";
}

/**
 *
 * \addtogroup sorting Sorting Algorithm
 * @{
 *
 * The heapify procedure can be thought of as building a heap from
 * the bottom up by successively sifting downward to establish the
 * heap property.
 *
 * @param arr array to be sorted
 * @param n size of array
 * @param i node position in Binary Tress or element position in
 *          Array to be compared with it's childern
 *
 */
template <typename T>
void heapify(T *arr, int n, int i) {
    int largest = i;
    int l = 2 * i + 1;
    int r = 2 * i + 2;

    if (l < n && arr[l] > arr[largest])
        largest = l;

    if (r < n && arr[r] > arr[largest])
        largest = r;

    if (largest != i) {
        std::swap(arr[i], arr[largest]);
        heapify(arr, n, largest);
    }
}

/**
 * Utilizes heapify procedure to sort
 * the array
 *
 * @param arr array to be sorted
 * @param n size of array
 *
 */
template <typename T>
void heapSort(T *arr, int n) {
    for (int i = n - 1; i >= 0; i--) heapify(arr, n, i);

    for (int i = n - 1; i >= 0; i--) {
        std::swap(arr[0], arr[i]);
        heapify(arr, i, 0);
    }
}

/**
 *
 * @}
 * Test cases to test the program
 *
 */
void test() {
    std::cout << "Test 1\n";
    int arr[] = {-10, 78, -1, -6, 7, 4, 94, 5, 99, 0};
    int sz = sizeof(arr) / sizeof(arr[0]);  // sz - size of array
    printArray(arr, sz);  // displaying the array before sorting
    heapSort(arr, sz);    // calling heapsort to sort the array
    printArray(arr, sz);  // display array after sorting
    assert(std::is_sorted(arr, arr + sz));
    std::cout << "Test 1 Passed\n========================\n";

    std::cout << "Test 2\n";
    double arr2[] = {4.5, -3.6, 7.6, 0, 12.9};
    sz = sizeof(arr2) / sizeof(arr2[0]);
    printArray(arr2, sz);
    heapSort(arr2, sz);
    printArray(arr2, sz);
    assert(std::is_sorted(arr2, arr2 + sz));
    std::cout << "Test 2 passed\n";
}

/** Main function */
int main() {
    test();
    return 0;
}

15、插入排序

​ 插入排序(Insertion Sort)是一种简单的排序算法,它的基本思想是将待排序的序列分为有序区和无序区,每次将无序区中的第一个元素插入到有序区中的合适位置,直到所有元素都有序为止。

​ 插入排序的时间复杂度为O(n^2),空间复杂度为O(1)。虽然插入排序的效率不如快速排序、归并排序等高级算法,但在某些情况下,它的效率可能会更高。例如,对于基本有序的序列,插入排序的时间复杂度接近O(n)。

普通版本的代码

C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
void insertionSort(vector<int>& arr) {
    int len = arr.size();
    for (int i = 1; i < len; i++) {
        int temp = arr[i];
        int j = i - 1;
        while (j >= 0 && arr[j] > temp) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = temp;
    }
}

​ 以上代码中,我们使用一个for循环来遍历整个序列,每次将无序区中的第一个元素插入到有序区中的合适位置。在内部while循环中,我们将有序区中大于当前元素的元素后移一位,直到找到合适的位置插入当前元素。时间复杂度为O(n^2),空间复杂度为O(1)。

专家版的代码

C++
  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
/**
 *
 * \file
 * \brief [Insertion Sort Algorithm
 * (Insertion Sort)](https://en.wikipedia.org/wiki/Insertion_sort)
 *
 * \details
 * Insertion sort is a simple sorting algorithm that builds the final
 * sorted array one at a time. It is much less efficient compared to
 * other sorting algorithms like heap sort, merge sort or quick sort.
 * However it has several advantages such as
 * 1. Easy to implement
 * 2. For small set of data it is quite efficient
 * 3. More efficient that other Quadratic complexity algorithms like
 *    Selection sort or bubble sort.
 * 4. It's stable that is it does not change the relative order of
 *    elements with equal keys
 * 5. Works on hand means it can sort the array or list as it receives.
 *
 * It is based on the same idea that people use to sort the playing cards in
 * their hands.
 * the algorithms goes in the manner that we start iterating over the array
 * of elements as soon as we find a unsorted element that is a misplaced
 * element we place it at a sorted position.
 *
 * Example execution steps:
 * 1. Suppose initially we have
 * \f{bmatrix}{4 &3 &2 &5 &1\f}
 * 2. We start traversing from 4 till we reach 1
 * when we reach at 3 we find that it is misplaced so we take 3 and place
 * it at a correct position thus the array will become
 * \f{bmatrix}{3 &4 &2 &5 &1\f}
 * 3. In the next iteration we are at 2 we find that this is also misplaced so
 * we place it at the correct sorted position thus the array in this iteration
 * becomes
 * \f{bmatrix}{2 &3 &4 &5 &1\f}
 * 4. We do not do anything with 5 and move on to the next iteration and
 * select 1 which is misplaced and place it at correct position. Thus, we have
 * \f{bmatrix}{1 &2 &3 &4 &5\f}
 */

#include <algorithm>
#include <cassert>
#include <iostream>
#include <vector>

/** \namespace sorting
 * \brief Sorting algorithms
 */
namespace sorting {
/** \brief
 * Insertion Sort Function
 *
 * @tparam T type of array
 * @param [in,out] arr Array to be sorted
 * @param n Size of Array
 */
template <typename T>
void insertionSort(T *arr, int n) {
    for (int i = 1; i < n; i++) {
        T temp = arr[i];
        int j = i - 1;
        while (j >= 0 && temp < arr[j]) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = temp;
    }
}

/** Insertion Sort Function
 *
 * @tparam T type of array
 * @param [in,out] arr pointer to array to be sorted
 */
template <typename T>
void insertionSort(std::vector<T> *arr) {
    size_t n = arr->size();

    for (size_t i = 1; i < n; i++) {
        T temp = arr[0][i];
        int32_t j = i - 1;
        while (j >= 0 && temp < arr[0][j]) {
            arr[0][j + 1] = arr[0][j];
            j--;
        }
        arr[0][j + 1] = temp;
    }
}

}  // namespace sorting

/**
 * @brief Create a random array objecthelper function to create a random array
 *
 * @tparam T type of array
 * @param arr array to fill (must be pre-allocated)
 * @param N number of array elements
 */
template <typename T>
static void create_random_array(T *arr, int N) {
    while (N--) {
        double r = (std::rand() % 10000 - 5000) / 100.f;
        arr[N] = static_cast<T>(r);
    }
}

/** Test Cases to test algorithm */
void tests() {
    int arr1[10] = {78, 34, 35, 6, 34, 56, 3, 56, 2, 4};
    std::cout << "Test 1... ";
    sorting::insertionSort(arr1, 10);
    assert(std::is_sorted(arr1, arr1 + 10));
    std::cout << "passed" << std::endl;

    int arr2[5] = {5, -3, 7, -2, 1};
    std::cout << "Test 2... ";
    sorting::insertionSort(arr2, 5);
    assert(std::is_sorted(arr2, arr2 + 5));
    std::cout << "passed" << std::endl;

    float arr3[5] = {5.6, -3.1, -3.0, -2.1, 1.8};
    std::cout << "Test 3... ";
    sorting::insertionSort(arr3, 5);
    assert(std::is_sorted(arr3, arr3 + 5));
    std::cout << "passed" << std::endl;

    std::vector<float> arr4({5.6, -3.1, -3.0, -2.1, 1.8});
    std::cout << "Test 4... ";
    sorting::insertionSort(&arr4);
    assert(std::is_sorted(std::begin(arr4), std::end(arr4)));
    std::cout << "passed" << std::endl;

    int arr5[50];
    std::cout << "Test 5... ";
    create_random_array(arr5, 50);
    sorting::insertionSort(arr5, 50);
    assert(std::is_sorted(arr5, arr5 + 50));
    std::cout << "passed" << std::endl;

    float arr6[50];
    std::cout << "Test 6... ";
    create_random_array(arr6, 50);
    sorting::insertionSort(arr6, 50);
    assert(std::is_sorted(arr6, arr6 + 50));
    std::cout << "passed" << std::endl;
}

/** Main Function */
int main() {
    /// Running predefined tests to test algorithm
    tests();

    /// For user insteraction
    size_t n;
    std::cout << "Enter the length of your array (0 to exit): ";
    std::cin >> n;
    if (n == 0) {
        return 0;
    }

    int *arr = new int[n];
    std::cout << "Enter any " << n << " Numbers for Unsorted Array : ";

    for (int i = 0; i < n; i++) {
        std::cin >> arr[i];
    }

    sorting::insertionSort(arr, n);

    std::cout << "\nSorted Array : ";
    for (int i = 0; i < n; i++) {
        std::cout << arr[i] << " ";
    }

    std::cout << std::endl;
    delete[] arr;
    return 0;
}

16、Library Sort 图书馆排序

​ Library Sort(图书馆排序)是一种适用于大量相同元素的排序算法。它的核心思想是将待排序的元素分成多个桶,并且每个桶内部使用其他排序算法(如插入排序或快速排序)进行排序。然后按照特定的顺序遍历所有的桶,将它们的元素合并成一个有序序列。

​ Library Sort的优点是可以处理大量相同元素的情况,且不需要进行全局的排序,因此可以提高算法的效率。其缺点是需要使用额外的空间来存储桶,且桶的数量可能很大。

普通版本的代码

C++
 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
// 插入排序
void insertionSort(vector<int>& arr, int left, int right) {
    for (int i = left + 1; i <= right; i++) {
        int temp = arr[i];
        int j = i - 1;
        while (j >= left && arr[j] > temp) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = temp;
    }
}

// Library Sort
void librarySort(vector<int>& arr) {
    int len = arr.size();
    int maxVal = *max_element(arr.begin(), arr.end());
    vector<vector<int>> bucket(maxVal + 1);
    for (int i = 0; i < len; i++) {
        bucket[arr[i]].push_back(arr[i]);
    }
    int j = 0;
    for (int i = 0; i <= maxVal; i++) {
        if (!bucket[i].empty()) {
            insertionSort(bucket[i], 0, bucket[i].size() - 1);
            for (int k = 0; k < bucket[i].size(); k++) {
                arr[j++] = bucket[i][k];
            }
        }
    }
}

​ 以上代码使用了插入排序作为桶内排序算法,并使用一个vector来存储每个桶。时间复杂度取决于桶内排序算法的复杂度,因此可以选择其他的排序算法进行优化。但由于使用了额外的空间存储桶,其空间复杂度为O(n)。虽然Library Sort在某些情况下可以提高效率,但其使用场景较为有限,因此在实际应用中使用时需要慎重考虑。

专家版的代码

C++
 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
#include <algorithm>
#include <iostream>

void librarySort(int *index, int n) {
    int lib_size, index_pos,
        *gaps,        // gaps
        *library[2];  // libraries

    bool target_lib, *numbered;

    for (int i = 0; i < 2; i++) library[i] = new int[n];

    gaps = new int[n + 1];
    numbered = new bool[n + 1];

    lib_size = 1;
    index_pos = 1;
    target_lib = 0;
    library[target_lib][0] = index[0];

    while (index_pos < n) {
        // binary search
        int insert = std::distance(
            library[target_lib],
            std::lower_bound(library[target_lib],
                             library[target_lib] + lib_size, index[index_pos]));

        // if there is no gap to insert a new index ...

        if (numbered[insert] == true) {
            int prov_size = 0, next_target_lib = !target_lib;

            // update library and clear gaps

            for (int i = 0; i <= n; i++) {
                if (numbered[i] == true) {
                    library[next_target_lib][prov_size] = gaps[i];
                    prov_size++;
                    numbered[i] = false;
                }

                if (i <= lib_size) {
                    library[next_target_lib][prov_size] =
                        library[target_lib][i];
                    prov_size++;
                }
            }

            target_lib = next_target_lib;
            lib_size = prov_size - 1;
        } else {
            numbered[insert] = true;
            gaps[insert] = index[index_pos];
            index_pos++;
        }
    }

    int index_pos_for_output = 0;
    for (int i = 0; index_pos_for_output < n; i++) {
        if (numbered[i] == true) {
            // std::cout << gaps[i] << std::endl;
            index[index_pos_for_output] = gaps[i];
            index_pos_for_output++;
        }

        if (i < lib_size) {
            // std::cout << library[target_lib][i] << std::endl;
            index[index_pos_for_output] = library[target_lib][i];
            index_pos_for_output++;
        }
    }
}

int main() {
    // ---example--
    int index_ex[] = {-6, 5, 9, 1, 9, 1, 0, 1, -8, 4, -12};
    int n_ex = sizeof(index_ex) / sizeof(index_ex[0]);

    librarySort(index_ex, n_ex);
    std::cout << "sorted array :" << std::endl;
    for (int i = 0; i < n_ex; i++) std::cout << index_ex[i] << " ";
    std::cout << std::endl;

    /* --output--
    sorted array :
    -12 -8 -6 0 1 1 1 4 5 9 9
    */
}

17、Merge Insertion Sort 归并插入排序

​ 归并插入排序(Merge Insertion Sort)是一种结合了归并排序和插入排序思想的排序算法,其基本思路是将序列分为较小的子序列,对每个子序列使用插入排序算法进行排序,最后将所有子序列合并成一个有序序列。

​ 归并插入排序的时间复杂度为O(nlogn),空间复杂度为O(n)。与归并排序相比,归并插入排序可以更好地处理小规模的子序列,减少递归调用的次数,从而提高效率。

普通版本的代码

C++
 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
void insertionSort(vector<int>& arr, int left, int right) {
    for (int i = left + 1; i <= right; i++) {
        int temp = arr[i];
        int j = i - 1;
        while (j >= left && arr[j] > temp) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = temp;
    }
}

void mergeInsertionSort(vector<int>& arr, int left, int right) {
    if (right - left <= 15) {
        insertionSort(arr, left, right);
        return;
    }
    int mid = left + (right - left) / 2;
    mergeInsertionSort(arr, left, mid);
    mergeInsertionSort(arr, mid + 1, right);
    if (arr[mid] > arr[mid + 1]) {
        merge(arr, left, mid, right);
    }
}

void mergeSort(vector<int>& arr) {
    mergeInsertionSort(arr, 0, arr.size() - 1);
}

​ 以上代码中,我们首先定义了一个insertionSort函数,用于对小规模的子序列进行排序。在mergeInsertionSort函数中,如果当前序列的长度小于等于15,我们使用insertionSort对其进行排序。否则,我们将序列分为两个子序列,分别递归调用mergeInsertionSort函数进行排序,最后调用merge函数将两个有序子序列合并为一个有序序列。

​ 时间复杂度为O(nlogn),空间复杂度为O(n)。

专家版的代码

C++
  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
/**
 * @file
 * @author [@sinkyoungdeok](https://github.com/sinkyoungdeok)
 * @author [Krishna Vedala](https://github.com/kvedala)
 * @brief Algorithm that combines insertion sort and merge sort. [Wiki
 * link](https://en.wikipedia.org/wiki/Merge-insertion_sort)
 *
 * @see Individual algorithms: insertion_sort.cpp and merge_sort.cpp
 */
#include <algorithm>
#include <array>
#include <cassert>
#include <ctime>
#include <iostream>
#include <memory>

/** \namespace sorting
 * \brief Sorting algorithms
 */
namespace sorting {
/** \namespace merge_insertion
 * \brief Combined Intersion-Merge sorting algorithm
 */
namespace merge_insertion {

/**
 * @brief Insertion merge algorithm
 * @see insertion_sort.cpp
 *
 * @tparam T array data type
 * @tparam N length of array
 * @param A pointer to array to sort
 * @param start start index of sorting window
 * @param end end index of sorting window
 */
template <typename T, size_t N>
static void InsertionSort(std::array<T, N> *A, size_t start, size_t end) {
    size_t i = 0, j = 0;
    T *ptr = A->data();

    for (i = start; i < end; i++) {
        T temp = ptr[i];
        j = i;
        while (j > start && temp < ptr[j - 1]) {
            ptr[j] = ptr[j - 1];
            j--;
        }
        //   for (j = i; j > start && temp < ptr[j - 1]; --j) {
        //       ptr[j] = ptr[j - 1];
        //   }

        ptr[j] = temp;
    }
}

/**
 * @brief Perform merge of data in a window
 *
 * @tparam T array data type
 * @tparam N length of array
 * @param A pointer to array to sort
 * @param min start index of window
 * @param max end index of window
 * @param mid mid-point of window
 */
template <typename T, size_t N>
static void merge(std::array<T, N> *array, size_t min, size_t max, size_t mid) {
    size_t firstIndex = min;
    size_t secondIndex = mid + 1;

    auto ptr = array->data();
    std::array<T, N + 1> tempArray{0};

    // While there are elements in the left or right runs
    for (size_t index = min; index <= max; index++) {
        // If left run head exists and is <= existing right run head.
        if (firstIndex <= mid &&
            (secondIndex > max || ptr[firstIndex] <= ptr[secondIndex])) {
            tempArray[index] = ptr[firstIndex];
            firstIndex++;
        } else {
            tempArray[index] = ptr[secondIndex];
            secondIndex++;
        }
    }

    // transfer to the initial array
    memcpy(ptr + min, tempArray.data() + min, (max - min) * sizeof(T));
    //  for (int index = min; index <= max; index++) ptr[index] =
    //  tempArray[index];
}

/**
 * @brief Final combined algorithm.
 * Algorithm utilizes ::sorting::merge_insertion::InsertionSort if window length
 * is less than threshold, else performs merge sort recursively using
 * ::sorting::merge_insertion::mergeSort
 *
 * @tparam T array data type
 * @tparam N length of array
 * @param A pointer to array to sort
 * @param min start index of sort window
 * @param max end index of sort window
 * @param threshold window length threshold
 */
template <typename T, size_t N>
void mergeSort(std::array<T, N> *array, size_t min, size_t max,
               size_t threshold) {
    // prerequisite
    if ((max - min) <= threshold) {
        InsertionSort(array, min, max);
    } else {
        // get the middle point
        size_t mid = (max + min) >> 1;

        // apply merge sort to both parts of this
        mergeSort(array, min, mid, threshold);
        mergeSort(array, mid, max, threshold);

        // and finally merge all that sorted stuff
        merge(array, min, max, mid);
    }
}

}  // namespace merge_insertion
}  // namespace sorting

/**
 * @brief Function to test code using random arrays
 * @returns none
 */
static void test() {
    constexpr size_t size = 30;
    std::array<int, size> array{0};
    // input
    for (int i = 0; i < size; i++) {
        array[i] = std::rand() % 100 - 50;
        std::cout << array[i] << " ";
    }
    std::cout << std::endl;

    sorting::merge_insertion::InsertionSort(&array, 0, size);
    //  sorting::merge_insertion::mergeSort(&array, 0, size, 10);

    // output
    for (int i = 0; i < size; ++i) {
        std::cout << array[i] << " ";
    }
    std::cout << std::endl;

    assert(std::is_sorted(std::begin(array), std::end(array)));
    std::cout << "Test passed\n";
}

/**
 * @brief Main function
 * @return 0 on exit
 */
int main() {
    std::srand(std::time(nullptr));
    test();
    return 0;
}

18、Merge Sort 归并排序

​ 归并排序(Merge Sort)是一种基于分治思想的排序算法。其基本思路是将待排序的序列不断拆分为更小的子序列,直到无法继续拆分,然后将这些子序列两两合并,最终得到有序的序列。

​ 归并排序的时间复杂度为O(nlogn),空间复杂度为O(n)。与快速排序相比,归并排序虽然时间复杂度相同,但其稳定性更好,适用于各种数据类型,包括链表数据结构。

普通版本的代码

C++
 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
void merge(vector<int>& arr, int left, int mid, int right) {
    int n1 = mid - left + 1;
    int n2 = right - mid;
    vector<int> L(n1);
    vector<int> R(n2);
    for (int i = 0; i < n1; i++) {
        L[i] = arr[left + i];
    }
    for (int i = 0; i < n2; i++) {
        R[i] = arr[mid + 1 + i];
    }
    int i = 0, j = 0, k = left;
    while (i < n1 && j < n2) {
        if (L[i] <= R[j]) {
            arr[k++] = L[i++];
        } else {
            arr[k++] = R[j++];
        }
    }
    while (i < n1) {
        arr[k++] = L[i++];
    }
    while (j < n2) {
        arr[k++] = R[j++];
    }
}

void mergeSort(vector<int>& arr, int left, int right) {
    if (left < right) {
        int mid = left + (right - left) / 2;
        mergeSort(arr, left, mid);
        mergeSort(arr, mid + 1, right);
        merge(arr, left, mid, right);
    }
}

void mergeSort(vector<int>& arr) {
    mergeSort(arr, 0, arr.size() - 1);
}

​ 以上代码中,我们首先定义了一个merge函数,用于将两个有序子序列合并为一个有序序列。在mergeSort函数中,我们首先将待排序序列拆分为左右两个子序列,分别递归调用mergeSort函数对其进行排序,最后调用merge函数将两个有序子序列合并为一个有序序列。

​ 时间复杂度为O(nlogn),空间复杂度为O(n)。

专家版的代码

C++
  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
/**
 *  \addtogroup sorting Sorting Algorithms
 *  @{
 *  \file
 *  \brief [Merege Sort Algorithm
 *  (MEREGE SORT)](https://en.wikipedia.org/wiki/Merge_sort) implementation
 *
 *  \author [Ayaan Khan](http://github.com/ayaankhan98)
 *
 *  \details
 *  Merge Sort is an efficient, general purpose, comparison
 *  based sorting algorithm.
 *  Merge Sort is a divide and conquer algorithm
 *
 */
#include <iostream>

/**
 *
 * The merge() function is used for merging two halves.
 * The merge(arr, l, m, r) is key process that assumes that
 * arr[l..m] and arr[m+1..r] are sorted and merges the two
 * sorted sub-arrays into one.
 *
 * @param arr - array with two halves arr[l...m] and arr[m+1...l]
 * @param l - left index or start index of first half array
 * @param m - right index or end index of first half array
 *
 * (The second array starts form m+1 and goes till l)
 *
 * @param l - end index or right index of second half array
 */
void merge(int *arr, int l, int m, int r) {
    int i, j, k;
    int n1 = m - l + 1;
    int n2 = r - m;

    int *L = new int[n1], *R = new int[n2];

    for (i = 0; i < n1; i++) L[i] = arr[l + i];
    for (j = 0; j < n2; j++) R[j] = arr[m + 1 + j];

    i = 0;
    j = 0;
    k = l;
    while (i < n1 || j < n2) {
        if (j >= n2 || (i < n1 && L[i] <= R[j])) {
            arr[k] = L[i];
            i++;
        } else {
            arr[k] = R[j];
            j++;
        }
        k++;
    }

    delete[] L;
    delete[] R;
}

/**
 * Merge sort is a divide and conquer algorithm, it divides the
 * input array into two halves and calls itself for the two halves
 * and then calls merge() to merge the two halves
 *
 * @param arr - array to be sorted
 * @param l - left index or start index of array
 * @param r - right index or end index of array
 *
 */
void mergeSort(int *arr, int l, int r) {
    if (l < r) {
        int m = l + (r - l) / 2;
        mergeSort(arr, l, m);
        mergeSort(arr, m + 1, r);
        merge(arr, l, m, r);
    }
}

/**
 * Utility function used to print the array after
 * sorting
 */
void show(int *arr, int size) {
    for (int i = 0; i < size; i++) std::cout << arr[i] << " ";
    std::cout << "\n";
}

/** Main function */
int main() {
    int size;
    std::cout << "Enter the number of elements : ";
    std::cin >> size;
    int *arr = new int[size];
    std::cout << "Enter the unsorted elements : ";
    for (int i = 0; i < size; ++i) {
        std::cin >> arr[i];
    }
    mergeSort(arr, 0, size - 1);
    std::cout << "Sorted array : ";
    show(arr, size);
    delete[] arr;
    return 0;
}
/** @} */

19、Non Recursive Merge Sort 非递归归并排序

​ 非递归归并排序(Non-recursive Merge Sort)是一种基于归并排序的排序算法,它使用迭代代替递归实现排序。与递归归并排序相比,非递归归并排序虽然实现稍微复杂一些,但却可以避免递归带来的额外空间和时间开销。

普通版本的代码

C++
 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
void merge(vector<int>& arr, int left, int mid, int right) {
    int n1 = mid - left + 1;
    int n2 = right - mid;
    vector<int> L(n1);
    vector<int> R(n2);
    for (int i = 0; i < n1; i++) {
        L[i] = arr[left + i];
    }
    for (int i = 0; i < n2; i++) {
        R[i] = arr[mid + 1 + i];
    }
    int i = 0, j = 0, k = left;
    while (i < n1 && j < n2) {
        if (L[i] <= R[j]) {
            arr[k++] = L[i++];
        } else {
            arr[k++] = R[j++];
        }
    }
    while (i < n1) {
        arr[k++] = L[i++];
    }
    while (j < n2) {
        arr[k++] = R[j++];
    }
}

void mergeSort(vector<int>& arr) {
    int n = arr.size();
    for (int curr_size = 1; curr_size <= n - 1; curr_size = 2 * curr_size) {
        for (int left_start = 0; left_start < n - 1; left_start += 2 * curr_size) {
            int mid = min(left_start + curr_size - 1, n - 1);
            int right_end = min(left_start + 2 * curr_size - 1, n - 1);
            merge(arr, left_start, mid, right_end);
        }
    }
}

​ 以上代码中,我们定义了一个merge函数和一个非递归归并排序函数mergeSort。在mergeSort函数中,我们首先遍历数组,将数组中的元素以curr_size个元素为一组进行两两合并,每次合并后的组数翻倍,直到最终只剩下一个有序的序列。在每次合并过程中,我们调用merge函数将两个有序子序列合并为一个有序序列。

​ 时间复杂度为O(nlogn),空间复杂度为O(n)。

专家版的代码

C++
  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
/**
 * Copyright 2020 @author Albirair
 * @file
 *
 * A generic implementation of non-recursive merge sort.
 */
#include <cstddef>  // for size_t
#include <iostream>
#include <utility>  // for std::move & std::remove_reference_t

namespace sorting {
template <class Iterator>
void merge(Iterator, Iterator, const Iterator, char[]);
/// bottom-up merge sort which sorts elements in a non-decreasing order
/**
 * sorts elements non-recursively by breaking them into small segments,
 * merging adjacent segments into larger sorted segments, then increasing
 * the sizes of segments by factors of 2 and repeating the same process.
 * best-case = worst-case = O(n log(n))
 * @param first points to the first element
 * @param last points to 1-step past the last element
 * @param n the number of elements
 */
template <class Iterator>
void non_recursive_merge_sort(const Iterator first, const Iterator last,
                              const size_t n) {
    // create a buffer large enough to store all elements
    // dynamically allocated to comply with cpplint
    char* buffer = new char[n * sizeof(*first)];
    // buffer size can be optimized to largest power of 2 less than n
    // elements divide the container into equally-sized segments whose
    // length start at 1 and keeps increasing by factors of 2
    for (size_t length(1); length < n; length <<= 1) {
        // merge adjacent segments whose number is n / (length * 2)
        Iterator left(first);
        for (size_t counter(n / (length << 1)); counter; --counter) {
            Iterator right(left + length), end(right + length);
            merge(left, right, end, buffer);
            left = end;
        }
        // if the number of remaining elements (n * 2 % length) is longer
        // than a segment, merge the remaining elements
        if ((n & ((length << 1) - 1)) > length)
            merge(left, left + length, last, buffer);
    }
    delete[] buffer;
}
/// merges 2 sorted adjacent segments into a larger sorted segment
/**
 * best-case = worst-case = O(n)
 * @param l points to the left part
 * @param r points to the right part, end of left part
 * @param e points to end of right part
 * @param b points at the buffer
 */
template <class Iterator>
void merge(Iterator l, Iterator r, const Iterator e, char b[]) {
    // create 2 pointers to point at the buffer
    auto p(reinterpret_cast<std::remove_reference_t<decltype(*l)>*>(b)), c(p);
    // move the left part of the segment
    for (Iterator t(l); r != t; ++t) *p++ = std::move(*t);
    // while neither the buffer nor the right part has been exhausted
    // move the smallest element of the two back to the container
    while (e != r && c != p) *l++ = std::move(*r < *c ? *r++ : *c++);
    // notice only one of the two following loops will be executed
    // while the right part hasn't bee exhausted, move it back
    while (e != r) *l++ = std::move(*r++);
    // while the buffer hasn't bee exhausted, move it back
    while (c != p) *l++ = std::move(*c++);
}
/// bottom-up merge sort which sorts elements in a non-decreasing order
/**
 * @param first points to the first element
 * @param n the number of elements
 */
template <class Iterator>
void non_recursive_merge_sort(const Iterator first, const size_t n) {
    non_recursive_merge_sort(first, first + n, n);
}
/// bottom-up merge sort which sorts elements in a non-decreasing order
/**
 * @param first points to the first element
 * @param last points to 1-step past the last element
 */
template <class Iterator>
void non_recursive_merge_sort(const Iterator first, const Iterator last) {
    non_recursive_merge_sort(first, last, last - first);
}

}  // namespace sorting

using sorting::non_recursive_merge_sort;

int main(int argc, char** argv) {
    int size;
    std::cout << "Enter the number of elements : ";
    std::cin >> size;
    int* arr = new int[size];
    for (int i = 0; i < size; ++i) {
        std::cout << "arr[" << i << "] = ";
        std::cin >> arr[i];
    }
    non_recursive_merge_sort(arr, size);
    std::cout << "Sorted array\n";
    for (int i = 0; i < size; ++i)
        std::cout << "arr[" << i << "] = " << arr[i] << '\n';
    delete[] arr;
    return 0;
}

20、Numeric String Sort

​ Numeric String Sort是一种特殊的字符串排序方法,主要用于对包含数字的字符串进行排序。传统的字符串排序方法会将数字和字母一起排序,而Numeric String Sort则将数字按照数值大小排序,字母按照字典序排序。

普通版本的代码

C++
 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
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>

using namespace std;

bool is_digit(char c) {
    return isdigit(c);
}

bool is_not_digit(char c) {
    return !isdigit(c);
}

bool numeric_string_sort(const string& s1, const string& s2) {
    if (isdigit(s1[0]) && isdigit(s2[0])) {
        return stoi(s1) < stoi(s2);
    } else if (is_not_digit(s1[0]) && is_not_digit(s2[0])) {
        return s1 < s2;
    } else if (isdigit(s1[0]) && is_not_digit(s2[0])) {
        return true;
    } else {
        return false;
    }
}

int main() {
    vector<string> strs = {"123", "abc", "45", "def", "67", "efg", "1a2b", "c3d4"};
    sort(strs.begin(), strs.end(), numeric_string_sort);
    for (const auto& s : strs) {
        cout << s << " ";
    }
    cout << endl;
    return 0;
}

​ 以上代码中,我们定义了一个numeric_string_sort函数,它判断两个字符串s1和s2的大小关系。当s1和s2的首位都是数字时,我们将它们转化为数字后比较大小;当s1和s2的首位都是字母时,我们直接比较它们的字典序;当s1的首位是数字,s2的首位是字母时,我们认为数字小于字母;当s1的首位是字母,s2的首位是数字时,我们认为字母大于数字。

​ 时间复杂度为O(nlogn),空间复杂度为O(1)。

专家版的代码

C++
 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
// Using general algorithms to sort a collection of strings results in
// alphanumeric sort. If it is a numeric string, it leads to unnatural sorting

// eg, an array of strings 1,10,100,2,20,200,3,30,300
// would be sorted in that same order by using conventional sorting,
// even though we know the correct sorting order is 1,2,3,10,20,30,100,200,300

// This Programme uses a comparator to sort the array in Numerical order instead
// of Alphanumeric order

#include <algorithm>
#include <iostream>
#include <string>
#include <vector>

bool NumericSort(std::string a, std::string b) {
    while (a[0] == '0') {
        a.erase(a.begin());
    }
    while (b[0] == '0') {
        b.erase(b.begin());
    }
    int n = a.length();
    int m = b.length();
    if (n == m)
        return a < b;
    return n < m;
}

int main() {
    int n;
    std::cout << "Enter number of elements to be sorted Numerically\n";
    std::cin >> n;

    std::vector<std::string> v(n);
    std::cout << "Enter the string of Numbers\n";
    for (int i = 0; i < n; i++) {
        std::cin >> v[i];
    }

    sort(v.begin(), v.end());
    std::cout << "Elements sorted normally \n";
    for (int i = 0; i < n; i++) {
        std::cout << v[i] << " ";
    }
    std::cout << "\n";

    std::sort(v.begin(), v.end(), NumericSort);
    std::cout << "Elements sorted Numerically \n";
    for (int i = 0; i < n; i++) {
        std::cout << v[i] << " ";
    }

    return 0;
}

21、Odd Even Sort

​ Odd-Even Sort是一种基于比较的排序算法,它可以对一组数据进行排序。该算法在运行过程中会交替地比较相邻的元素,并将它们交换位置,直到所有的元素都被排序好为止。

具体实现过程如下:

  1. 首先对奇数下标和偶数下标分别进行一次冒泡排序,得到两个子序列分别为[0, 2, 4, ...]和[1, 3, 5, ...]。
  2. 然后对这两个子序列进行归并排序,得到有序的序列。
  3. 重复执行第1和第2步,直到整个序列都被排好序。

普通版本的代码

C++
 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
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

void odd_even_sort(vector<int>& nums) {
    int n = nums.size();
    bool sorted = false;
    while (!sorted) {
        sorted = true;
        // odd-even sort
        for (int i = 1; i < n - 1; i += 2) {
            if (nums[i] > nums[i + 1]) {
                swap(nums[i], nums[i + 1]);
                sorted = false;
            }
        }
        for (int i = 0; i < n - 1; i += 2) {
            if (nums[i] > nums[i + 1]) {
                swap(nums[i], nums[i + 1]);
                sorted = false;
            }
        }
        // merge
        for (int i = 0; i < n - 1; i += 2) {
            if (nums[i] > nums[i + 1]) {
                swap(nums[i], nums[i + 1]);
                sorted = false;
            }
        }
    }
}

int main() {
    vector<int> nums = {5, 2, 8, 7, 1, 3, 6, 4};
    odd_even_sort(nums);
    for (const auto& n : nums) {
        cout << n << " ";
    }
    cout << endl;
    return 0;
}

​ 以上代码中,我们首先使用一个sorted变量来判断当前序列是否已经排好序。然后,在while循环中重复执行奇偶排序和归并排序操作,直到序列已经排好序。

​ 奇偶排序过程中,我们先对奇数下标的元素进行一次冒泡排序,然后对偶数下标的元素进行一次冒泡排序,最后再对奇数下标的元素进行一次冒泡排序。这样,我们就可以保证所有相邻的元素都进行了比较和交换。

​ 归并排序过程中,我们只需要对奇数下标的元素和偶数下标的元素进行比较,然后进行交换即可。这里的归并排序是非递归实现的。

​ 时间复杂度为O(n^2),空间复杂度为O(1)。

专家版的代码

C++
 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
/* C++ implementation Odd Even Sort */
#include <iostream>
#include <vector>

using namespace std;

void oddEven(vector<int> &arr, int size) {
    bool sorted = false;
    while (!sorted) {
        sorted = true;
        for (int i = 1; i < size - 1; i += 2)  // Odd
        {
            if (arr[i] > arr[i + 1]) {
                swap(arr[i], arr[i + 1]);
                sorted = false;
            }
        }

        for (int i = 0; i < size - 1; i += 2)  // Even
        {
            if (arr[i] > arr[i + 1]) {
                swap(arr[i], arr[i + 1]);
                sorted = false;
            }
        }
    }
}

void show(vector<int> A, int size) {
    int i;
    for (i = 0; i < size; i++) cout << A[i] << "\n";
}

int main() {
    int size, temp;
    cout << "\nEnter the number of elements : ";
    cin >> size;

    vector<int> arr;

    cout << "\nEnter the unsorted elements : \n";

    for (int i = 0; i < size; ++i) {
        cin >> temp;
        arr.push_back(temp);
    }

    oddEven(arr, size);

    cout << "Sorted array\n";
    show(arr, size);
    return 0;
}

22、Pancake Sort 煎饼排序

​ Pancake Sort(煎饼排序)是一种排序算法,它通过反复翻转一对元素来对数组进行排序。该算法的思想是通过一系列煎饼翻转将最大元素带到数组前面进行排序。一个煎饼翻转定义为将数组中前k个元素的顺序翻转。

​ 该算法通过不断查找未排序部分中最大元素的索引,并执行煎饼翻转将该元素带到数组前面来进行排序。每次翻转后,最大元素被移动到未排序部分数组的开头,将未排序部分数组的大小减少1。该过程重复进行,直到整个数组排序完成。

普通版本的代码

C++
 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
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

// 翻转arr[0...k]数组
void flip(vector<int>& arr, int k) {
    int i = 0;
    while (i < k) {
        swap(arr[i], arr[k]);
        i++;
        k--;
    }
}

// 查找arr[0...k]数组中的最大元素的索引
int findMaxIndex(vector<int>& arr, int k) {
    int maxIndex = 0;
    for (int i = 0; i <= k; i++) {
        if (arr[i] > arr[maxIndex]) {
            maxIndex = i;
        }
    }
    return maxIndex;
}

// Pancake Sort算法实现
void pancakeSort(vector<int>& arr) {
    int n = arr.size();
    for (int i = n - 1; i > 0; i--) {
        int maxIndex = findMaxIndex(arr, i);
        if (maxIndex != i) {
            flip(arr, maxIndex);
            flip(arr, i);
        }
    }
}

// 测试代码
int main() {
    vector<int> arr = {23, 10, 20, 11, 12, 6, 7};
    pancakeSort(arr);
    for (int i = 0; i < arr.size(); i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
    return 0;
}

专家版的代码

C++
  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
/**
 * @file
 * @brief pancake sort sorts a disordered stack of pancakes by flipping any
 * number of pancakes using a spatula using minimum number of flips.
 *
 * @details
 * Unlike a traditional sorting algorithm, which attempts to sort with the
 * fewest comparisons possible, the goal is to sort the sequence in as few
 * reversals as possible. Overall time complexity of pancake sort is O(n^2) For
 * example: example 1:- Disordered pancake sizes: {2,5,3,7,8} Sorted:
 * {2,3,5,7,8} For example: example 2:- Disordered pancake sizes:
 * {22,51,37,73,81} Sorted: {22,37,51,73,81}
 * @author [Divyansh Gupta](https://github.com/divyansh12323)
 * @see more on [Pancake sort](https://en.wikipedia.org/wiki/Pancake_sorting)
 * @see related problem at
 * [Leetcode](https://leetcode.com/problems/pancake-sorting/)
 */

#include <algorithm>  // for std::is_sorted
#include <cassert>    // for std::assert
#include <iostream>   // for io operations
#include <vector>     // for std::vector

/**
 * @namespace sorting
 * @brief Sorting algorithms
 */
namespace sorting {
/**
 * @namespace pancake_sort
 * @brief Functions for [Pancake
 * sort](https://en.wikipedia.org/wiki/Pancake_sorting) algorithm
 */
namespace pancake_sort {
/**
 * @brief This implementation is for reversing elements in a a C-style array .
 * @param [start,end] arr our vector of elements.
 * @param start starting index of array
 * @param end ending index of array
 * @returns void
 */
template <typename T>
void reverse(std::vector<T> &arr, int start, int end) {
    T temp;  // Temporary variable
    while (start <= end) {
        temp = arr[start];
        arr[start] = arr[end];
        arr[end] = temp;
        start++;
        end--;
    }
}
/**
 * @brief This implementation is for a C-style array input that gets modified in
 * place.
 * @param [start,end] arr our vector of elements.
 * @param size size of given array
 * @returns 0 on exit
 */
template <typename T>
int pancakeSort(std::vector<T> &arr, int size) {
    for (int i = size; i > 1; --i) {
        int max_index = 0, j = 0;  // intialize some variables.
        T max_value = 0;
        for (j = 0; j < i; j++) {
            if (arr[j] >= max_value) {
                max_value = arr[j];
                max_index = j;
            }
        }
        if (max_index != i - 1)  // check for reversing
        {
            reverse(arr, 0, max_index);
            reverse(arr, 0, i - 1);
        }
    }
    return 0;
}
}  // namespace pancake_sort
}  // namespace sorting

/**
 * @brief Test implementations
 * @returns void
 */
static void test() {
    // example 1: vector of int
    const int size1 = 7;
    std::cout << "\nTest 1- as std::vector<int>...";
    std::vector<int> arr1 = {23, 10, 20, 11, 12, 6, 7};
    sorting::pancake_sort::pancakeSort(arr1, size1);
    assert(std::is_sorted(arr1.begin(), arr1.end()));
    std::cout << "Passed\n";
    for (int i = 0; i < size1; i++) {
        std::cout << arr1[i] << " ,";
    }
    std::cout << std::endl;

    // example 2: vector of double
    const int size2 = 8;
    std::cout << "\nTest 2- as std::vector<double>...";
    std::vector<double> arr2 = {23.56, 10.62, 200.78, 111.484,
                                3.9,   1.2,   61.77,  79.6};
    sorting::pancake_sort::pancakeSort(arr2, size2);
    assert(std::is_sorted(arr2.begin(), arr2.end()));
    std::cout << "Passed\n";
    for (int i = 0; i < size2; i++) {
        std::cout << arr2[i] << ", ";
    }
    std::cout << std::endl;

    // example 3:vector of float
    const int size3 = 7;
    std::cout << "\nTest 3- as std::vector<float>...";
    std::vector<float> arr3 = {6.56, 12.62, 200.78, 768.484, 19.27, 68.87, 9.6};
    sorting::pancake_sort::pancakeSort(arr3, size3);
    assert(std::is_sorted(arr3.begin(), arr3.end()));
    std::cout << "Passed\n";
    for (int i = 0; i < size3; i++) {
        std::cout << arr3[i] << ", ";
    }
    std::cout << std::endl;
}
/**
 * @brief Main function
 * @returns 0 on exit
 */
int main() {
    test();
    return 0;
}

23、Pigeonhole Sort

​ Pigeonhole Sort是一种排序算法,它基于鸽巢原理,适用于已知数据范围的情况。该算法的基本思想是,将每个元素分配到一个桶中,然后按照桶的顺序将元素收集起来,从而完成排序。

具体步骤如下:

  1. 找到最小值和最大值,确定需要的鸽巢数量。
  2. 创建鸽巢,即桶。
  3. 将每个元素放入鸽巢中。
  4. 按照鸽巢的顺序将元素取出,形成有序序列。

​ Pigeonhole Sort的时间复杂度为O(n+k),其中k表示鸽巢的数量。当数据范围很小而元素数量很大时,该算法可以快速地完成排序。然而,当数据范围很大时,需要创建大量的鸽巢,可能会导致空间不足的问题。

普通版本的代码

C++
 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
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

// Pigeonhole Sort算法实现
void pigeonholeSort(vector<int>& arr) {
    int n = arr.size();
    int minVal = arr[0], maxVal = arr[0];
    for (int i = 1; i < n; i++) {
        minVal = min(minVal, arr[i]);
        maxVal = max(maxVal, arr[i]);
    }
    int range = maxVal - minVal + 1;
    vector<int> holes(range);
    for (int i = 0; i < n; i++) {
        holes[arr[i] - minVal]++;
    }
    int index = 0;
    for (int i = 0; i < range; i++) {
        while (holes[i] > 0) {
            arr[index++] = i + minVal;
            holes[i]--;
        }
    }
}

// 测试代码
int main() {
    vector<int> arr = {23, 10, 20, 11, 12, 6, 7};
    pigeonholeSort(arr);
    for (int i = 0; i < arr.size(); i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
    return 0;
}

​ 在该实现中,我们首先找到数组中的最小值和最大值,确定需要的鸽巢数量。然后创建一个鸽巢数组holes,并将每个元素放入相应的鸽巢中。最后,按照鸽巢的顺序将元素取出,形成有序序列。

专家版的代码

C++
  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
/**
 * @file
 * @brief Implementation of [Pigeonhole Sort algorithm]
 * (https://en.wikipedia.org/wiki/Pigeonhole_sort)
 * @author [Lownish](https://github.com/Lownish)
 * @details
 * Pigeonhole sorting is a sorting algorithm that is suitable for sorting lists
 * of elements where the number of elements and the number of possible key
 * values are approximately the same. It requires O(n + Range) time where n is
 * number of elements in input array and ‘Range’ is number of possible values in
 * array.
 *
 * The time Complexity of the algorithm is \f$O(n+N)\f$.
 */

#include <algorithm>  //for std::is_sorted
#include <array>      //for std::array
#include <cassert>    //for assert
#include <iostream>   //for io operations

/**
 * @namespace sorting
 * @brief Sorting algorithms
 */
namespace sorting {

/**
 * Pigeonhole sorting of array of size n
 * The function will sort the array through Pigeonhole algorithm and print
 * @param arr unsorted array of elements
 * @returns sorted array of elements
 */
template <std::size_t N>
std::array<int, N> pigeonSort(std::array<int, N> arr) {
    // Finding min and max*
    auto min = std::min_element(std::begin(arr), std::end(arr));
    auto max = std::max_element(std::begin(arr), std::end(arr));

    // Range refers to the number of holes required
    int range = *max - *min + 1;
    int *hole = new int[range]();

    // Copying all array values to pigeonhole
    for (int i = 0; i < N; i++) {
        hole[arr[i] - *min] = arr[i];
    }

    // Deleting elements from list and storing to original array
    int count = 0;
    for (int i = 0; i < range; i++) {
        while (hole[i] != '\0') {
            arr[count] = hole[i];
            hole[i] = {};
            count++;
        }
    }
    delete[] hole;

    return arr;
}
}  // namespace sorting

/**
 * Test function 1 with unsorted array
 * {8, 3, 2, 7, 4, 6, 8}
 * @returns none
 */
static void test_1() {
    const int n = 7;
    std::array<int, n> test_array = {8, 3, 2, 7, 4, 6, 8};

    test_array = sorting::pigeonSort<n>(test_array);

    assert(std::is_sorted(std::begin(test_array), std::end(test_array)));

    // Printing sorted array
    for (int i = 0; i < n; i++) {
        std::cout << test_array.at(i) << " ";
    }
    std::cout << "\nPassed\n";
}

/**
 * Test function 2 with unsorted array
 * {802, 630, 20, 745, 52, 300, 612, 932, 78, 187}
 * @returns none
 */
static void test_2() {
    const int n = 10;
    std::array<int, n> test_array = {802, 630, 20,  745, 52,
                                     300, 612, 932, 78,  187};

    test_array = sorting::pigeonSort<n>(test_array);

    assert(std::is_sorted(std::begin(test_array), std::end(test_array)));

    // Printing sorted array
    for (int i = 0; i < n; i++) {
        std::cout << test_array.at(i) << " ";
    }
    std::cout << "\nPassed\n";
}

/**
 * Test function 1 with unsorted array
 * {11,13,12,14}
 * @returns none
 */
static void test_3() {
    const int n = 4;
    std::array<int, n> test_array = {11, 13, 12, 14};

    test_array = sorting::pigeonSort<n>(test_array);

    assert(std::is_sorted(std::begin(test_array), std::end(test_array)));

    // Printing sorted array
    for (int i = 0; i < n; i++) {
        std::cout << test_array.at(i) << " ";
    }
    std::cout << "\nPassed\n";
}

/**
 * Main function
 */
int main() {
    test_1();
    test_2();
    test_3();

    return 0;
}

24、Quick Sort 快速排序

​ 快速排序(Quick Sort)是一种经典的基于比较的排序算法,也是目前被广泛应用的排序算法之一。它的基本思想是通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,然后再分别对这两部分记录进行排序,以达到整个序列有序的目的。

快速排序的具体实现过程如下:

  1. 选择一个基准元素(pivot),一般选择第一个元素或者随机选取一个元素。
  2. 将序列中所有比基准元素小的元素放在它的左边,所有比基准元素大的元素放在它的右边,相等的元素可以放在任意一边。
  3. 对基准元素的左右两边分别进行递归操作,直到每个子序列只有一个元素。
  4. 最终整个序列就被排序了。

​ 快速排序的时间复杂度为 \(O(n \log n)\),但是最坏情况下的时间复杂度为 \(O(n^2)\),发生在每次分割只能得到一个比当前元素小的元素和一个比当前元素大的元素的时候。为了避免这种情况的发生,可以采用随机化的方式来选择基准元素,或者采用三数取中的方式来选择基准元素。

普通版本的代码

C++
 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
#include <iostream>
#include <vector>

using namespace std;

int partition(vector<int>& nums, int left, int right) {
    int pivot = nums[left];
    int i = left + 1, j = right;
    while (true) {
        while (i <= j && nums[i] <= pivot) i++;
        while (i <= j && nums[j] >= pivot) j--;
        if (i > j) break;
        swap(nums[i], nums[j]);
    }
    swap(nums[left], nums[j]);
    return j;
}

void quickSort(vector<int>& nums, int left, int right) {
    if (left >= right) return;
    int mid = partition(nums, left, right);
    quickSort(nums, left, mid - 1);
    quickSort(nums, mid + 1, right);
}

void quickSort(vector<int>& nums) {
    int n = nums.size();
    quickSort(nums, 0, n - 1);
}

int main() {
    vector<int> nums = {5, 2, 3, 1, 4};
    quickSort(nums);
    for (int num : nums) {
        cout << num << " ";
    }
    cout << endl;
    return 0;
}

​ 在上述代码中,partition()函数用于将序列分为两部分,并返回基准元素的位置。quickSort()函数用于递归地对分割出来的两部分进行排序。最后,在main()函数中调用quickSort()函数对给定的序列进行排序。

专家版的代码

C++
  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
/**
 * @file
 * @brief Quick sort algorithm
 *
 * Implementation Details -
 *      Quick Sort is a divide and conquer algorithm. It picks and element as
 *      pivot and partition the given array around the picked pivot. There
 *      are many different versions of quickSort that pick pivot in different
 *      ways.
 *
 *      1. Always pick the first element as pivot
 *      2. Always pick the last element as pivot (implemented below)
 *      3. Pick a random element as pivot
 *      4. Pick median as pivot
 *
 *      The key process in quickSort is partition(). Target of partition is,
 *      given an array and an element x(say) of array as pivot, put x at it's
 *      correct position in sorted array and put all smaller elements (samller
 *      than x) before x, and put all greater elements (greater than x) after
 *      x. All this should be done in linear time
 *
 */

#include <cstdlib>
#include <iostream>

namespace sorting {
/**
 *      This function takes last element as pivot, places
 *      the pivot element at its correct position in sorted
 *      array, and places all smaller (smaller than pivot)
 *      to left of pivot and all greater elements to right
 *      of pivot
 *
 */

int partition(int arr[], int low, int high) {
    int pivot = arr[high];  // taking the last element as pivot
    int i = (low - 1);      // Index of smaller element

    for (int j = low; j < high; j++) {
        // If current element is smaller than or
        // equal to pivot
        if (arr[j] <= pivot) {
            i++;  // increment index of smaller element
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    }
    int temp = arr[i + 1];
    arr[i + 1] = arr[high];
    arr[high] = temp;
    return (i + 1);
}

/**
 *      The main function that implements QuickSort
 *      arr[] --> Array to be sorted,
 *      low --> Starting index,
 *      high --> Ending index
 */
void quickSort(int arr[], int low, int high) {
    if (low < high) {
        int p = partition(arr, low, high);
        quickSort(arr, low, p - 1);
        quickSort(arr, p + 1, high);
    }
}

}  // namespace sorting

using sorting::quickSort;

// prints the array after sorting
void show(int arr[], int size) {
    for (int i = 0; i < size; i++) std::cout << arr[i] << " ";
    std::cout << "\n";
}

/** Driver program to test above functions */
int main() {
    int size;
    std::cout << "\nEnter the number of elements : ";

    std::cin >> size;

    int *arr = new int[size];

    std::cout << "\nEnter the unsorted elements : ";

    for (int i = 0; i < size; ++i) {
        std::cout << "\n";
        std::cin >> arr[i];
    }
    quickSort(arr, 0, size);
    std::cout << "Sorted array\n";
    show(arr, size);
    delete[] arr;
    return 0;
}
C++
  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
/**
 * @file
 * @brief Implementation Details
 * @details Quick sort 3 works on Dutch National Flag Algorithm
 * The major difference between simple quicksort and quick sort 3 comes in the
 * function partition3 In quick_sort_partition3 we divide the vector/array into
 * 3 parts. quick sort 3 works faster in some cases as compared to simple
 * quicksort.
 * @author immortal-j
 * @author [Krishna Vedala](https://github/kvedala)
 */
#include <algorithm>
#include <cassert>
#include <ctime>
#include <iostream>
#include <vector>

namespace {
/**
 * Operator to print the array.
 * @param out std::ostream object to write to
 * @param arr array to write
 */
template <typename T>
std::ostream &operator<<(std::ostream &out, const std::vector<T> &arr) {
    for (size_t i = 0; i < arr.size(); ++i) {
        out << arr[i];
        if (i < arr.size() - 1) {
            out << ", ";
        }
    }
    return out;
}

}  // namespace

/**
 * @namespace sorting
 * @brief Sorting Algorithms
 */
namespace sorting {
namespace {  // using un-named namespace here to prevent partition function
             // being visible to end-users
/** This function partitions `arr[]` in three parts
 * 1. \f$arr[l\ldots i]\f$ contains all elements smaller than pivot
 * 2. \f$arr[(i+1)\ldots (j-1)]\f$ contains all occurrences of pivot
 * 3. \f$arr[j\ldots r]\f$ contains all elements greater than pivot
 * @tparam T type of data in the vector array
 * @param [in,out] arr vector array being partitioned
 * @param [in] low lower limit of window to partition
 * @param [in] high upper limit of window to partition
 * @param [out] i updated lower limit of partition
 * @param [out] j updated upper limit of partition
 */
template <typename T>
void partition3(std::vector<T> *arr, int32_t low, int32_t high, int32_t *i,
                int32_t *j) {
    // To handle 2 elements
    if (high - low <= 1) {
        if ((*arr)[high] < (*arr)[low]) {
            std::swap((*arr)[high], (*arr)[low]);
        }
        *i = low;
        *j = high;
        return;
    }

    int32_t mid = low;
    T pivot = (*arr)[high];
    while (mid <= high) {
        if ((*arr)[mid] < pivot) {
            std::swap((*arr)[low++], (*arr)[mid++]);
        } else if ((*arr)[mid] == pivot) {
            mid++;
        } else if ((*arr)[mid] > pivot) {
            std::swap((*arr)[mid], (*arr)[high--]);
        }
    }

    // update i and j
    *i = low - 1;
    *j = mid;  // or high-1
}
}  // namespace

/** 3-way partition based quick sort. This function accepts array pointer and
 * modified the input array.
 * @tparam T type of data in the vector array
 * @param [in,out] arr vector array to sort
 * @param [in] low lower limit of window to partition
 * @param [in] high upper limit of window to partition
 */
template <typename T>
void quicksort(std::vector<T> *arr, int32_t low, int32_t high) {
    if (low >= high) {  // 1 or 0 elements
        return;
    }

    int32_t i = 0, j = 0;

    // i and j are passed as reference
    partition3(arr, low, high, &i, &j);

    // Recur two halves
    quicksort(arr, low, i);
    quicksort(arr, j, high);
}

/** 3-way partition based quick sort. This function accepts array by value and
 * creates a copy of it. The array copy gets sorted and returned by the
 * function.
 * @tparam T type of data in the vector array
 * @param [in] arr vector array to sort
 * @param [in] low lower limit of window to partition
 * @param [in] high upper limit of window to partition
 * @returns sorted array vector
 */
template <typename T>
std::vector<T> quicksort(std::vector<T> arr, int32_t low, int32_t high) {
    if (low >= high) {  // 1 or 0 elements
        return arr;
    }

    int32_t i = 0, j = 0;

    // i and j are passed as reference
    partition3(&arr, low, high, &i, &j);

    // Recur two halves
    quicksort(&arr, low, i);
    quicksort(&arr, j, high);

    return arr;
}
}  // namespace sorting

/** Test function for integer type arrays */
static void test_int() {
    std::cout << "\nTesting integer type arrays\n";

    for (int num_tests = 1; num_tests < 21; num_tests++) {
        size_t size = std::rand() % 500;
        std::vector<int> arr(size);
        for (auto &a : arr) {
            a = std::rand() % 500 - 250;  // random numbers between -250, 249
        }

        std::cout << "Test " << num_tests << "\t Array size:" << size << "\t ";
        std::vector<int> sorted = sorting::quicksort(arr, 0, size - 1);
        if (size < 20) {
            std::cout << "\t Sorted Array is:\n\t";
            std::cout << sorted << "\n";
        }
        assert(std::is_sorted(std::begin(sorted), std::end(sorted)));
        std::cout << "\t Passed\n";
    }
}

/** Test function for double type arrays */
static void test_double() {
    std::cout << "\nTesting Double type arrays\n";
    for (int num_tests = 1; num_tests < 21; num_tests++) {
        size_t size = std::rand() % 500;
        std::vector<double> arr(size);
        for (auto &a : arr) {
            a = double(std::rand() % 500) -
                250.f;   // random numbers between -250, 249
            a /= 100.f;  // convert to -2.5 to 2.49
        }

        std::cout << "Test " << num_tests << "\t Array size:" << size << "\t ";
        std::vector<double> sorted = sorting::quicksort(arr, 0, size - 1);
        if (size < 20) {
            std::cout << "\t Sorted Array is:\n\t";
            std::cout << sorted << "\n";
        }
        assert(std::is_sorted(std::begin(sorted), std::end(sorted)));
        std::cout << "\t Passed\n";
    }
}

/** Driver program for above functions */
int main() {
    std::srand(std::time(nullptr));
    test_int();
    test_double();
    return 0;
}

随机版本:

C++
  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
/**
 * @file
 * @brief Implementation of the [Random Pivot Quick
 * Sort](https://www.sanfoundry.com/cpp-program-implement-quick-sort-using-randomisation)
 * algorithm.
 * @details
 *          * A random pivot quick sort algorithm is pretty much same as quick
 * sort with a difference of having a logic of selecting next pivot element from
 * the input array.
 *          * Where in quick sort is fast, but still can give you the time
 * complexity of O(n^2) in worst case.
 *          * To avoid hitting the time complexity of O(n^2), we use the logic
 * of randomize the selection process of pivot element.
 *
 *          ### Logic
 *              * The logic is pretty simple, the only change is in the
 * partitioning algorithm, which is selecting the pivot element.
 *              * Instead of selecting the last or the first element from array
 * for pivot we use a random index to select pivot element.
 *              * This avoids hitting the O(n^2) time complexity in practical
 * use cases.
 *
 *       ### Partition Logic
 *           * Partitions are done such as numbers lower than the "pivot"
 * element is arranged on the left side of the "pivot", and number larger than
 * the "pivot" element are arranged on the right part of the array.
 *
 *       ### Algorithm
 *           * Select the pivot element randomly using getRandomIndex() function
 * from this namespace.
 *           * Initialize the pInd (partition index) from the start of the
 * array.
 *           * Loop through the array from start to less than end. (from start
 * to < end). (Inside the loop) :-
 *                   * Check if the current element (arr[i]) is less than the
 * pivot element in each iteration.
 *                   * If current element in the iteration is less than the
 * pivot element, then swap the elements at current index (i) and partition
 * index (pInd) and increment the partition index by one.
 *           * At the end of the loop, swap the pivot element with partition
 * index element.
 *           * Return the partition index from the function.
 *
 * @author [Nitin Sharma](https://github.com/foo290)
 */

#include <algorithm>  /// for std::is_sorted(), std::swap()
#include <array>      /// for std::array
#include <cassert>    /// for assert
#include <ctime>      /// for initializing random number generator
#include <iostream>   /// for IO operations
#include <tuple>      /// for returning multiple values form a function at once

/**
 * @namespace sorting
 * @brief Sorting algorithms
 */
namespace sorting {
/**
 * @brief Functions for the [Random Pivot Quick
 * Sort](https://www.sanfoundry.com/cpp-program-implement-quick-sort-using-randomisation)
 * implementation
 * @namespace random_pivot_quick_sort
 */
namespace random_pivot_quick_sort {
/**
 * @brief Utility function to print the array
 * @tparam T size of the array
 * @param arr array used to print its content
 * @returns void
 * */
template <size_t T>
void showArray(std::array<int64_t, T> arr) {
    for (int64_t i = 0; i < arr.size(); i++) {
        std::cout << arr[i] << " ";
    }
    std::cout << std::endl;
}

/**
 * @brief Takes the start and end indices of an array and returns a random
 * int64_teger between the range of those two for selecting pivot element.
 *
 * @param start The starting index.
 * @param end The ending index.
 * @returns int64_t A random number between start and end index.
 * */
int64_t getRandomIndex(int64_t start, int64_t end) {
    srand(time(nullptr));  // Initialize random number generator.
    int64_t randomPivotIndex = start + rand() % (end - start + 1);
    return randomPivotIndex;
}

/**
 * @brief A partition function which handles the partition logic of quick sort.
 * @tparam size size of the array to be passed as argument.
 * @param start The start index of the passed array
 * @param end The ending index of the passed array
 * @returns std::tuple<int64_t , std::array<int64_t , size>> A tuple of pivot
 * index and pivot sorted array.
 */
template <size_t size>
std::tuple<int64_t, std::array<int64_t, size>> partition(
    std::array<int64_t, size> arr, int64_t start, int64_t end) {
    int64_t pivot = arr[end];  // Randomly selected element will be here from
                               // caller function (quickSortRP()).
    int64_t pInd = start;

    for (int64_t i = start; i < end; i++) {
        if (arr[i] <= pivot) {
            std::swap(arr[i], arr[pInd]);  // swapping the elements from current
                                           // index to pInd.
            pInd++;
        }
    }
    std::swap(arr[pInd],
              arr[end]);  // swapping the pivot element to its sorted position
    return std::make_tuple(pInd, arr);
}

/**
 * @brief Random pivot quick sort function. This function is the starting point
 * of the algorithm.
 * @tparam size size of the array to be passed as argument.
 * @param start The start index of the passed array
 * @param end The ending index of the passed array
 * @returns std::array<int64_t , size> A fully sorted array in ascending order.
 */
template <size_t size>
std::array<int64_t, size> quickSortRP(std::array<int64_t, size> arr,
                                      int64_t start, int64_t end) {
    if (start < end) {
        int64_t randomIndex = getRandomIndex(start, end);

        // switching the pivot with right most bound.
        std::swap(arr[end], arr[randomIndex]);

        int64_t pivotIndex = 0;
        // getting pivot index and pivot sorted array.
        std::tie(pivotIndex, arr) = partition(arr, start, end);

        // Recursively calling
        std::array<int64_t, arr.size()> rightSortingLeft =
            quickSortRP(arr, start, pivotIndex - 1);
        std::array<int64_t, arr.size()> full_sorted =
            quickSortRP(rightSortingLeft, pivotIndex + 1, end);
        arr = full_sorted;
    }
    return arr;
}

/**
 * @brief A function utility to generate unsorted array of given size and range.
 * @tparam size Size of the output array.
 * @param from Stating of the range.
 * @param to Ending of the range.
 * @returns std::array<int64_t , size> Unsorted array of specified size.
 * */
template <size_t size>
std::array<int64_t, size> generateUnsortedArray(int64_t from, int64_t to) {
    srand(time(nullptr));
    std::array<int64_t, size> unsortedArray{};
    assert(from < to);
    int64_t i = 0;
    while (i < size) {
        int64_t randomNum = from + rand() % (to - from + 1);
        if (randomNum) {
            unsortedArray[i] = randomNum;
            i++;
        }
    }
    return unsortedArray;
}

}  // namespace random_pivot_quick_sort
}  // namespace sorting

/**
 * @brief a class containing the necessary test cases
 */
class TestCases {
 private:
    /**
     * @brief A function to print64_t given message on console.
     * @tparam T Type of the given message.
     * @returns void
     * */
    template <typename T>
    void log(T msg) {
        // It's just to avoid writing cout and endl
        std::cout << "[TESTS] : ---> " << msg << std::endl;
    }

 public:
    /**
     * @brief Executes test cases
     * @returns void
     * */
    void runTests() {
        log("Running Tests...");

        testCase_1();
        testCase_2();
        testCase_3();

        log("Test Cases over!");
        std::cout << std::endl;
    }

    /**
     * @brief A test case with single input
     * @returns void
     * */
    void testCase_1() {
        const int64_t inputSize = 1;
        log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
            "~");
        log("This is test case 1 for Random Pivot Quick Sort Algorithm : ");
        log("Description:");
        log("   EDGE CASE : Only contains one element");
        std::array<int64_t, inputSize> unsorted_arr{2};

        int64_t start = 0;
        int64_t end = unsorted_arr.size() - 1;  // length - 1

        log("Running algorithm of data of length 50 ...");
        std::array<int64_t, unsorted_arr.size()> sorted_arr =
            sorting::random_pivot_quick_sort::quickSortRP(unsorted_arr, start,
                                                          end);
        log("Algorithm finished!");

        log("Checking assert expression...");
        assert(std::is_sorted(sorted_arr.begin(), sorted_arr.end()));
        log("Assertion check passed!");

        log("[PASS] : TEST CASE 1 PASS!");
        log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
            "~");
    }

    /**
     * @brief A test case with input array of length 500
     * @returns void
     * */
    void testCase_2() {
        const int64_t inputSize = 500;
        log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
            "~");
        log("Description:");
        log("   BIG INPUT : Contains 500 elements and repeated elements");
        log("This is test case 2 for Random Pivot Quick Sort Algorithm : ");
        std::array<int64_t, inputSize> unsorted_arr =
            sorting::random_pivot_quick_sort::generateUnsortedArray<inputSize>(
                1, 10000);

        int64_t start = 0;
        int64_t end = unsorted_arr.size() - 1;  // length - 1

        log("Running algorithm of data of length 500 ...");
        std::array<int64_t, unsorted_arr.size()> sorted_arr =
            sorting::random_pivot_quick_sort::quickSortRP(unsorted_arr, start,
                                                          end);
        log("Algorithm finished!");

        log("Checking assert expression...");
        assert(std::is_sorted(sorted_arr.begin(), sorted_arr.end()));
        log("Assertion check passed!");

        log("[PASS] : TEST CASE 2 PASS!");
        log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
            "~");
    }

    /**
     * @brief A test case with array of length 1000.
     * @returns void
     * */
    void testCase_3() {
        const int64_t inputSize = 1000;
        log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
            "~");
        log("This is test case 3 for Random Pivot Quick Sort Algorithm : ");
        log("Description:");
        log("   LARGE INPUT : Contains 1000 elements and repeated elements");
        std::array<int64_t, inputSize> unsorted_arr =
            sorting::random_pivot_quick_sort::generateUnsortedArray<inputSize>(
                1, 10000);

        int64_t start = 0;
        int64_t end = unsorted_arr.size() - 1;  // length - 1

        log("Running algorithm...");
        std::array<int64_t, unsorted_arr.size()> sorted_arr =
            sorting::random_pivot_quick_sort::quickSortRP(unsorted_arr, start,
                                                          end);
        log("Algorithm finished!");

        log("Checking assert expression...");
        assert(std::is_sorted(sorted_arr.begin(), sorted_arr.end()));
        log("Assertion check passed!");

        log("[PASS] : TEST CASE 3 PASS!");
        log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
            "~");
    }
};

/**
 * @brief Self-test implementations
 * @returns void
 */
static void test() {
    TestCases tc = TestCases();
    tc.runTests();
}

/**
 * @brief Main function
 * @param argc commandline argument count (ignored)
 * @param argv commandline array of arguments (ignored)
 * @returns 0 on exit
 */
int main(int argc, char *argv[]) {
    test();  // Executes various test cases.

    const int64_t inputSize = 10;
    std::array<int64_t, inputSize> unsorted_array =
        sorting::random_pivot_quick_sort::generateUnsortedArray<inputSize>(
            50, 1000);
    std::cout << "Unsorted array is : " << std::endl;
    sorting::random_pivot_quick_sort::showArray(unsorted_array);

    std::array<int64_t, inputSize> sorted_array =
        sorting::random_pivot_quick_sort::quickSortRP(
            unsorted_array, 0, unsorted_array.size() - 1);
    std::cout << "Sorted array is : " << std::endl;
    sorting::random_pivot_quick_sort::showArray(sorted_array);
    return 0;
}

25、Radix Sort 基数排序

​ Radix Sort是一种基于键值排序的算法,其中键是数字或字符串的一部分。 它被分类为稳定排序算法,它不会改变相等键的相对顺序。 在计数排序和桶排序中,排序的过程是基于键值的; 它们使用键作为索引以在值桶中存储值。 但是,Radix Sort是不同的,它的排序过程是按照每个键位的值来排序的,从最低有效位(LSB)到最高有效位(MSB)。 因此,Radix Sort被称为基数排序。

​ 基数排序有两种主要类型:最高有效位(MSD)和最低有效位(LSD)。 在LSD Radix Sort中,我们从最低有效位开始排序,从右到左进行。 在MSD Radix Sort中,我们从最高有效位开始排序,从左到右进行。

​ LSD Radix Sort非常适合数字排序,而MSD Radix Sort非常适合字符串排序。

​ Radix Sort的时间复杂度为O(d * (n+b)),其中n是元素的数量,d是数字或字符串的最大位数,b是基数的大小。

普通版本的代码

C++
 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
#include <iostream>
#include <cstring>

using namespace std;

void radixSort(int arr[], int n) {
    int maxVal = arr[0];
    for (int i = 1; i < n; i++) {
        if (arr[i] > maxVal) {
            maxVal = arr[i];
        }
    }
    int exp = 1;
    int *tmp = new int[n];
    while (maxVal / exp > 0) {
        int bucket[10] = {0};
        for (int i = 0; i < n; i++) {
            bucket[(arr[i] / exp) % 10]++;
        }
        for (int i = 1; i < 10; i++) {
            bucket[i] += bucket[i - 1];
        }
        for (int i = n - 1; i >= 0; i--) {
            tmp[--bucket[(arr[i] / exp) % 10]] = arr[i];
        }
        for (int i = 0; i < n; i++) {
            arr[i] = tmp[i];
        }
        exp *= 10;
    }
    delete[] tmp;
}

int main() {
    int arr[] = {170, 45, 75, 90, 802, 24, 2, 66};
    int n = sizeof(arr) / sizeof(arr[0]);
    radixSort(arr, n);
    cout << "Sorted array: ";
    for (int i = 0; i < n; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
    return 0;
}

专家版的代码

C++
 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
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <iostream>

void radixsort(int a[], int n) {
    int count[10];
    int* output = new int[n];
    memset(output, 0, n * sizeof(*output));
    memset(count, 0, sizeof(count));
    int max = 0;
    for (int i = 0; i < n; ++i) {
        if (a[i] > max) {
            max = a[i];
        }
    }
    int maxdigits = 0;
    while (max) {
        maxdigits++;
        max /= 10;
    }
    for (int j = 0; j < maxdigits; j++) {
        for (int i = 0; i < n; i++) {
            int t = std::pow(10, j);
            count[(a[i] % (10 * t)) / t]++;
        }
        int k = 0;
        for (int p = 0; p < 10; p++) {
            for (int i = 0; i < n; i++) {
                int t = std::pow(10, j);
                if ((a[i] % (10 * t)) / t == p) {
                    output[k] = a[i];
                    k++;
                }
            }
        }
        memset(count, 0, sizeof(count));
        for (int i = 0; i < n; ++i) {
            a[i] = output[i];
        }
    }
    delete[] output;
}

void print(int a[], int n) {
    for (int i = 0; i < n; ++i) {
        std::cout << a[i] << " ";
    }
    std::cout << std::endl;
}

int main(int argc, char const* argv[]) {
    int a[] = {170, 45, 75, 90, 802, 24, 2, 66};
    int n = sizeof(a) / sizeof(a[0]);
    radixsort(a, n);
    print(a, n);
    return 0;
}
C++
  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
/**
 * @file
 * @brief Algorithm of [Radix sort](https://en.wikipedia.org/wiki/Radix_sort)
 * @author [Suyash Jaiswal](https://github.com/Suyashjaiswal)
 * @details
 * Sort the vector of unsigned integers using radix sort i.e. sorting digit by
 * digit using [Counting Sort](https://en.wikipedia.org/wiki/Counting_sort) as
 * subroutine. Running time of radix sort is O(d*(n+b)) where b is the base for
 * representing numbers and d in the max digits in input integers and n is
 * number of unsigned integers. consider example for n = 5, aray elements =
 * 432,234,143,332,123 sorting digit by digit sorting according to 1) 1st digit
 * place
 * => 432, 332, 143, 123, 234
 *
 * 2) 2nd digit place
 * => 123, 432, 332, 234, 143
 *
 * 3) 3rd digit place
 * => 123, 143, 234, 332, 432
 *
 * using count sort at each step, which is stable.
 * stable => already sorted according to previous digits.
 */

/// header files
#include <algorithm>  /// for collection of functions
#include <cassert>  /// for a macro called assert which can be used to verify assumptions
#include <iostream>  /// for io operations
#include <vector>    /// for std::vector

/**
 * @namespace sorting
 * @brief Sorting algorithms
 */
namespace sorting {
/**
 * @namespace radix_sort
 * @brief Functions for [Radix sort](https://en.wikipedia.org/wiki/Radix_sort)
 * algorithm
 */
namespace radix_sort {
/**
 * @brief Function to sort vector according to current digit using stable
 * sorting.
 * @param cur_digit - sort according to the cur_digit
 * @param ar - vector to be sorted
 * @returns std::vector sorted till ith digit
 */
std::vector<uint64_t> step_ith(
    uint16_t cur_digit,
    const std::vector<uint64_t>& ar) {  // sorting according to current digit.
    int n = ar.size();
    std::vector<uint32_t> position(10, 0);
    for (int i = 0; i < n; ++i) {
        position[(ar[i] / cur_digit) %
                 10]++;  // counting frequency of 0-9 at cur_digit.
    }
    int cur = 0;
    for (int i = 0; i < 10; ++i) {
        int a = position[i];
        position[i] = cur;  // assingning starting position of 0-9.
        cur += a;
    }
    std::vector<uint64_t> temp(n);
    for (int i = 0; i < n; ++i) {
        temp[position[(ar[i] / cur_digit) % 10]] =
            ar[i];  // storing ar[i] in ar[i]'s cur_digit expected position of
                    // this step.
        position[(ar[i] / cur_digit) %
                 10]++;  // incrementing ar[i]'s cur_digit position by 1, as
                         // current place used by ar[i].
    }
    return temp;
}
/**
 * @brief Function to sort vector digit by digit.
 * @param ar - vector to be sorted
 * @returns sorted vector
 */
std::vector<uint64_t> radix(const std::vector<uint64_t>& ar) {
    uint64_t max_ele =
        *max_element(ar.begin(), ar.end());  // returns the max element.
    std::vector<uint64_t> temp = ar;
    for (int i = 1; max_ele / i > 0;
         i *= 10) {  // loop breaks when i > max_ele because no further digits
                     // left to makes changes in aray.
        temp = step_ith(i, temp);
    }
    for (uint64_t i : temp) {
        std::cout << i << " ";
    }
    std::cout << "\n";
    return temp;
}
}  // namespace radix_sort
}  // namespace sorting

/**
 * @brief Function to test the above algorithm
 * @returns none
 */
static void tests() {
    /// Test 1
    std::vector<uint64_t> ar1 = {432, 234, 143, 332, 123};
    ar1 = sorting::radix_sort::radix(ar1);
    assert(std::is_sorted(ar1.begin(), ar1.end()));
    /// Test 2
    std::vector<uint64_t> ar2 = {213, 3214, 123, 111, 112, 142,
                                 133, 132,  32,  12,  113};
    ar2 = sorting::radix_sort::radix(ar2);
    assert(std::is_sorted(ar2.begin(), ar2.end()));
}
/**
 * @brief Main function
 * @returns 0 on exit
 */
int main() {
    tests();  // execute the tests
    return 0;
}

26、Recursive Bubble Sort 递归冒泡排序

​ Recursive Bubble Sort是冒泡排序的一种变体,它使用递归而不是循环来进行排序。 在Recursive Bubble Sort中,我们首先对数组的前n-1个元素进行冒泡排序,然后再对剩下的n-1个元素进行递归地进行相同的过程,直到整个数组都被排序为止。

​ 递归冒泡排序的时间复杂度与普通冒泡排序相同,都是O(n^2),因为它们在最坏情况下需要比较n * (n-1)次。

​ 递归冒泡排序的主要优点是它是一种简单而易于理解的算法,并且它可以对任何类型的数据进行排序,包括自定义类型。此外,由于它是一种递归算法,因此可以很容易地将其转换为并行算法,以利用多核处理器的性能优势。

普通版本的代码

C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
void recursive_bubble_sort(int arr[], int n)
{
    if(n == 1)
        return;

    for(int i=0; i<n-1; i++)
    {
        if(arr[i] > arr[i+1])
            swap(arr[i], arr[i+1]);
    }

    recursive_bubble_sort(arr, n-1);
}

专家版的代码

C++
  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
/**
 * @file
 * @author [Aditya Prakash](https://adityaprakash.tech)
 * @brief This is an implementation of a recursive version of the [Bubble sort algorithm](https://www.geeksforgeeks.org/recursive-bubble-sort/)
 *
 * @details
 * The working principle of the Bubble sort algorithm.

 * Bubble sort is a simple sorting algorithm used to rearrange a set of ascending or descending order elements.
 * Bubble sort gets its name from the fact that data "bubbles" to the top of the dataset.

 * ### Algorithm

 * What is Swap?

 * Swapping two numbers means that we interchange their values.
 * Often, an additional variable is required for this operation. 
 * This is further illustrated in the following:

 * void swap(int x, int y){
 *     int z = x;
 *     x = y;
 *     y = z;
 * }

 * The above process is a typical displacement process.
 * When we assign a value to x, the old value of x is lost.
 * That's why we create a temporary variable z to store the initial value of x.
 * z is further used to assign the initial value of x to y, to complete swapping.

 * Recursion

 * While the recursive method does not necessarily have advantages over iterative
 * versions, but it is useful to enhance the understanding of the algorithm and
 * recursion itself. In Recursive Bubble sort algorithm, we firstly call the
 * function on the entire array, and for every subsequent function call, we exclude
 * the last element. This fixes the last element for that sub-array.Formally, for
 * `ith` iteration, we consider elements up to n-i, where n is the number of
 * elements in the array. Exit condition: n==1; i.e. the sub-array contains only
 * one element.

 * Complexity
 * Time complexity: O(n) best case; O(n²) average case; O(n²) worst case
 * Space complexity: O(n)

 * We need to traverse the array `n * (n-1)` times. However, if the entire array is
 * already sorted, then we need to traverse it only once. Hence, O(n) is the best case
 * complexity
*/

#include <cassert>   /// for assert
#include <iostream>  /// for IO operations
#include <vector>    /// for std::vector
#include <array>     /// for std::array
#include <algorithm> /// for std::is_sorted

/**
 * @namespace sorting
 * @brief Sorting algorithms
 */
namespace sorting {

/**
 * @brief This is an implementation of the recursive_bubble_sort. A vector is passed
 * to the function which is then dereferenced, so that the changes are
 * reflected in the original vector. It also accepts a second parameter of
 * type `int` and name `n`, which is the size of the array.
 * 
 * @tparam T type of data variables in the array
 * @param nums our array of elements.
 * @param n size of the array
 */
template <typename T>
void recursive_bubble_sort(std::vector<T> *nums, uint64_t n) {
    if (n == 1) {  //!< base case; when size of the array is 1
        return;
    }

    for (uint64_t i = 0; i < n - 1; i++) {  //!< iterating over the entire array
        //!< if a larger number appears before the smaller one, swap them.
        if ((*nums)[i] > (*nums)[i + 1]) {
            std::swap((*nums)[i], (*nums)[i + 1]);
        }
    }

    //!< calling the function after we have fixed the last element
    recursive_bubble_sort(nums, n - 1);
}
}  // namespace sorting

/**
 * @brief Self-test implementations
 * @returns void
 */
static void test() {
    // 1st example. Creating an array of type `int`.
    std::cout << "1st test using `int`\n";
    const uint64_t size = 6;
    std::vector<int64_t> arr;
    // populating the array
    arr.push_back(22);
    arr.push_back(46);
    arr.push_back(94);
    arr.push_back(12);
    arr.push_back(37);
    arr.push_back(63);
    // array populating ends

    sorting::recursive_bubble_sort(&arr, size);
    assert(std::is_sorted(std::begin(arr), std::end(arr)));
    std::cout << " 1st test passed!\n";
    // printing the array
    for (uint64_t i = 0; i < size; i++) {
        std::cout << arr[i] << ", ";
    }
    std::cout << std::endl;

    // 2nd example. Creating an array of type `double`.
    std::cout << "2nd test using doubles\n";
    std::vector<double> double_arr;

    // populating the array
    double_arr.push_back(20.4);
    double_arr.push_back(62.7);
    double_arr.push_back(12.2);
    double_arr.push_back(43.6);
    double_arr.push_back(74.1);
    double_arr.push_back(57.9);
    // array populating ends

    sorting::recursive_bubble_sort(&double_arr, size);
    assert(std::is_sorted(std::begin(double_arr), std::end(double_arr)));
    std::cout << " 2nd test passed!\n";
    // printing the array
    for (uint64_t i = 0; i < size; i++) {
        std::cout << double_arr[i] << ", ";
    }
    std::cout << std::endl;

}

/**
 * @brief Main function
 * @returns 0 on exit
 */
int main() { 
    test();  // run self-test implementations
    return 0;
}

27、Selection Sort 选择排序

​ 选择排序(Selection Sort)是一种简单的排序算法。它的基本思想是:每次在未排序的数组中选择最小的元素,并将其放置在已排序数组的末尾。具体地,选择排序通过重复从未排序的数组中找到最小的元素,然后将其交换到已排序的数组的末尾来工作。

​ 选择排序的时间复杂度是O(n^2),其中n是待排序数组的长度。虽然选择排序的时间复杂度与冒泡排序相同,但在实践中,它通常比冒泡排序要快,因为它减少了元素之间的交换次数。

普通版本的代码

C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
void selection_sort(int arr[], int n)
{
    int i, j, min_idx;

    // 在未排序的部分中选择最小的元素并将其放在已排序的末尾
    for (i = 0; i < n-1; i++)
    {
        min_idx = i;
        for (j = i+1; j < n; j++)
            if (arr[j] < arr[min_idx])
                min_idx = j;
        swap(arr[min_idx], arr[i]);
    }
}

专家版的代码

C++
 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
// Selection Sort

#include <iostream>
using namespace std;

int main() {
    int Array[6];
    cout << "\nEnter any 6 Numbers for Unsorted Array : ";

    // Input
    for (int i = 0; i < 6; i++) {
        cin >> Array[i];
    }

    // Selection Sorting
    for (int i = 0; i < 6; i++) {
        int min = i;
        for (int j = i + 1; j < 6; j++) {
            if (Array[j] < Array[min]) {
                min = j;  // Finding the smallest number in Array
            }
        }
        int temp = Array[i];
        Array[i] = Array[min];
        Array[min] = temp;
    }

    // Output
    cout << "\nSorted Array : ";
    for (int i = 0; i < 6; i++) {
        cout << Array[i] << "\t";
    }
}

28、Shell Sort 希尔排序

​ 希尔排序(Shell Sort)是一种高效的排序算法,它是直接插入排序算法的改进版。其基本思想是:将待排序的元素分为若干组,对每组内的元素进行直接插入排序,然后逐步减少组的数量,直到只剩下一组为止。在排序过程中,对于每个分组内的元素,通过插入排序使得它们逐渐有序,然后再将它们合并成一个有序的序列。

​ 希尔排序的时间复杂度为O(n^2),其中n是待排序数组的长度。不过,它的实际表现比插入排序要好得多,因为它将待排序数组分组排序,这样每个元素都可以在更短的时间内到达最终的位置。

普通版本的代码

Text Only
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
void shell_sort(int arr[], int n)
{
    // 生成增量序列
    for (int gap = n/2; gap > 0; gap /= 2)
    {
        // 对每个子序列进行插入排序
        for (int i = gap; i < n; i++)
        {
            int temp = arr[i];
            int j;
            for (j = i; j >= gap && arr[j-gap] > temp; j -= gap)
                arr[j] = arr[j-gap];
            arr[j] = temp;
        }
    }
}

​ 在这个实现中,我们首先选择一个增量序列,然后对每个子序列进行插入排序。这里我们使用了插入排序的实现,但是每次比较和交换的间隔是增量gap。逐步缩小增量序列,直到只有一个子序列为止。

专家版的代码

C++
 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
#include <iostream>

int main() {
    int size = 10;
    int* array = new int[size];
    // Input
    std::cout << "\nHow many numbers do want to enter in unsorted array : ";
    std::cin >> size;
    std::cout << "\nEnter the numbers for unsorted array : ";
    for (int i = 0; i < size; i++) {
        std::cin >> array[i];
    }

    // Sorting
    for (int i = size / 2; i > 0; i = i / 2) {
        for (int j = i; j < size; j++) {
            for (int k = j - i; k >= 0; k = k - i) {
                if (array[k] < array[k + i]) {
                    break;
                } else {
                    int temp = array[k + i];
                    array[k + i] = array[k];
                    array[k] = temp;
                }
            }
        }
    }

    // Output
    std::cout << "\nSorted array : ";
    for (int i = 0; i < size; ++i) {
        std::cout << array[i] << "\t";
    }

    delete[] array;
    return 0;
}
C++
  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
/**
 * \file
 * \brief [Shell sort](https://en.wikipedia.org/wiki/Shell_sort) algorithm
 * \author [Krishna Vedala](https://github.com/kvedala)
 */
#include <cassert>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <utility>  // for std::swap
#include <vector>

/** pretty print array
 * \param[in] arr array to print
 * \param[in] LEN length of array to print
 */
template <class T>
void show_data(T *arr, size_t LEN) {
    size_t i;

    for (i = 0; i < LEN; i++) {
        std::cout << arr[i] << ", ";
    }
    std::cout << std::endl;
}

/** pretty print array
 * \param[in] arr array to print
 * \param[in] N length of array to print
 */
template <typename T, size_t N>
void show_data(T (&arr)[N]) {
    show_data(arr, N);
}

/** \namespace sorting
 * \brief Sorting algorithms
 */
namespace sorting {
/**
 * Optimized algorithm - takes half the time by utilizing
 * Mar
 **/
template <typename T>
void shell_sort(T *arr, size_t LEN) {
    const unsigned int gaps[] = {701, 301, 132, 57, 23, 10, 4, 1};
    const unsigned int gap_len = 8;
    size_t i, j, g;

    for (g = 0; g < gap_len; g++) {
        unsigned int gap = gaps[g];
        for (i = gap; i < LEN; i++) {
            T tmp = arr[i];

            for (j = i; j >= gap && (arr[j - gap] - tmp) > 0; j -= gap) {
                arr[j] = arr[j - gap];
            }

            arr[j] = tmp;
        }
    }
}

/** function overload - when input array is of a known length array type
 */
template <typename T, size_t N>
void shell_sort(T (&arr)[N]) {
    shell_sort(arr, N);
}

/** function overload - when input array is of type std::vector,
 * simply send the data content and the data length to the above function.
 */
template <typename T>
void shell_sort(std::vector<T> *arr) {
    shell_sort(arr->data(), arr->size());
}

}  // namespace sorting

using sorting::shell_sort;

/**
 * function to compare sorting using cstdlib's qsort
 **/
template <typename T>
int compare(const void *a, const void *b) {
    T arg1 = *static_cast<const T *>(a);
    T arg2 = *static_cast<const T *>(b);

    if (arg1 < arg2)
        return -1;
    if (arg1 > arg2)
        return 1;
    return 0;

    //  return (arg1 > arg2) - (arg1 < arg2); // possible shortcut
    //  return arg1 - arg2; // erroneous shortcut (fails if INT_MIN is present)
}

/**
 * Test implementation of shell_sort on integer arrays by comparing results
 * against std::qsort.
 */
void test_int(const int NUM_DATA) {
    // int array = new int[NUM_DATA];
    int *data = new int[NUM_DATA];
    int *data2 = new int[NUM_DATA];
    // int array2 = new int[NUM_DATA];
    int range = 1800;

    for (int i = 0; i < NUM_DATA; i++)
        data[i] = data2[i] = (std::rand() % range) - (range >> 1);

    /* sort using our implementation */
    std::clock_t start = std::clock();
    shell_sort(data, NUM_DATA);
    std::clock_t end = std::clock();
    double elapsed_time = static_cast<double>(end - start) / CLOCKS_PER_SEC;
    std::cout << "Time spent sorting using shell_sort2: " << elapsed_time
              << "s\n";

    /* sort using std::qsort */
    start = std::clock();
    std::qsort(data2, NUM_DATA, sizeof(data2[0]), compare<int>);
    end = std::clock();

    elapsed_time = static_cast<double>(end - start) / CLOCKS_PER_SEC;
    std::cout << "Time spent sorting using std::qsort: " << elapsed_time
              << "s\n";

    for (int i = 0; i < NUM_DATA; i++) {
        assert(data[i] == data2[i]);  // ensure that our sorting results match
                                      // the standard results
    }

    delete[] data;
    delete[] data2;
}

/**
 * Test implementation of shell_sort on float arrays by comparing results
 * against std::qsort.
 */
void test_f(const int NUM_DATA) {
    // int array = new int[NUM_DATA];
    float *data = new float[NUM_DATA];
    float *data2 = new float[NUM_DATA];
    // int array2 = new int[NUM_DATA];
    int range = 1000;

    for (int i = 0; i < NUM_DATA; i++) {
        data[i] = data2[i] = ((std::rand() % range) - (range >> 1)) / 100.;
    }

    /* sort using our implementation */
    std::clock_t start = std::clock();
    shell_sort(data, NUM_DATA);
    std::clock_t end = std::clock();
    double elapsed_time = static_cast<double>(end - start) / CLOCKS_PER_SEC;
    std::cout << "Time spent sorting using shell_sort2: " << elapsed_time
              << "s\n";

    /* sort using std::qsort */
    start = std::clock();
    std::qsort(data2, NUM_DATA, sizeof(data2[0]), compare<float>);
    end = std::clock();

    elapsed_time = static_cast<double>(end - start) / CLOCKS_PER_SEC;
    std::cout << "Time spent sorting using std::qsort: " << elapsed_time
              << "s\n";

    for (int i = 0; i < NUM_DATA; i++) {
        assert(data[i] == data2[i]);  // ensure that our sorting results match
                                      // the standard results
    }

    delete[] data;
    delete[] data2;
}

/** Main function */
int main(int argc, char *argv[]) {
    // initialize random number generator - once per program
    std::srand(std::time(NULL));

    test_int(100);  // test with sorting random array of 100 values
    std::cout << "Test 1 - 100 int values - passed. \n";
    test_int(1000);  // test with sorting random array of 1000 values
    std::cout << "Test 2 - 1000 int values - passed.\n";
    test_int(10000);  // test with sorting random array of 10000 values
    std::cout << "Test 3 - 10000 int values - passed.\n";

    test_f(100);  // test with sorting random array of 100 values
    std::cout << "Test 1 - 100 float values - passed. \n";
    test_f(1000);  // test with sorting random array of 1000 values
    std::cout << "Test 2 - 1000 float values - passed.\n";
    test_f(10000);  // test with sorting random array of 10000 values
    std::cout << "Test 3 - 10000 float values - passed.\n";

    int i, NUM_DATA;

    if (argc == 2)
        NUM_DATA = atoi(argv[1]);
    else
        NUM_DATA = 200;

    // int array = new int[NUM_DATA];
    int *data = new int[NUM_DATA];
    // int array2 = new int[NUM_DATA];
    int range = 1800;

    std::srand(time(NULL));
    for (i = 0; i < NUM_DATA; i++) {
        // allocate random numbers in the given range
        data[i] = (std::rand() % range) - (range >> 1);
    }

    std::cout << "Unsorted original data: " << std::endl;
    show_data(data, NUM_DATA);
    std::clock_t start = std::clock();
    shell_sort(data, NUM_DATA);  // perform sorting
    std::clock_t end = std::clock();

    std::cout << std::endl
              << "Data Sorted using custom implementation: " << std::endl;
    show_data(data, NUM_DATA);

    double elapsed_time = (end - start) * 1.f / CLOCKS_PER_SEC;
    std::cout << "Time spent sorting: " << elapsed_time << "s\n" << std::endl;

    delete[] data;
    return 0;
}

29、Slow Sort

​ Slow Sort是一种排序算法,其思想是模拟慢速排序(Slow sort),即使得排序过程变得非常缓慢,但最终能够得到正确的排序结果。

​ Slow Sort的基本思想是:先将待排序的序列的中间部分进行递归地Slow Sort,然后再将序列两端的元素交换,接着再递归地Slow Sort序列的两端部分。这个过程不断重复,直到序列变得有序为止。

​ 虽然Slow Sort的时间复杂度非常高(O(n^4/ln(n))),但它在某些特定情况下表现得非常优秀。例如,对于已经有序的序列,Slow Sort的时间复杂度是O(n)。

普通版本的代码

C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
void slow_sort(int arr[], int l, int r)
{
    if (l >= r) return;

    int m = l + (r-l)/2;
    slow_sort(arr, l, m);
    slow_sort(arr, m+1, r);

    if (arr[r] < arr[m])
        swap(arr[r], arr[m]);

    slow_sort(arr, l, r-1);
}

​ 这个实现中,我们首先将序列分成两个部分,然后递归地Slow Sort每个部分。接着我们比较序列的两端元素,如果右端元素小于中间元素,则交换它们。最后我们递归地Slow Sort序列的两端部分,直到序列变得有序。

专家版的代码

C++
 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
// Returns the sorted vector after performing SlowSort
// It is a sorting algorithm that is of humorous nature and not useful.
// It's based on the principle of multiply and surrender, a tongue-in-cheek joke
// of divide and conquer. It was published in 1986 by Andrei Broder and Jorge
// Stolfi in their paper Pessimal Algorithms and Simplexity Analysis. This
// algorithm multiplies a single problem into multiple subproblems It is
// interesting because it is provably the least efficient sorting algorithm that
// can be built asymptotically, and with the restriction that such an algorithm,
// while being slow, must still all the time be working towards a result.

#include <iostream>

void SlowSort(int a[], int i, int j) {
    if (i >= j)
        return;
    int m = i + (j - i) / 2;  // midpoint, implemented this way to avoid
                              // overflow
    int temp;
    SlowSort(a, i, m);
    SlowSort(a, m + 1, j);
    if (a[j] < a[m]) {
        temp = a[j];  // swapping a[j] & a[m]
        a[j] = a[m];
        a[m] = temp;
    }
    SlowSort(a, i, j - 1);
}

// Sample Main function

int main() {
    int size;
    std::cout << "\nEnter the number of elements : ";

    std::cin >> size;

    int *arr = new int[size];

    std::cout << "\nEnter the unsorted elements : ";

    for (int i = 0; i < size; ++i) {
        std::cout << "\n";
        std::cin >> arr[i];
    }

    SlowSort(arr, 0, size);

    std::cout << "Sorted array\n";

    for (int i = 0; i < size; ++i) {
        std::cout << arr[i] << " ";
    }

    delete[] arr;
    return 0;
}

30、Strand Sort

​ Strand Sort是一种比较少见的排序算法,它基于串联操作(strand operation),该操作是指将一个序列与另一个序列连接起来,形成一个新的序列。在Strand Sort中,我们不断地将元素从待排序序列中提取出来,然后将它们插入到一个已排序的序列中,最终得到有序序列。

Strand Sort的基本思想是:

  1. 将待排序的序列分成若干个无序的子序列。
  2. 从每个子序列中提取出一个元素,将它们串联起来形成一个新的序列。
  3. 重复步骤2,直到待排序序列为空。
  4. 对新的序列进行递归排序。

普通版本的代码

C++
 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
void strand_sort(list<int>& input, list<int>& output)
{
    if (input.empty()) return;

    list<int> sublist;
    sublist.push_back(input.front());
    input.pop_front();

    for (auto it = input.begin(); it != input.end(); )
    {
        if (*it > sublist.back())
        {
            sublist.push_back(*it);
            it = input.erase(it);
        }
        else
        {
            ++it;
        }
    }

    strand_sort(input, output);

    auto s_it = sublist.begin();
    auto o_it = output.begin();

    while (s_it != sublist.end() && o_it != output.end())
    {
        if (*s_it < *o_it)
        {
            input.push_back(*s_it);
            ++s_it;
        }
        else
        {
            input.push_back(*o_it);
            ++o_it;
        }
    }

    input.splice(input.end(), sublist, s_it, sublist.end());
    input.splice(input.end(), output, o_it, output.end());
}

专家版的代码

C++
 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
/**
 * @file strand_sort.cpp
 * @brief Implementation of [Strand Sort](https://en.wikipedia.org/wiki/Strand_sort) algorithm.
 *
 * @details
 * Strand Sort is a sorting algorithm that works in \f$O(n)\f$ time if list is already sorted and works in \f$O(n^2)\f$ in worst case.
 * 
 * It is passed over the array to be sorted once and the ascending (sequential) numbers are taken.
 * After the first iteration, the sequential sub-array is put on the empty sorted array.
 * The main sequence is passed over again and a new sub-sequence is created in order.
 * Now that the sorted array is not empty, the newly extracted substring is merged with the sorted array.
 * Repeat types 3 and 4 until the sub-sequence and main sequence are empty.
 * 
 * @author [Mertcan Davulcu](https://github.com/mertcandav)
 */
#include <iostream>
#include <list>

/**
 * @namespace sorting
 * @brief Sorting algorithms
 */
namespace sorting {
    /**
    * @namespace strand
    * @brief Functions for [Strand Sort](https://en.wikipedia.org/wiki/Strand_sort) algorithm
    */
    namespace strand {
        /**
        * @brief Apply sorting
        * @tparam element type of list
        * @param lst List to be sorted
        * @returns Sorted list<T> instance
        */
        template <typename T>
        std::list<T> strand_sort(std::list<T> lst) {
            if (lst.size() < 2) { // Returns list if empty or contains only one element
                return lst; // Returns list
            }
            std::list<T> result; // Define new "result" named list instance.
            std::list<T> sorted; // Define new "sorted" named list instance.
            while(!lst.empty()) /* if lst is not empty */ {
                sorted.push_back(lst.front()); // Adds the first element of "lst" list to the bottom of the "sorted" list.
                lst.pop_front(); // Remove first element of "lst" list.
                for (auto it = lst.begin(); it != lst.end(); ) { // Return the loop as long as the current iterator is not equal to the last literator of the "lst" list.
                    if (sorted.back() <= *it) { // If the last reference of the "sorted" list is less than or equal to the current iterator reference.
                        sorted.push_back(*it); // Adds the iterator retrieved in the loop under the "sorted" list.
                        it = lst.erase(it); // Deletes the element with the current iterator and assigns the deleted element to the iterator.
                    } else {
                        it++; // Next iterator.
                    }
                }
                result.merge(sorted); // Merge "result" list with "sorted" list.
            }
            return result; // Returns sorted list
        }
    }  // namespace strand
}  // namespace sorting

/**
 * @brief Function for testing
 * @return N/A
 */
static void test() {
    std::list<int> lst = { -333, 525, 1, 0, 94, 52, 33 };

    std::cout << "Before: ";
    for(auto item: lst) {
        std::cout << item << " ";
    }

    lst = sorting::strand::strand_sort(lst); // Sort list.

    std::cout << "\nAfter: ";
    for(auto item: lst) {
        std::cout << item << " ";
    }
}

/**
 * @brief Main function
 * @returns 0 on exit
 */
int main() {
    test();
    return 0;
}

31、Swap Sort

​ Swap Sort是一种排序算法,其思想是通过反复地执行交换操作来将元素按照升序或降序排列。它是一种比较简单但不是很高效的排序算法,通常只用于教学目的。

​ Swap Sort的实现比较直接,它基本上就是一个双重循环,每次将相邻的两个元素进行比较,如果它们的顺序不正确,则进行交换。这个过程会一直进行下去,直到所有元素都排列好为止。

​ Swap Sort算法的时间复杂度为O(n^2),与冒泡排序和选择排序的时间复杂度相同。由于它的效率较低,因此在实际应用中很少使用。但是,它可以作为一种基础的排序算法,用于教学和演示。

普通版本的代码

C++
1
2
3
4
5
6
7
8
9
void swapSort(int arr[], int n) {
    for (int i = 0; i < n - 1; i++) {
        for (int j = i + 1; j < n; j++) {
            if (arr[i] > arr[j]) {
                swap(arr[i], arr[j]);
            }
        }
    }
}

​ 这段代码使用了两个嵌套的循环来遍历整个数组。在内部循环中,它比较了当前元素和后面的每个元素,并在需要时进行交换。循环结束后,数组中的所有元素都按照升序排列。

专家版的代码

C++
 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
// C++ program to find minimum number of swaps required to sort an array
#include <algorithm>
#include <iostream>
#include <utility>
#include <vector>

// Function returns the minimum number of swaps
// required to sort the array
int minSwaps(int arr[], int n) {
    // Create an array of pairs where first
    // element is array element and second element
    // is position of first element
    std::pair<int, int> *arrPos = new std::pair<int, int>[n];
    for (int i = 0; i < n; i++) {
        arrPos[i].first = arr[i];
        arrPos[i].second = i;
    }

    // Sort the array by array element values to
    // get right position of every element as second
    // element of pair.
    std::sort(arrPos, arrPos + n);

    // To keep track of visited elements. Initialize
    // all elements as not visited or false.
    std::vector<bool> vis(n, false);

    // Initialize result
    int ans = 0;

    // Traverse array elements
    for (int i = 0; i < n; i++) {
        // already swapped and corrected or
        // already present at correct pos
        if (vis[i] || arrPos[i].second == i)
            continue;

        // find out the number of node in
        // this cycle and add in ans
        int cycle_size = 0;
        int j = i;
        while (!vis[j]) {
            vis[j] = 1;

            // move to next node
            j = arrPos[j].second;
            cycle_size++;
        }

        // Update answer by adding current cycle.
        if (cycle_size > 0) {
            ans += (cycle_size - 1);
        }
    }

    delete[] arrPos;

    // Return result
    return ans;
}

// program to test
int main() {
    int arr[] = {6, 7, 8, 1, 2, 3, 9, 12};
    int n = (sizeof(arr) / sizeof(int));
    std::cout << minSwaps(arr, n);
    return 0;
}

32、Tim Sort

​ Tim Sort是一种用于排序的高效算法,它是由Tim Peters在2002年设计的。它结合了合并排序和插入排序的思想,可以在大多数情况下快速地对任意类型的数据进行排序。

​ Tim Sort的主要思想是使用一个自适应的算法,在数组的各个部分使用不同的排序策略。对于小的数组,使用插入排序可以更有效地排序。对于较大的数组,合并排序更适合。

​ Tim Sort的实现方法是首先将数组分成大小为M的块,每个块都使用插入排序进行排序。然后将排好序的块合并为一个更大的排好序的数组。不断重复这个过程,每次将块的大小加倍,直到整个数组被完全排序为止。

​ Tim Sort的时间复杂度为O(n log n),空间复杂度为O(n)。在实践中,Tim Sort通常比其他排序算法更快,尤其是对于已经部分排序的数组。

普通版本的代码

C++
 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
// 定义块大小为32
const int RUN = 32;

// 插入排序算法
void insertionSort(int arr[], int left, int right) {
    for (int i = left + 1; i <= right; i++) {
        int temp = arr[i];
        int j = i - 1;
        while (j >= left && arr[j] > temp) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = temp;
    }
}

// 合并两个排好序的数组
void merge(int arr[], int l, int m, int r) {
    int len1 = m - l + 1, len2 = r - m;
    int left[len1], right[len2];
    for (int i = 0; i < len1; i++)
        left[i] = arr[l + i];
    for (int i = 0; i < len2; i++)
        right[i] = arr[m + 1 + i];

    int i = 0, j = 0, k = l;
    while (i < len1 && j < len2) {
        if (left[i] <= right[j])
            arr[k++] = left[i++];
        else
            arr[k++] = right[j++];
    }

    while (i < len1)
        arr[k++] = left[i++];

    while (j < len2)
        arr[k++] = right[j++];
}

// Tim Sort主算法
void timSort(int arr[], int n) {
    for (int i = 0; i < n; i += RUN) {
        insertionSort(arr, i, min((i + RUN - 1), (n - 1)));
    }

    for (int size = RUN; size < n; size = 2 * size) {
        for (int left = 0; left < n; left += 2 * size) {
            int mid = left + size - 1;
            int right = min((left + 2 * size - 1), (n - 1));
            merge(arr, left, mid, right);
        }
    }
}

专家版的代码

C++
  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
// C++ program to perform TimSort.
#include <algorithm>
#include <iostream>

const int RUN = 32;

// this function sorts array from left index to to right index which is of size
// atmost RUN
void insertionSort(int arr[], int left, int right) {
    for (int i = left + 1; i <= right; i++) {
        int temp = arr[i];
        int j = i - 1;
        while (arr[j] > temp && j >= left) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = temp;
    }
}

// merge function merges the sorted runs
void merge(int arr[], int l, int m, int r) {
    // original array is broken in two parts, left and right array
    int len1 = m - l + 1, len2 = r - m;
    int *left = new int[len1], *right = new int[len2];
    for (int i = 0; i < len1; i++) left[i] = arr[l + i];
    for (int i = 0; i < len2; i++) right[i] = arr[m + 1 + i];

    int i = 0;
    int j = 0;
    int k = l;

    // after comparing, we merge those two array in larger sub array
    while (i < len1 && j < len2) {
        if (left[i] <= right[j]) {
            arr[k] = left[i];
            i++;
        } else {
            arr[k] = right[j];
            j++;
        }
        k++;
    }

    // copy remaining elements of left, if any
    while (i < len1) {
        arr[k] = left[i];
        k++;
        i++;
    }

    // copy remaining element of right, if any
    while (j < len2) {
        arr[k] = right[j];
        k++;
        j++;
    }
    delete[] left;
    delete[] right;
}

// iterative Timsort function to sort the array[0...n-1] (similar to merge sort)
void timSort(int arr[], int n) {
    // Sort individual subarrays of size RUN
    for (int i = 0; i < n; i += RUN)
        insertionSort(arr, i, std::min((i + 31), (n - 1)));

    // start merging from size RUN (or 32). It will merge to form size 64, then
    // 128, 256 and so on ....
    for (int size = RUN; size < n; size = 2 * size) {
        // pick starting point of left sub array. We are going to merge
        // arr[left..left+size-1] and arr[left+size, left+2*size-1] After every
        // merge, we increase left by 2*size
        for (int left = 0; left < n; left += 2 * size) {
            // find ending point of left sub array
            // mid+1 is starting point of right sub array
            int mid = left + size - 1;
            int right = std::min((left + 2 * size - 1), (n - 1));

            // merge sub array arr[left.....mid] & arr[mid+1....right]
            merge(arr, left, mid, right);
        }
    }
}

// utility function to print the Array
void printArray(int arr[], int n) {
    for (int i = 0; i < n; i++) printf("%d  ", arr[i]);
    std::cout << std::endl;
}

// Driver program to test above function
int main() {
    int arr[] = {5, 21, 7, 23, 19};
    int n = sizeof(arr) / sizeof(arr[0]);
    printf("Given Array is\n");
    printArray(arr, n);

    timSort(arr, n);

    printf("After Sorting Array is\n");
    printArray(arr, n);
    return 0;
}

33、Wave Sort

​ Wave Sort是一种简单的排序算法,其思想是通过将数组元素排列成一个波浪形的形状来进行排序。在排序过程中,它会比较相邻的元素并交换它们的位置,以便确保较小的元素位于前面的位置,而较大的元素则位于后面的位置。这个过程会依次应用于数组中的每个元素,直到整个数组都被排列成一个波浪形的形状。

​ Wave Sort的时间复杂度为O(n),空间复杂度为O(1)。虽然它的时间复杂度很小,但由于其只能在一定范围内对数组进行排序,因此它并不是所有情况下最优的选择。它的优点是易于理解和实现,并且可以在很短的时间内对小型数据集进行排序。

普通版本的代码

C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
void waveSort(int arr[], int n) {
    for (int i = 0; i < n; i += 2) {
        if (i > 0 && arr[i] < arr[i-1]) {
            swap(arr[i], arr[i-1]);
        }
        if (i < n-1 && arr[i] < arr[i+1]) {
            swap(arr[i], arr[i+1]);
        }
    }
}

专家版的代码

C++
 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
/**
 * @file
 * @brief Implementation of the [Wave
 * sort](https://www.geeksforgeeks.org/sort-array-wave-form-2/) algorithm
 * @details
 * Wave Sort is a sorting algorithm that works in \f$O(nlogn)\f$ time assuming
 * the sort function used works in \f$O(nlogn)\f$ time.
 * @author [Swastika Gupta](https://github.com/Swastyy)
 */

#include <algorithm>  /// for std::is_sorted, std::swap
#include <cassert>    /// for assert
#include <iostream>   /// for IO operations
#include <vector>     /// for std::vector

/**
 * @namespace sorting
 * @brief Sorting algorithms
 */
namespace sorting {
/**
 * @namespace wave_sort
 * @brief Functions for the [Wave
 * sort](https://www.geeksforgeeks.org/sort-array-wave-form-2/) implementation
 */
namespace wave_sort {
/**
 * @brief The main function implements that implements the Wave Sort algorithm
 * @tparam T type of array
 * @param in_arr array to be sorted
 * @returns arr the wave sorted array
 */
template <typename T>
std::vector<T> waveSort(const std::vector<T> &in_arr, int64_t n) {
    std::vector<T> arr(in_arr);

    for (int64_t i = 0; i < n; i++) {
        arr[i] = in_arr[i];
    }
    std::sort(arr.begin(), arr.end());
    for (int64_t i = 0; i < n - 1; i += 2) {  // swap all the adjacent elements
        std::swap(arr[i], arr[i + 1]);
    }
    return arr;
}
}  // namespace wave_sort
}  // namespace sorting

/**
 * @brief Self-test implementations
 * @returns void
 */
static void test() {
    // [10, 90, 49, 2, 1, 5, 23] return [2, 1, 10, 5, 49, 23, 90]
    std::vector<int64_t> array1 = {10, 90, 49, 2, 1, 5, 23};
    std::cout << "Test 1... ";
    std::vector<int64_t> arr1 = sorting::wave_sort::waveSort(array1, 7);
    const std::vector<int64_t> o1 = {2, 1, 10, 5, 49, 23, 90};
    assert(arr1 == o1);
    std::cout << "passed" << std::endl;

    // [1, 3, 4, 2, 7, 8] return [2, 1, 4, 3, 8, 7]
    std::vector<int64_t> array2 = {1, 3, 4, 2, 7, 8};
    std::cout << "Test 2... ";
    std::vector<int64_t> arr2 = sorting::wave_sort::waveSort(array2, 6);
    const std::vector<int64_t> o2 = {2, 1, 4, 3, 8, 7};
    assert(arr2 == o2);
    std::cout << "passed" << std::endl;

    // [3, 3, 3, 3] return [3, 3, 3, 3]
    std::vector<int64_t> array3 = {3, 3, 3, 3};
    std::cout << "Test 3... ";
    std::vector<int64_t> arr3 = sorting::wave_sort::waveSort(array3, 4);
    const std::vector<int64_t> o3 = {3, 3, 3, 3};
    assert(arr3 == o3);
    std::cout << "passed" << std::endl;

    // [9, 4, 6, 8, 14, 3] return [4, 3, 8, 6, 14, 9]
    std::vector<int64_t> array4 = {9, 4, 6, 8, 14, 3};
    std::cout << "Test 4... ";
    std::vector<int64_t> arr4 = sorting::wave_sort::waveSort(array4, 6);
    const std::vector<int64_t> o4 = {4, 3, 8, 6, 14, 9};
    assert(arr4 == o4);
    std::cout << "passed" << std::endl;
}

/**
 * @brief Main function
 * @returns 0 on exit
 */
int main() {
    test();  // run self-test implementations
    return 0;
}

34、Wiggle Sort

​ Wiggle Sort是一种比较巧妙的排序算法,其基本思想是将数组中的元素按照大小交替排列,也就是说,数组中的元素需要满足一定的条件:对于所有的i,如果i为奇数,则arr[i]>=arr[i-1] && arr[i]>=arr[i+1];如果i为偶数,则arr[i]<=arr[i-1] && arr[i]<=arr[i+1]。

Wiggle Sort算法的具体实现方法如下:

  1. 遍历数组,对于所有奇数位置i,如果arr[i] < arr[i-1],则交换arr[i]和arr[i-1];

  2. 对于所有偶数位置i,如果arr[i] > arr[i-1],则交换arr[i]和arr[i-1];

  3. 重复执行上述两个步骤,直到数组中的所有元素满足上述条件。

由于每次操作都会使得满足条件的元素位置增加,因此Wiggle Sort算法的时间复杂度为O(n),空间复杂度为O(1)。

需要注意的是,Wiggle Sort算法并不保证排序后的数组是唯一的,只要满足上述条件即可。因此,可能存在多种排序结果。

普通版本的代码

C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
void wiggleSort(vector<int>& nums) {
    int n = nums.size();
    if (n < 2) {
        return;
    }
    for (int i = 1; i < n; ++i) {
        if (((i & 1) && nums[i] < nums[i - 1]) || (!(i & 1) && nums[i] > nums[i - 1])) {
            swap(nums[i], nums[i - 1]);
        }
    }
}

该算法的时间复杂度为\(O(n)\),其中\(n\)为数组的长度。

专家版的代码

C++
  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
/**
 * \addtogroup sorting Sorting Algorithms
 * @{
 * \file
 * \brief [Wiggle Sort Algorithm]
 * (https://leetcode.com/problems/wiggle-sort-ii/) Implementation
 *
 * \author [Roshan Kanwar](http://github.com/roshan0708)
 *
 * \details
 * Wiggle Sort sorts the array into a wave like array.
 * An array ‘arr[0..n-1]’ is sorted in wave form,
 * if arr[0] >= arr[1] <= arr[2] >= arr[3] <= arr[4] >= …..
 *
 * \example
 * arr = [1,1,5,6,1,4], after wiggle sort arr will become equal to [1,1,6,1,5,4]
 * arr = [2,8,9,1,7], after wiggle sort arr will become equal to [8,2,9,1,7]
 */

#include <algorithm>
#include <cassert>
#include <ctime>
#include <iostream>  /// for io operations
#include <vector>

/**
 * @namespace sorting
 * @brief Sorting algorithms
 */
namespace sorting {
/**
 * @namespace wiggle_sort
 * @brief Functions for [Wiggle
 * Sort](https://leetcode.com/problems/wiggle-sort-ii/) algorithm
 */
namespace wiggle_sort {

/**
 *
 * @brief Function used for sorting the elements in wave form.
 * @details
 * Checking whether the even indexed elements are greater than
 * their adjacent odd elements.
 * Traversing all even indexed elements of the input arr.
 * If current element is smaller than the previous odd element, swap them.
 * If current element is smaller than the next odd element, swap them.
 *
 * @param arr input array (unsorted elements)
 *
 */
template <typename T>  // this allows to have vectors of ints, double, float,
                       // etc
                       std::vector<T> wiggleSort(const std::vector<T> &arr) {
    uint32_t size = arr.size();

    std::vector<T> out(
        arr);  // create a copy of input vector. this way, the original input
               // vector does not get modified. a sorted array is is returned.

    for (int i = 0; i < size; i += 2) {
        if (i > 0 && out[i - 1] > out[i]) {
            std::swap(out[i], out[i - 1]);  // swapping the two values
        }

        if (i < size - 1 && out[i] < out[i + 1]) {
            std::swap(out[i], out[i + 1]);  // swapping the two values
        }
    }

    return out;  // returns the sorted vector
}
}  // namespace wiggle_sort
}  // namespace sorting

/**
 *
 * @brief Utility function used for printing the elements.
 * Prints elements of the array after they're sorted using wiggle sort
 * algorithm.
 *
 * @param arr array containing the sorted elements
 *
 */
template <typename T>
static void displayElements(const std::vector<T> &arr) {
    uint32_t size = arr.size();

    std::cout << "Sorted elements are as follows: ";

    std::cout << "[";

    for (int i = 0; i < size; i++) {
        std::cout << arr[i];
        if (i != size - 1) {
            std::cout << ", ";
        }
    }

    std::cout << "]" << std::endl;
}

/**
 * Test function
 * @returns void
 */
static void test() {
    std::srand(std::time(nullptr));  // initialize random number generator

    std::vector<float> data1(100);
    for (auto &d : data1) {  // generate random numbers between -5.0 and 4.99
        d = float(std::rand() % 1000 - 500) / 100.f;
    }

    std::vector<float> sorted = sorting::wiggle_sort::wiggleSort<float>(data1);

    displayElements(sorted);

    for (uint32_t j = 0; j < data1.size(); j += 2) {
        assert(data1[j] <= data1[j + 1] &&
               data1[j + 1] >= data1[j + 2]);  // check the validation condition
    }

    std::cout << "Test 1 passed\n";
}

/** Driver Code */
int main() {
    test();
    return 0;
}

/** @} */