十大排序算法

30 阅读3分钟

归并排序

归并排序是一种分治算法,它将待排序的序列不断地分割成两个子序列,直到每个子序列只有一个元素,然后将这些子序列合并成一个有序序列。

下面分别介绍归并排序的递归和非递归算法。

递归算法:

  1. 将待排序的序列平均分割成两半。
  2. 对左半部分和右半部分分别进行递归调用归并排序。
  3. 对左右两个有序序列进行合并,得到完整的有序序列。

递归算法的实现比较简单,代码如下:

public static void mergeSort(int[] arr, int left, int right) {
    if (left < right) {
        int mid = (left + right) / 2;
        mergeSort(arr, left, mid);
        mergeSort(arr, mid + 1, right);
        merge(arr, left, mid, right);
    }
}

public static void merge(int[] arr, int left, int mid, int right) {
    int[] temp = new int[right - left + 1];
    int i = left;
    int j = mid + 1;
    int k = 0;
    while (i <= mid && j <= right) {
        if (arr[i] < arr[j]) {
            temp[k++] = arr[i++];
        } else {
            temp[k++] = arr[j++];
        }
    }
    while (i <= mid) {
        temp[k++] = arr[i++];
    }
    while (j <= right) {
        temp[k++] = arr[j++];
    }
    for (int m = 0; m < temp.length; m++) {
        arr[left + m] = temp[m];
    }
}

非递归算法:

  1. 将待排序的序列按照一定的大小划分成若干个子序列。
  2. 对每个子序列进行插入排序,使得每个子序列都有序。
  3. 将相邻的两个子序列合并,得到一个更大的有序序列。
  4. 重复步骤3,直到所有子序列都被合并成一个有序序列。

非递归算法需要使用一个辅助数组,代码如下:

public static void mergeSort(int[] arr) {
    int len = arr.length;
    int[] temp = new int[len];
    for (int size = 1; size < len; size += size) {
        for (int left = 0; left < len - size; left += size + size) {
            int mid = left + size - 1;
            int right = Math.min(left + size + size - 1, len - 1);
            merge(arr, temp, left, mid, right);
        }
    }
}

public static void merge(int[] arr, int[] temp, int left, int mid, int right) {
    int i = left;
    int j = mid + 1;
    int k = left;
    while (i <= mid && j <= right) {
        if (arr[i] < arr[j]) {
            temp[k++] = arr[i++];
        } else {
            temp[k++] = arr[j++];
        }
    }
    while (i <= mid) {
        temp[k++] = arr[i++];
    }
    while (j <= right) {
        temp[k++] = arr[j++];
    }
    for (int m = left; m <= right; m++) {
        arr[m] = temp[m];
    }
}

该代码实现了归并排序的非递归算法,首先将输入数组按照一定的大小划分成若干个子序列,然后对每个子序列进行插入排序,接着将相邻的两个有序子序列合并,得到一个更大的有序序列。重复这个过程,直到所有子序列都被合并成一个有序序列。

以下是C++中使用递归实现归并排序的代码示例:

#include <iostream>
using namespace std;

void merge(int arr[], int left, int mid, int right)
{
    int i, j, k;
    int n1 = mid - left + 1;
    int n2 = right - mid;

    int LeftArr[n1], RightArr[n2];

    for (i = 0; i < n1; i++)
        LeftArr[i] = arr[left + i];
    for (j = 0; j < n2; j++)
        RightArr[j] = arr[mid + 1 + j];

    i = 0;
    j = 0;
    k = left;

    while (i < n1 && j < n2) {
        if (LeftArr[i] <= RightArr[j]) {
            arr[k] = LeftArr[i];
            i++;
        }
        else {
            arr[k] = RightArr[j];
            j++;
        }
        k++;
    }

    while (i < n1) {
        arr[k] = LeftArr[i];
        i++;
        k++;
    }

    while (j < n2) {
        arr[k] = RightArr[j];
        j++;
        k++;
    }
}

void mergeSort(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);
    }
}

int main()
{
    int arr[] = { 12, 11, 13, 5, 6, 7 };
    int arr_size = sizeof(arr) / sizeof(arr[0]);

    cout << "Given array is \n";
    for (int i = 0; i < arr_size; i++)
        cout << arr[i] << " ";

    mergeSort(arr, 0, arr_size - 1);

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

在这个代码中,merge()函数被用来合并两个有序数组。mergeSort()函数则是递归调用了自己,每次把给定数组分成两半,然后再进行归并排序。最终得到一个排序好的数组。

以下是 C++ 实现非递归排序的归并排序的代码示例:

#include <iostream>
using namespace std;

void merge(int arr[], int l, int m, int r) {
    int n1 = m - l + 1;
    int n2 = r - m;
  
    int L[n1], R[n2];
  
    for (int i = 0; i < n1; i++)
        L[i] = arr[l + i];
    for (int j = 0; j < n2; j++)
        R[j] = arr[m + 1 + j];
  
    int i = 0, j = 0, k = l;
  
    while (i < n1 && j < n2) {
        if (L[i] <= R[j]) {
            arr[k] = L[i];
            i++;
        }
        else {
            arr[k] = R[j];
            j++;
        }
        k++;
    }

    while (i < n1) {
        arr[k] = L[i];
        i++;
        k++;
    }

    while (j < n2) {
        arr[k] = R[j];
        j++;
        k++;
    }
}

void mergeSort(int arr[], int n) {
    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);
        }
    }
}

