diff --git a/CountingSort/README.md b/CountingSort/README.md new file mode 100644 index 0000000..038ffa2 --- /dev/null +++ b/CountingSort/README.md @@ -0,0 +1,46 @@ +## 计数排序 + +计数排序(Counting Sort)是一种非比较性质的排序算法。它的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中,也就是说这个辅助空间的长度取决于待排序列中的数据范围(就是序列中的最大值与最小值的差加上1)。根据元素本身的值,将每个元素出现的次数记录到辅助空间后,通过对辅助空间内数据的计算,即可确定每一个元素最终的位置。 + +### 算法步骤 + +1. 找出待排序列中最大值 max 和最小值 min,根据它们的差值,申请辅助空间 C[max-min+1]; +2. 遍历待排序列,统计序列中每个值为 i 的元素出现的次数,记录在辅助空间的第 i 位; +3. 对辅助空间内的数据进行计算(从空间中的第一个元素开始,每一项和前一项相加),以确定值为 i 的元素在数组中出现的位置; +4. 反向填充目标数组:将每个元素 i 放在目标数组的第 C[i] 位,每放一个元素就将 C[i] 减1,直到 C 中所有值都是 0 + +### 动图演示 + +![](counting-sort.gif) + +### 代码实现 + +#### C语言 +```c +void counting_sort(int arr[], int n) { + if (arr == NULL) return; + // 定义辅助空间并初始化 + int max = arr[0], min = arr[0]; + int i; + for (i = 1; i < n; i++) { + if (max < arr[i]) max = arr[i]; + if (min > arr[i]) min = arr[i]; + } + int size = max - min + 1; + int C[size]; + memset(C, 0, sizeof(C)); + // 定义目标数组 + int R[n]; + // 统计每个元素出现的次数 + for (i = 0; i < n; i++) C[arr[i] - min]++; + // 对辅助空间内数据进行计算 + for (i = 1; i < size; i++) C[i] += C[i - 1]; + // 反向填充目标数组 + for (i = n - 1; i >= 0; i--) R[--C[arr[i] - min]] = arr[i]; + // 目标数组里的结果重新赋值给 arr + for (i = 0; i < n; i++) arr[i] = R[i]; +} +``` +### 算法分析 + +计数排序属于**非交换排序**,是**稳定排序**,适合数据范围不显著大于数据数量的序列。它的时间复杂度是线性的,为 O(n+k),空间复杂度也是 O(n+k),它快于任何比较排序算法,但这是通过牺牲空间换取时间实现的。 \ No newline at end of file diff --git a/CountingSort/counting-sort.gif b/CountingSort/counting-sort.gif new file mode 100644 index 0000000..404b10e Binary files /dev/null and b/CountingSort/counting-sort.gif differ diff --git a/CountingSort/counting_sort.c b/CountingSort/counting_sort.c new file mode 100644 index 0000000..d5a8adf --- /dev/null +++ b/CountingSort/counting_sort.c @@ -0,0 +1,36 @@ +#include +#include + +void counting_sort(int arr[], int n) { + if (arr == NULL) return; + // 定义辅助空间并初始化 + int max = arr[0], min = arr[0]; + int i; + for (i = 1; i < n; i++) { + if (max < arr[i]) max = arr[i]; + if (min > arr[i]) min = arr[i]; + } + int size = max - min + 1; + int C[size]; + memset(C, 0, sizeof(C)); + // 定义目标数组 + int R[n]; + // 统计每个元素出现的次数 + for (i = 0; i < n; i++) C[arr[i] - min]++; + // 对辅助空间内数据进行计算 + for (i = 1; i < size; i++) C[i] += C[i - 1]; + // 反向填充目标数组 + for (i = n - 1; i >= 0; i--) R[--C[arr[i] - min]] = arr[i]; + // 目标数组里的结果重新赋值给 arr + for (i = 0; i < n; i++) arr[i] = R[i]; +} + +int main() { + int arr[] = {2, 4, 3, 1, 4, 6, 4, 2}; + int n = sizeof(arr) / sizeof(*arr); + counting_sort(arr, n); + printf("Sort result:\n"); + for (int i = 0; i < n; i++) + printf("%d ", arr[i]); + return 0; +} diff --git a/README.md b/README.md index 8c9a9be..36e6e52 100644 --- a/README.md +++ b/README.md @@ -20,11 +20,14 @@ **非比较类排序**:不通过比较来决定元素间的相对次序,其时间复杂度可以突破 O(nlogn),以线性时间运行。属于非比较类的有: -| 排序算法 | 时间复杂度 | 最差情况 | 最好情况 | 空间复杂度 | 稳定性 | 排序方式 | -| :------: | :--------: | :------: | :------: | :--------: | :----: | :-------: | -| 计数排序 | O(n+k)​ | O(n+k)​ | O(n+k)​ | O(n+k)​ | 稳定 | Out-place | -| 桶排序 | O(n+k)​ | O(n²) | O(n)​ | O(n+k)​ | 稳定 | Out-place | -| 基数排序 | O(n×k)​ | O(n×k)​ | O(n×k)​ | O(n+k)​ | 稳定 | Out-place | +| 排序算法 | 时间复杂度 | 最差情况 | 最好情况 | 空间复杂度 | 稳定性 | 排序方式 | +| :----------------------: | :--------: | :------: | :------: | :--------: | :----: | :-------: | +| [计数排序](CountingSort) | O(n+k)​ | O(n+k)​ | O(n+k)​ | O(n+k)​ | 稳定 | Out-place | +| 桶排序 | O(n+k)​ | O(n²) | O(n)​ | O(n+k)​ | 稳定 | Out-place | +| 基数排序 | O(n×k)​ | O(n×k)​ | O(n×k)​ | O(n+k)​ | 稳定 | Out-place | + + +**n**:待排序列的个数 **In-place**:占用常用内存,不占用额外内存