This commit is contained in:
krahets
2023-09-04 03:16:55 +08:00
parent f07e94ab0c
commit 8f74a87eba
54 changed files with 23015 additions and 23015 deletions
+242 -242
View File
@@ -44,24 +44,19 @@ comments: true
<p align="center"> 图 11-5 &nbsp; 冒泡排序流程 </p>
=== "Java"
=== "Python"
```java title="bubble_sort.java"
/* 冒泡排序 */
void bubbleSort(int[] nums) {
// 外循环:未排序区间为 [0, i]
for (int i = nums.length - 1; i > 0; i--) {
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
for (int j = 0; j < i; j++) {
if (nums[j] > nums[j + 1]) {
// 交换 nums[j] nums[j + 1]
int tmp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = tmp;
}
}
}
}
```python title="bubble_sort.py"
def bubble_sort(nums: list[int]):
"""冒泡排序"""
n = len(nums)
# 外循环:未排序区间为 [0, i]
for i in range(n - 1, 0, -1):
# 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
for j in range(i):
if nums[j] > nums[j + 1]:
# 交换 nums[j] 与 nums[j + 1]
nums[j], nums[j + 1] = nums[j + 1], nums[j]
```
=== "C++"
@@ -83,19 +78,44 @@ comments: true
}
```
=== "Python"
=== "Java"
```python title="bubble_sort.py"
def bubble_sort(nums: list[int]):
"""冒泡排序"""
n = len(nums)
# 外循环:未排序区间为 [0, i]
for i in range(n - 1, 0, -1):
# 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
for j in range(i):
if nums[j] > nums[j + 1]:
# 交换 nums[j] 与 nums[j + 1]
nums[j], nums[j + 1] = nums[j + 1], nums[j]
```java title="bubble_sort.java"
/* 冒泡排序 */
void bubbleSort(int[] nums) {
// 外循环:未排序区间为 [0, i]
for (int i = nums.length - 1; i > 0; i--) {
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
for (int j = 0; j < i; j++) {
if (nums[j] > nums[j + 1]) {
// 交换 nums[j] nums[j + 1]
int tmp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = tmp;
}
}
}
}
```
=== "C#"
```csharp title="bubble_sort.cs"
/* 冒泡排序 */
void bubbleSort(int[] nums) {
// 外循环:未排序区间为 [0, i]
for (int i = nums.Length - 1; i > 0; i--) {
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
for (int j = 0; j < i; j++) {
if (nums[j] > nums[j + 1]) {
// 交换 nums[j] 与 nums[j + 1]
int tmp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = tmp;
}
}
}
}
```
=== "Go"
@@ -116,6 +136,26 @@ comments: true
}
```
=== "Swift"
```swift title="bubble_sort.swift"
/* 冒泡排序 */
func bubbleSort(nums: inout [Int]) {
// 外循环:未排序区间为 [0, i]
for i in stride(from: nums.count - 1, to: 0, by: -1) {
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
for j in stride(from: 0, to: i, by: 1) {
if nums[j] > nums[j + 1] {
// 交换 nums[j] 与 nums[j + 1]
let tmp = nums[j]
nums[j] = nums[j + 1]
nums[j + 1] = tmp
}
}
}
}
```
=== "JS"
```javascript title="bubble_sort.js"
@@ -156,87 +196,6 @@ comments: true
}
```
=== "C"
```c title="bubble_sort.c"
/* 冒泡排序 */
void bubbleSort(int nums[], int size) {
// 外循环:未排序区间为 [0, i]
for (int i = 0; i < size - 1; i++) {
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
for (int j = 0; j < size - 1 - i; j++) {
if (nums[j] > nums[j + 1]) {
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
}
}
}
}
```
=== "C#"
```csharp title="bubble_sort.cs"
/* 冒泡排序 */
void bubbleSort(int[] nums) {
// 外循环:未排序区间为 [0, i]
for (int i = nums.Length - 1; i > 0; i--) {
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
for (int j = 0; j < i; j++) {
if (nums[j] > nums[j + 1]) {
// 交换 nums[j] 与 nums[j + 1]
int tmp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = tmp;
}
}
}
}
```
=== "Swift"
```swift title="bubble_sort.swift"
/* 冒泡排序 */
func bubbleSort(nums: inout [Int]) {
// 外循环:未排序区间为 [0, i]
for i in stride(from: nums.count - 1, to: 0, by: -1) {
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
for j in stride(from: 0, to: i, by: 1) {
if nums[j] > nums[j + 1] {
// 交换 nums[j] 与 nums[j + 1]
let tmp = nums[j]
nums[j] = nums[j + 1]
nums[j + 1] = tmp
}
}
}
}
```
=== "Zig"
```zig title="bubble_sort.zig"
// 冒泡排序
fn bubbleSort(nums: []i32) void {
// 外循环:未排序区间为 [0, i]
var i: usize = nums.len - 1;
while (i > 0) : (i -= 1) {
var j: usize = 0;
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
while (j < i) : (j += 1) {
if (nums[j] > nums[j + 1]) {
// 交换 nums[j] 与 nums[j + 1]
var tmp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = tmp;
}
}
}
}
```
=== "Dart"
```dart title="bubble_sort.dart"
@@ -277,34 +236,70 @@ comments: true
}
```
=== "C"
```c title="bubble_sort.c"
/* 冒泡排序 */
void bubbleSort(int nums[], int size) {
// 外循环:未排序区间为 [0, i]
for (int i = 0; i < size - 1; i++) {
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
for (int j = 0; j < size - 1 - i; j++) {
if (nums[j] > nums[j + 1]) {
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
}
}
}
}
```
=== "Zig"
```zig title="bubble_sort.zig"
// 冒泡排序
fn bubbleSort(nums: []i32) void {
// 外循环:未排序区间为 [0, i]
var i: usize = nums.len - 1;
while (i > 0) : (i -= 1) {
var j: usize = 0;
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
while (j < i) : (j += 1) {
if (nums[j] > nums[j + 1]) {
// 交换 nums[j] 与 nums[j + 1]
var tmp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = tmp;
}
}
}
}
```
## 11.3.2 &nbsp; 效率优化
我们发现,如果某轮“冒泡”中没有执行任何交换操作,说明数组已经完成排序,可直接返回结果。因此,可以增加一个标志位 `flag` 来监测这种情况,一旦出现就立即返回。
经过优化,冒泡排序的最差和平均时间复杂度仍为 $O(n^2)$ ;但当输入数组完全有序时,可达到最佳时间复杂度 $O(n)$ 。
=== "Java"
=== "Python"
```java title="bubble_sort.java"
/* 冒泡排序(标志优化) */
void bubbleSortWithFlag(int[] nums) {
// 外循环:未排序区间为 [0, i]
for (int i = nums.length - 1; i > 0; i--) {
boolean flag = false; // 初始化标志位
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
for (int j = 0; j < i; j++) {
if (nums[j] > nums[j + 1]) {
// 交换 nums[j] nums[j + 1]
int tmp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = tmp;
flag = true; // 记录交换元素
}
}
if (!flag)
break; // 此轮冒泡未交换任何元素,直接跳出
}
}
```python title="bubble_sort.py"
def bubble_sort_with_flag(nums: list[int]):
"""冒泡排序(标志优化)"""
n = len(nums)
# 外循环:未排序区间为 [0, i]
for i in range(n - 1, 0, -1):
flag = False # 初始化标志位
# 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
for j in range(i):
if nums[j] > nums[j + 1]:
# 交换 nums[j] 与 nums[j + 1]
nums[j], nums[j + 1] = nums[j + 1], nums[j]
flag = True # 记录交换元素
if not flag:
break # 此轮冒泡未交换任何元素,直接跳出
```
=== "C++"
@@ -330,23 +325,51 @@ comments: true
}
```
=== "Python"
=== "Java"
```python title="bubble_sort.py"
def bubble_sort_with_flag(nums: list[int]):
"""冒泡排序(标志优化)"""
n = len(nums)
# 外循环:未排序区间为 [0, i]
for i in range(n - 1, 0, -1):
flag = False # 初始化标志位
# 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
for j in range(i):
if nums[j] > nums[j + 1]:
# 交换 nums[j] 与 nums[j + 1]
nums[j], nums[j + 1] = nums[j + 1], nums[j]
flag = True # 记录交换元素
if not flag:
break # 此轮冒泡未交换任何元素,直接跳出
```java title="bubble_sort.java"
/* 冒泡排序(标志优化) */
void bubbleSortWithFlag(int[] nums) {
// 外循环:未排序区间为 [0, i]
for (int i = nums.length - 1; i > 0; i--) {
boolean flag = false; // 初始化标志位
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
for (int j = 0; j < i; j++) {
if (nums[j] > nums[j + 1]) {
// 交换 nums[j] nums[j + 1]
int tmp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = tmp;
flag = true; // 记录交换元素
}
}
if (!flag)
break; // 此轮冒泡未交换任何元素,直接跳出
}
}
```
=== "C#"
```csharp title="bubble_sort.cs"
/* 冒泡排序(标志优化)*/
void bubbleSortWithFlag(int[] nums) {
// 外循环:未排序区间为 [0, i]
for (int i = nums.Length - 1; i > 0; i--) {
bool flag = false; // 初始化标志位
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
for (int j = 0; j < i; j++) {
if (nums[j] > nums[j + 1]) {
// 交换 nums[j] 与 nums[j + 1]
int tmp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = tmp;
flag = true; // 记录交换元素
}
}
if (!flag) break; // 此轮冒泡未交换任何元素,直接跳出
}
}
```
=== "Go"
@@ -372,6 +395,30 @@ comments: true
}
```
=== "Swift"
```swift title="bubble_sort.swift"
/* 冒泡排序(标志优化)*/
func bubbleSortWithFlag(nums: inout [Int]) {
// 外循环:未排序区间为 [0, i]
for i in stride(from: nums.count - 1, to: 0, by: -1) {
var flag = false // 初始化标志位
for j in stride(from: 0, to: i, by: 1) {
if nums[j] > nums[j + 1] {
// 交换 nums[j] 与 nums[j + 1]
let tmp = nums[j]
nums[j] = nums[j + 1]
nums[j + 1] = tmp
flag = true // 记录交换元素
}
}
if !flag { // 此轮冒泡未交换任何元素,直接跳出
break
}
}
}
```
=== "JS"
```javascript title="bubble_sort.js"
@@ -418,101 +465,6 @@ comments: true
}
```
=== "C"
```c title="bubble_sort.c"
/* 冒泡排序(标志优化)*/
void bubbleSortWithFlag(int nums[], int size) {
// 外循环:未排序区间为 [0, i]
for (int i = 0; i < size - 1; i++) {
bool flag = false;
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
for (int j = 0; j < size - 1 - i; j++) {
if (nums[j] > nums[j + 1]) {
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
flag = true;
}
}
if (!flag)
break;
}
}
```
=== "C#"
```csharp title="bubble_sort.cs"
/* 冒泡排序(标志优化)*/
void bubbleSortWithFlag(int[] nums) {
// 外循环:未排序区间为 [0, i]
for (int i = nums.Length - 1; i > 0; i--) {
bool flag = false; // 初始化标志位
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
for (int j = 0; j < i; j++) {
if (nums[j] > nums[j + 1]) {
// 交换 nums[j] 与 nums[j + 1]
int tmp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = tmp;
flag = true; // 记录交换元素
}
}
if (!flag) break; // 此轮冒泡未交换任何元素,直接跳出
}
}
```
=== "Swift"
```swift title="bubble_sort.swift"
/* 冒泡排序(标志优化)*/
func bubbleSortWithFlag(nums: inout [Int]) {
// 外循环:未排序区间为 [0, i]
for i in stride(from: nums.count - 1, to: 0, by: -1) {
var flag = false // 初始化标志位
for j in stride(from: 0, to: i, by: 1) {
if nums[j] > nums[j + 1] {
// 交换 nums[j] 与 nums[j + 1]
let tmp = nums[j]
nums[j] = nums[j + 1]
nums[j + 1] = tmp
flag = true // 记录交换元素
}
}
if !flag { // 此轮冒泡未交换任何元素,直接跳出
break
}
}
}
```
=== "Zig"
```zig title="bubble_sort.zig"
// 冒泡排序(标志优化)
fn bubbleSortWithFlag(nums: []i32) void {
// 外循环:未排序区间为 [0, i]
var i: usize = nums.len - 1;
while (i > 0) : (i -= 1) {
var flag = false; // 初始化标志位
var j: usize = 0;
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
while (j < i) : (j += 1) {
if (nums[j] > nums[j + 1]) {
// 交换 nums[j] 与 nums[j + 1]
var tmp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = tmp;
flag = true;
}
}
if (!flag) break; // 此轮冒泡未交换任何元素,直接跳出
}
}
```
=== "Dart"
```dart title="bubble_sort.dart"
@@ -559,6 +511,54 @@ comments: true
}
```
=== "C"
```c title="bubble_sort.c"
/* 冒泡排序(标志优化)*/
void bubbleSortWithFlag(int nums[], int size) {
// 外循环:未排序区间为 [0, i]
for (int i = 0; i < size - 1; i++) {
bool flag = false;
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
for (int j = 0; j < size - 1 - i; j++) {
if (nums[j] > nums[j + 1]) {
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
flag = true;
}
}
if (!flag)
break;
}
}
```
=== "Zig"
```zig title="bubble_sort.zig"
// 冒泡排序(标志优化)
fn bubbleSortWithFlag(nums: []i32) void {
// 外循环:未排序区间为 [0, i]
var i: usize = nums.len - 1;
while (i > 0) : (i -= 1) {
var flag = false; // 初始化标志位
var j: usize = 0;
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
while (j < i) : (j += 1) {
if (nums[j] > nums[j + 1]) {
// 交换 nums[j] 与 nums[j + 1]
var tmp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = tmp;
flag = true;
}
}
if (!flag) break; // 此轮冒泡未交换任何元素,直接跳出
}
}
```
## 11.3.3 &nbsp; 算法特性
- **时间复杂度为 $O(n^2)$、自适应排序**:各轮“冒泡”遍历的数组长度依次为 $n - 1$、$n - 2$、$\dots$、$2$、$1$ ,总和为 $(n - 1) n / 2$ 。在引入 `flag` 优化后,最佳时间复杂度可达到 $O(n)$ 。
+163 -163
View File
@@ -20,6 +20,62 @@ comments: true
<p align="center"> 图 11-13 &nbsp; 桶排序算法流程 </p>
=== "Python"
```python title="bucket_sort.py"
def bucket_sort(nums: list[float]):
"""桶排序"""
# 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素
k = len(nums) // 2
buckets = [[] for _ in range(k)]
# 1. 将数组元素分配到各个桶中
for num in nums:
# 输入数据范围 [0, 1),使用 num * k 映射到索引范围 [0, k-1]
i = int(num * k)
# 将 num 添加进桶 i
buckets[i].append(num)
# 2. 对各个桶执行排序
for bucket in buckets:
# 使用内置排序函数,也可以替换成其他排序算法
bucket.sort()
# 3. 遍历桶合并结果
i = 0
for bucket in buckets:
for num in bucket:
nums[i] = num
i += 1
```
=== "C++"
```cpp title="bucket_sort.cpp"
/* 桶排序 */
void bucketSort(vector<float> &nums) {
// 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素
int k = nums.size() / 2;
vector<vector<float>> buckets(k);
// 1. 将数组元素分配到各个桶中
for (float num : nums) {
// 输入数据范围 [0, 1),使用 num * k 映射到索引范围 [0, k-1]
int i = num * k;
// 将 num 添加进桶 bucket_idx
buckets[i].push_back(num);
}
// 2. 对各个桶执行排序
for (vector<float> &bucket : buckets) {
// 使用内置排序函数,也可以替换成其他排序算法
sort(bucket.begin(), bucket.end());
}
// 3. 遍历桶合并结果
int i = 0;
for (vector<float> &bucket : buckets) {
for (float num : bucket) {
nums[i++] = num;
}
}
}
```
=== "Java"
```java title="bucket_sort.java"
@@ -53,62 +109,39 @@ comments: true
}
```
=== "C++"
=== "C#"
```cpp title="bucket_sort.cpp"
```csharp title="bucket_sort.cs"
/* 桶排序 */
void bucketSort(vector<float> &nums) {
void bucketSort(float[] nums) {
// 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素
int k = nums.size() / 2;
vector<vector<float>> buckets(k);
int k = nums.Length / 2;
List<List<float>> buckets = new List<List<float>>();
for (int i = 0; i < k; i++) {
buckets.Add(new List<float>());
}
// 1. 将数组元素分配到各个桶中
for (float num : nums) {
foreach (float num in nums) {
// 输入数据范围 [0, 1),使用 num * k 映射到索引范围 [0, k-1]
int i = num * k;
// 将 num 添加进桶 bucket_idx
buckets[i].push_back(num);
int i = (int)(num * k);
// 将 num 添加进桶 i
buckets[i].Add(num);
}
// 2. 对各个桶执行排序
for (vector<float> &bucket : buckets) {
foreach (List<float> bucket in buckets) {
// 使用内置排序函数,也可以替换成其他排序算法
sort(bucket.begin(), bucket.end());
bucket.Sort();
}
// 3. 遍历桶合并结果
int i = 0;
for (vector<float> &bucket : buckets) {
for (float num : bucket) {
nums[i++] = num;
int j = 0;
foreach (List<float> bucket in buckets) {
foreach (float num in bucket) {
nums[j++] = num;
}
}
}
```
=== "Python"
```python title="bucket_sort.py"
def bucket_sort(nums: list[float]):
"""桶排序"""
# 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素
k = len(nums) // 2
buckets = [[] for _ in range(k)]
# 1. 将数组元素分配到各个桶中
for num in nums:
# 输入数据范围 [0, 1),使用 num * k 映射到索引范围 [0, k-1]
i = int(num * k)
# 将 num 添加进桶 i
buckets[i].append(num)
# 2. 对各个桶执行排序
for bucket in buckets:
# 使用内置排序函数,也可以替换成其他排序算法
bucket.sort()
# 3. 遍历桶合并结果
i = 0
for bucket in buckets:
for num in bucket:
nums[i] = num
i += 1
```
=== "Go"
```go title="bucket_sort.go"
@@ -143,6 +176,37 @@ comments: true
}
```
=== "Swift"
```swift title="bucket_sort.swift"
/* 桶排序 */
func bucketSort(nums: inout [Double]) {
// 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素
let k = nums.count / 2
var buckets = (0 ..< k).map { _ in [Double]() }
// 1. 将数组元素分配到各个桶中
for num in nums {
// 输入数据范围 [0, 1),使用 num * k 映射到索引范围 [0, k-1]
let i = Int(num * Double(k))
// 将 num 添加进桶 i
buckets[i].append(num)
}
// 2. 对各个桶执行排序
for i in buckets.indices {
// 使用内置排序函数,也可以替换成其他排序算法
buckets[i].sort()
}
// 3. 遍历桶合并结果
var i = nums.startIndex
for bucket in buckets {
for num in bucket {
nums[i] = num
nums.formIndex(after: &i)
}
}
}
```
=== "JS"
```javascript title="bucket_sort.js"
@@ -209,128 +273,6 @@ comments: true
}
```
=== "C"
```c title="bucket_sort.c"
/* 桶排序 */
void bucketSort(float nums[], int size) {
// 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素
int k = size / 2;
float **buckets = calloc(k, sizeof(float *));
for (int i = 0; i < k; i++) {
// 每个桶最多可以分配 k 个元素
buckets[i] = calloc(ARRAY_SIZE, sizeof(float));
}
// 1. 将数组元素分配到各个桶中
for (int i = 0; i < size; i++) {
// 输入数据范围 [0, 1),使用 num * k 映射到索引范围 [0, k-1]
int bucket_idx = nums[i] * k;
int j = 0;
// 如果桶中有数据且数据小于当前值 nums[i], 要将其放到当前桶的后面,相当于 cpp 中的 push_back
while (buckets[bucket_idx][j] > 0 && buckets[bucket_idx][j] < nums[i]) {
j++;
}
float temp = nums[i];
while (j < ARRAY_SIZE && buckets[bucket_idx][j] > 0) {
swap(&temp, &buckets[bucket_idx][j]);
j++;
}
buckets[bucket_idx][j] = temp;
}
// 2. 对各个桶执行排序
for (int i = 0; i < k; i++) {
qsort(buckets[i], ARRAY_SIZE, sizeof(float), compare_float);
}
// 3. 遍历桶合并结果
for (int i = 0, j = 0; j < k; j++) {
for (int l = 0; l < ARRAY_SIZE; l++) {
if (buckets[j][l] > 0) {
nums[i++] = buckets[j][l];
}
}
}
// 释放上述分配的内存
for (int i = 0; i < k; i++) {
free(buckets[i]);
}
free(buckets);
}
```
=== "C#"
```csharp title="bucket_sort.cs"
/* 桶排序 */
void bucketSort(float[] nums) {
// 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素
int k = nums.Length / 2;
List<List<float>> buckets = new List<List<float>>();
for (int i = 0; i < k; i++) {
buckets.Add(new List<float>());
}
// 1. 将数组元素分配到各个桶中
foreach (float num in nums) {
// 输入数据范围 [0, 1),使用 num * k 映射到索引范围 [0, k-1]
int i = (int)(num * k);
// 将 num 添加进桶 i
buckets[i].Add(num);
}
// 2. 对各个桶执行排序
foreach (List<float> bucket in buckets) {
// 使用内置排序函数,也可以替换成其他排序算法
bucket.Sort();
}
// 3. 遍历桶合并结果
int j = 0;
foreach (List<float> bucket in buckets) {
foreach (float num in bucket) {
nums[j++] = num;
}
}
}
```
=== "Swift"
```swift title="bucket_sort.swift"
/* 桶排序 */
func bucketSort(nums: inout [Double]) {
// 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素
let k = nums.count / 2
var buckets = (0 ..< k).map { _ in [Double]() }
// 1. 将数组元素分配到各个桶中
for num in nums {
// 输入数据范围 [0, 1),使用 num * k 映射到索引范围 [0, k-1]
let i = Int(num * Double(k))
// 将 num 添加进桶 i
buckets[i].append(num)
}
// 2. 对各个桶执行排序
for i in buckets.indices {
// 使用内置排序函数,也可以替换成其他排序算法
buckets[i].sort()
}
// 3. 遍历桶合并结果
var i = nums.startIndex
for bucket in buckets {
for num in bucket {
nums[i] = num
nums.formIndex(after: &i)
}
}
}
```
=== "Zig"
```zig title="bucket_sort.zig"
[class]{}-[func]{bucketSort}
```
=== "Dart"
```dart title="bucket_sort.dart"
@@ -392,6 +334,64 @@ comments: true
}
```
=== "C"
```c title="bucket_sort.c"
/* 桶排序 */
void bucketSort(float nums[], int size) {
// 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素
int k = size / 2;
float **buckets = calloc(k, sizeof(float *));
for (int i = 0; i < k; i++) {
// 每个桶最多可以分配 k 个元素
buckets[i] = calloc(ARRAY_SIZE, sizeof(float));
}
// 1. 将数组元素分配到各个桶中
for (int i = 0; i < size; i++) {
// 输入数据范围 [0, 1),使用 num * k 映射到索引范围 [0, k-1]
int bucket_idx = nums[i] * k;
int j = 0;
// 如果桶中有数据且数据小于当前值 nums[i], 要将其放到当前桶的后面,相当于 cpp 中的 push_back
while (buckets[bucket_idx][j] > 0 && buckets[bucket_idx][j] < nums[i]) {
j++;
}
float temp = nums[i];
while (j < ARRAY_SIZE && buckets[bucket_idx][j] > 0) {
swap(&temp, &buckets[bucket_idx][j]);
j++;
}
buckets[bucket_idx][j] = temp;
}
// 2. 对各个桶执行排序
for (int i = 0; i < k; i++) {
qsort(buckets[i], ARRAY_SIZE, sizeof(float), compare_float);
}
// 3. 遍历桶合并结果
for (int i = 0, j = 0; j < k; j++) {
for (int l = 0; l < ARRAY_SIZE; l++) {
if (buckets[j][l] > 0) {
nums[i++] = buckets[j][l];
}
}
}
// 释放上述分配的内存
for (int i = 0; i < k; i++) {
free(buckets[i]);
}
free(buckets);
}
```
=== "Zig"
```zig title="bucket_sort.zig"
[class]{}-[func]{bucketSort}
```
## 11.8.2 &nbsp; 算法特性
桶排序适用于处理体量很大的数据。例如,输入数据包含 100 万个元素,由于空间限制,系统内存无法一次性加载所有数据。此时,可以将数据分成 1000 个桶,然后分别对每个桶进行排序,最后将结果合并。
+308 -308
View File
@@ -18,31 +18,27 @@ comments: true
<p align="center"> 图 11-16 &nbsp; 计数排序流程 </p>
=== "Java"
=== "Python"
```java title="counting_sort.java"
/* 计数排序 */
// 简单实现,无法用于排序对象
void countingSortNaive(int[] nums) {
// 1. 统计数组最大元素 m
int m = 0;
for (int num : nums) {
m = Math.max(m, num);
}
// 2. 统计各数字的出现次数
// counter[num] 代表 num 的出现次数
int[] counter = new int[m + 1];
for (int num : nums) {
counter[num]++;
}
// 3. 遍历 counter ,将各元素填入原数组 nums
int i = 0;
for (int num = 0; num < m + 1; num++) {
for (int j = 0; j < counter[num]; j++, i++) {
nums[i] = num;
}
}
}
```python title="counting_sort.py"
def counting_sort_naive(nums: list[int]):
"""计数排序"""
# 简单实现,无法用于排序对象
# 1. 统计数组最大元素 m
m = 0
for num in nums:
m = max(m, num)
# 2. 统计各数字的出现次数
# counter[num] 代表 num 的出现次数
counter = [0] * (m + 1)
for num in nums:
counter[num] += 1
# 3. 遍历 counter ,将各元素填入原数组 nums
i = 0
for num in range(m + 1):
for _ in range(counter[num]):
nums[i] = num
i += 1
```
=== "C++"
@@ -72,27 +68,58 @@ comments: true
}
```
=== "Python"
=== "Java"
```python title="counting_sort.py"
def counting_sort_naive(nums: list[int]):
"""计数排序"""
# 简单实现,无法用于排序对象
# 1. 统计数组最大元素 m
m = 0
for num in nums:
m = max(m, num)
# 2. 统计各数字的出现次数
# counter[num] 代表 num 的出现次数
counter = [0] * (m + 1)
for num in nums:
counter[num] += 1
# 3. 遍历 counter ,将各元素填入原数组 nums
i = 0
for num in range(m + 1):
for _ in range(counter[num]):
nums[i] = num
i += 1
```java title="counting_sort.java"
/* 计数排序 */
// 简单实现,无法用于排序对象
void countingSortNaive(int[] nums) {
// 1. 统计数组最大元素 m
int m = 0;
for (int num : nums) {
m = Math.max(m, num);
}
// 2. 统计各数字的出现次数
// counter[num] 代表 num 的出现次数
int[] counter = new int[m + 1];
for (int num : nums) {
counter[num]++;
}
// 3. 遍历 counter ,将各元素填入原数组 nums
int i = 0;
for (int num = 0; num < m + 1; num++) {
for (int j = 0; j < counter[num]; j++, i++) {
nums[i] = num;
}
}
}
```
=== "C#"
```csharp title="counting_sort.cs"
/* 计数排序 */
// 简单实现,无法用于排序对象
void countingSortNaive(int[] nums) {
// 1. 统计数组最大元素 m
int m = 0;
foreach (int num in nums) {
m = Math.Max(m, num);
}
// 2. 统计各数字的出现次数
// counter[num] 代表 num 的出现次数
int[] counter = new int[m + 1];
foreach (int num in nums) {
counter[num]++;
}
// 3. 遍历 counter ,将各元素填入原数组 nums
int i = 0;
for (int num = 0; num < m + 1; num++) {
for (int j = 0; j < counter[num]; j++, i++) {
nums[i] = num;
}
}
}
```
=== "Go"
@@ -124,6 +151,31 @@ comments: true
}
```
=== "Swift"
```swift title="counting_sort.swift"
/* 计数排序 */
// 简单实现,无法用于排序对象
func countingSortNaive(nums: inout [Int]) {
// 1. 统计数组最大元素 m
let m = nums.max()!
// 2. 统计各数字的出现次数
// counter[num] 代表 num 的出现次数
var counter = Array(repeating: 0, count: m + 1)
for num in nums {
counter[num] += 1
}
// 3. 遍历 counter ,将各元素填入原数组 nums
var i = 0
for num in stride(from: 0, to: m + 1, by: 1) {
for _ in stride(from: 0, to: counter[num], by: 1) {
nums[i] = num
i += 1
}
}
}
```
=== "JS"
```javascript title="counting_sort.js"
@@ -178,93 +230,6 @@ comments: true
}
```
=== "C"
```c title="counting_sort.c"
/* 计数排序 */
// 简单实现,无法用于排序对象
void countingSortNaive(int nums[], int size) {
// 1. 统计数组最大元素 m
int m = 0;
for (int i = 0; i < size; i++) {
if (nums[i] > m) {
m = nums[i];
}
}
// 2. 统计各数字的出现次数
// counter[num] 代表 num 的出现次数
int *counter = malloc(sizeof(int) * m);
for (int i = 0; i < size; i++) {
counter[nums[i]]++;
}
// 3. 遍历 counter ,将各元素填入原数组 nums
int i = 0;
for (int num = 0; num < m + 1; num++) {
for (int j = 0; j < counter[num]; j++, i++) {
nums[i] = num;
}
}
}
```
=== "C#"
```csharp title="counting_sort.cs"
/* 计数排序 */
// 简单实现,无法用于排序对象
void countingSortNaive(int[] nums) {
// 1. 统计数组最大元素 m
int m = 0;
foreach (int num in nums) {
m = Math.Max(m, num);
}
// 2. 统计各数字的出现次数
// counter[num] 代表 num 的出现次数
int[] counter = new int[m + 1];
foreach (int num in nums) {
counter[num]++;
}
// 3. 遍历 counter ,将各元素填入原数组 nums
int i = 0;
for (int num = 0; num < m + 1; num++) {
for (int j = 0; j < counter[num]; j++, i++) {
nums[i] = num;
}
}
}
```
=== "Swift"
```swift title="counting_sort.swift"
/* 计数排序 */
// 简单实现,无法用于排序对象
func countingSortNaive(nums: inout [Int]) {
// 1. 统计数组最大元素 m
let m = nums.max()!
// 2. 统计各数字的出现次数
// counter[num] 代表 num 的出现次数
var counter = Array(repeating: 0, count: m + 1)
for num in nums {
counter[num] += 1
}
// 3. 遍历 counter ,将各元素填入原数组 nums
var i = 0
for num in stride(from: 0, to: m + 1, by: 1) {
for _ in stride(from: 0, to: counter[num], by: 1) {
nums[i] = num
i += 1
}
}
}
```
=== "Zig"
```zig title="counting_sort.zig"
[class]{}-[func]{countingSortNaive}
```
=== "Dart"
```dart title="counting_sort.dart"
@@ -317,6 +282,41 @@ comments: true
}
```
=== "C"
```c title="counting_sort.c"
/* 计数排序 */
// 简单实现,无法用于排序对象
void countingSortNaive(int nums[], int size) {
// 1. 统计数组最大元素 m
int m = 0;
for (int i = 0; i < size; i++) {
if (nums[i] > m) {
m = nums[i];
}
}
// 2. 统计各数字的出现次数
// counter[num] 代表 num 的出现次数
int *counter = malloc(sizeof(int) * m);
for (int i = 0; i < size; i++) {
counter[nums[i]]++;
}
// 3. 遍历 counter ,将各元素填入原数组 nums
int i = 0;
for (int num = 0; num < m + 1; num++) {
for (int j = 0; j < counter[num]; j++, i++) {
nums[i] = num;
}
}
}
```
=== "Zig"
```zig title="counting_sort.zig"
[class]{}-[func]{countingSortNaive}
```
!!! note "计数排序与桶排序的联系"
从桶排序的角度看,我们可以将计数排序中的计数数组 `counter` 的每个索引视为一个桶,将统计数量的过程看作是将各个元素分配到对应的桶中。本质上,计数排序是桶排序在整型数据下的一个特例。
@@ -366,42 +366,34 @@ $$
计数排序的实现代码如下所示。
=== "Java"
=== "Python"
```java title="counting_sort.java"
/* 计数排序 */
// 完整实现,可排序对象,并且是稳定排序
void countingSort(int[] nums) {
// 1. 统计数组最大元素 m
int m = 0;
for (int num : nums) {
m = Math.max(m, num);
}
// 2. 统计各数字的出现次数
// counter[num] 代表 num 的出现次数
int[] counter = new int[m + 1];
for (int num : nums) {
counter[num]++;
}
// 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引”
// 即 counter[num]-1 是 num 在 res 中最后一次出现的索引
for (int i = 0; i < m; i++) {
counter[i + 1] += counter[i];
}
// 4. 倒序遍历 nums ,将各元素填入结果数组 res
// 初始化数组 res 用于记录结果
int n = nums.length;
int[] res = new int[n];
for (int i = n - 1; i >= 0; i--) {
int num = nums[i];
res[counter[num] - 1] = num; // 将 num 放置到对应索引处
counter[num]--; // 令前缀和自减 1 ,得到下次放置 num 的索引
}
// 使用结果数组 res 覆盖原数组 nums
for (int i = 0; i < n; i++) {
nums[i] = res[i];
}
}
```python title="counting_sort.py"
def counting_sort(nums: list[int]):
"""计数排序"""
# 完整实现,可排序对象,并且是稳定排序
# 1. 统计数组最大元素 m
m = max(nums)
# 2. 统计各数字的出现次数
# counter[num] 代表 num 的出现次数
counter = [0] * (m + 1)
for num in nums:
counter[num] += 1
# 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引”
# 即 counter[num]-1 是 num 在 res 中最后一次出现的索引
for i in range(m):
counter[i + 1] += counter[i]
# 4. 倒序遍历 nums ,将各元素填入结果数组 res
# 初始化数组 res 用于记录结果
n = len(nums)
res = [0] * n
for i in range(n - 1, -1, -1):
num = nums[i]
res[counter[num] - 1] = num # 将 num 放置到对应索引处
counter[num] -= 1 # 令前缀和自减 1 ,得到下次放置 num 的索引
# 使用结果数组 res 覆盖原数组 nums
for i in range(n):
nums[i] = res[i]
```
=== "C++"
@@ -440,34 +432,80 @@ $$
}
```
=== "Python"
=== "Java"
```python title="counting_sort.py"
def counting_sort(nums: list[int]):
"""计数排序"""
# 完整实现,可排序对象,并且是稳定排序
# 1. 统计数组最大元素 m
m = max(nums)
# 2. 统计各数字的出现次数
# counter[num] 代表 num 的出现次数
counter = [0] * (m + 1)
for num in nums:
counter[num] += 1
# 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引”
# 即 counter[num]-1 是 num 在 res 中最后一次出现的索引
for i in range(m):
counter[i + 1] += counter[i]
# 4. 倒序遍历 nums ,将各元素填入结果数组 res
# 初始化数组 res 用于记录结果
n = len(nums)
res = [0] * n
for i in range(n - 1, -1, -1):
num = nums[i]
res[counter[num] - 1] = num # 将 num 放置到对应索引处
counter[num] -= 1 # 令前缀和自减 1 ,得到下次放置 num 的索引
# 使用结果数组 res 覆盖原数组 nums
for i in range(n):
nums[i] = res[i]
```java title="counting_sort.java"
/* 计数排序 */
// 完整实现,可排序对象,并且是稳定排序
void countingSort(int[] nums) {
// 1. 统计数组最大元素 m
int m = 0;
for (int num : nums) {
m = Math.max(m, num);
}
// 2. 统计各数字的出现次数
// counter[num] 代表 num 的出现次数
int[] counter = new int[m + 1];
for (int num : nums) {
counter[num]++;
}
// 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引”
// 即 counter[num]-1 是 num 在 res 中最后一次出现的索引
for (int i = 0; i < m; i++) {
counter[i + 1] += counter[i];
}
// 4. 倒序遍历 nums ,将各元素填入结果数组 res
// 初始化数组 res 用于记录结果
int n = nums.length;
int[] res = new int[n];
for (int i = n - 1; i >= 0; i--) {
int num = nums[i];
res[counter[num] - 1] = num; // 将 num 放置到对应索引处
counter[num]--; // 令前缀和自减 1 ,得到下次放置 num 的索引
}
// 使用结果数组 res 覆盖原数组 nums
for (int i = 0; i < n; i++) {
nums[i] = res[i];
}
}
```
=== "C#"
```csharp title="counting_sort.cs"
/* 计数排序 */
// 完整实现,可排序对象,并且是稳定排序
void countingSort(int[] nums) {
// 1. 统计数组最大元素 m
int m = 0;
foreach (int num in nums) {
m = Math.Max(m, num);
}
// 2. 统计各数字的出现次数
// counter[num] 代表 num 的出现次数
int[] counter = new int[m + 1];
foreach (int num in nums) {
counter[num]++;
}
// 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引”
// 即 counter[num]-1 是 num 在 res 中最后一次出现的索引
for (int i = 0; i < m; i++) {
counter[i + 1] += counter[i];
}
// 4. 倒序遍历 nums ,将各元素填入结果数组 res
// 初始化数组 res 用于记录结果
int n = nums.Length;
int[] res = new int[n];
for (int i = n - 1; i >= 0; i--) {
int num = nums[i];
res[counter[num] - 1] = num; // 将 num 放置到对应索引处
counter[num]--; // 令前缀和自减 1 ,得到下次放置 num 的索引
}
// 使用结果数组 res 覆盖原数组 nums
for (int i = 0; i < n; i++) {
nums[i] = res[i];
}
}
```
=== "Go"
@@ -510,6 +548,40 @@ $$
}
```
=== "Swift"
```swift title="counting_sort.swift"
/* 计数排序 */
// 完整实现,可排序对象,并且是稳定排序
func countingSort(nums: inout [Int]) {
// 1. 统计数组最大元素 m
let m = nums.max()!
// 2. 统计各数字的出现次数
// counter[num] 代表 num 的出现次数
var counter = Array(repeating: 0, count: m + 1)
for num in nums {
counter[num] += 1
}
// 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引”
// 即 counter[num]-1 是 num 在 res 中最后一次出现的索引
for i in stride(from: 0, to: m, by: 1) {
counter[i + 1] += counter[i]
}
// 4. 倒序遍历 nums ,将各元素填入结果数组 res
// 初始化数组 res 用于记录结果
var res = Array(repeating: 0, count: nums.count)
for i in stride(from: nums.count - 1, through: 0, by: -1) {
let num = nums[i]
res[counter[num] - 1] = num // 将 num 放置到对应索引处
counter[num] -= 1 // 令前缀和自减 1 ,得到下次放置 num 的索引
}
// 使用结果数组 res 覆盖原数组 nums
for i in stride(from: 0, to: nums.count, by: 1) {
nums[i] = res[i]
}
}
```
=== "JS"
```javascript title="counting_sort.js"
@@ -586,121 +658,6 @@ $$
}
```
=== "C"
```c title="counting_sort.c"
/* 计数排序 */
// 完整实现,可排序对象,并且是稳定排序
void countingSort(int nums[], int size) {
// 1. 统计数组最大元素 m
int m = 0;
for (int i = 0; i < size; i++) {
if (nums[i] > m) {
m = nums[i];
}
}
// 2. 统计各数字的出现次数
// counter[num] 代表 num 的出现次数
int *counter = malloc(sizeof(int) * m);
for (int i = 0; i < size; i++) {
counter[nums[i]]++;
}
// 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引”
// 即 counter[num]-1 是 num 在 res 中最后一次出现的索引
for (int i = 0; i < m; i++) {
counter[i + 1] += counter[i];
}
// 4. 倒序遍历 nums ,将各元素填入结果数组 res
// 初始化数组 res 用于记录结果
int *res = malloc(sizeof(int) * size);
for (int i = size - 1; i >= 0; i--) {
int num = nums[i];
res[counter[num] - 1] = num; // 将 num 放置到对应索引处
counter[num]--; // 令前缀和自减 1 ,得到下次放置 num 的索引
}
// 使用结果数组 res 覆盖原数组 nums
memcpy(nums, res, size * sizeof(int));
}
```
=== "C#"
```csharp title="counting_sort.cs"
/* 计数排序 */
// 完整实现,可排序对象,并且是稳定排序
void countingSort(int[] nums) {
// 1. 统计数组最大元素 m
int m = 0;
foreach (int num in nums) {
m = Math.Max(m, num);
}
// 2. 统计各数字的出现次数
// counter[num] 代表 num 的出现次数
int[] counter = new int[m + 1];
foreach (int num in nums) {
counter[num]++;
}
// 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引”
// 即 counter[num]-1 是 num 在 res 中最后一次出现的索引
for (int i = 0; i < m; i++) {
counter[i + 1] += counter[i];
}
// 4. 倒序遍历 nums ,将各元素填入结果数组 res
// 初始化数组 res 用于记录结果
int n = nums.Length;
int[] res = new int[n];
for (int i = n - 1; i >= 0; i--) {
int num = nums[i];
res[counter[num] - 1] = num; // 将 num 放置到对应索引处
counter[num]--; // 令前缀和自减 1 ,得到下次放置 num 的索引
}
// 使用结果数组 res 覆盖原数组 nums
for (int i = 0; i < n; i++) {
nums[i] = res[i];
}
}
```
=== "Swift"
```swift title="counting_sort.swift"
/* 计数排序 */
// 完整实现,可排序对象,并且是稳定排序
func countingSort(nums: inout [Int]) {
// 1. 统计数组最大元素 m
let m = nums.max()!
// 2. 统计各数字的出现次数
// counter[num] 代表 num 的出现次数
var counter = Array(repeating: 0, count: m + 1)
for num in nums {
counter[num] += 1
}
// 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引”
// 即 counter[num]-1 是 num 在 res 中最后一次出现的索引
for i in stride(from: 0, to: m, by: 1) {
counter[i + 1] += counter[i]
}
// 4. 倒序遍历 nums ,将各元素填入结果数组 res
// 初始化数组 res 用于记录结果
var res = Array(repeating: 0, count: nums.count)
for i in stride(from: nums.count - 1, through: 0, by: -1) {
let num = nums[i]
res[counter[num] - 1] = num // 将 num 放置到对应索引处
counter[num] -= 1 // 令前缀和自减 1 ,得到下次放置 num 的索引
}
// 使用结果数组 res 覆盖原数组 nums
for i in stride(from: 0, to: nums.count, by: 1) {
nums[i] = res[i]
}
}
```
=== "Zig"
```zig title="counting_sort.zig"
[class]{}-[func]{countingSort}
```
=== "Dart"
```dart title="counting_sort.dart"
@@ -772,6 +729,49 @@ $$
}
```
=== "C"
```c title="counting_sort.c"
/* 计数排序 */
// 完整实现,可排序对象,并且是稳定排序
void countingSort(int nums[], int size) {
// 1. 统计数组最大元素 m
int m = 0;
for (int i = 0; i < size; i++) {
if (nums[i] > m) {
m = nums[i];
}
}
// 2. 统计各数字的出现次数
// counter[num] 代表 num 的出现次数
int *counter = malloc(sizeof(int) * m);
for (int i = 0; i < size; i++) {
counter[nums[i]]++;
}
// 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引”
// 即 counter[num]-1 是 num 在 res 中最后一次出现的索引
for (int i = 0; i < m; i++) {
counter[i + 1] += counter[i];
}
// 4. 倒序遍历 nums ,将各元素填入结果数组 res
// 初始化数组 res 用于记录结果
int *res = malloc(sizeof(int) * size);
for (int i = size - 1; i >= 0; i--) {
int num = nums[i];
res[counter[num] - 1] = num; // 将 num 放置到对应索引处
counter[num]--; // 令前缀和自减 1 ,得到下次放置 num 的索引
}
// 使用结果数组 res 覆盖原数组 nums
memcpy(nums, res, size * sizeof(int));
}
```
=== "Zig"
```zig title="counting_sort.zig"
[class]{}-[func]{countingSort}
```
## 11.9.3 &nbsp; 算法特性
- **时间复杂度 $O(n + m)$** :涉及遍历 `nums` 和遍历 `counter` ,都使用线性时间。一般情况下 $n \gg m$ ,时间复杂度趋于 $O(n)$ 。
+183 -183
View File
@@ -68,6 +68,82 @@ comments: true
在代码实现中,我们使用了与堆章节相同的从顶至底堆化 `sift_down()` 函数。值得注意的是,由于堆的长度会随着提取最大元素而减小,因此我们需要给 `sift_down()` 函数添加一个长度参数 $n$ ,用于指定堆的当前有效长度。
=== "Python"
```python title="heap_sort.py"
def sift_down(nums: list[int], n: int, i: int):
"""堆的长度为 n ,从节点 i 开始,从顶至底堆化"""
while True:
# 判断节点 i, l, r 中值最大的节点,记为 ma
l = 2 * i + 1
r = 2 * i + 2
ma = i
if l < n and nums[l] > nums[ma]:
ma = l
if r < n and nums[r] > nums[ma]:
ma = r
# 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出
if ma == i:
break
# 交换两节点
nums[i], nums[ma] = nums[ma], nums[i]
# 循环向下堆化
i = ma
def heap_sort(nums: list[int]):
"""堆排序"""
# 建堆操作:堆化除叶节点以外的其他所有节点
for i in range(len(nums) // 2 - 1, -1, -1):
sift_down(nums, len(nums), i)
# 从堆中提取最大元素,循环 n-1 轮
for i in range(len(nums) - 1, 0, -1):
# 交换根节点与最右叶节点(即交换首元素与尾元素)
nums[0], nums[i] = nums[i], nums[0]
# 以根节点为起点,从顶至底进行堆化
sift_down(nums, i, 0)
```
=== "C++"
```cpp title="heap_sort.cpp"
/* 堆的长度为 n ,从节点 i 开始,从顶至底堆化 */
void siftDown(vector<int> &nums, int n, int i) {
while (true) {
// 判断节点 i, l, r 中值最大的节点,记为 ma
int l = 2 * i + 1;
int r = 2 * i + 2;
int ma = i;
if (l < n && nums[l] > nums[ma])
ma = l;
if (r < n && nums[r] > nums[ma])
ma = r;
// 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出
if (ma == i) {
break;
}
// 交换两节点
swap(nums[i], nums[ma]);
// 循环向下堆化
i = ma;
}
}
/* 堆排序 */
void heapSort(vector<int> &nums) {
// 建堆操作:堆化除叶节点以外的其他所有节点
for (int i = nums.size() / 2 - 1; i >= 0; --i) {
siftDown(nums, nums.size(), i);
}
// 从堆中提取最大元素,循环 n-1 轮
for (int i = nums.size() - 1; i > 0; --i) {
// 交换根节点与最右叶节点(即交换首元素与尾元素)
swap(nums[0], nums[i]);
// 以根节点为起点,从顶至底进行堆化
siftDown(nums, i, 0);
}
}
```
=== "Java"
```java title="heap_sort.java"
@@ -112,11 +188,11 @@ comments: true
}
```
=== "C++"
=== "C#"
```cpp title="heap_sort.cpp"
```csharp title="heap_sort.cs"
/* 堆的长度为 n ,从节点 i 开始,从顶至底堆化 */
void siftDown(vector<int> &nums, int n, int i) {
void siftDown(int[] nums, int n, int i) {
while (true) {
// 判断节点 i, l, r 中值最大的节点,记为 ma
int l = 2 * i + 1;
@@ -127,67 +203,31 @@ comments: true
if (r < n && nums[r] > nums[ma])
ma = r;
// 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出
if (ma == i) {
if (ma == i)
break;
}
// 交换两节点
swap(nums[i], nums[ma]);
(nums[ma], nums[i]) = (nums[i], nums[ma]);
// 循环向下堆化
i = ma;
}
}
/* 堆排序 */
void heapSort(vector<int> &nums) {
void heapSort(int[] nums) {
// 建堆操作:堆化除叶节点以外的其他所有节点
for (int i = nums.size() / 2 - 1; i >= 0; --i) {
siftDown(nums, nums.size(), i);
for (int i = nums.Length / 2 - 1; i >= 0; i--) {
siftDown(nums, nums.Length, i);
}
// 从堆中提取最大元素,循环 n-1 轮
for (int i = nums.size() - 1; i > 0; --i) {
for (int i = nums.Length - 1; i > 0; i--) {
// 交换根节点与最右叶节点(即交换首元素与尾元素)
swap(nums[0], nums[i]);
(nums[i], nums[0]) = (nums[0], nums[i]);
// 以根节点为起点,从顶至底进行堆化
siftDown(nums, i, 0);
}
}
```
=== "Python"
```python title="heap_sort.py"
def sift_down(nums: list[int], n: int, i: int):
"""堆的长度为 n ,从节点 i 开始,从顶至底堆化"""
while True:
# 判断节点 i, l, r 中值最大的节点,记为 ma
l = 2 * i + 1
r = 2 * i + 2
ma = i
if l < n and nums[l] > nums[ma]:
ma = l
if r < n and nums[r] > nums[ma]:
ma = r
# 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出
if ma == i:
break
# 交换两节点
nums[i], nums[ma] = nums[ma], nums[i]
# 循环向下堆化
i = ma
def heap_sort(nums: list[int]):
"""堆排序"""
# 建堆操作:堆化除叶节点以外的其他所有节点
for i in range(len(nums) // 2 - 1, -1, -1):
sift_down(nums, len(nums), i)
# 从堆中提取最大元素,循环 n-1 轮
for i in range(len(nums) - 1, 0, -1):
# 交换根节点与最右叶节点(即交换首元素与尾元素)
nums[0], nums[i] = nums[i], nums[0]
# 以根节点为起点,从顶至底进行堆化
sift_down(nums, i, 0)
```
=== "Go"
```go title="heap_sort.go"
@@ -231,6 +271,50 @@ comments: true
}
```
=== "Swift"
```swift title="heap_sort.swift"
/* 堆的长度为 n ,从节点 i 开始,从顶至底堆化 */
func siftDown(nums: inout [Int], n: Int, i: Int) {
var i = i
while true {
// 判断节点 i, l, r 中值最大的节点,记为 ma
let l = 2 * i + 1
let r = 2 * i + 2
var ma = i
if l < n, nums[l] > nums[ma] {
ma = l
}
if r < n, nums[r] > nums[ma] {
ma = r
}
// 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出
if ma == i {
break
}
// 交换两节点
nums.swapAt(i, ma)
// 循环向下堆化
i = ma
}
}
/* 堆排序 */
func heapSort(nums: inout [Int]) {
// 建堆操作:堆化除叶节点以外的其他所有节点
for i in stride(from: nums.count / 2 - 1, through: 0, by: -1) {
siftDown(nums: &nums, n: nums.count, i: i)
}
// 从堆中提取最大元素,循环 n-1 轮
for i in stride(from: nums.count - 1, to: 0, by: -1) {
// 交换根节点与最右叶节点(即交换首元素与尾元素)
nums.swapAt(0, i)
// 以根节点为起点,从顶至底进行堆化
siftDown(nums: &nums, n: i, i: 0)
}
}
```
=== "JS"
```javascript title="heap_sort.js"
@@ -317,143 +401,6 @@ comments: true
}
```
=== "C"
```c title="heap_sort.c"
/* 堆的长度为 n ,从节点 i 开始,从顶至底堆化 */
void siftDown(int nums[], int n, int i) {
while (1) {
// 判断节点 i, l, r 中值最大的节点,记为 ma
int l = 2 * i + 1;
int r = 2 * i + 2;
int ma = i;
if (l < n && nums[l] > nums[ma])
ma = l;
if (r < n && nums[r] > nums[ma])
ma = r;
// 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出
if (ma == i) {
break;
}
// 交换两节点
int temp = nums[i];
nums[i] = nums[ma];
nums[ma] = temp;
// 循环向下堆化
i = ma;
}
}
/* 堆排序 */
void heapSort(int nums[], int n) {
// 建堆操作:堆化除叶节点以外的其他所有节点
for (int i = n / 2 - 1; i >= 0; --i) {
siftDown(nums, n, i);
}
// 从堆中提取最大元素,循环 n-1 轮
for (int i = n - 1; i > 0; --i) {
// 交换根节点与最右叶节点(即交换首元素与尾元素)
int tmp = nums[0];
nums[0] = nums[i];
nums[i] = tmp;
// 以根节点为起点,从顶至底进行堆化
siftDown(nums, i, 0);
}
}
```
=== "C#"
```csharp title="heap_sort.cs"
/* 堆的长度为 n ,从节点 i 开始,从顶至底堆化 */
void siftDown(int[] nums, int n, int i) {
while (true) {
// 判断节点 i, l, r 中值最大的节点,记为 ma
int l = 2 * i + 1;
int r = 2 * i + 2;
int ma = i;
if (l < n && nums[l] > nums[ma])
ma = l;
if (r < n && nums[r] > nums[ma])
ma = r;
// 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出
if (ma == i)
break;
// 交换两节点
(nums[ma], nums[i]) = (nums[i], nums[ma]);
// 循环向下堆化
i = ma;
}
}
/* 堆排序 */
void heapSort(int[] nums) {
// 建堆操作:堆化除叶节点以外的其他所有节点
for (int i = nums.Length / 2 - 1; i >= 0; i--) {
siftDown(nums, nums.Length, i);
}
// 从堆中提取最大元素,循环 n-1 轮
for (int i = nums.Length - 1; i > 0; i--) {
// 交换根节点与最右叶节点(即交换首元素与尾元素)
(nums[i], nums[0]) = (nums[0], nums[i]);
// 以根节点为起点,从顶至底进行堆化
siftDown(nums, i, 0);
}
}
```
=== "Swift"
```swift title="heap_sort.swift"
/* 堆的长度为 n ,从节点 i 开始,从顶至底堆化 */
func siftDown(nums: inout [Int], n: Int, i: Int) {
var i = i
while true {
// 判断节点 i, l, r 中值最大的节点,记为 ma
let l = 2 * i + 1
let r = 2 * i + 2
var ma = i
if l < n, nums[l] > nums[ma] {
ma = l
}
if r < n, nums[r] > nums[ma] {
ma = r
}
// 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出
if ma == i {
break
}
// 交换两节点
nums.swapAt(i, ma)
// 循环向下堆化
i = ma
}
}
/* 堆排序 */
func heapSort(nums: inout [Int]) {
// 建堆操作:堆化除叶节点以外的其他所有节点
for i in stride(from: nums.count / 2 - 1, through: 0, by: -1) {
siftDown(nums: &nums, n: nums.count, i: i)
}
// 从堆中提取最大元素,循环 n-1 轮
for i in stride(from: nums.count - 1, to: 0, by: -1) {
// 交换根节点与最右叶节点(即交换首元素与尾元素)
nums.swapAt(0, i)
// 以根节点为起点,从顶至底进行堆化
siftDown(nums: &nums, n: i, i: 0)
}
}
```
=== "Zig"
```zig title="heap_sort.zig"
[class]{}-[func]{siftDown}
[class]{}-[func]{heapSort}
```
=== "Dart"
```dart title="heap_sort.dart"
@@ -542,6 +489,59 @@ comments: true
}
```
=== "C"
```c title="heap_sort.c"
/* 堆的长度为 n ,从节点 i 开始,从顶至底堆化 */
void siftDown(int nums[], int n, int i) {
while (1) {
// 判断节点 i, l, r 中值最大的节点,记为 ma
int l = 2 * i + 1;
int r = 2 * i + 2;
int ma = i;
if (l < n && nums[l] > nums[ma])
ma = l;
if (r < n && nums[r] > nums[ma])
ma = r;
// 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出
if (ma == i) {
break;
}
// 交换两节点
int temp = nums[i];
nums[i] = nums[ma];
nums[ma] = temp;
// 循环向下堆化
i = ma;
}
}
/* 堆排序 */
void heapSort(int nums[], int n) {
// 建堆操作:堆化除叶节点以外的其他所有节点
for (int i = n / 2 - 1; i >= 0; --i) {
siftDown(nums, n, i);
}
// 从堆中提取最大元素,循环 n-1 轮
for (int i = n - 1; i > 0; --i) {
// 交换根节点与最右叶节点(即交换首元素与尾元素)
int tmp = nums[0];
nums[0] = nums[i];
nums[i] = tmp;
// 以根节点为起点,从顶至底进行堆化
siftDown(nums, i, 0);
}
}
```
=== "Zig"
```zig title="heap_sort.zig"
[class]{}-[func]{siftDown}
[class]{}-[func]{heapSort}
```
## 11.7.2 &nbsp; 算法特性
- **时间复杂度 $O(n \log n)$、非自适应排序**:建堆操作使用 $O(n)$ 时间。从堆中提取最大元素的时间复杂度为 $O(\log n)$ ,共循环 $n - 1$ 轮。
+104 -104
View File
@@ -27,22 +27,20 @@ comments: true
<p align="center"> 图 11-7 &nbsp; 插入排序流程 </p>
=== "Java"
=== "Python"
```java title="insertion_sort.java"
/* 插入排序 */
void insertionSort(int[] nums) {
// 外循环:已排序元素数量为 1, 2, ..., n
for (int i = 1; i < nums.length; i++) {
int base = nums[i], j = i - 1;
// 内循环:将 base 插入到已排序部分的正确位置
while (j >= 0 && nums[j] > base) {
nums[j + 1] = nums[j]; // 将 nums[j] 向右移动一位
j--;
}
nums[j + 1] = base; // 将 base 赋值到正确位置
}
}
```python title="insertion_sort.py"
def insertion_sort(nums: list[int]):
"""插入排序"""
# 外循环:已排序区间为 [0, i-1]
for i in range(1, len(nums)):
base = nums[i]
j = i - 1
# 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置
while j >= 0 and nums[j] > base:
nums[j + 1] = nums[j] # 将 nums[j] 向右移动一位
j -= 1
nums[j + 1] = base # 将 base 赋值到正确位置
```
=== "C++"
@@ -63,20 +61,40 @@ comments: true
}
```
=== "Python"
=== "Java"
```python title="insertion_sort.py"
def insertion_sort(nums: list[int]):
"""插入排序"""
# 外循环:已排序区间为 [0, i-1]
for i in range(1, len(nums)):
base = nums[i]
j = i - 1
# 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置
while j >= 0 and nums[j] > base:
nums[j + 1] = nums[j] # 将 nums[j] 向右移动一位
j -= 1
nums[j + 1] = base # 将 base 赋值到正确位置
```java title="insertion_sort.java"
/* 插入排序 */
void insertionSort(int[] nums) {
// 外循环:已排序元素数量为 1, 2, ..., n
for (int i = 1; i < nums.length; i++) {
int base = nums[i], j = i - 1;
// 内循环:将 base 插入到已排序部分的正确位置
while (j >= 0 && nums[j] > base) {
nums[j + 1] = nums[j]; // 将 nums[j] 向右移动一位
j--;
}
nums[j + 1] = base; // 将 base 赋值到正确位置
}
}
```
=== "C#"
```csharp title="insertion_sort.cs"
/* 插入排序 */
void insertionSort(int[] nums) {
// 外循环:已排序元素数量为 1, 2, ..., n
for (int i = 1; i < nums.Length; i++) {
int bas = nums[i], j = i - 1;
// 内循环:将 base 插入到已排序部分的正确位置
while (j >= 0 && nums[j] > bas) {
nums[j + 1] = nums[j]; // 将 nums[j] 向右移动一位
j--;
}
nums[j + 1] = bas; // 将 base 赋值到正确位置
}
}
```
=== "Go"
@@ -98,6 +116,25 @@ comments: true
}
```
=== "Swift"
```swift title="insertion_sort.swift"
/* 插入排序 */
func insertionSort(nums: inout [Int]) {
// 外循环:已排序元素数量为 1, 2, ..., n
for i in stride(from: 1, to: nums.count, by: 1) {
let base = nums[i]
var j = i - 1
// 内循环:将 base 插入到已排序部分的正确位置
while j >= 0, nums[j] > base {
nums[j + 1] = nums[j] // 将 nums[j] 向右移动一位
j -= 1
}
nums[j + 1] = base // 将 base 赋值到正确位置
}
}
```
=== "JS"
```javascript title="insertion_sort.js"
@@ -136,82 +173,6 @@ comments: true
}
```
=== "C"
```c title="insertion_sort.c"
/* 插入排序 */
void insertionSort(int nums[], int size) {
// 外循环:已排序元素数量为 1, 2, ..., n
for (int i = 1; i < size; i++) {
int base = nums[i], j = i - 1;
// 内循环:将 base 插入到已排序部分的正确位置
while (j >= 0 && nums[j] > base) {
// 将 nums[j] 向右移动一位
nums[j + 1] = nums[j];
j--;
}
// 将 base 赋值到正确位置
nums[j + 1] = base;
}
}
```
=== "C#"
```csharp title="insertion_sort.cs"
/* 插入排序 */
void insertionSort(int[] nums) {
// 外循环:已排序元素数量为 1, 2, ..., n
for (int i = 1; i < nums.Length; i++) {
int bas = nums[i], j = i - 1;
// 内循环:将 base 插入到已排序部分的正确位置
while (j >= 0 && nums[j] > bas) {
nums[j + 1] = nums[j]; // 将 nums[j] 向右移动一位
j--;
}
nums[j + 1] = bas; // 将 base 赋值到正确位置
}
}
```
=== "Swift"
```swift title="insertion_sort.swift"
/* 插入排序 */
func insertionSort(nums: inout [Int]) {
// 外循环:已排序元素数量为 1, 2, ..., n
for i in stride(from: 1, to: nums.count, by: 1) {
let base = nums[i]
var j = i - 1
// 内循环:将 base 插入到已排序部分的正确位置
while j >= 0, nums[j] > base {
nums[j + 1] = nums[j] // 将 nums[j] 向右移动一位
j -= 1
}
nums[j + 1] = base // 将 base 赋值到正确位置
}
}
```
=== "Zig"
```zig title="insertion_sort.zig"
// 插入排序
fn insertionSort(nums: []i32) void {
// 外循环:已排序元素数量为 1, 2, ..., n
var i: usize = 1;
while (i < nums.len) : (i += 1) {
var base = nums[i];
var j: usize = i;
// 内循环:将 base 插入到已排序部分的正确位置
while (j >= 1 and nums[j - 1] > base) : (j -= 1) {
nums[j] = nums[j - 1]; // 将 nums[j] 向右移动一位
}
nums[j] = base; // 将 base 赋值到正确位置
}
}
```
=== "Dart"
```dart title="insertion_sort.dart"
@@ -248,6 +209,45 @@ comments: true
}
```
=== "C"
```c title="insertion_sort.c"
/* 插入排序 */
void insertionSort(int nums[], int size) {
// 外循环:已排序元素数量为 1, 2, ..., n
for (int i = 1; i < size; i++) {
int base = nums[i], j = i - 1;
// 内循环:将 base 插入到已排序部分的正确位置
while (j >= 0 && nums[j] > base) {
// 将 nums[j] 向右移动一位
nums[j + 1] = nums[j];
j--;
}
// 将 base 赋值到正确位置
nums[j + 1] = base;
}
}
```
=== "Zig"
```zig title="insertion_sort.zig"
// 插入排序
fn insertionSort(nums: []i32) void {
// 外循环:已排序元素数量为 1, 2, ..., n
var i: usize = 1;
while (i < nums.len) : (i += 1) {
var base = nums[i];
var j: usize = i;
// 内循环:将 base 插入到已排序部分的正确位置
while (j >= 1 and nums[j - 1] > base) : (j -= 1) {
nums[j] = nums[j - 1]; // 将 nums[j] 向右移动一位
}
nums[j] = base; // 将 base 赋值到正确位置
}
}
```
## 11.4.2 &nbsp; 算法特性
- **时间复杂度 $O(n^2)$、自适应排序**:最差情况下,每次插入操作分别需要循环 $n - 1$、$n-2$、$\dots$、$2$、$1$ 次,求和得到 $(n - 1) n / 2$ ,因此时间复杂度为 $O(n^2)$ 。在遇到有序数据时,插入操作会提前终止。当输入数组完全有序时,插入排序达到最佳时间复杂度 $O(n)$ 。
+277 -277
View File
@@ -59,47 +59,50 @@ comments: true
- **后序遍历**:先递归左子树,再递归右子树,最后处理根节点。
- **归并排序**:先递归左子数组,再递归右子数组,最后处理合并。
=== "Java"
=== "Python"
```java title="merge_sort.java"
/* 合并左子数组和右子数组 */
// 左子数组区间 [left, mid]
// 右子数组区间 [mid + 1, right]
void merge(int[] nums, int left, int mid, int right) {
// 初始化辅助数组
int[] tmp = Arrays.copyOfRange(nums, left, right + 1);
// 左子数组的起始索引和结束索引
int leftStart = left - left, leftEnd = mid - left;
// 右子数组的起始索引和结束索引
int rightStart = mid + 1 - left, rightEnd = right - left;
// i, j 分别指向左子数组、右子数组的首元素
int i = leftStart, j = rightStart;
// 通过覆盖原数组 nums 来合并左子数组右子数组
for (int k = left; k <= right; k++) {
// 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++
if (i > leftEnd)
nums[k] = tmp[j++];
// 否则,若“子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取子数组元素,并且 i++
else if (j > rightEnd || tmp[i] <= tmp[j])
nums[k] = tmp[i++];
// 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++
else
nums[k] = tmp[j++];
}
}
```python title="merge_sort.py"
def merge(nums: list[int], left: int, mid: int, right: int):
"""合并左子数组和右子数组"""
# 左子数组区间 [left, mid]
# 右子数组区间 [mid + 1, right]
# 初始化辅助数组
tmp = list(nums[left : right + 1])
# 左子数组的起始索引和结束索引
left_start = 0
left_end = mid - left
# 右子数组的起始索引和结束索引
right_start = mid + 1 - left
right_end = right - left
# i, j 分别指向左子数组右子数组的首元素
i = left_start
j = right_start
# 通过覆盖原数组 nums 来合并左子数组和右子数组
for k in range(left, right + 1):
# 若“子数组已全部合并完”,则选取子数组元素,并且 j++
if i > left_end:
nums[k] = tmp[j]
j += 1
# 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++
elif j > right_end or tmp[i] <= tmp[j]:
nums[k] = tmp[i]
i += 1
# 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++
else:
nums[k] = tmp[j]
j += 1
/* 归并排序 */
void mergeSort(int[] nums, int left, int right) {
// 终止条件
if (left >= right)
return; // 当子数组长度为 1 时终止递归
// 划分阶段
int mid = (left + right) / 2; // 计算中点
mergeSort(nums, left, mid); // 递归左子数组
mergeSort(nums, mid + 1, right); // 递归右子数组
// 合并阶段
merge(nums, left, mid, right);
}
def merge_sort(nums: list[int], left: int, right: int):
"""归并排序"""
# 终止条件
if left >= right:
return # 当子数组长度为 1 时终止递归
# 划分阶段
mid = (left + right) // 2 # 计算中点
merge_sort(nums, left, mid) # 递归左子数组
merge_sort(nums, mid + 1, right) # 递归右子数组
# 合并阶段
merge(nums, left, mid, right)
```
=== "C++"
@@ -145,50 +148,89 @@ comments: true
}
```
=== "Python"
=== "Java"
```python title="merge_sort.py"
def merge(nums: list[int], left: int, mid: int, right: int):
"""合并左子数组和右子数组"""
# 左子数组区间 [left, mid]
# 右子数组区间 [mid + 1, right]
# 初始化辅助数组
tmp = list(nums[left : right + 1])
# 左子数组的起始索引和结束索引
left_start = 0
left_end = mid - left
# 右子数组的起始索引和结束索引
right_start = mid + 1 - left
right_end = right - left
# i, j 分别指向左子数组右子数组的首元素
i = left_start
j = right_start
# 通过覆盖原数组 nums 来合并左子数组和右子数组
for k in range(left, right + 1):
# 若“子数组已全部合并完”,则选取子数组元素,并且 j++
if i > left_end:
nums[k] = tmp[j]
j += 1
# 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++
elif j > right_end or tmp[i] <= tmp[j]:
nums[k] = tmp[i]
i += 1
# 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++
else:
nums[k] = tmp[j]
j += 1
```java title="merge_sort.java"
/* 合并左子数组和右子数组 */
// 左子数组区间 [left, mid]
// 右子数组区间 [mid + 1, right]
void merge(int[] nums, int left, int mid, int right) {
// 初始化辅助数组
int[] tmp = Arrays.copyOfRange(nums, left, right + 1);
// 左子数组的起始索引和结束索引
int leftStart = left - left, leftEnd = mid - left;
// 右子数组的起始索引和结束索引
int rightStart = mid + 1 - left, rightEnd = right - left;
// i, j 分别指向左子数组、右子数组的首元素
int i = leftStart, j = rightStart;
// 通过覆盖原数组 nums 来合并左子数组右子数组
for (int k = left; k <= right; k++) {
// 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++
if (i > leftEnd)
nums[k] = tmp[j++];
// 否则,若“子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取子数组元素,并且 i++
else if (j > rightEnd || tmp[i] <= tmp[j])
nums[k] = tmp[i++];
// 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++
else
nums[k] = tmp[j++];
}
}
def merge_sort(nums: list[int], left: int, right: int):
"""归并排序"""
# 终止条件
if left >= right:
return # 当子数组长度为 1 时终止递归
# 划分阶段
mid = (left + right) // 2 # 计算中点
merge_sort(nums, left, mid) # 递归左子数组
merge_sort(nums, mid + 1, right) # 递归右子数组
# 合并阶段
merge(nums, left, mid, right)
/* 归并排序 */
void mergeSort(int[] nums, int left, int right) {
// 终止条件
if (left >= right)
return; // 当子数组长度为 1 时终止递归
// 划分阶段
int mid = (left + right) / 2; // 计算中点
mergeSort(nums, left, mid); // 递归左子数组
mergeSort(nums, mid + 1, right); // 递归右子数组
// 合并阶段
merge(nums, left, mid, right);
}
```
=== "C#"
```csharp title="merge_sort.cs"
/* 合并左子数组和右子数组 */
// 左子数组区间 [left, mid]
// 右子数组区间 [mid + 1, right]
void merge(int[] nums, int left, int mid, int right) {
// 初始化辅助数组
int[] tmp = nums[left..(right + 1)];
// 左子数组的起始索引和结束索引
int leftStart = left - left, leftEnd = mid - left;
// 右子数组的起始索引和结束索引
int rightStart = mid + 1 - left, rightEnd = right - left;
// i, j 分别指向左子数组、右子数组的首元素
int i = leftStart, j = rightStart;
// 通过覆盖原数组 nums 来合并左子数组和右子数组
for (int k = left; k <= right; k++) {
// 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++
if (i > leftEnd)
nums[k] = tmp[j++];
// 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++
else if (j > rightEnd || tmp[i] <= tmp[j])
nums[k] = tmp[i++];
// 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++
else
nums[k] = tmp[j++];
}
}
/* 归并排序 */
void mergeSort(int[] nums, int left, int right) {
// 终止条件
if (left >= right) return; // 当子数组长度为 1 时终止递归
// 划分阶段
int mid = (left + right) / 2; // 计算中点
mergeSort(nums, left, mid); // 递归左子数组
mergeSort(nums, mid + 1, right); // 递归右子数组
// 合并阶段
merge(nums, left, mid, right);
}
```
=== "Go"
@@ -242,6 +284,59 @@ comments: true
}
```
=== "Swift"
```swift title="merge_sort.swift"
/* 合并左子数组和右子数组 */
// 左子数组区间 [left, mid]
// 右子数组区间 [mid + 1, right]
func merge(nums: inout [Int], left: Int, mid: Int, right: Int) {
// 初始化辅助数组
let tmp = Array(nums[left ..< (right + 1)])
// 左子数组的起始索引和结束索引
let leftStart = left - left
let leftEnd = mid - left
// 右子数组的起始索引和结束索引
let rightStart = mid + 1 - left
let rightEnd = right - left
// i, j 分别指向左子数组、右子数组的首元素
var i = leftStart
var j = rightStart
// 通过覆盖原数组 nums 来合并左子数组和右子数组
for k in left ... right {
// 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++
if i > leftEnd {
nums[k] = tmp[j]
j += 1
}
// 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++
else if j > rightEnd || tmp[i] <= tmp[j] {
nums[k] = tmp[i]
i += 1
}
// 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++
else {
nums[k] = tmp[j]
j += 1
}
}
}
/* 归并排序 */
func mergeSort(nums: inout [Int], left: Int, right: Int) {
// 终止条件
if left >= right { // 当子数组长度为 1 时终止递归
return
}
// 划分阶段
let mid = (left + right) / 2 // 计算中点
mergeSort(nums: &nums, left: left, right: mid) // 递归左子数组
mergeSort(nums: &nums, left: mid + 1, right: right) // 递归右子数组
// 合并阶段
merge(nums: &nums, left: left, mid: mid, right: right)
}
```
=== "JS"
```javascript title="merge_sort.js"
@@ -334,202 +429,6 @@ comments: true
}
```
=== "C"
```c title="merge_sort.c"
/* 合并左子数组和右子数组 */
// 左子数组区间 [left, mid]
// 右子数组区间 [mid + 1, right]
void merge(int *nums, int left, int mid, int right) {
int index;
// 初始化辅助数组
int tmp[right + 1 - left];
for (index = left; index < right + 1; index++) {
tmp[index - left] = nums[index];
}
// 左子数组的起始索引和结束索引
int leftStart = left - left, leftEnd = mid - left;
// 右子数组的起始索引和结束索引
int rightStart = mid + 1 - left, rightEnd = right - left;
// i, j 分别指向左子数组、右子数组的首元素
int i = leftStart, j = rightStart;
// 通过覆盖原数组 nums 来合并左子数组和右子数组
for (int k = left; k <= right; k++) {
// 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++
if (i > leftEnd)
nums[k] = tmp[j++];
// 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++
else if (j > rightEnd || tmp[i] <= tmp[j])
nums[k] = tmp[i++];
// 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++
else
nums[k] = tmp[j++];
}
}
/* 归并排序 */
void mergeSort(int *nums, int left, int right) {
// 终止条件
if (left >= right)
return; // 当子数组长度为 1 时终止递归
// 划分阶段
int mid = (left + right) / 2; // 计算中点
mergeSort(nums, left, mid); // 递归左子数组
mergeSort(nums, mid + 1, right); // 递归右子数组
// 合并阶段
merge(nums, left, mid, right);
}
```
=== "C#"
```csharp title="merge_sort.cs"
/* 合并左子数组和右子数组 */
// 左子数组区间 [left, mid]
// 右子数组区间 [mid + 1, right]
void merge(int[] nums, int left, int mid, int right) {
// 初始化辅助数组
int[] tmp = nums[left..(right + 1)];
// 左子数组的起始索引和结束索引
int leftStart = left - left, leftEnd = mid - left;
// 右子数组的起始索引和结束索引
int rightStart = mid + 1 - left, rightEnd = right - left;
// i, j 分别指向左子数组、右子数组的首元素
int i = leftStart, j = rightStart;
// 通过覆盖原数组 nums 来合并左子数组和右子数组
for (int k = left; k <= right; k++) {
// 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++
if (i > leftEnd)
nums[k] = tmp[j++];
// 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++
else if (j > rightEnd || tmp[i] <= tmp[j])
nums[k] = tmp[i++];
// 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++
else
nums[k] = tmp[j++];
}
}
/* 归并排序 */
void mergeSort(int[] nums, int left, int right) {
// 终止条件
if (left >= right) return; // 当子数组长度为 1 时终止递归
// 划分阶段
int mid = (left + right) / 2; // 计算中点
mergeSort(nums, left, mid); // 递归左子数组
mergeSort(nums, mid + 1, right); // 递归右子数组
// 合并阶段
merge(nums, left, mid, right);
}
```
=== "Swift"
```swift title="merge_sort.swift"
/* 合并左子数组和右子数组 */
// 左子数组区间 [left, mid]
// 右子数组区间 [mid + 1, right]
func merge(nums: inout [Int], left: Int, mid: Int, right: Int) {
// 初始化辅助数组
let tmp = Array(nums[left ..< (right + 1)])
// 左子数组的起始索引和结束索引
let leftStart = left - left
let leftEnd = mid - left
// 右子数组的起始索引和结束索引
let rightStart = mid + 1 - left
let rightEnd = right - left
// i, j 分别指向左子数组、右子数组的首元素
var i = leftStart
var j = rightStart
// 通过覆盖原数组 nums 来合并左子数组和右子数组
for k in left ... right {
// 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++
if i > leftEnd {
nums[k] = tmp[j]
j += 1
}
// 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++
else if j > rightEnd || tmp[i] <= tmp[j] {
nums[k] = tmp[i]
i += 1
}
// 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++
else {
nums[k] = tmp[j]
j += 1
}
}
}
/* 归并排序 */
func mergeSort(nums: inout [Int], left: Int, right: Int) {
// 终止条件
if left >= right { // 当子数组长度为 1 时终止递归
return
}
// 划分阶段
let mid = (left + right) / 2 // 计算中点
mergeSort(nums: &nums, left: left, right: mid) // 递归左子数组
mergeSort(nums: &nums, left: mid + 1, right: right) // 递归右子数组
// 合并阶段
merge(nums: &nums, left: left, mid: mid, right: right)
}
```
=== "Zig"
```zig title="merge_sort.zig"
// 合并左子数组和右子数组
// 左子数组区间 [left, mid]
// 右子数组区间 [mid + 1, right]
fn merge(nums: []i32, left: usize, mid: usize, right: usize) !void {
// 初始化辅助数组
var mem_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer mem_arena.deinit();
const mem_allocator = mem_arena.allocator();
var tmp = try mem_allocator.alloc(i32, right + 1 - left);
std.mem.copy(i32, tmp, nums[left..right+1]);
// 左子数组的起始索引和结束索引
var leftStart = left - left;
var leftEnd = mid - left;
// 右子数组的起始索引和结束索引
var rightStart = mid + 1 - left;
var rightEnd = right - left;
// i, j 分别指向左子数组、右子数组的首元素
var i = leftStart;
var j = rightStart;
// 通过覆盖原数组 nums 来合并左子数组和右子数组
var k = left;
while (k <= right) : (k += 1) {
// 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++
if (i > leftEnd) {
nums[k] = tmp[j];
j += 1;
// 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++
} else if (j > rightEnd or tmp[i] <= tmp[j]) {
nums[k] = tmp[i];
i += 1;
// 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++
} else {
nums[k] = tmp[j];
j += 1;
}
}
}
// 归并排序
fn mergeSort(nums: []i32, left: usize, right: usize) !void {
// 终止条件
if (left >= right) return; // 当子数组长度为 1 时终止递归
// 划分阶段
var mid = (left + right) / 2; // 计算中点
try mergeSort(nums, left, mid); // 递归左子数组
try mergeSort(nums, mid + 1, right); // 递归右子数组
// 合并阶段
try merge(nums, left, mid, right);
}
```
=== "Dart"
```dart title="merge_sort.dart"
@@ -620,6 +519,107 @@ comments: true
}
```
=== "C"
```c title="merge_sort.c"
/* 合并左子数组和右子数组 */
// 左子数组区间 [left, mid]
// 右子数组区间 [mid + 1, right]
void merge(int *nums, int left, int mid, int right) {
int index;
// 初始化辅助数组
int tmp[right + 1 - left];
for (index = left; index < right + 1; index++) {
tmp[index - left] = nums[index];
}
// 左子数组的起始索引和结束索引
int leftStart = left - left, leftEnd = mid - left;
// 右子数组的起始索引和结束索引
int rightStart = mid + 1 - left, rightEnd = right - left;
// i, j 分别指向左子数组、右子数组的首元素
int i = leftStart, j = rightStart;
// 通过覆盖原数组 nums 来合并左子数组和右子数组
for (int k = left; k <= right; k++) {
// 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++
if (i > leftEnd)
nums[k] = tmp[j++];
// 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++
else if (j > rightEnd || tmp[i] <= tmp[j])
nums[k] = tmp[i++];
// 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++
else
nums[k] = tmp[j++];
}
}
/* 归并排序 */
void mergeSort(int *nums, int left, int right) {
// 终止条件
if (left >= right)
return; // 当子数组长度为 1 时终止递归
// 划分阶段
int mid = (left + right) / 2; // 计算中点
mergeSort(nums, left, mid); // 递归左子数组
mergeSort(nums, mid + 1, right); // 递归右子数组
// 合并阶段
merge(nums, left, mid, right);
}
```
=== "Zig"
```zig title="merge_sort.zig"
// 合并左子数组和右子数组
// 左子数组区间 [left, mid]
// 右子数组区间 [mid + 1, right]
fn merge(nums: []i32, left: usize, mid: usize, right: usize) !void {
// 初始化辅助数组
var mem_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer mem_arena.deinit();
const mem_allocator = mem_arena.allocator();
var tmp = try mem_allocator.alloc(i32, right + 1 - left);
std.mem.copy(i32, tmp, nums[left..right+1]);
// 左子数组的起始索引和结束索引
var leftStart = left - left;
var leftEnd = mid - left;
// 右子数组的起始索引和结束索引
var rightStart = mid + 1 - left;
var rightEnd = right - left;
// i, j 分别指向左子数组、右子数组的首元素
var i = leftStart;
var j = rightStart;
// 通过覆盖原数组 nums 来合并左子数组和右子数组
var k = left;
while (k <= right) : (k += 1) {
// 若“左子数组已全部合并完”,则选取右子数组元素,并且 j++
if (i > leftEnd) {
nums[k] = tmp[j];
j += 1;
// 否则,若“右子数组已全部合并完”或“左子数组元素 <= 右子数组元素”,则选取左子数组元素,并且 i++
} else if (j > rightEnd or tmp[i] <= tmp[j]) {
nums[k] = tmp[i];
i += 1;
// 否则,若“左右子数组都未全部合并完”且“左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++
} else {
nums[k] = tmp[j];
j += 1;
}
}
}
// 归并排序
fn mergeSort(nums: []i32, left: usize, right: usize) !void {
// 终止条件
if (left >= right) return; // 当子数组长度为 1 时终止递归
// 划分阶段
var mid = (left + right) / 2; // 计算中点
try mergeSort(nums, left, mid); // 递归左子数组
try mergeSort(nums, mid + 1, right); // 递归右子数组
// 合并阶段
try merge(nums, left, mid, right);
}
```
实现合并函数 `merge()` 存在以下难点。
- **需要特别注意各个变量的含义**。`nums` 的待合并区间为 `[left, right]` ,但由于 `tmp` 仅复制了 `nums` 该区间的元素,因此 `tmp` 对应区间为 `[0, right - left]` 。
File diff suppressed because it is too large Load Diff
+289 -289
View File
@@ -30,6 +30,102 @@ $$
此外,我们需要小幅改动计数排序代码,使之可以根据数字的第 $k$ 位进行排序。
=== "Python"
```python title="radix_sort.py"
def digit(num: int, exp: int) -> int:
"""获取元素 num 的第 k 位,其中 exp = 10^(k-1)"""
# 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算
return (num // exp) % 10
def counting_sort_digit(nums: list[int], exp: int):
"""计数排序(根据 nums 第 k 位排序)"""
# 十进制的位范围为 0~9 ,因此需要长度为 10 的桶
counter = [0] * 10
n = len(nums)
# 统计 0~9 各数字的出现次数
for i in range(n):
d = digit(nums[i], exp) # 获取 nums[i] 第 k 位,记为 d
counter[d] += 1 # 统计数字 d 的出现次数
# 求前缀和,将“出现个数”转换为“数组索引”
for i in range(1, 10):
counter[i] += counter[i - 1]
# 倒序遍历,根据桶内统计结果,将各元素填入 res
res = [0] * n
for i in range(n - 1, -1, -1):
d = digit(nums[i], exp)
j = counter[d] - 1 # 获取 d 在数组中的索引 j
res[j] = nums[i] # 将当前元素填入索引 j
counter[d] -= 1 # 将 d 的数量减 1
# 使用结果覆盖原数组 nums
for i in range(n):
nums[i] = res[i]
def radix_sort(nums: list[int]):
"""基数排序"""
# 获取数组的最大元素,用于判断最大位数
m = max(nums)
# 按照从低位到高位的顺序遍历
exp = 1
while exp <= m:
# 对数组元素的第 k 位执行计数排序
# k = 1 -> exp = 1
# k = 2 -> exp = 10
# 即 exp = 10^(k-1)
counting_sort_digit(nums, exp)
exp *= 10
```
=== "C++"
```cpp title="radix_sort.cpp"
/* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */
int digit(int num, int exp) {
// 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算
return (num / exp) % 10;
}
/* 计数排序(根据 nums 第 k 位排序) */
void countingSortDigit(vector<int> &nums, int exp) {
// 十进制的位范围为 0~9 ,因此需要长度为 10 的桶
vector<int> counter(10, 0);
int n = nums.size();
// 统计 0~9 各数字的出现次数
for (int i = 0; i < n; i++) {
int d = digit(nums[i], exp); // 获取 nums[i] 第 k 位,记为 d
counter[d]++; // 统计数字 d 的出现次数
}
// 求前缀和,将“出现个数”转换为“数组索引”
for (int i = 1; i < 10; i++) {
counter[i] += counter[i - 1];
}
// 倒序遍历,根据桶内统计结果,将各元素填入 res
vector<int> res(n, 0);
for (int i = n - 1; i >= 0; i--) {
int d = digit(nums[i], exp);
int j = counter[d] - 1; // 获取 d 在数组中的索引 j
res[j] = nums[i]; // 将当前元素填入索引 j
counter[d]--; // 将 d 的数量减 1
}
// 使用结果覆盖原数组 nums
for (int i = 0; i < n; i++)
nums[i] = res[i];
}
/* 基数排序 */
void radixSort(vector<int> &nums) {
// 获取数组的最大元素,用于判断最大位数
int m = *max_element(nums.begin(), nums.end());
// 按照从低位到高位的顺序遍历
for (int exp = 1; exp <= m; exp *= 10)
// 对数组元素的第 k 位执行计数排序
// k = 1 -> exp = 1
// k = 2 -> exp = 10
// 即 exp = 10^(k-1)
countingSortDigit(nums, exp);
}
```
=== "Java"
```java title="radix_sort.java"
@@ -83,9 +179,9 @@ $$
}
```
=== "C++"
=== "C#"
```cpp title="radix_sort.cpp"
```csharp title="radix_sort.cs"
/* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */
int digit(int num, int exp) {
// 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算
@@ -93,10 +189,10 @@ $$
}
/* 计数排序(根据 nums 第 k 位排序) */
void countingSortDigit(vector<int> &nums, int exp) {
void countingSortDigit(int[] nums, int exp) {
// 十进制的位范围为 0~9 ,因此需要长度为 10 的桶
vector<int> counter(10, 0);
int n = nums.size();
int[] counter = new int[10];
int n = nums.Length;
// 统计 0~9 各数字的出现次数
for (int i = 0; i < n; i++) {
int d = digit(nums[i], exp); // 获取 nums[i] 第 k 位,记为 d
@@ -107,7 +203,7 @@ $$
counter[i] += counter[i - 1];
}
// 倒序遍历,根据桶内统计结果,将各元素填入 res
vector<int> res(n, 0);
int[] res = new int[n];
for (int i = n - 1; i >= 0; i--) {
int d = digit(nums[i], exp);
int j = counter[d] - 1; // 获取 d 在数组中的索引 j
@@ -115,70 +211,29 @@ $$
counter[d]--; // 将 d 的数量减 1
}
// 使用结果覆盖原数组 nums
for (int i = 0; i < n; i++)
for (int i = 0; i < n; i++) {
nums[i] = res[i];
}
}
/* 基数排序 */
void radixSort(vector<int> &nums) {
void radixSort(int[] nums) {
// 获取数组的最大元素,用于判断最大位数
int m = *max_element(nums.begin(), nums.end());
int m = int.MinValue;
foreach (int num in nums) {
if (num > m) m = num;
}
// 按照从低位到高位的顺序遍历
for (int exp = 1; exp <= m; exp *= 10)
for (int exp = 1; exp <= m; exp *= 10) {
// 对数组元素的第 k 位执行计数排序
// k = 1 -> exp = 1
// k = 2 -> exp = 10
// 即 exp = 10^(k-1)
countingSortDigit(nums, exp);
}
}
```
=== "Python"
```python title="radix_sort.py"
def digit(num: int, exp: int) -> int:
"""获取元素 num 的第 k 位,其中 exp = 10^(k-1)"""
# 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算
return (num // exp) % 10
def counting_sort_digit(nums: list[int], exp: int):
"""计数排序(根据 nums 第 k 位排序)"""
# 十进制的位范围为 0~9 ,因此需要长度为 10 的桶
counter = [0] * 10
n = len(nums)
# 统计 0~9 各数字的出现次数
for i in range(n):
d = digit(nums[i], exp) # 获取 nums[i] 第 k 位,记为 d
counter[d] += 1 # 统计数字 d 的出现次数
# 求前缀和,将“出现个数”转换为“数组索引”
for i in range(1, 10):
counter[i] += counter[i - 1]
# 倒序遍历,根据桶内统计结果,将各元素填入 res
res = [0] * n
for i in range(n - 1, -1, -1):
d = digit(nums[i], exp)
j = counter[d] - 1 # 获取 d 在数组中的索引 j
res[j] = nums[i] # 将当前元素填入索引 j
counter[d] -= 1 # 将 d 的数量减 1
# 使用结果覆盖原数组 nums
for i in range(n):
nums[i] = res[i]
def radix_sort(nums: list[int]):
"""基数排序"""
# 获取数组的最大元素,用于判断最大位数
m = max(nums)
# 按照从低位到高位的顺序遍历
exp = 1
while exp <= m:
# 对数组元素的第 k 位执行计数排序
# k = 1 -> exp = 1
# k = 2 -> exp = 10
# 即 exp = 10^(k-1)
counting_sort_digit(nums, exp)
exp *= 10
```
=== "Go"
```go title="radix_sort.go"
@@ -236,6 +291,63 @@ $$
}
```
=== "Swift"
```swift title="radix_sort.swift"
/* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */
func digit(num: Int, exp: Int) -> Int {
// 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算
(num / exp) % 10
}
/* 计数排序(根据 nums 第 k 位排序) */
func countingSortDigit(nums: inout [Int], exp: Int) {
// 十进制的位范围为 0~9 ,因此需要长度为 10 的桶
var counter = Array(repeating: 0, count: 10)
let n = nums.count
// 统计 0~9 各数字的出现次数
for i in nums.indices {
let d = digit(num: nums[i], exp: exp) // 获取 nums[i] 第 k 位,记为 d
counter[d] += 1 // 统计数字 d 的出现次数
}
// 求前缀和,将“出现个数”转换为“数组索引”
for i in 1 ..< 10 {
counter[i] += counter[i - 1]
}
// 倒序遍历,根据桶内统计结果,将各元素填入 res
var res = Array(repeating: 0, count: n)
for i in stride(from: n - 1, through: 0, by: -1) {
let d = digit(num: nums[i], exp: exp)
let j = counter[d] - 1 // 获取 d 在数组中的索引 j
res[j] = nums[i] // 将当前元素填入索引 j
counter[d] -= 1 // 将 d 的数量减 1
}
// 使用结果覆盖原数组 nums
for i in nums.indices {
nums[i] = res[i]
}
}
/* 基数排序 */
func radixSort(nums: inout [Int]) {
// 获取数组的最大元素,用于判断最大位数
var m = Int.min
for num in nums {
if num > m {
m = num
}
}
// 按照从低位到高位的顺序遍历
for exp in sequence(first: 1, next: { m >= ($0 * 10) ? $0 * 10 : nil }) {
// 对数组元素的第 k 位执行计数排序
// k = 1 -> exp = 1
// k = 2 -> exp = 10
// 即 exp = 10^(k-1)
countingSortDigit(nums: &nums, exp: exp)
}
}
```
=== "JS"
```javascript title="radix_sort.js"
@@ -350,239 +462,6 @@ $$
}
```
=== "C"
```c title="radix_sort.c"
/* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */
int digit(int num, int exp) {
// 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算
return (num / exp) % 10;
}
/* 计数排序(根据 nums 第 k 位排序) */
void countingSortDigit(int nums[], int size, int exp) {
// 十进制的位范围为 0~9 ,因此需要长度为 10 的桶
int *counter = (int *)malloc((sizeof(int) * 10));
// 统计 0~9 各数字的出现次数
for (int i = 0; i < size; i++) {
// 获取 nums[i] 第 k 位,记为 d
int d = digit(nums[i], exp);
// 统计数字 d 的出现次数
counter[d]++;
}
// 求前缀和,将“出现个数”转换为“数组索引”
for (int i = 1; i < 10; i++) {
counter[i] += counter[i - 1];
}
// 倒序遍历,根据桶内统计结果,将各元素填入 res
int *res = (int *)malloc(sizeof(int) * size);
for (int i = size - 1; i >= 0; i--) {
int d = digit(nums[i], exp);
int j = counter[d] - 1; // 获取 d 在数组中的索引 j
res[j] = nums[i]; // 将当前元素填入索引 j
counter[d]--; // 将 d 的数量减 1
}
// 使用结果覆盖原数组 nums
for (int i = 0; i < size; i++) {
nums[i] = res[i];
}
}
/* 基数排序 */
void radixSort(int nums[], int size) {
// 获取数组的最大元素,用于判断最大位数
int max = INT32_MIN;
for (size_t i = 0; i < size - 1; i++) {
if (nums[i] > max) {
max = nums[i];
}
}
// 按照从低位到高位的顺序遍历
for (int exp = 1; max >= exp; exp *= 10)
// 对数组元素的第 k 位执行计数排序
// k = 1 -> exp = 1
// k = 2 -> exp = 10
// 即 exp = 10^(k-1)
countingSortDigit(nums, size, exp);
}
```
=== "C#"
```csharp title="radix_sort.cs"
/* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */
int digit(int num, int exp) {
// 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算
return (num / exp) % 10;
}
/* 计数排序(根据 nums 第 k 位排序) */
void countingSortDigit(int[] nums, int exp) {
// 十进制的位范围为 0~9 ,因此需要长度为 10 的桶
int[] counter = new int[10];
int n = nums.Length;
// 统计 0~9 各数字的出现次数
for (int i = 0; i < n; i++) {
int d = digit(nums[i], exp); // 获取 nums[i] 第 k 位,记为 d
counter[d]++; // 统计数字 d 的出现次数
}
// 求前缀和,将“出现个数”转换为“数组索引”
for (int i = 1; i < 10; i++) {
counter[i] += counter[i - 1];
}
// 倒序遍历,根据桶内统计结果,将各元素填入 res
int[] res = new int[n];
for (int i = n - 1; i >= 0; i--) {
int d = digit(nums[i], exp);
int j = counter[d] - 1; // 获取 d 在数组中的索引 j
res[j] = nums[i]; // 将当前元素填入索引 j
counter[d]--; // 将 d 的数量减 1
}
// 使用结果覆盖原数组 nums
for (int i = 0; i < n; i++) {
nums[i] = res[i];
}
}
/* 基数排序 */
void radixSort(int[] nums) {
// 获取数组的最大元素,用于判断最大位数
int m = int.MinValue;
foreach (int num in nums) {
if (num > m) m = num;
}
// 按照从低位到高位的顺序遍历
for (int exp = 1; exp <= m; exp *= 10) {
// 对数组元素的第 k 位执行计数排序
// k = 1 -> exp = 1
// k = 2 -> exp = 10
// 即 exp = 10^(k-1)
countingSortDigit(nums, exp);
}
}
```
=== "Swift"
```swift title="radix_sort.swift"
/* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */
func digit(num: Int, exp: Int) -> Int {
// 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算
(num / exp) % 10
}
/* 计数排序(根据 nums 第 k 位排序) */
func countingSortDigit(nums: inout [Int], exp: Int) {
// 十进制的位范围为 0~9 ,因此需要长度为 10 的桶
var counter = Array(repeating: 0, count: 10)
let n = nums.count
// 统计 0~9 各数字的出现次数
for i in nums.indices {
let d = digit(num: nums[i], exp: exp) // 获取 nums[i] 第 k 位,记为 d
counter[d] += 1 // 统计数字 d 的出现次数
}
// 求前缀和,将“出现个数”转换为“数组索引”
for i in 1 ..< 10 {
counter[i] += counter[i - 1]
}
// 倒序遍历,根据桶内统计结果,将各元素填入 res
var res = Array(repeating: 0, count: n)
for i in stride(from: n - 1, through: 0, by: -1) {
let d = digit(num: nums[i], exp: exp)
let j = counter[d] - 1 // 获取 d 在数组中的索引 j
res[j] = nums[i] // 将当前元素填入索引 j
counter[d] -= 1 // 将 d 的数量减 1
}
// 使用结果覆盖原数组 nums
for i in nums.indices {
nums[i] = res[i]
}
}
/* 基数排序 */
func radixSort(nums: inout [Int]) {
// 获取数组的最大元素,用于判断最大位数
var m = Int.min
for num in nums {
if num > m {
m = num
}
}
// 按照从低位到高位的顺序遍历
for exp in sequence(first: 1, next: { m >= ($0 * 10) ? $0 * 10 : nil }) {
// 对数组元素的第 k 位执行计数排序
// k = 1 -> exp = 1
// k = 2 -> exp = 10
// 即 exp = 10^(k-1)
countingSortDigit(nums: &nums, exp: exp)
}
}
```
=== "Zig"
```zig title="radix_sort.zig"
// 获取元素 num 的第 k 位,其中 exp = 10^(k-1)
fn digit(num: i32, exp: i32) i32 {
// 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算
return @mod(@divFloor(num, exp), 10);
}
// 计数排序(根据 nums 第 k 位排序)
fn countingSortDigit(nums: []i32, exp: i32) !void {
// 十进制的位范围为 0~9 ,因此需要长度为 10 的桶
var mem_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
// defer mem_arena.deinit();
const mem_allocator = mem_arena.allocator();
var counter = try mem_allocator.alloc(usize, 10);
@memset(counter, 0);
var n = nums.len;
// 统计 0~9 各数字的出现次数
for (nums) |num| {
var d: u32 = @bitCast(digit(num, exp)); // 获取 nums[i] 第 k 位,记为 d
counter[d] += 1; // 统计数字 d 的出现次数
}
// 求前缀和,将“出现个数”转换为“数组索引”
var i: usize = 1;
while (i < 10) : (i += 1) {
counter[i] += counter[i - 1];
}
// 倒序遍历,根据桶内统计结果,将各元素填入 res
var res = try mem_allocator.alloc(i32, n);
i = n - 1;
while (i >= 0) : (i -= 1) {
var d: u32 = @bitCast(digit(nums[i], exp));
var j = counter[d] - 1; // 获取 d 在数组中的索引 j
res[j] = nums[i]; // 将当前元素填入索引 j
counter[d] -= 1; // 将 d 的数量减 1
if (i == 0) break;
}
// 使用结果覆盖原数组 nums
i = 0;
while (i < n) : (i += 1) {
nums[i] = res[i];
}
}
// 基数排序
fn radixSort(nums: []i32) !void {
// 获取数组的最大元素,用于判断最大位数
var m: i32 = std.math.minInt(i32);
for (nums) |num| {
if (num > m) m = num;
}
// 按照从低位到高位的顺序遍历
var exp: i32 = 1;
while (exp <= m) : (exp *= 10) {
// 对数组元素的第 k 位执行计数排序
// k = 1 -> exp = 1
// k = 2 -> exp = 10
// 即 exp = 10^(k-1)
try countingSortDigit(nums, exp);
}
}
```
=== "Dart"
```dart title="radix_sort.dart"
@@ -684,6 +563,127 @@ $$
}
```
=== "C"
```c title="radix_sort.c"
/* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */
int digit(int num, int exp) {
// 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算
return (num / exp) % 10;
}
/* 计数排序(根据 nums 第 k 位排序) */
void countingSortDigit(int nums[], int size, int exp) {
// 十进制的位范围为 0~9 ,因此需要长度为 10 的桶
int *counter = (int *)malloc((sizeof(int) * 10));
// 统计 0~9 各数字的出现次数
for (int i = 0; i < size; i++) {
// 获取 nums[i] 第 k 位,记为 d
int d = digit(nums[i], exp);
// 统计数字 d 的出现次数
counter[d]++;
}
// 求前缀和,将“出现个数”转换为“数组索引”
for (int i = 1; i < 10; i++) {
counter[i] += counter[i - 1];
}
// 倒序遍历,根据桶内统计结果,将各元素填入 res
int *res = (int *)malloc(sizeof(int) * size);
for (int i = size - 1; i >= 0; i--) {
int d = digit(nums[i], exp);
int j = counter[d] - 1; // 获取 d 在数组中的索引 j
res[j] = nums[i]; // 将当前元素填入索引 j
counter[d]--; // 将 d 的数量减 1
}
// 使用结果覆盖原数组 nums
for (int i = 0; i < size; i++) {
nums[i] = res[i];
}
}
/* 基数排序 */
void radixSort(int nums[], int size) {
// 获取数组的最大元素,用于判断最大位数
int max = INT32_MIN;
for (size_t i = 0; i < size - 1; i++) {
if (nums[i] > max) {
max = nums[i];
}
}
// 按照从低位到高位的顺序遍历
for (int exp = 1; max >= exp; exp *= 10)
// 对数组元素的第 k 位执行计数排序
// k = 1 -> exp = 1
// k = 2 -> exp = 10
// 即 exp = 10^(k-1)
countingSortDigit(nums, size, exp);
}
```
=== "Zig"
```zig title="radix_sort.zig"
// 获取元素 num 的第 k 位,其中 exp = 10^(k-1)
fn digit(num: i32, exp: i32) i32 {
// 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算
return @mod(@divFloor(num, exp), 10);
}
// 计数排序(根据 nums 第 k 位排序)
fn countingSortDigit(nums: []i32, exp: i32) !void {
// 十进制的位范围为 0~9 ,因此需要长度为 10 的桶
var mem_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
// defer mem_arena.deinit();
const mem_allocator = mem_arena.allocator();
var counter = try mem_allocator.alloc(usize, 10);
@memset(counter, 0);
var n = nums.len;
// 统计 0~9 各数字的出现次数
for (nums) |num| {
var d: u32 = @bitCast(digit(num, exp)); // 获取 nums[i] 第 k 位,记为 d
counter[d] += 1; // 统计数字 d 的出现次数
}
// 求前缀和,将“出现个数”转换为“数组索引”
var i: usize = 1;
while (i < 10) : (i += 1) {
counter[i] += counter[i - 1];
}
// 倒序遍历,根据桶内统计结果,将各元素填入 res
var res = try mem_allocator.alloc(i32, n);
i = n - 1;
while (i >= 0) : (i -= 1) {
var d: u32 = @bitCast(digit(nums[i], exp));
var j = counter[d] - 1; // 获取 d 在数组中的索引 j
res[j] = nums[i]; // 将当前元素填入索引 j
counter[d] -= 1; // 将 d 的数量减 1
if (i == 0) break;
}
// 使用结果覆盖原数组 nums
i = 0;
while (i < n) : (i += 1) {
nums[i] = res[i];
}
}
// 基数排序
fn radixSort(nums: []i32) !void {
// 获取数组的最大元素,用于判断最大位数
var m: i32 = std.math.minInt(i32);
for (nums) |num| {
if (num > m) m = num;
}
// 按照从低位到高位的顺序遍历
var exp: i32 = 1;
while (exp <= m) : (exp *= 10) {
// 对数组元素的第 k 位执行计数排序
// k = 1 -> exp = 1
// k = 2 -> exp = 10
// 即 exp = 10^(k-1)
try countingSortDigit(nums, exp);
}
}
```
!!! question "为什么从最低位开始排序?"
在连续的排序轮次中,后一轮排序会覆盖前一轮排序的结果。举例来说,如果第一轮排序结果 $a < b$ ,而第二轮排序结果 $a > b$ ,那么第二轮的结果将取代第一轮的结果。由于数字的高位优先级高于低位,我们应该先排序低位再排序高位。
+100 -100
View File
@@ -51,26 +51,21 @@ comments: true
在代码中,我们用 $k$ 来记录未排序区间内的最小元素。
=== "Java"
=== "Python"
```java title="selection_sort.java"
/* 选择排序 */
void selectionSort(int[] nums) {
int n = nums.length;
// 外循环:未排序区间为 [i, n-1]
for (int i = 0; i < n - 1; i++) {
// 内循环:找到未排序区间内的最小元素
int k = i;
for (int j = i + 1; j < n; j++) {
if (nums[j] < nums[k])
k = j; // 记录最小元素的索引
}
// 将该最小元素与未排序区间的首个元素交换
int temp = nums[i];
nums[i] = nums[k];
nums[k] = temp;
}
}
```python title="selection_sort.py"
def selection_sort(nums: list[int]):
"""选择排序"""
n = len(nums)
# 外循环:未排序区间为 [i, n-1]
for i in range(n - 1):
# 内循环:找到未排序区间内的最小元素
k = i
for j in range(i + 1, n):
if nums[j] < nums[k]:
k = j # 记录最小元素的索引
# 将该最小元素与未排序区间的首个元素交换
nums[i], nums[k] = nums[k], nums[i]
```
=== "C++"
@@ -93,21 +88,46 @@ comments: true
}
```
=== "Python"
=== "Java"
```python title="selection_sort.py"
def selection_sort(nums: list[int]):
"""选择排序"""
n = len(nums)
# 外循环:未排序区间为 [i, n-1]
for i in range(n - 1):
# 内循环:找到未排序区间内的最小元素
k = i
for j in range(i + 1, n):
if nums[j] < nums[k]:
k = j # 记录最小元素的索引
# 将该最小元素与未排序区间的首个元素交换
nums[i], nums[k] = nums[k], nums[i]
```java title="selection_sort.java"
/* 选择排序 */
void selectionSort(int[] nums) {
int n = nums.length;
// 外循环:未排序区间为 [i, n-1]
for (int i = 0; i < n - 1; i++) {
// 内循环:找到未排序区间内的最小元素
int k = i;
for (int j = i + 1; j < n; j++) {
if (nums[j] < nums[k])
k = j; // 记录最小元素的索引
}
// 将该最小元素与未排序区间的首个元素交换
int temp = nums[i];
nums[i] = nums[k];
nums[k] = temp;
}
}
```
=== "C#"
```csharp title="selection_sort.cs"
/* 选择排序 */
void selectionSort(int[] nums) {
int n = nums.Length;
// 外循环:未排序区间为 [i, n-1]
for (int i = 0; i < n - 1; i++) {
// 内循环:找到未排序区间内的最小元素
int k = i;
for (int j = i + 1; j < n; j++) {
if (nums[j] < nums[k])
k = j; // 记录最小元素的索引
}
// 将该最小元素与未排序区间的首个元素交换
(nums[k], nums[i]) = (nums[i], nums[k]);
}
}
```
=== "Go"
@@ -133,6 +153,26 @@ comments: true
}
```
=== "Swift"
```swift title="selection_sort.swift"
/* 选择排序 */
func selectionSort(nums: inout [Int]) {
// 外循环:未排序区间为 [i, n-1]
for i in nums.indices.dropLast() {
// 内循环:找到未排序区间内的最小元素
var k = i
for j in nums.indices.dropFirst(i + 1) {
if nums[j] < nums[k] {
k = j // 记录最小元素的索引
}
}
// 将该最小元素与未排序区间的首个元素交换
nums.swapAt(i, k)
}
}
```
=== "JS"
```javascript title="selection_sort.js"
@@ -175,73 +215,6 @@ comments: true
}
```
=== "C"
```c title="selection_sort.c"
/* 选择排序 */
void selectionSort(int nums[], int n) {
// 外循环:未排序区间为 [i, n-1]
for (int i = 0; i < n - 1; i++) {
// 内循环:找到未排序区间内的最小元素
int k = i;
for (int j = i + 1; j < n; j++) {
if (nums[j] < nums[k])
k = j; // 记录最小元素的索引
}
// 将该最小元素与未排序区间的首个元素交换
int temp = nums[i];
nums[i] = nums[k];
nums[k] = temp;
}
}
```
=== "C#"
```csharp title="selection_sort.cs"
/* 选择排序 */
void selectionSort(int[] nums) {
int n = nums.Length;
// 外循环:未排序区间为 [i, n-1]
for (int i = 0; i < n - 1; i++) {
// 内循环:找到未排序区间内的最小元素
int k = i;
for (int j = i + 1; j < n; j++) {
if (nums[j] < nums[k])
k = j; // 记录最小元素的索引
}
// 将该最小元素与未排序区间的首个元素交换
(nums[k], nums[i]) = (nums[i], nums[k]);
}
}
```
=== "Swift"
```swift title="selection_sort.swift"
/* 选择排序 */
func selectionSort(nums: inout [Int]) {
// 外循环:未排序区间为 [i, n-1]
for i in nums.indices.dropLast() {
// 内循环:找到未排序区间内的最小元素
var k = i
for j in nums.indices.dropFirst(i + 1) {
if nums[j] < nums[k] {
k = j // 记录最小元素的索引
}
}
// 将该最小元素与未排序区间的首个元素交换
nums.swapAt(i, k)
}
}
```
=== "Zig"
```zig title="selection_sort.zig"
[class]{}-[func]{selectionSort}
```
=== "Dart"
```dart title="selection_sort.dart"
@@ -284,6 +257,33 @@ comments: true
}
```
=== "C"
```c title="selection_sort.c"
/* 选择排序 */
void selectionSort(int nums[], int n) {
// 外循环:未排序区间为 [i, n-1]
for (int i = 0; i < n - 1; i++) {
// 内循环:找到未排序区间内的最小元素
int k = i;
for (int j = i + 1; j < n; j++) {
if (nums[j] < nums[k])
k = j; // 记录最小元素的索引
}
// 将该最小元素与未排序区间的首个元素交换
int temp = nums[i];
nums[i] = nums[k];
nums[k] = temp;
}
}
```
=== "Zig"
```zig title="selection_sort.zig"
[class]{}-[func]{selectionSort}
```
## 11.2.1 &nbsp; 算法特性
- **时间复杂度为 $O(n^2)$、非自适应排序**:外循环共 $n - 1$ 轮,第一轮的未排序区间长度为 $n$ ,最后一轮的未排序区间长度为 $2$ ,即各轮外循环分别包含 $n$、$n - 1$、$\dots$、$3$、$2$ 轮内循环,求和为 $\frac{(n - 1)(n + 2)}{2}$ 。