void printArray(int A[], int size) {
    for (int i = 0; i < size; i++)
        cout << A[i] << " ";
}
  
// 测试代码
int main() {
    int arr[] = { 12, 11, 13, 5, 6, 7 };
    int arr_size = sizeof(arr) / sizeof(arr[0]);
  
    cout << "给定数组为" << endl;
    printArray(arr, arr_size);
  
    mergeSort(arr, arr_size);
  
    cout << "\n排序后的数组为" << endl;
    printArray(arr, arr_size);
  
    return 0;
}

在这个实现中,merge() 函数用于将两个有序数组合并。mergeSort() 函数使用非递归方式对整个数组进行归并排序。

主函数中给定了一个测试数组,并输出排序前和排序后的数组。

计数排序

计数排序是一种非比较排序算法,它的基本思想是对于给定的输入序列中的每一个元素x,确定该序列中值小于x的元素的个数,然后将x直接放在输出序列的第n个位置上(n为小于x的元素的个数加上等于x的元素的个数),从而实现了序列的排序。

下面是Java语言的计数排序代码实现:

public static void countingSort(int[] arr) {
    int len = arr.length;
    if (len < 2) {
        return;
    }
    // 计算最大值和最小值
    int max = arr[0];
    int min = arr[0];
    for (int i = 1; i < len; i++) {
        if (arr[i] > max) {
            max = arr[i];
        }
        if (arr[i] < min) {
            min = arr[i];
        }
    }
    // 计算计数数组
    int range = max - min + 1;
    int[] count = new int[range];
    for (int i = 0; i < len; i++) {
        count[arr[i] - min]++;
    }
    // 累加计数数组
    for (int i = 1; i < range; i++) {
        count[i] += count[i - 1];
    }
    // 排序到输出数组
    int[] output = new int[len];
    for (int i = len - 1; i >= 0; i--) {
        output[--count[arr[i] - min]] = arr[i];
    }
    // 复制到原数组
    for (int i = 0; i < len; i++) {
        arr[i] = output[i];
    }
}

该代码实现了计数排序算法,首先计算输入数组的最大值和最小值,然后初始化一个计数数组count,遍历输入数组并将每个元素出现的次数记录在count中,接着将count数组累加,计算出小于等于某个元素的元素个数,最后根据count数组将元素输出到输出数组中。

计数排序是一种非比较排序算法,可以对于一定范围内的整数排序,时间复杂度为O(n+k),其中n为要排序的元素个数,k为整数的范围。

好的,以下是C++实现计数排序的示例代码:

#include <iostream>
using namespace std;

const int MAX_VALUE = 10000; // 假设数组中的元素最大值为10000

void countingSort(int arr[], int n) 
{
    int count[MAX_VALUE + 1] = {0}; // 初始化一个计数数组

    for (int i = 0; i < n; ++i) 
    {
        ++count[arr[i]]; // 将arr[i]出现的次数计入计数数组
    }

    int index = 0;
    for (int i = 0; i <= MAX_VALUE; ++i) 
    {
        for (int j = 0; j < count[i]; ++j) 
        {
            arr[index++] = i; // 按计数数组顺序重构原始数组
        }
    }
}

int main() 
{
    int arr[] = {3, 1, 4, 7, 2, 8, 5, 6};
    int n = sizeof(arr) / sizeof(arr[0]);

    countingSort(arr, n);

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

    return 0;
}

该代码中,我们首先定义了一个常量MAX_VALUE,这个常量用于表示数组中元素的最大值。然后,我们定义了一个计数数组count,并初始化为0。

接下来,我们遍历原始数组arr,将每个元素出现的次数计入计数数组count中。然后,我们按计数数组count的顺序重构原始数组arr

最后,我们通过循环遍历重构后的数组arr,将其输出到控制台。


计数排序是一种非比较排序算法,可以对于一定范围内的整数排序,时间复杂度为O(n+k),其中n为要排序的元素个数,k为整数的范围。

以下是Python实现计数排序的代码:

def count_sort(arr):
    n = len(arr)
    # 确定数组中最大值
    max_val = max(arr) 
    # 计数数组
    count = [0] * (max_val + 1)
    # 存放排序结果的数组
    output = [0] * n

    # 统计每个元素出现的次数
    for i in range(n):
        count[arr[i]] += 1

    # 累加count数组
    for i in range(1, max_val + 1):
        count[i] += count[i - 1]

    # 将元素放入output数组中
    for i in range(n - 1, -1, -1):
        output[count[arr[i]] - 1] = arr[i]
        count[arr[i]] -= 1

    # 将结果复制回原始数组
    for i in range(n):
        arr[i] = output[i]

    return arr

# 测试
arr = [4, 2, 10, 5, 3, 1, 9, 6, 8, 7]
print(count_sort(arr))

在该实现中,首先确定数组中的最大值max_val,然后创建一个count数组,用于统计每个数字出现的次数。接着遍历原始数组arr,在count[arr[i]]处加1,统计每个数字出现的次数。

之后遍历count数组,将其元素累加,这样count[i]的值就表示小于等于i的数字有多少个。接着从原始数组arr末尾开始向前遍历,将它的值放入output数组中,同时将count[arr[i]]的值减1,以便处理重复元素的情况。

最后再将output数组中的元素复制回原始数组arr,完成排序。