mirror of
https://github.com/krahets/hello-algo.git
synced 2026-07-11 06:56:06 +00:00
build
This commit is contained in:
+242
-242
@@ -44,24 +44,19 @@ comments: true
|
||||
|
||||
<p align="center"> 图 11-5 冒泡排序流程 </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 效率优化
|
||||
|
||||
我们发现,如果某轮“冒泡”中没有执行任何交换操作,说明数组已经完成排序,可直接返回结果。因此,可以增加一个标志位 `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 算法特性
|
||||
|
||||
- **时间复杂度为 $O(n^2)$、自适应排序**:各轮“冒泡”遍历的数组长度依次为 $n - 1$、$n - 2$、$\dots$、$2$、$1$ ,总和为 $(n - 1) n / 2$ 。在引入 `flag` 优化后,最佳时间复杂度可达到 $O(n)$ 。
|
||||
|
||||
+163
-163
@@ -20,6 +20,62 @@ comments: true
|
||||
|
||||
<p align="center"> 图 11-13 桶排序算法流程 </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 算法特性
|
||||
|
||||
桶排序适用于处理体量很大的数据。例如,输入数据包含 100 万个元素,由于空间限制,系统内存无法一次性加载所有数据。此时,可以将数据分成 1000 个桶,然后分别对每个桶进行排序,最后将结果合并。
|
||||
|
||||
+308
-308
@@ -18,31 +18,27 @@ comments: true
|
||||
|
||||
<p align="center"> 图 11-16 计数排序流程 </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 算法特性
|
||||
|
||||
- **时间复杂度 $O(n + m)$** :涉及遍历 `nums` 和遍历 `counter` ,都使用线性时间。一般情况下 $n \gg m$ ,时间复杂度趋于 $O(n)$ 。
|
||||
|
||||
+183
-183
@@ -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 算法特性
|
||||
|
||||
- **时间复杂度 $O(n \log n)$、非自适应排序**:建堆操作使用 $O(n)$ 时间。从堆中提取最大元素的时间复杂度为 $O(\log n)$ ,共循环 $n - 1$ 轮。
|
||||
|
||||
+104
-104
@@ -27,22 +27,20 @@ comments: true
|
||||
|
||||
<p align="center"> 图 11-7 插入排序流程 </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 算法特性
|
||||
|
||||
- **时间复杂度 $O(n^2)$、自适应排序**:最差情况下,每次插入操作分别需要循环 $n - 1$、$n-2$、$\dots$、$2$、$1$ 次,求和得到 $(n - 1) n / 2$ ,因此时间复杂度为 $O(n^2)$ 。在遇到有序数据时,插入操作会提前终止。当输入数组完全有序时,插入排序达到最佳时间复杂度 $O(n)$ 。
|
||||
|
||||
+277
-277
@@ -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]` 。
|
||||
|
||||
+568
-568
File diff suppressed because it is too large
Load Diff
+289
-289
@@ -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
@@ -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 算法特性
|
||||
|
||||
- **时间复杂度为 $O(n^2)$、非自适应排序**:外循环共 $n - 1$ 轮,第一轮的未排序区间长度为 $n$ ,最后一轮的未排序区间长度为 $2$ ,即各轮外循环分别包含 $n$、$n - 1$、$\dots$、$3$、$2$ 轮内循环,求和为 $\frac{(n - 1)(n + 2)}{2}$ 。
|
||||
|
||||
Reference in New Issue
Block a user