mirror of
https://github.com/krahets/hello-algo.git
synced 2026-04-14 02:10:37 +08:00
build
This commit is contained in:
@@ -50,47 +50,34 @@ Example code is as follows:
|
||||
|
||||
```python title="bubble_sort.py"
|
||||
def bubble_sort(nums: list[int]):
|
||||
"""冒泡排序"""
|
||||
"""Bubble sort"""
|
||||
n = len(nums)
|
||||
# 外循环:未排序区间为 [0, i]
|
||||
# Outer loop: unsorted range is [0, i]
|
||||
for i in range(n - 1, 0, -1):
|
||||
# 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
# Inner loop: swap the largest element in the unsorted range [0, i] to the right end of the range
|
||||
for j in range(i):
|
||||
if nums[j] > nums[j + 1]:
|
||||
# 交换 nums[j] 与 nums[j + 1]
|
||||
# Swap nums[j] and nums[j + 1]
|
||||
nums[j], nums[j + 1] = nums[j + 1], nums[j]
|
||||
```
|
||||
|
||||
=== "C++"
|
||||
|
||||
```cpp title="bubble_sort.cpp"
|
||||
/* 冒泡排序 */
|
||||
void bubbleSort(vector<int> &nums) {
|
||||
// 外循环:未排序区间为 [0, i]
|
||||
for (int i = nums.size() - 1; i > 0; i--) {
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
for (int j = 0; j < i; j++) {
|
||||
if (nums[j] > nums[j + 1]) {
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
// 这里使用了 std::swap() 函数
|
||||
swap(nums[j], nums[j + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{bubbleSort}
|
||||
```
|
||||
|
||||
=== "Java"
|
||||
|
||||
```java title="bubble_sort.java"
|
||||
/* 冒泡排序 */
|
||||
/* Bubble sort */
|
||||
void bubbleSort(int[] nums) {
|
||||
// 外循环:未排序区间为 [0, i]
|
||||
// Outer loop: unsorted range is [0, i]
|
||||
for (int i = nums.length - 1; i > 0; i--) {
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
// Inner loop: swap the largest element in the unsorted range [0, i] to the right end of the range
|
||||
for (int j = 0; j < i; j++) {
|
||||
if (nums[j] > nums[j + 1]) {
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
// Swap nums[j] and nums[j + 1]
|
||||
int tmp = nums[j];
|
||||
nums[j] = nums[j + 1];
|
||||
nums[j + 1] = tmp;
|
||||
@@ -103,222 +90,69 @@ Example code is as follows:
|
||||
=== "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]
|
||||
(nums[j + 1], nums[j]) = (nums[j], nums[j + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{bubble_sort}-[func]{BubbleSort}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
||||
```go title="bubble_sort.go"
|
||||
/* 冒泡排序 */
|
||||
func bubbleSort(nums []int) {
|
||||
// 外循环:未排序区间为 [0, i]
|
||||
for i := len(nums) - 1; i > 0; i-- {
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
for j := 0; j < i; j++ {
|
||||
if nums[j] > nums[j+1] {
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
nums[j], nums[j+1] = nums[j+1], nums[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{bubbleSort}
|
||||
```
|
||||
|
||||
=== "Swift"
|
||||
|
||||
```swift title="bubble_sort.swift"
|
||||
/* 冒泡排序 */
|
||||
func bubbleSort(nums: inout [Int]) {
|
||||
// 外循环:未排序区间为 [0, i]
|
||||
for i in nums.indices.dropFirst().reversed() {
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
for j in 0 ..< i {
|
||||
if nums[j] > nums[j + 1] {
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
nums.swapAt(j, j + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{bubbleSort}
|
||||
```
|
||||
|
||||
=== "JS"
|
||||
|
||||
```javascript title="bubble_sort.js"
|
||||
/* 冒泡排序 */
|
||||
function bubbleSort(nums) {
|
||||
// 外循环:未排序区间为 [0, i]
|
||||
for (let i = nums.length - 1; i > 0; i--) {
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
for (let j = 0; j < i; j++) {
|
||||
if (nums[j] > nums[j + 1]) {
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
let tmp = nums[j];
|
||||
nums[j] = nums[j + 1];
|
||||
nums[j + 1] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{bubbleSort}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="bubble_sort.ts"
|
||||
/* 冒泡排序 */
|
||||
function bubbleSort(nums: number[]): void {
|
||||
// 外循环:未排序区间为 [0, i]
|
||||
for (let i = nums.length - 1; i > 0; i--) {
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
for (let j = 0; j < i; j++) {
|
||||
if (nums[j] > nums[j + 1]) {
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
let tmp = nums[j];
|
||||
nums[j] = nums[j + 1];
|
||||
nums[j + 1] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{bubbleSort}
|
||||
```
|
||||
|
||||
=== "Dart"
|
||||
|
||||
```dart title="bubble_sort.dart"
|
||||
/* 冒泡排序 */
|
||||
void bubbleSort(List<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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{bubbleSort}
|
||||
```
|
||||
|
||||
=== "Rust"
|
||||
|
||||
```rust title="bubble_sort.rs"
|
||||
/* 冒泡排序 */
|
||||
fn bubble_sort(nums: &mut [i32]) {
|
||||
// 外循环:未排序区间为 [0, i]
|
||||
for i in (1..nums.len()).rev() {
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
for j in 0..i {
|
||||
if nums[j] > nums[j + 1] {
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
let tmp = nums[j];
|
||||
nums[j] = nums[j + 1];
|
||||
nums[j + 1] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{bubble_sort}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
|
||||
```c title="bubble_sort.c"
|
||||
/* 冒泡排序 */
|
||||
void bubbleSort(int nums[], int size) {
|
||||
// 外循环:未排序区间为 [0, i]
|
||||
for (int i = size - 1; i > 0; i--) {
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
for (int j = 0; j < i; j++) {
|
||||
if (nums[j] > nums[j + 1]) {
|
||||
int temp = nums[j];
|
||||
nums[j] = nums[j + 1];
|
||||
nums[j + 1] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{bubbleSort}
|
||||
```
|
||||
|
||||
=== "Kotlin"
|
||||
|
||||
```kotlin title="bubble_sort.kt"
|
||||
/* 冒泡排序 */
|
||||
fun bubbleSort(nums: IntArray) {
|
||||
// 外循环:未排序区间为 [0, i]
|
||||
for (i in nums.size - 1 downTo 1) {
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
for (j in 0..<i) {
|
||||
if (nums[j] > nums[j + 1]) {
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
val temp = nums[j]
|
||||
nums[j] = nums[j + 1]
|
||||
nums[j + 1] = temp
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{bubbleSort}
|
||||
```
|
||||
|
||||
=== "Ruby"
|
||||
|
||||
```ruby title="bubble_sort.rb"
|
||||
### 冒泡排序 ###
|
||||
def bubble_sort(nums)
|
||||
n = nums.length
|
||||
# 外循环:未排序区间为 [0, i]
|
||||
for i in (n - 1).downto(1)
|
||||
# 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
for j in 0...i
|
||||
if nums[j] > nums[j + 1]
|
||||
# 交换 nums[j] 与 nums[j + 1]
|
||||
nums[j], nums[j + 1] = nums[j + 1], nums[j]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
[class]{}-[func]{bubble_sort}
|
||||
```
|
||||
|
||||
=== "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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{bubbleSort}
|
||||
```
|
||||
|
||||
??? pythontutor "Code Visualization"
|
||||
|
||||
<div style="height: 477px; width: 100%;"><iframe class="pythontutor-iframe" src="https://pythontutor.com/iframe-embed.html#code=def%20bubble_sort%28nums%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22%E5%86%92%E6%B3%A1%E6%8E%92%E5%BA%8F%22%22%22%0A%20%20%20%20n%20%3D%20len%28nums%29%0A%20%20%20%20%23%20%E5%A4%96%E5%BE%AA%E7%8E%AF%EF%BC%9A%E6%9C%AA%E6%8E%92%E5%BA%8F%E5%8C%BA%E9%97%B4%E4%B8%BA%20%5B0,%20i%5D%0A%20%20%20%20for%20i%20in%20range%28n%20-%201,%200,%20-1%29%3A%0A%20%20%20%20%20%20%20%20%23%20%E5%86%85%E5%BE%AA%E7%8E%AF%EF%BC%9A%E5%B0%86%E6%9C%AA%E6%8E%92%E5%BA%8F%E5%8C%BA%E9%97%B4%20%5B0,%20i%5D%20%E4%B8%AD%E7%9A%84%E6%9C%80%E5%A4%A7%E5%85%83%E7%B4%A0%E4%BA%A4%E6%8D%A2%E8%87%B3%E8%AF%A5%E5%8C%BA%E9%97%B4%E7%9A%84%E6%9C%80%E5%8F%B3%E7%AB%AF%0A%20%20%20%20%20%20%20%20for%20j%20in%20range%28i%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20nums%5Bj%5D%20%3E%20nums%5Bj%20%2B%201%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E4%BA%A4%E6%8D%A2%20nums%5Bj%5D%20%E4%B8%8E%20nums%5Bj%20%2B%201%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20nums%5Bj%5D,%20nums%5Bj%20%2B%201%5D%20%3D%20nums%5Bj%20%2B%201%5D,%20nums%5Bj%5D%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B4,%201,%203,%201,%205,%202%5D%0A%20%20%20%20bubble_sort%28nums%29%0A%20%20%20%20print%28%22%E5%86%92%E6%B3%A1%E6%8E%92%E5%BA%8F%E5%AE%8C%E6%88%90%E5%90%8E%20nums%20%3D%22,%20nums%29&codeDivHeight=472&codeDivWidth=350&cumulative=false&curInstr=4&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe></div>
|
||||
<div style="margin-top: 5px;"><a href="https://pythontutor.com/iframe-embed.html#code=def%20bubble_sort%28nums%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22%E5%86%92%E6%B3%A1%E6%8E%92%E5%BA%8F%22%22%22%0A%20%20%20%20n%20%3D%20len%28nums%29%0A%20%20%20%20%23%20%E5%A4%96%E5%BE%AA%E7%8E%AF%EF%BC%9A%E6%9C%AA%E6%8E%92%E5%BA%8F%E5%8C%BA%E9%97%B4%E4%B8%BA%20%5B0,%20i%5D%0A%20%20%20%20for%20i%20in%20range%28n%20-%201,%200,%20-1%29%3A%0A%20%20%20%20%20%20%20%20%23%20%E5%86%85%E5%BE%AA%E7%8E%AF%EF%BC%9A%E5%B0%86%E6%9C%AA%E6%8E%92%E5%BA%8F%E5%8C%BA%E9%97%B4%20%5B0,%20i%5D%20%E4%B8%AD%E7%9A%84%E6%9C%80%E5%A4%A7%E5%85%83%E7%B4%A0%E4%BA%A4%E6%8D%A2%E8%87%B3%E8%AF%A5%E5%8C%BA%E9%97%B4%E7%9A%84%E6%9C%80%E5%8F%B3%E7%AB%AF%0A%20%20%20%20%20%20%20%20for%20j%20in%20range%28i%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20nums%5Bj%5D%20%3E%20nums%5Bj%20%2B%201%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E4%BA%A4%E6%8D%A2%20nums%5Bj%5D%20%E4%B8%8E%20nums%5Bj%20%2B%201%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20nums%5Bj%5D,%20nums%5Bj%20%2B%201%5D%20%3D%20nums%5Bj%20%2B%201%5D,%20nums%5Bj%5D%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B4,%201,%203,%201,%205,%202%5D%0A%20%20%20%20bubble_sort%28nums%29%0A%20%20%20%20print%28%22%E5%86%92%E6%B3%A1%E6%8E%92%E5%BA%8F%E5%AE%8C%E6%88%90%E5%90%8E%20nums%20%3D%22,%20nums%29&codeDivHeight=800&codeDivWidth=600&cumulative=false&curInstr=4&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false" target="_blank" rel="noopener noreferrer">Full Screen ></a></div>
|
||||
|
||||
## 11.3.2 Efficiency optimization
|
||||
|
||||
We find that if no swaps are performed in a round of "bubbling," the array is already sorted, and we can return the result immediately. Thus, we can add a flag `flag` to monitor this situation and return immediately when it occurs.
|
||||
@@ -329,64 +163,47 @@ Even after optimization, the worst-case time complexity and average time complex
|
||||
|
||||
```python title="bubble_sort.py"
|
||||
def bubble_sort_with_flag(nums: list[int]):
|
||||
"""冒泡排序(标志优化)"""
|
||||
"""Bubble sort (optimized with flag)"""
|
||||
n = len(nums)
|
||||
# 外循环:未排序区间为 [0, i]
|
||||
# Outer loop: unsorted range is [0, i]
|
||||
for i in range(n - 1, 0, -1):
|
||||
flag = False # 初始化标志位
|
||||
# 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
flag = False # Initialize flag
|
||||
# Inner loop: swap the largest element in the unsorted range [0, i] to the right end of the range
|
||||
for j in range(i):
|
||||
if nums[j] > nums[j + 1]:
|
||||
# 交换 nums[j] 与 nums[j + 1]
|
||||
# Swap nums[j] and nums[j + 1]
|
||||
nums[j], nums[j + 1] = nums[j + 1], nums[j]
|
||||
flag = True # 记录交换元素
|
||||
flag = True # Record swapped elements
|
||||
if not flag:
|
||||
break # 此轮“冒泡”未交换任何元素,直接跳出
|
||||
break # If no elements were swapped in this round of "bubbling", exit
|
||||
```
|
||||
|
||||
=== "C++"
|
||||
|
||||
```cpp title="bubble_sort.cpp"
|
||||
/* 冒泡排序(标志优化)*/
|
||||
void bubbleSortWithFlag(vector<int> &nums) {
|
||||
// 外循环:未排序区间为 [0, i]
|
||||
for (int i = nums.size() - 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]
|
||||
// 这里使用了 std::swap() 函数
|
||||
swap(nums[j], nums[j + 1]);
|
||||
flag = true; // 记录交换元素
|
||||
}
|
||||
}
|
||||
if (!flag)
|
||||
break; // 此轮“冒泡”未交换任何元素,直接跳出
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{bubbleSortWithFlag}
|
||||
```
|
||||
|
||||
=== "Java"
|
||||
|
||||
```java title="bubble_sort.java"
|
||||
/* 冒泡排序(标志优化) */
|
||||
/* Bubble sort (optimized with flag) */
|
||||
void bubbleSortWithFlag(int[] nums) {
|
||||
// 外循环:未排序区间为 [0, i]
|
||||
// Outer loop: unsorted range is [0, i]
|
||||
for (int i = nums.length - 1; i > 0; i--) {
|
||||
boolean flag = false; // 初始化标志位
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
boolean flag = false; // Initialize flag
|
||||
// Inner loop: swap the largest element in the unsorted range [0, i] to the right end of the range
|
||||
for (int j = 0; j < i; j++) {
|
||||
if (nums[j] > nums[j + 1]) {
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
// Swap nums[j] and nums[j + 1]
|
||||
int tmp = nums[j];
|
||||
nums[j] = nums[j + 1];
|
||||
nums[j + 1] = tmp;
|
||||
flag = true; // 记录交换元素
|
||||
flag = true; // Record swapped elements
|
||||
}
|
||||
}
|
||||
if (!flag)
|
||||
break; // 此轮“冒泡”未交换任何元素,直接跳出
|
||||
break; // If no elements were swapped in this round of "bubbling", exit
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -394,263 +211,69 @@ Even after optimization, the worst-case time complexity and average time complex
|
||||
=== "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]
|
||||
(nums[j + 1], nums[j]) = (nums[j], nums[j + 1]);
|
||||
flag = true; // 记录交换元素
|
||||
}
|
||||
}
|
||||
if (!flag) break; // 此轮“冒泡”未交换任何元素,直接跳出
|
||||
}
|
||||
}
|
||||
[class]{bubble_sort}-[func]{BubbleSortWithFlag}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
||||
```go title="bubble_sort.go"
|
||||
/* 冒泡排序(标志优化)*/
|
||||
func bubbleSortWithFlag(nums []int) {
|
||||
// 外循环:未排序区间为 [0, i]
|
||||
for i := len(nums) - 1; i > 0; i-- {
|
||||
flag := false // 初始化标志位
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
for j := 0; j < i; j++ {
|
||||
if nums[j] > nums[j+1] {
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
nums[j], nums[j+1] = nums[j+1], nums[j]
|
||||
flag = true // 记录交换元素
|
||||
}
|
||||
}
|
||||
if flag == false { // 此轮“冒泡”未交换任何元素,直接跳出
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{bubbleSortWithFlag}
|
||||
```
|
||||
|
||||
=== "Swift"
|
||||
|
||||
```swift title="bubble_sort.swift"
|
||||
/* 冒泡排序(标志优化)*/
|
||||
func bubbleSortWithFlag(nums: inout [Int]) {
|
||||
// 外循环:未排序区间为 [0, i]
|
||||
for i in nums.indices.dropFirst().reversed() {
|
||||
var flag = false // 初始化标志位
|
||||
for j in 0 ..< i {
|
||||
if nums[j] > nums[j + 1] {
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
nums.swapAt(j, j + 1)
|
||||
flag = true // 记录交换元素
|
||||
}
|
||||
}
|
||||
if !flag { // 此轮“冒泡”未交换任何元素,直接跳出
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{bubbleSortWithFlag}
|
||||
```
|
||||
|
||||
=== "JS"
|
||||
|
||||
```javascript title="bubble_sort.js"
|
||||
/* 冒泡排序(标志优化)*/
|
||||
function bubbleSortWithFlag(nums) {
|
||||
// 外循环:未排序区间为 [0, i]
|
||||
for (let i = nums.length - 1; i > 0; i--) {
|
||||
let flag = false; // 初始化标志位
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
for (let j = 0; j < i; j++) {
|
||||
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; // 此轮“冒泡”未交换任何元素,直接跳出
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{bubbleSortWithFlag}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="bubble_sort.ts"
|
||||
/* 冒泡排序(标志优化)*/
|
||||
function bubbleSortWithFlag(nums: number[]): void {
|
||||
// 外循环:未排序区间为 [0, i]
|
||||
for (let i = nums.length - 1; i > 0; i--) {
|
||||
let flag = false; // 初始化标志位
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
for (let j = 0; j < i; j++) {
|
||||
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; // 此轮“冒泡”未交换任何元素,直接跳出
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{bubbleSortWithFlag}
|
||||
```
|
||||
|
||||
=== "Dart"
|
||||
|
||||
```dart title="bubble_sort.dart"
|
||||
/* 冒泡排序(标志优化)*/
|
||||
void bubbleSortWithFlag(List<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; // 此轮“冒泡”未交换任何元素,直接跳出
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{bubbleSortWithFlag}
|
||||
```
|
||||
|
||||
=== "Rust"
|
||||
|
||||
```rust title="bubble_sort.rs"
|
||||
/* 冒泡排序(标志优化) */
|
||||
fn bubble_sort_with_flag(nums: &mut [i32]) {
|
||||
// 外循环:未排序区间为 [0, i]
|
||||
for i in (1..nums.len()).rev() {
|
||||
let mut flag = false; // 初始化标志位
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
for j in 0..i {
|
||||
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; // 此轮“冒泡”未交换任何元素,直接跳出
|
||||
};
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{bubble_sort_with_flag}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
|
||||
```c title="bubble_sort.c"
|
||||
/* 冒泡排序(标志优化)*/
|
||||
void bubbleSortWithFlag(int nums[], int size) {
|
||||
// 外循环:未排序区间为 [0, i]
|
||||
for (int i = size - 1; i > 0; i--) {
|
||||
bool flag = false;
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
for (int j = 0; j < 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;
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{bubbleSortWithFlag}
|
||||
```
|
||||
|
||||
=== "Kotlin"
|
||||
|
||||
```kotlin title="bubble_sort.kt"
|
||||
/* 冒泡排序(标志优化) */
|
||||
fun bubbleSortWithFlag(nums: IntArray) {
|
||||
// 外循环:未排序区间为 [0, i]
|
||||
for (i in nums.size - 1 downTo 1) {
|
||||
var flag = false // 初始化标志位
|
||||
// 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
for (j in 0..<i) {
|
||||
if (nums[j] > nums[j + 1]) {
|
||||
// 交换 nums[j] 与 nums[j + 1]
|
||||
val temp = nums[j]
|
||||
nums[j] = nums[j + 1]
|
||||
nums[j + 1] = temp
|
||||
flag = true // 记录交换元素
|
||||
}
|
||||
}
|
||||
if (!flag) break // 此轮“冒泡”未交换任何元素,直接跳出
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{bubbleSortWithFlag}
|
||||
```
|
||||
|
||||
=== "Ruby"
|
||||
|
||||
```ruby title="bubble_sort.rb"
|
||||
### 冒泡排序(标志优化)###
|
||||
def bubble_sort_with_flag(nums)
|
||||
n = nums.length
|
||||
# 外循环:未排序区间为 [0, i]
|
||||
for i in (n - 1).downto(1)
|
||||
flag = false # 初始化标志位
|
||||
|
||||
# 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
|
||||
for j in 0...i
|
||||
if nums[j] > nums[j + 1]
|
||||
# 交换 nums[j] 与 nums[j + 1]
|
||||
nums[j], nums[j + 1] = nums[j + 1], nums[j]
|
||||
flag = true # 记录交换元素
|
||||
end
|
||||
end
|
||||
|
||||
break unless flag # 此轮“冒泡”未交换任何元素,直接跳出
|
||||
end
|
||||
end
|
||||
[class]{}-[func]{bubble_sort_with_flag}
|
||||
```
|
||||
|
||||
=== "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; // 此轮“冒泡”未交换任何元素,直接跳出
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{bubbleSortWithFlag}
|
||||
```
|
||||
|
||||
??? pythontutor "Code Visualization"
|
||||
|
||||
<div style="height: 549px; width: 100%;"><iframe class="pythontutor-iframe" src="https://pythontutor.com/iframe-embed.html#code=def%20bubble_sort_with_flag%28nums%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22%E5%86%92%E6%B3%A1%E6%8E%92%E5%BA%8F%EF%BC%88%E6%A0%87%E5%BF%97%E4%BC%98%E5%8C%96%EF%BC%89%22%22%22%0A%20%20%20%20n%20%3D%20len%28nums%29%0A%20%20%20%20%23%20%E5%A4%96%E5%BE%AA%E7%8E%AF%EF%BC%9A%E6%9C%AA%E6%8E%92%E5%BA%8F%E5%8C%BA%E9%97%B4%E4%B8%BA%20%5B0,%20i%5D%0A%20%20%20%20for%20i%20in%20range%28n%20-%201,%200,%20-1%29%3A%0A%20%20%20%20%20%20%20%20flag%20%3D%20False%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E6%A0%87%E5%BF%97%E4%BD%8D%0A%20%20%20%20%20%20%20%20%23%20%E5%86%85%E5%BE%AA%E7%8E%AF%EF%BC%9A%E5%B0%86%E6%9C%AA%E6%8E%92%E5%BA%8F%E5%8C%BA%E9%97%B4%20%5B0,%20i%5D%20%E4%B8%AD%E7%9A%84%E6%9C%80%E5%A4%A7%E5%85%83%E7%B4%A0%E4%BA%A4%E6%8D%A2%E8%87%B3%E8%AF%A5%E5%8C%BA%E9%97%B4%E7%9A%84%E6%9C%80%E5%8F%B3%E7%AB%AF%0A%20%20%20%20%20%20%20%20for%20j%20in%20range%28i%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20nums%5Bj%5D%20%3E%20nums%5Bj%20%2B%201%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E4%BA%A4%E6%8D%A2%20nums%5Bj%5D%20%E4%B8%8E%20nums%5Bj%20%2B%201%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20nums%5Bj%5D,%20nums%5Bj%20%2B%201%5D%20%3D%20nums%5Bj%20%2B%201%5D,%20nums%5Bj%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20flag%20%3D%20True%20%20%23%20%E8%AE%B0%E5%BD%95%E4%BA%A4%E6%8D%A2%E5%85%83%E7%B4%A0%0A%20%20%20%20%20%20%20%20if%20not%20flag%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20break%20%20%23%20%E6%AD%A4%E8%BD%AE%E2%80%9C%E5%86%92%E6%B3%A1%E2%80%9D%E6%9C%AA%E4%BA%A4%E6%8D%A2%E4%BB%BB%E4%BD%95%E5%85%83%E7%B4%A0%EF%BC%8C%E7%9B%B4%E6%8E%A5%E8%B7%B3%E5%87%BA%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B4,%201,%203,%201,%205,%202%5D%0A%20%20%20%20bubble_sort_with_flag%28nums%29%0A%20%20%20%20print%28%22%E5%86%92%E6%B3%A1%E6%8E%92%E5%BA%8F%E5%AE%8C%E6%88%90%E5%90%8E%20nums%20%3D%22,%20nums%29&codeDivHeight=472&codeDivWidth=350&cumulative=false&curInstr=4&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe></div>
|
||||
<div style="margin-top: 5px;"><a href="https://pythontutor.com/iframe-embed.html#code=def%20bubble_sort_with_flag%28nums%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22%E5%86%92%E6%B3%A1%E6%8E%92%E5%BA%8F%EF%BC%88%E6%A0%87%E5%BF%97%E4%BC%98%E5%8C%96%EF%BC%89%22%22%22%0A%20%20%20%20n%20%3D%20len%28nums%29%0A%20%20%20%20%23%20%E5%A4%96%E5%BE%AA%E7%8E%AF%EF%BC%9A%E6%9C%AA%E6%8E%92%E5%BA%8F%E5%8C%BA%E9%97%B4%E4%B8%BA%20%5B0,%20i%5D%0A%20%20%20%20for%20i%20in%20range%28n%20-%201,%200,%20-1%29%3A%0A%20%20%20%20%20%20%20%20flag%20%3D%20False%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E6%A0%87%E5%BF%97%E4%BD%8D%0A%20%20%20%20%20%20%20%20%23%20%E5%86%85%E5%BE%AA%E7%8E%AF%EF%BC%9A%E5%B0%86%E6%9C%AA%E6%8E%92%E5%BA%8F%E5%8C%BA%E9%97%B4%20%5B0,%20i%5D%20%E4%B8%AD%E7%9A%84%E6%9C%80%E5%A4%A7%E5%85%83%E7%B4%A0%E4%BA%A4%E6%8D%A2%E8%87%B3%E8%AF%A5%E5%8C%BA%E9%97%B4%E7%9A%84%E6%9C%80%E5%8F%B3%E7%AB%AF%0A%20%20%20%20%20%20%20%20for%20j%20in%20range%28i%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20nums%5Bj%5D%20%3E%20nums%5Bj%20%2B%201%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E4%BA%A4%E6%8D%A2%20nums%5Bj%5D%20%E4%B8%8E%20nums%5Bj%20%2B%201%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20nums%5Bj%5D,%20nums%5Bj%20%2B%201%5D%20%3D%20nums%5Bj%20%2B%201%5D,%20nums%5Bj%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20flag%20%3D%20True%20%20%23%20%E8%AE%B0%E5%BD%95%E4%BA%A4%E6%8D%A2%E5%85%83%E7%B4%A0%0A%20%20%20%20%20%20%20%20if%20not%20flag%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20break%20%20%23%20%E6%AD%A4%E8%BD%AE%E2%80%9C%E5%86%92%E6%B3%A1%E2%80%9D%E6%9C%AA%E4%BA%A4%E6%8D%A2%E4%BB%BB%E4%BD%95%E5%85%83%E7%B4%A0%EF%BC%8C%E7%9B%B4%E6%8E%A5%E8%B7%B3%E5%87%BA%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B4,%201,%203,%201,%205,%202%5D%0A%20%20%20%20bubble_sort_with_flag%28nums%29%0A%20%20%20%20print%28%22%E5%86%92%E6%B3%A1%E6%8E%92%E5%BA%8F%E5%AE%8C%E6%88%90%E5%90%8E%20nums%20%3D%22,%20nums%29&codeDivHeight=800&codeDivWidth=600&cumulative=false&curInstr=4&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false" target="_blank" rel="noopener noreferrer">Full Screen ></a></div>
|
||||
|
||||
## 11.3.3 Algorithm characteristics
|
||||
|
||||
- **Time complexity of $O(n^2)$, adaptive sorting**: The length of the array traversed in each round of "bubbling" decreases sequentially from $n - 1$, $n - 2$, $\dots$, $2$, $1$, totaling $(n - 1) n / 2$. With the introduction of `flag` optimization, the best time complexity can reach $O(n)$.
|
||||
|
||||
@@ -26,21 +26,21 @@ The code is shown as follows:
|
||||
|
||||
```python title="bucket_sort.py"
|
||||
def bucket_sort(nums: list[float]):
|
||||
"""桶排序"""
|
||||
# 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素
|
||||
"""Bucket sort"""
|
||||
# Initialize k = n/2 buckets, expected to allocate 2 elements per bucket
|
||||
k = len(nums) // 2
|
||||
buckets = [[] for _ in range(k)]
|
||||
# 1. 将数组元素分配到各个桶中
|
||||
# 1. Distribute array elements into various buckets
|
||||
for num in nums:
|
||||
# 输入数据范围为 [0, 1),使用 num * k 映射到索引范围 [0, k-1]
|
||||
# Input data range is [0, 1), use num * k to map to index range [0, k-1]
|
||||
i = int(num * k)
|
||||
# 将 num 添加进桶 i
|
||||
# Add num to bucket i
|
||||
buckets[i].append(num)
|
||||
# 2. 对各个桶执行排序
|
||||
# 2. Sort each bucket
|
||||
for bucket in buckets:
|
||||
# 使用内置排序函数,也可以替换成其他排序算法
|
||||
# Use built-in sorting function, can also replace with other sorting algorithms
|
||||
bucket.sort()
|
||||
# 3. 遍历桶合并结果
|
||||
# 3. Traverse buckets to merge results
|
||||
i = 0
|
||||
for bucket in buckets:
|
||||
for num in bucket:
|
||||
@@ -51,57 +51,33 @@ The code is shown as follows:
|
||||
=== "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;
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{bucketSort}
|
||||
```
|
||||
|
||||
=== "Java"
|
||||
|
||||
```java title="bucket_sort.java"
|
||||
/* 桶排序 */
|
||||
/* Bucket sort */
|
||||
void bucketSort(float[] nums) {
|
||||
// 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素
|
||||
// Initialize k = n/2 buckets, expected to allocate 2 elements per bucket
|
||||
int k = nums.length / 2;
|
||||
List<List<Float>> buckets = new ArrayList<>();
|
||||
for (int i = 0; i < k; i++) {
|
||||
buckets.add(new ArrayList<>());
|
||||
}
|
||||
// 1. 将数组元素分配到各个桶中
|
||||
// 1. Distribute array elements into various buckets
|
||||
for (float num : nums) {
|
||||
// 输入数据范围为 [0, 1),使用 num * k 映射到索引范围 [0, k-1]
|
||||
// Input data range is [0, 1), use num * k to map to index range [0, k-1]
|
||||
int i = (int) (num * k);
|
||||
// 将 num 添加进桶 i
|
||||
// Add num to bucket i
|
||||
buckets.get(i).add(num);
|
||||
}
|
||||
// 2. 对各个桶执行排序
|
||||
// 2. Sort each bucket
|
||||
for (List<Float> bucket : buckets) {
|
||||
// 使用内置排序函数,也可以替换成其他排序算法
|
||||
// Use built-in sorting function, can also replace with other sorting algorithms
|
||||
Collections.sort(bucket);
|
||||
}
|
||||
// 3. 遍历桶合并结果
|
||||
// 3. Traverse buckets to merge results
|
||||
int i = 0;
|
||||
for (List<Float> bucket : buckets) {
|
||||
for (float num : bucket) {
|
||||
@@ -114,327 +90,61 @@ The code is shown as follows:
|
||||
=== "C#"
|
||||
|
||||
```csharp title="bucket_sort.cs"
|
||||
/* 桶排序 */
|
||||
void BucketSort(float[] nums) {
|
||||
// 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素
|
||||
int k = nums.Length / 2;
|
||||
List<List<float>> buckets = [];
|
||||
for (int i = 0; i < k; i++) {
|
||||
buckets.Add([]);
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{bucket_sort}-[func]{BucketSort}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
||||
```go title="bucket_sort.go"
|
||||
/* 桶排序 */
|
||||
func bucketSort(nums []float64) {
|
||||
// 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素
|
||||
k := len(nums) / 2
|
||||
buckets := make([][]float64, k)
|
||||
for i := 0; i < k; i++ {
|
||||
buckets[i] = make([]float64, 0)
|
||||
}
|
||||
// 1. 将数组元素分配到各个桶中
|
||||
for _, num := range nums {
|
||||
// 输入数据范围为 [0, 1),使用 num * k 映射到索引范围 [0, k-1]
|
||||
i := int(num * float64(k))
|
||||
// 将 num 添加进桶 i
|
||||
buckets[i] = append(buckets[i], num)
|
||||
}
|
||||
// 2. 对各个桶执行排序
|
||||
for i := 0; i < k; i++ {
|
||||
// 使用内置切片排序函数,也可以替换成其他排序算法
|
||||
sort.Float64s(buckets[i])
|
||||
}
|
||||
// 3. 遍历桶合并结果
|
||||
i := 0
|
||||
for _, bucket := range buckets {
|
||||
for _, num := range bucket {
|
||||
nums[i] = num
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{bucketSort}
|
||||
```
|
||||
|
||||
=== "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
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{bucketSort}
|
||||
```
|
||||
|
||||
=== "JS"
|
||||
|
||||
```javascript title="bucket_sort.js"
|
||||
/* 桶排序 */
|
||||
function bucketSort(nums) {
|
||||
// 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素
|
||||
const k = nums.length / 2;
|
||||
const buckets = [];
|
||||
for (let i = 0; i < k; i++) {
|
||||
buckets.push([]);
|
||||
}
|
||||
// 1. 将数组元素分配到各个桶中
|
||||
for (const num of nums) {
|
||||
// 输入数据范围为 [0, 1),使用 num * k 映射到索引范围 [0, k-1]
|
||||
const i = Math.floor(num * k);
|
||||
// 将 num 添加进桶 i
|
||||
buckets[i].push(num);
|
||||
}
|
||||
// 2. 对各个桶执行排序
|
||||
for (const bucket of buckets) {
|
||||
// 使用内置排序函数,也可以替换成其他排序算法
|
||||
bucket.sort((a, b) => a - b);
|
||||
}
|
||||
// 3. 遍历桶合并结果
|
||||
let i = 0;
|
||||
for (const bucket of buckets) {
|
||||
for (const num of bucket) {
|
||||
nums[i++] = num;
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{bucketSort}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="bucket_sort.ts"
|
||||
/* 桶排序 */
|
||||
function bucketSort(nums: number[]): void {
|
||||
// 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素
|
||||
const k = nums.length / 2;
|
||||
const buckets: number[][] = [];
|
||||
for (let i = 0; i < k; i++) {
|
||||
buckets.push([]);
|
||||
}
|
||||
// 1. 将数组元素分配到各个桶中
|
||||
for (const num of nums) {
|
||||
// 输入数据范围为 [0, 1),使用 num * k 映射到索引范围 [0, k-1]
|
||||
const i = Math.floor(num * k);
|
||||
// 将 num 添加进桶 i
|
||||
buckets[i].push(num);
|
||||
}
|
||||
// 2. 对各个桶执行排序
|
||||
for (const bucket of buckets) {
|
||||
// 使用内置排序函数,也可以替换成其他排序算法
|
||||
bucket.sort((a, b) => a - b);
|
||||
}
|
||||
// 3. 遍历桶合并结果
|
||||
let i = 0;
|
||||
for (const bucket of buckets) {
|
||||
for (const num of bucket) {
|
||||
nums[i++] = num;
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{bucketSort}
|
||||
```
|
||||
|
||||
=== "Dart"
|
||||
|
||||
```dart title="bucket_sort.dart"
|
||||
/* 桶排序 */
|
||||
void bucketSort(List<double> nums) {
|
||||
// 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素
|
||||
int k = nums.length ~/ 2;
|
||||
List<List<double>> buckets = List.generate(k, (index) => []);
|
||||
|
||||
// 1. 将数组元素分配到各个桶中
|
||||
for (double _num in nums) {
|
||||
// 输入数据范围为 [0, 1),使用 _num * k 映射到索引范围 [0, k-1]
|
||||
int i = (_num * k).toInt();
|
||||
// 将 _num 添加进桶 bucket_idx
|
||||
buckets[i].add(_num);
|
||||
}
|
||||
// 2. 对各个桶执行排序
|
||||
for (List<double> bucket in buckets) {
|
||||
bucket.sort();
|
||||
}
|
||||
// 3. 遍历桶合并结果
|
||||
int i = 0;
|
||||
for (List<double> bucket in buckets) {
|
||||
for (double _num in bucket) {
|
||||
nums[i++] = _num;
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{bucketSort}
|
||||
```
|
||||
|
||||
=== "Rust"
|
||||
|
||||
```rust title="bucket_sort.rs"
|
||||
/* 桶排序 */
|
||||
fn bucket_sort(nums: &mut [f64]) {
|
||||
// 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素
|
||||
let k = nums.len() / 2;
|
||||
let mut buckets = vec![vec![]; k];
|
||||
// 1. 将数组元素分配到各个桶中
|
||||
for &mut num in &mut *nums {
|
||||
// 输入数据范围为 [0, 1),使用 num * k 映射到索引范围 [0, k-1]
|
||||
let i = (num * k as f64) as usize;
|
||||
// 将 num 添加进桶 i
|
||||
buckets[i].push(num);
|
||||
}
|
||||
// 2. 对各个桶执行排序
|
||||
for bucket in &mut buckets {
|
||||
// 使用内置排序函数,也可以替换成其他排序算法
|
||||
bucket.sort_by(|a, b| a.partial_cmp(b).unwrap());
|
||||
}
|
||||
// 3. 遍历桶合并结果
|
||||
let mut i = 0;
|
||||
for bucket in &mut buckets {
|
||||
for &mut num in bucket {
|
||||
nums[i] = num;
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{bucket_sort}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
|
||||
```c title="bucket_sort.c"
|
||||
/* 桶排序 */
|
||||
void bucketSort(float nums[], int n) {
|
||||
int k = n / 2; // 初始化 k = n/2 个桶
|
||||
int *sizes = malloc(k * sizeof(int)); // 记录每个桶的大小
|
||||
float **buckets = malloc(k * sizeof(float *)); // 动态数组的数组(桶)
|
||||
// 为每个桶预分配足够的空间
|
||||
for (int i = 0; i < k; ++i) {
|
||||
buckets[i] = (float *)malloc(n * sizeof(float));
|
||||
sizes[i] = 0;
|
||||
}
|
||||
// 1. 将数组元素分配到各个桶中
|
||||
for (int i = 0; i < n; ++i) {
|
||||
int idx = (int)(nums[i] * k);
|
||||
buckets[idx][sizes[idx]++] = nums[i];
|
||||
}
|
||||
// 2. 对各个桶执行排序
|
||||
for (int i = 0; i < k; ++i) {
|
||||
qsort(buckets[i], sizes[i], sizeof(float), compare);
|
||||
}
|
||||
// 3. 合并排序后的桶
|
||||
int idx = 0;
|
||||
for (int i = 0; i < k; ++i) {
|
||||
for (int j = 0; j < sizes[i]; ++j) {
|
||||
nums[idx++] = buckets[i][j];
|
||||
}
|
||||
// 释放内存
|
||||
free(buckets[i]);
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{bucketSort}
|
||||
```
|
||||
|
||||
=== "Kotlin"
|
||||
|
||||
```kotlin title="bucket_sort.kt"
|
||||
/* 桶排序 */
|
||||
fun bucketSort(nums: FloatArray) {
|
||||
// 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素
|
||||
val k = nums.size / 2
|
||||
val buckets = mutableListOf<MutableList<Float>>()
|
||||
for (i in 0..<k) {
|
||||
buckets.add(mutableListOf())
|
||||
}
|
||||
// 1. 将数组元素分配到各个桶中
|
||||
for (num in nums) {
|
||||
// 输入数据范围为 [0, 1),使用 num * k 映射到索引范围 [0, k-1]
|
||||
val i = (num * k).toInt()
|
||||
// 将 num 添加进桶 i
|
||||
buckets[i].add(num)
|
||||
}
|
||||
// 2. 对各个桶执行排序
|
||||
for (bucket in buckets) {
|
||||
// 使用内置排序函数,也可以替换成其他排序算法
|
||||
bucket.sort()
|
||||
}
|
||||
// 3. 遍历桶合并结果
|
||||
var i = 0
|
||||
for (bucket in buckets) {
|
||||
for (num in bucket) {
|
||||
nums[i++] = num
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{bucketSort}
|
||||
```
|
||||
|
||||
=== "Ruby"
|
||||
|
||||
```ruby title="bucket_sort.rb"
|
||||
### 桶排序 ###
|
||||
def bucket_sort(nums)
|
||||
# 初始化 k = n/2 个桶,预期向每个桶分配 2 个元素
|
||||
k = nums.length / 2
|
||||
buckets = Array.new(k) { [] }
|
||||
|
||||
# 1. 将数组元素分配到各个桶中
|
||||
nums.each do |num|
|
||||
# 输入数据范围为 [0, 1),使用 num * k 映射到索引范围 [0, k-1]
|
||||
i = (num * k).to_i
|
||||
# 将 num 添加进桶 i
|
||||
buckets[i] << num
|
||||
end
|
||||
|
||||
# 2. 对各个桶执行排序
|
||||
buckets.each do |bucket|
|
||||
# 使用内置排序函数,也可以替换成其他排序算法
|
||||
bucket.sort!
|
||||
end
|
||||
|
||||
# 3. 遍历桶合并结果
|
||||
i = 0
|
||||
buckets.each do |bucket|
|
||||
bucket.each do |num|
|
||||
nums[i] = num
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
[class]{}-[func]{bucket_sort}
|
||||
```
|
||||
|
||||
=== "Zig"
|
||||
@@ -443,11 +153,6 @@ The code is shown as follows:
|
||||
[class]{}-[func]{bucketSort}
|
||||
```
|
||||
|
||||
??? pythontutor "Code Visualization"
|
||||
|
||||
<div style="height: 549px; width: 100%;"><iframe class="pythontutor-iframe" src="https://pythontutor.com/iframe-embed.html#code=def%20bucket_sort%28nums%3A%20list%5Bfloat%5D%29%3A%0A%20%20%20%20%22%22%22%E6%A1%B6%E6%8E%92%E5%BA%8F%22%22%22%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%20k%20%3D%20n/2%20%E4%B8%AA%E6%A1%B6%EF%BC%8C%E9%A2%84%E6%9C%9F%E5%90%91%E6%AF%8F%E4%B8%AA%E6%A1%B6%E5%88%86%E9%85%8D%202%20%E4%B8%AA%E5%85%83%E7%B4%A0%0A%20%20%20%20k%20%3D%20len%28nums%29%20//%202%0A%20%20%20%20buckets%20%3D%20%5B%5B%5D%20for%20_%20in%20range%28k%29%5D%0A%20%20%20%20%23%201.%20%E5%B0%86%E6%95%B0%E7%BB%84%E5%85%83%E7%B4%A0%E5%88%86%E9%85%8D%E5%88%B0%E5%90%84%E4%B8%AA%E6%A1%B6%E4%B8%AD%0A%20%20%20%20for%20num%20in%20nums%3A%0A%20%20%20%20%20%20%20%20%23%20%E8%BE%93%E5%85%A5%E6%95%B0%E6%8D%AE%E8%8C%83%E5%9B%B4%E4%B8%BA%20%5B0,%201%29%EF%BC%8C%E4%BD%BF%E7%94%A8%20num%20*%20k%20%E6%98%A0%E5%B0%84%E5%88%B0%E7%B4%A2%E5%BC%95%E8%8C%83%E5%9B%B4%20%5B0,%20k-1%5D%0A%20%20%20%20%20%20%20%20i%20%3D%20int%28num%20*%20k%29%0A%20%20%20%20%20%20%20%20%23%20%E5%B0%86%20num%20%E6%B7%BB%E5%8A%A0%E8%BF%9B%E6%A1%B6%20i%0A%20%20%20%20%20%20%20%20buckets%5Bi%5D.append%28num%29%0A%20%20%20%20%23%202.%20%E5%AF%B9%E5%90%84%E4%B8%AA%E6%A1%B6%E6%89%A7%E8%A1%8C%E6%8E%92%E5%BA%8F%0A%20%20%20%20for%20bucket%20in%20buckets%3A%0A%20%20%20%20%20%20%20%20%23%20%E4%BD%BF%E7%94%A8%E5%86%85%E7%BD%AE%E6%8E%92%E5%BA%8F%E5%87%BD%E6%95%B0%EF%BC%8C%E4%B9%9F%E5%8F%AF%E4%BB%A5%E6%9B%BF%E6%8D%A2%E6%88%90%E5%85%B6%E4%BB%96%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%0A%20%20%20%20%20%20%20%20bucket.sort%28%29%0A%20%20%20%20%23%203.%20%E9%81%8D%E5%8E%86%E6%A1%B6%E5%90%88%E5%B9%B6%E7%BB%93%E6%9E%9C%0A%20%20%20%20i%20%3D%200%0A%20%20%20%20for%20bucket%20in%20buckets%3A%0A%20%20%20%20%20%20%20%20for%20num%20in%20bucket%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20nums%5Bi%5D%20%3D%20num%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%2B%3D%201%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E8%AE%BE%E8%BE%93%E5%85%A5%E6%95%B0%E6%8D%AE%E4%B8%BA%E6%B5%AE%E7%82%B9%E6%95%B0%EF%BC%8C%E8%8C%83%E5%9B%B4%E4%B8%BA%20%5B0,%201%29%0A%20%20%20%20nums%20%3D%20%5B0.49,%200.96,%200.82,%200.09,%200.57,%200.43,%200.91,%200.75,%200.15,%200.37%5D%0A%20%20%20%20bucket_sort%28nums%29%0A%20%20%20%20print%28%22%E6%A1%B6%E6%8E%92%E5%BA%8F%E5%AE%8C%E6%88%90%E5%90%8E%20nums%20%3D%22,%20nums%29&codeDivHeight=472&codeDivWidth=350&cumulative=false&curInstr=3&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe></div>
|
||||
<div style="margin-top: 5px;"><a href="https://pythontutor.com/iframe-embed.html#code=def%20bucket_sort%28nums%3A%20list%5Bfloat%5D%29%3A%0A%20%20%20%20%22%22%22%E6%A1%B6%E6%8E%92%E5%BA%8F%22%22%22%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%20k%20%3D%20n/2%20%E4%B8%AA%E6%A1%B6%EF%BC%8C%E9%A2%84%E6%9C%9F%E5%90%91%E6%AF%8F%E4%B8%AA%E6%A1%B6%E5%88%86%E9%85%8D%202%20%E4%B8%AA%E5%85%83%E7%B4%A0%0A%20%20%20%20k%20%3D%20len%28nums%29%20//%202%0A%20%20%20%20buckets%20%3D%20%5B%5B%5D%20for%20_%20in%20range%28k%29%5D%0A%20%20%20%20%23%201.%20%E5%B0%86%E6%95%B0%E7%BB%84%E5%85%83%E7%B4%A0%E5%88%86%E9%85%8D%E5%88%B0%E5%90%84%E4%B8%AA%E6%A1%B6%E4%B8%AD%0A%20%20%20%20for%20num%20in%20nums%3A%0A%20%20%20%20%20%20%20%20%23%20%E8%BE%93%E5%85%A5%E6%95%B0%E6%8D%AE%E8%8C%83%E5%9B%B4%E4%B8%BA%20%5B0,%201%29%EF%BC%8C%E4%BD%BF%E7%94%A8%20num%20*%20k%20%E6%98%A0%E5%B0%84%E5%88%B0%E7%B4%A2%E5%BC%95%E8%8C%83%E5%9B%B4%20%5B0,%20k-1%5D%0A%20%20%20%20%20%20%20%20i%20%3D%20int%28num%20*%20k%29%0A%20%20%20%20%20%20%20%20%23%20%E5%B0%86%20num%20%E6%B7%BB%E5%8A%A0%E8%BF%9B%E6%A1%B6%20i%0A%20%20%20%20%20%20%20%20buckets%5Bi%5D.append%28num%29%0A%20%20%20%20%23%202.%20%E5%AF%B9%E5%90%84%E4%B8%AA%E6%A1%B6%E6%89%A7%E8%A1%8C%E6%8E%92%E5%BA%8F%0A%20%20%20%20for%20bucket%20in%20buckets%3A%0A%20%20%20%20%20%20%20%20%23%20%E4%BD%BF%E7%94%A8%E5%86%85%E7%BD%AE%E6%8E%92%E5%BA%8F%E5%87%BD%E6%95%B0%EF%BC%8C%E4%B9%9F%E5%8F%AF%E4%BB%A5%E6%9B%BF%E6%8D%A2%E6%88%90%E5%85%B6%E4%BB%96%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%0A%20%20%20%20%20%20%20%20bucket.sort%28%29%0A%20%20%20%20%23%203.%20%E9%81%8D%E5%8E%86%E6%A1%B6%E5%90%88%E5%B9%B6%E7%BB%93%E6%9E%9C%0A%20%20%20%20i%20%3D%200%0A%20%20%20%20for%20bucket%20in%20buckets%3A%0A%20%20%20%20%20%20%20%20for%20num%20in%20bucket%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20nums%5Bi%5D%20%3D%20num%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%2B%3D%201%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E8%AE%BE%E8%BE%93%E5%85%A5%E6%95%B0%E6%8D%AE%E4%B8%BA%E6%B5%AE%E7%82%B9%E6%95%B0%EF%BC%8C%E8%8C%83%E5%9B%B4%E4%B8%BA%20%5B0,%201%29%0A%20%20%20%20nums%20%3D%20%5B0.49,%200.96,%200.82,%200.09,%200.57,%200.43,%200.91,%200.75,%200.15,%200.37%5D%0A%20%20%20%20bucket_sort%28nums%29%0A%20%20%20%20print%28%22%E6%A1%B6%E6%8E%92%E5%BA%8F%E5%AE%8C%E6%88%90%E5%90%8E%20nums%20%3D%22,%20nums%29&codeDivHeight=800&codeDivWidth=600&cumulative=false&curInstr=3&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false" target="_blank" rel="noopener noreferrer">Full Screen ></a></div>
|
||||
|
||||
## 11.8.2 Algorithm characteristics
|
||||
|
||||
Bucket sort is suitable for handling very large data sets. For example, if the input data includes 1 million elements, and system memory limitations prevent loading all the data at once, you can divide the data into 1,000 buckets and sort each bucket separately before merging the results.
|
||||
|
||||
@@ -24,18 +24,18 @@ The code is shown below:
|
||||
|
||||
```python title="counting_sort.py"
|
||||
def counting_sort_naive(nums: list[int]):
|
||||
"""计数排序"""
|
||||
# 简单实现,无法用于排序对象
|
||||
# 1. 统计数组最大元素 m
|
||||
"""Counting sort"""
|
||||
# Simple implementation, cannot be used for sorting objects
|
||||
# 1. Count the maximum element m in the array
|
||||
m = 0
|
||||
for num in nums:
|
||||
m = max(m, num)
|
||||
# 2. 统计各数字的出现次数
|
||||
# counter[num] 代表 num 的出现次数
|
||||
# 2. Count the occurrence of each digit
|
||||
# counter[num] represents the occurrence of num
|
||||
counter = [0] * (m + 1)
|
||||
for num in nums:
|
||||
counter[num] += 1
|
||||
# 3. 遍历 counter ,将各元素填入原数组 nums
|
||||
# 3. Traverse counter, filling each element back into the original array nums
|
||||
i = 0
|
||||
for num in range(m + 1):
|
||||
for _ in range(counter[num]):
|
||||
@@ -46,48 +46,27 @@ The code is shown below:
|
||||
=== "C++"
|
||||
|
||||
```cpp title="counting_sort.cpp"
|
||||
/* 计数排序 */
|
||||
// 简单实现,无法用于排序对象
|
||||
void countingSortNaive(vector<int> &nums) {
|
||||
// 1. 统计数组最大元素 m
|
||||
int m = 0;
|
||||
for (int num : nums) {
|
||||
m = max(m, num);
|
||||
}
|
||||
// 2. 统计各数字的出现次数
|
||||
// counter[num] 代表 num 的出现次数
|
||||
vector<int> counter(m + 1, 0);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{countingSortNaive}
|
||||
```
|
||||
|
||||
=== "Java"
|
||||
|
||||
```java title="counting_sort.java"
|
||||
/* 计数排序 */
|
||||
// 简单实现,无法用于排序对象
|
||||
/* Counting sort */
|
||||
// Simple implementation, cannot be used for sorting objects
|
||||
void countingSortNaive(int[] nums) {
|
||||
// 1. 统计数组最大元素 m
|
||||
// 1. Count the maximum element m in the array
|
||||
int m = 0;
|
||||
for (int num : nums) {
|
||||
m = Math.max(m, num);
|
||||
}
|
||||
// 2. 统计各数字的出现次数
|
||||
// counter[num] 代表 num 的出现次数
|
||||
// 2. Count the occurrence of each digit
|
||||
// counter[num] represents the occurrence of num
|
||||
int[] counter = new int[m + 1];
|
||||
for (int num : nums) {
|
||||
counter[num]++;
|
||||
}
|
||||
// 3. 遍历 counter ,将各元素填入原数组 nums
|
||||
// 3. Traverse counter, filling each element back into the original array nums
|
||||
int i = 0;
|
||||
for (int num = 0; num < m + 1; num++) {
|
||||
for (int j = 0; j < counter[num]; j++, i++) {
|
||||
@@ -100,273 +79,61 @@ The code is shown below:
|
||||
=== "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;
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{counting_sort}-[func]{CountingSortNaive}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
||||
```go title="counting_sort.go"
|
||||
/* 计数排序 */
|
||||
// 简单实现,无法用于排序对象
|
||||
func countingSortNaive(nums []int) {
|
||||
// 1. 统计数组最大元素 m
|
||||
m := 0
|
||||
for _, num := range nums {
|
||||
if num > m {
|
||||
m = num
|
||||
}
|
||||
}
|
||||
// 2. 统计各数字的出现次数
|
||||
// counter[num] 代表 num 的出现次数
|
||||
counter := make([]int, m+1)
|
||||
for _, num := range nums {
|
||||
counter[num]++
|
||||
}
|
||||
// 3. 遍历 counter ,将各元素填入原数组 nums
|
||||
for i, num := 0, 0; num < m+1; num++ {
|
||||
for j := 0; j < counter[num]; j++ {
|
||||
nums[i] = num
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{countingSortNaive}
|
||||
```
|
||||
|
||||
=== "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 0 ..< m + 1 {
|
||||
for _ in 0 ..< counter[num] {
|
||||
nums[i] = num
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{countingSortNaive}
|
||||
```
|
||||
|
||||
=== "JS"
|
||||
|
||||
```javascript title="counting_sort.js"
|
||||
/* 计数排序 */
|
||||
// 简单实现,无法用于排序对象
|
||||
function countingSortNaive(nums) {
|
||||
// 1. 统计数组最大元素 m
|
||||
let m = 0;
|
||||
for (const num of nums) {
|
||||
m = Math.max(m, num);
|
||||
}
|
||||
// 2. 统计各数字的出现次数
|
||||
// counter[num] 代表 num 的出现次数
|
||||
const counter = new Array(m + 1).fill(0);
|
||||
for (const num of nums) {
|
||||
counter[num]++;
|
||||
}
|
||||
// 3. 遍历 counter ,将各元素填入原数组 nums
|
||||
let i = 0;
|
||||
for (let num = 0; num < m + 1; num++) {
|
||||
for (let j = 0; j < counter[num]; j++, i++) {
|
||||
nums[i] = num;
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{countingSortNaive}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="counting_sort.ts"
|
||||
/* 计数排序 */
|
||||
// 简单实现,无法用于排序对象
|
||||
function countingSortNaive(nums: number[]): void {
|
||||
// 1. 统计数组最大元素 m
|
||||
let m = 0;
|
||||
for (const num of nums) {
|
||||
m = Math.max(m, num);
|
||||
}
|
||||
// 2. 统计各数字的出现次数
|
||||
// counter[num] 代表 num 的出现次数
|
||||
const counter: number[] = new Array<number>(m + 1).fill(0);
|
||||
for (const num of nums) {
|
||||
counter[num]++;
|
||||
}
|
||||
// 3. 遍历 counter ,将各元素填入原数组 nums
|
||||
let i = 0;
|
||||
for (let num = 0; num < m + 1; num++) {
|
||||
for (let j = 0; j < counter[num]; j++, i++) {
|
||||
nums[i] = num;
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{countingSortNaive}
|
||||
```
|
||||
|
||||
=== "Dart"
|
||||
|
||||
```dart title="counting_sort.dart"
|
||||
/* 计数排序 */
|
||||
// 简单实现,无法用于排序对象
|
||||
void countingSortNaive(List<int> nums) {
|
||||
// 1. 统计数组最大元素 m
|
||||
int m = 0;
|
||||
for (int _num in nums) {
|
||||
m = max(m, _num);
|
||||
}
|
||||
// 2. 统计各数字的出现次数
|
||||
// counter[_num] 代表 _num 的出现次数
|
||||
List<int> counter = List.filled(m + 1, 0);
|
||||
for (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;
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{countingSortNaive}
|
||||
```
|
||||
|
||||
=== "Rust"
|
||||
|
||||
```rust title="counting_sort.rs"
|
||||
/* 计数排序 */
|
||||
// 简单实现,无法用于排序对象
|
||||
fn counting_sort_naive(nums: &mut [i32]) {
|
||||
// 1. 统计数组最大元素 m
|
||||
let m = *nums.into_iter().max().unwrap();
|
||||
// 2. 统计各数字的出现次数
|
||||
// counter[num] 代表 num 的出现次数
|
||||
let mut counter = vec![0; m as usize + 1];
|
||||
for &num in &*nums {
|
||||
counter[num as usize] += 1;
|
||||
}
|
||||
// 3. 遍历 counter ,将各元素填入原数组 nums
|
||||
let mut i = 0;
|
||||
for num in 0..m + 1 {
|
||||
for _ in 0..counter[num as usize] {
|
||||
nums[i] = num;
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{counting_sort_naive}
|
||||
```
|
||||
|
||||
=== "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 = calloc(m + 1, sizeof(int));
|
||||
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;
|
||||
}
|
||||
}
|
||||
// 4. 释放内存
|
||||
free(counter);
|
||||
}
|
||||
[class]{}-[func]{countingSortNaive}
|
||||
```
|
||||
|
||||
=== "Kotlin"
|
||||
|
||||
```kotlin title="counting_sort.kt"
|
||||
/* 计数排序 */
|
||||
// 简单实现,无法用于排序对象
|
||||
fun countingSortNaive(nums: IntArray) {
|
||||
// 1. 统计数组最大元素 m
|
||||
var m = 0
|
||||
for (num in nums) {
|
||||
m = max(m, num)
|
||||
}
|
||||
// 2. 统计各数字的出现次数
|
||||
// counter[num] 代表 num 的出现次数
|
||||
val counter = IntArray(m + 1)
|
||||
for (num in nums) {
|
||||
counter[num]++
|
||||
}
|
||||
// 3. 遍历 counter ,将各元素填入原数组 nums
|
||||
var i = 0
|
||||
for (num in 0..<m + 1) {
|
||||
var j = 0
|
||||
while (j < counter[num]) {
|
||||
nums[i] = num
|
||||
j++
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{countingSortNaive}
|
||||
```
|
||||
|
||||
=== "Ruby"
|
||||
|
||||
```ruby title="counting_sort.rb"
|
||||
### 计数排序 ###
|
||||
def counting_sort_naive(nums)
|
||||
# 简单实现,无法用于排序对象
|
||||
# 1. 统计数组最大元素 m
|
||||
m = 0
|
||||
nums.each { |num| m = [m, num].max }
|
||||
# 2. 统计各数字的出现次数
|
||||
# counter[num] 代表 num 的出现次数
|
||||
counter = Array.new(m + 1, 0)
|
||||
nums.each { |num| counter[num] += 1 }
|
||||
# 3. 遍历 counter ,将各元素填入原数组 nums
|
||||
i = 0
|
||||
for num in 0...(m + 1)
|
||||
(0...counter[num]).each do
|
||||
nums[i] = num
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
[class]{}-[func]{counting_sort_naive}
|
||||
```
|
||||
|
||||
=== "Zig"
|
||||
@@ -375,11 +142,6 @@ The code is shown below:
|
||||
[class]{}-[func]{countingSortNaive}
|
||||
```
|
||||
|
||||
??? pythontutor "Code Visualization"
|
||||
|
||||
<div style="height: 549px; width: 100%;"><iframe class="pythontutor-iframe" src="https://pythontutor.com/iframe-embed.html#code=def%20counting_sort_naive%28nums%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22%E8%AE%A1%E6%95%B0%E6%8E%92%E5%BA%8F%22%22%22%0A%20%20%20%20%23%20%E7%AE%80%E5%8D%95%E5%AE%9E%E7%8E%B0%EF%BC%8C%E6%97%A0%E6%B3%95%E7%94%A8%E4%BA%8E%E6%8E%92%E5%BA%8F%E5%AF%B9%E8%B1%A1%0A%20%20%20%20%23%201.%20%E7%BB%9F%E8%AE%A1%E6%95%B0%E7%BB%84%E6%9C%80%E5%A4%A7%E5%85%83%E7%B4%A0%20m%0A%20%20%20%20m%20%3D%200%0A%20%20%20%20for%20num%20in%20nums%3A%0A%20%20%20%20%20%20%20%20m%20%3D%20max%28m,%20num%29%0A%20%20%20%20%23%202.%20%E7%BB%9F%E8%AE%A1%E5%90%84%E6%95%B0%E5%AD%97%E7%9A%84%E5%87%BA%E7%8E%B0%E6%AC%A1%E6%95%B0%0A%20%20%20%20%23%20counter%5Bnum%5D%20%E4%BB%A3%E8%A1%A8%20num%20%E7%9A%84%E5%87%BA%E7%8E%B0%E6%AC%A1%E6%95%B0%0A%20%20%20%20counter%20%3D%20%5B0%5D%20*%20%28m%20%2B%201%29%0A%20%20%20%20for%20num%20in%20nums%3A%0A%20%20%20%20%20%20%20%20counter%5Bnum%5D%20%2B%3D%201%0A%20%20%20%20%23%203.%20%E9%81%8D%E5%8E%86%20counter%20%EF%BC%8C%E5%B0%86%E5%90%84%E5%85%83%E7%B4%A0%E5%A1%AB%E5%85%A5%E5%8E%9F%E6%95%B0%E7%BB%84%20nums%0A%20%20%20%20i%20%3D%200%0A%20%20%20%20for%20num%20in%20range%28m%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20for%20_%20in%20range%28counter%5Bnum%5D%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20nums%5Bi%5D%20%3D%20num%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%2B%3D%201%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B1,%200,%201,%202,%200,%204,%200,%202,%202,%204%5D%0A%20%20%20%20counting_sort_naive%28nums%29%0A%20%20%20%20print%28f%22%E8%AE%A1%E6%95%B0%E6%8E%92%E5%BA%8F%EF%BC%88%E6%97%A0%E6%B3%95%E6%8E%92%E5%BA%8F%E5%AF%B9%E8%B1%A1%EF%BC%89%E5%AE%8C%E6%88%90%E5%90%8E%20nums%20%3D%20%7Bnums%7D%22%29&codeDivHeight=472&codeDivWidth=350&cumulative=false&curInstr=4&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe></div>
|
||||
<div style="margin-top: 5px;"><a href="https://pythontutor.com/iframe-embed.html#code=def%20counting_sort_naive%28nums%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22%E8%AE%A1%E6%95%B0%E6%8E%92%E5%BA%8F%22%22%22%0A%20%20%20%20%23%20%E7%AE%80%E5%8D%95%E5%AE%9E%E7%8E%B0%EF%BC%8C%E6%97%A0%E6%B3%95%E7%94%A8%E4%BA%8E%E6%8E%92%E5%BA%8F%E5%AF%B9%E8%B1%A1%0A%20%20%20%20%23%201.%20%E7%BB%9F%E8%AE%A1%E6%95%B0%E7%BB%84%E6%9C%80%E5%A4%A7%E5%85%83%E7%B4%A0%20m%0A%20%20%20%20m%20%3D%200%0A%20%20%20%20for%20num%20in%20nums%3A%0A%20%20%20%20%20%20%20%20m%20%3D%20max%28m,%20num%29%0A%20%20%20%20%23%202.%20%E7%BB%9F%E8%AE%A1%E5%90%84%E6%95%B0%E5%AD%97%E7%9A%84%E5%87%BA%E7%8E%B0%E6%AC%A1%E6%95%B0%0A%20%20%20%20%23%20counter%5Bnum%5D%20%E4%BB%A3%E8%A1%A8%20num%20%E7%9A%84%E5%87%BA%E7%8E%B0%E6%AC%A1%E6%95%B0%0A%20%20%20%20counter%20%3D%20%5B0%5D%20*%20%28m%20%2B%201%29%0A%20%20%20%20for%20num%20in%20nums%3A%0A%20%20%20%20%20%20%20%20counter%5Bnum%5D%20%2B%3D%201%0A%20%20%20%20%23%203.%20%E9%81%8D%E5%8E%86%20counter%20%EF%BC%8C%E5%B0%86%E5%90%84%E5%85%83%E7%B4%A0%E5%A1%AB%E5%85%A5%E5%8E%9F%E6%95%B0%E7%BB%84%20nums%0A%20%20%20%20i%20%3D%200%0A%20%20%20%20for%20num%20in%20range%28m%20%2B%201%29%3A%0A%20%20%20%20%20%20%20%20for%20_%20in%20range%28counter%5Bnum%5D%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20nums%5Bi%5D%20%3D%20num%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%2B%3D%201%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B1,%200,%201,%202,%200,%204,%200,%202,%202,%204%5D%0A%20%20%20%20counting_sort_naive%28nums%29%0A%20%20%20%20print%28f%22%E8%AE%A1%E6%95%B0%E6%8E%92%E5%BA%8F%EF%BC%88%E6%97%A0%E6%B3%95%E6%8E%92%E5%BA%8F%E5%AF%B9%E8%B1%A1%EF%BC%89%E5%AE%8C%E6%88%90%E5%90%8E%20nums%20%3D%20%7Bnums%7D%22%29&codeDivHeight=800&codeDivWidth=600&cumulative=false&curInstr=4&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false" target="_blank" rel="noopener noreferrer">Full Screen ></a></div>
|
||||
|
||||
!!! note "Connection between counting sort and bucket sort"
|
||||
|
||||
From the perspective of bucket sort, we can consider each index of the counting array `counter` in counting sort as a bucket, and the process of counting as distributing elements into the corresponding buckets. Essentially, counting sort is a special case of bucket sort for integer data.
|
||||
@@ -433,28 +195,28 @@ The implementation code of counting sort is shown below:
|
||||
|
||||
```python title="counting_sort.py"
|
||||
def counting_sort(nums: list[int]):
|
||||
"""计数排序"""
|
||||
# 完整实现,可排序对象,并且是稳定排序
|
||||
# 1. 统计数组最大元素 m
|
||||
"""Counting sort"""
|
||||
# Complete implementation, can sort objects and is a stable sort
|
||||
# 1. Count the maximum element m in the array
|
||||
m = max(nums)
|
||||
# 2. 统计各数字的出现次数
|
||||
# counter[num] 代表 num 的出现次数
|
||||
# 2. Count the occurrence of each digit
|
||||
# counter[num] represents the occurrence of num
|
||||
counter = [0] * (m + 1)
|
||||
for num in nums:
|
||||
counter[num] += 1
|
||||
# 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引”
|
||||
# 即 counter[num]-1 是 num 在 res 中最后一次出现的索引
|
||||
# 3. Calculate the prefix sum of counter, converting "occurrence count" to "tail index"
|
||||
# counter[num]-1 is the last index where num appears in res
|
||||
for i in range(m):
|
||||
counter[i + 1] += counter[i]
|
||||
# 4. 倒序遍历 nums ,将各元素填入结果数组 res
|
||||
# 初始化数组 res 用于记录结果
|
||||
# 4. Traverse nums in reverse order, placing each element into the result array res
|
||||
# Initialize the array res to record results
|
||||
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
|
||||
res[counter[num] - 1] = num # Place num at the corresponding index
|
||||
counter[num] -= 1 # Decrement the prefix sum by 1, getting the next index to place num
|
||||
# Use result array res to overwrite the original array nums
|
||||
for i in range(n):
|
||||
nums[i] = res[i]
|
||||
```
|
||||
@@ -462,71 +224,41 @@ The implementation code of counting sort is shown below:
|
||||
=== "C++"
|
||||
|
||||
```cpp title="counting_sort.cpp"
|
||||
/* 计数排序 */
|
||||
// 完整实现,可排序对象,并且是稳定排序
|
||||
void countingSort(vector<int> &nums) {
|
||||
// 1. 统计数组最大元素 m
|
||||
int m = 0;
|
||||
for (int num : nums) {
|
||||
m = max(m, num);
|
||||
}
|
||||
// 2. 统计各数字的出现次数
|
||||
// counter[num] 代表 num 的出现次数
|
||||
vector<int> counter(m + 1, 0);
|
||||
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.size();
|
||||
vector<int> res(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
|
||||
nums = res;
|
||||
}
|
||||
[class]{}-[func]{countingSort}
|
||||
```
|
||||
|
||||
=== "Java"
|
||||
|
||||
```java title="counting_sort.java"
|
||||
/* 计数排序 */
|
||||
// 完整实现,可排序对象,并且是稳定排序
|
||||
/* Counting sort */
|
||||
// Complete implementation, can sort objects and is a stable sort
|
||||
void countingSort(int[] nums) {
|
||||
// 1. 统计数组最大元素 m
|
||||
// 1. Count the maximum element m in the array
|
||||
int m = 0;
|
||||
for (int num : nums) {
|
||||
m = Math.max(m, num);
|
||||
}
|
||||
// 2. 统计各数字的出现次数
|
||||
// counter[num] 代表 num 的出现次数
|
||||
// 2. Count the occurrence of each digit
|
||||
// counter[num] represents the occurrence of num
|
||||
int[] counter = new int[m + 1];
|
||||
for (int num : nums) {
|
||||
counter[num]++;
|
||||
}
|
||||
// 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引”
|
||||
// 即 counter[num]-1 是 num 在 res 中最后一次出现的索引
|
||||
// 3. Calculate the prefix sum of counter, converting "occurrence count" to "tail index"
|
||||
// counter[num]-1 is the last index where num appears in res
|
||||
for (int i = 0; i < m; i++) {
|
||||
counter[i + 1] += counter[i];
|
||||
}
|
||||
// 4. 倒序遍历 nums ,将各元素填入结果数组 res
|
||||
// 初始化数组 res 用于记录结果
|
||||
// 4. Traverse nums in reverse order, placing each element into the result array res
|
||||
// Initialize the array res to record results
|
||||
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[counter[num] - 1] = num; // Place num at the corresponding index
|
||||
counter[num]--; // Decrement the prefix sum by 1, getting the next index to place num
|
||||
}
|
||||
// 使用结果数组 res 覆盖原数组 nums
|
||||
// Use result array res to overwrite the original array nums
|
||||
for (int i = 0; i < n; i++) {
|
||||
nums[i] = res[i];
|
||||
}
|
||||
@@ -536,366 +268,61 @@ The implementation code of counting sort is shown below:
|
||||
=== "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];
|
||||
}
|
||||
}
|
||||
[class]{counting_sort}-[func]{CountingSort}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
||||
```go title="counting_sort.go"
|
||||
/* 计数排序 */
|
||||
// 完整实现,可排序对象,并且是稳定排序
|
||||
func countingSort(nums []int) {
|
||||
// 1. 统计数组最大元素 m
|
||||
m := 0
|
||||
for _, num := range nums {
|
||||
if num > m {
|
||||
m = num
|
||||
}
|
||||
}
|
||||
// 2. 统计各数字的出现次数
|
||||
// counter[num] 代表 num 的出现次数
|
||||
counter := make([]int, m+1)
|
||||
for _, num := range nums {
|
||||
counter[num]++
|
||||
}
|
||||
// 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引”
|
||||
// 即 counter[num]-1 是 num 在 res 中最后一次出现的索引
|
||||
for i := 0; i < m; i++ {
|
||||
counter[i+1] += counter[i]
|
||||
}
|
||||
// 4. 倒序遍历 nums ,将各元素填入结果数组 res
|
||||
// 初始化数组 res 用于记录结果
|
||||
n := len(nums)
|
||||
res := make([]int, n)
|
||||
for i := n - 1; i >= 0; i-- {
|
||||
num := nums[i]
|
||||
// 将 num 放置到对应索引处
|
||||
res[counter[num]-1] = num
|
||||
// 令前缀和自减 1 ,得到下次放置 num 的索引
|
||||
counter[num]--
|
||||
}
|
||||
// 使用结果数组 res 覆盖原数组 nums
|
||||
copy(nums, res)
|
||||
}
|
||||
[class]{}-[func]{countingSort}
|
||||
```
|
||||
|
||||
=== "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 0 ..< m {
|
||||
counter[i + 1] += counter[i]
|
||||
}
|
||||
// 4. 倒序遍历 nums ,将各元素填入结果数组 res
|
||||
// 初始化数组 res 用于记录结果
|
||||
var res = Array(repeating: 0, count: nums.count)
|
||||
for i in nums.indices.reversed() {
|
||||
let num = nums[i]
|
||||
res[counter[num] - 1] = num // 将 num 放置到对应索引处
|
||||
counter[num] -= 1 // 令前缀和自减 1 ,得到下次放置 num 的索引
|
||||
}
|
||||
// 使用结果数组 res 覆盖原数组 nums
|
||||
for i in nums.indices {
|
||||
nums[i] = res[i]
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{countingSort}
|
||||
```
|
||||
|
||||
=== "JS"
|
||||
|
||||
```javascript title="counting_sort.js"
|
||||
/* 计数排序 */
|
||||
// 完整实现,可排序对象,并且是稳定排序
|
||||
function countingSort(nums) {
|
||||
// 1. 统计数组最大元素 m
|
||||
let m = 0;
|
||||
for (const num of nums) {
|
||||
m = Math.max(m, num);
|
||||
}
|
||||
// 2. 统计各数字的出现次数
|
||||
// counter[num] 代表 num 的出现次数
|
||||
const counter = new Array(m + 1).fill(0);
|
||||
for (const num of nums) {
|
||||
counter[num]++;
|
||||
}
|
||||
// 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引”
|
||||
// 即 counter[num]-1 是 num 在 res 中最后一次出现的索引
|
||||
for (let i = 0; i < m; i++) {
|
||||
counter[i + 1] += counter[i];
|
||||
}
|
||||
// 4. 倒序遍历 nums ,将各元素填入结果数组 res
|
||||
// 初始化数组 res 用于记录结果
|
||||
const n = nums.length;
|
||||
const res = new Array(n);
|
||||
for (let i = n - 1; i >= 0; i--) {
|
||||
const num = nums[i];
|
||||
res[counter[num] - 1] = num; // 将 num 放置到对应索引处
|
||||
counter[num]--; // 令前缀和自减 1 ,得到下次放置 num 的索引
|
||||
}
|
||||
// 使用结果数组 res 覆盖原数组 nums
|
||||
for (let i = 0; i < n; i++) {
|
||||
nums[i] = res[i];
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{countingSort}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="counting_sort.ts"
|
||||
/* 计数排序 */
|
||||
// 完整实现,可排序对象,并且是稳定排序
|
||||
function countingSort(nums: number[]): void {
|
||||
// 1. 统计数组最大元素 m
|
||||
let m = 0;
|
||||
for (const num of nums) {
|
||||
m = Math.max(m, num);
|
||||
}
|
||||
// 2. 统计各数字的出现次数
|
||||
// counter[num] 代表 num 的出现次数
|
||||
const counter: number[] = new Array<number>(m + 1).fill(0);
|
||||
for (const num of nums) {
|
||||
counter[num]++;
|
||||
}
|
||||
// 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引”
|
||||
// 即 counter[num]-1 是 num 在 res 中最后一次出现的索引
|
||||
for (let i = 0; i < m; i++) {
|
||||
counter[i + 1] += counter[i];
|
||||
}
|
||||
// 4. 倒序遍历 nums ,将各元素填入结果数组 res
|
||||
// 初始化数组 res 用于记录结果
|
||||
const n = nums.length;
|
||||
const res: number[] = new Array<number>(n);
|
||||
for (let i = n - 1; i >= 0; i--) {
|
||||
const num = nums[i];
|
||||
res[counter[num] - 1] = num; // 将 num 放置到对应索引处
|
||||
counter[num]--; // 令前缀和自减 1 ,得到下次放置 num 的索引
|
||||
}
|
||||
// 使用结果数组 res 覆盖原数组 nums
|
||||
for (let i = 0; i < n; i++) {
|
||||
nums[i] = res[i];
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{countingSort}
|
||||
```
|
||||
|
||||
=== "Dart"
|
||||
|
||||
```dart title="counting_sort.dart"
|
||||
/* 计数排序 */
|
||||
// 完整实现,可排序对象,并且是稳定排序
|
||||
void countingSort(List<int> nums) {
|
||||
// 1. 统计数组最大元素 m
|
||||
int m = 0;
|
||||
for (int _num in nums) {
|
||||
m = max(m, _num);
|
||||
}
|
||||
// 2. 统计各数字的出现次数
|
||||
// counter[_num] 代表 _num 的出现次数
|
||||
List<int> counter = List.filled(m + 1, 0);
|
||||
for (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;
|
||||
List<int> res = List.filled(n, 0);
|
||||
for (int i = n - 1; i >= 0; i--) {
|
||||
int _num = nums[i];
|
||||
res[counter[_num] - 1] = _num; // 将 _num 放置到对应索引处
|
||||
counter[_num]--; // 令前缀和自减 1 ,得到下次放置 _num 的索引
|
||||
}
|
||||
// 使用结果数组 res 覆盖原数组 nums
|
||||
nums.setAll(0, res);
|
||||
}
|
||||
[class]{}-[func]{countingSort}
|
||||
```
|
||||
|
||||
=== "Rust"
|
||||
|
||||
```rust title="counting_sort.rs"
|
||||
/* 计数排序 */
|
||||
// 完整实现,可排序对象,并且是稳定排序
|
||||
fn counting_sort(nums: &mut [i32]) {
|
||||
// 1. 统计数组最大元素 m
|
||||
let m = *nums.into_iter().max().unwrap();
|
||||
// 2. 统计各数字的出现次数
|
||||
// counter[num] 代表 num 的出现次数
|
||||
let mut counter = vec![0; m as usize + 1];
|
||||
for &num in &*nums {
|
||||
counter[num as usize] += 1;
|
||||
}
|
||||
// 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引”
|
||||
// 即 counter[num]-1 是 num 在 res 中最后一次出现的索引
|
||||
for i in 0..m as usize {
|
||||
counter[i + 1] += counter[i];
|
||||
}
|
||||
// 4. 倒序遍历 nums ,将各元素填入结果数组 res
|
||||
// 初始化数组 res 用于记录结果
|
||||
let n = nums.len();
|
||||
let mut res = vec![0; n];
|
||||
for i in (0..n).rev() {
|
||||
let num = nums[i];
|
||||
res[counter[num as usize] - 1] = num; // 将 num 放置到对应索引处
|
||||
counter[num as usize] -= 1; // 令前缀和自减 1 ,得到下次放置 num 的索引
|
||||
}
|
||||
// 使用结果数组 res 覆盖原数组 nums
|
||||
for i in 0..n {
|
||||
nums[i] = res[i];
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{counting_sort}
|
||||
```
|
||||
|
||||
=== "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 = calloc(m, sizeof(int));
|
||||
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));
|
||||
// 5. 释放内存
|
||||
free(counter);
|
||||
}
|
||||
[class]{}-[func]{countingSort}
|
||||
```
|
||||
|
||||
=== "Kotlin"
|
||||
|
||||
```kotlin title="counting_sort.kt"
|
||||
/* 计数排序 */
|
||||
// 完整实现,可排序对象,并且是稳定排序
|
||||
fun countingSort(nums: IntArray) {
|
||||
// 1. 统计数组最大元素 m
|
||||
var m = 0
|
||||
for (num in nums) {
|
||||
m = max(m, num)
|
||||
}
|
||||
// 2. 统计各数字的出现次数
|
||||
// counter[num] 代表 num 的出现次数
|
||||
val counter = IntArray(m + 1)
|
||||
for (num in nums) {
|
||||
counter[num]++
|
||||
}
|
||||
// 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引”
|
||||
// 即 counter[num]-1 是 num 在 res 中最后一次出现的索引
|
||||
for (i in 0..<m) {
|
||||
counter[i + 1] += counter[i]
|
||||
}
|
||||
// 4. 倒序遍历 nums ,将各元素填入结果数组 res
|
||||
// 初始化数组 res 用于记录结果
|
||||
val n = nums.size
|
||||
val res = IntArray(n)
|
||||
for (i in n - 1 downTo 0) {
|
||||
val num = nums[i]
|
||||
res[counter[num] - 1] = num // 将 num 放置到对应索引处
|
||||
counter[num]-- // 令前缀和自减 1 ,得到下次放置 num 的索引
|
||||
}
|
||||
// 使用结果数组 res 覆盖原数组 nums
|
||||
for (i in 0..<n) {
|
||||
nums[i] = res[i]
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{countingSort}
|
||||
```
|
||||
|
||||
=== "Ruby"
|
||||
|
||||
```ruby title="counting_sort.rb"
|
||||
### 计数排序 ###
|
||||
def counting_sort(nums)
|
||||
# 完整实现,可排序对象,并且是稳定排序
|
||||
# 1. 统计数组最大元素 m
|
||||
m = nums.max
|
||||
# 2. 统计各数字的出现次数
|
||||
# counter[num] 代表 num 的出现次数
|
||||
counter = Array.new(m + 1, 0)
|
||||
nums.each { |num| counter[num] += 1 }
|
||||
# 3. 求 counter 的前缀和,将“出现次数”转换为“尾索引”
|
||||
# 即 counter[num]-1 是 num 在 res 中最后一次出现的索引
|
||||
(0...m).each { |i| counter[i + 1] += counter[i] }
|
||||
# 4. 倒序遍历 nums, 将各元素填入结果数组 res
|
||||
# 初始化数组 res 用于记录结果
|
||||
n = nums.length
|
||||
res = Array.new(n, 0)
|
||||
(n - 1).downto(0).each do |i|
|
||||
num = nums[i]
|
||||
res[counter[num] - 1] = num # 将 num 放置到对应索引处
|
||||
counter[num] -= 1 # 令前缀和自减 1 ,得到下次放置 num 的索引
|
||||
end
|
||||
# 使用结果数组 res 覆盖原数组 nums
|
||||
(0...n).each { |i| nums[i] = res[i] }
|
||||
end
|
||||
[class]{}-[func]{counting_sort}
|
||||
```
|
||||
|
||||
=== "Zig"
|
||||
@@ -904,11 +331,6 @@ The implementation code of counting sort is shown below:
|
||||
[class]{}-[func]{countingSort}
|
||||
```
|
||||
|
||||
??? pythontutor "Code Visualization"
|
||||
|
||||
<div style="height: 549px; width: 100%;"><iframe class="pythontutor-iframe" src="https://pythontutor.com/iframe-embed.html#code=def%20counting_sort%28nums%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22%E8%AE%A1%E6%95%B0%E6%8E%92%E5%BA%8F%22%22%22%0A%20%20%20%20%23%20%E5%AE%8C%E6%95%B4%E5%AE%9E%E7%8E%B0%EF%BC%8C%E5%8F%AF%E6%8E%92%E5%BA%8F%E5%AF%B9%E8%B1%A1%EF%BC%8C%E5%B9%B6%E4%B8%94%E6%98%AF%E7%A8%B3%E5%AE%9A%E6%8E%92%E5%BA%8F%0A%20%20%20%20%23%201.%20%E7%BB%9F%E8%AE%A1%E6%95%B0%E7%BB%84%E6%9C%80%E5%A4%A7%E5%85%83%E7%B4%A0%20m%0A%20%20%20%20m%20%3D%20max%28nums%29%0A%20%20%20%20%23%202.%20%E7%BB%9F%E8%AE%A1%E5%90%84%E6%95%B0%E5%AD%97%E7%9A%84%E5%87%BA%E7%8E%B0%E6%AC%A1%E6%95%B0%0A%20%20%20%20%23%20counter%5Bnum%5D%20%E4%BB%A3%E8%A1%A8%20num%20%E7%9A%84%E5%87%BA%E7%8E%B0%E6%AC%A1%E6%95%B0%0A%20%20%20%20counter%20%3D%20%5B0%5D%20*%20%28m%20%2B%201%29%0A%20%20%20%20for%20num%20in%20nums%3A%0A%20%20%20%20%20%20%20%20counter%5Bnum%5D%20%2B%3D%201%0A%20%20%20%20%23%203.%20%E6%B1%82%20counter%20%E7%9A%84%E5%89%8D%E7%BC%80%E5%92%8C%EF%BC%8C%E5%B0%86%E2%80%9C%E5%87%BA%E7%8E%B0%E6%AC%A1%E6%95%B0%E2%80%9D%E8%BD%AC%E6%8D%A2%E4%B8%BA%E2%80%9C%E5%B0%BE%E7%B4%A2%E5%BC%95%E2%80%9D%0A%20%20%20%20%23%20%E5%8D%B3%20counter%5Bnum%5D-1%20%E6%98%AF%20num%20%E5%9C%A8%20res%20%E4%B8%AD%E6%9C%80%E5%90%8E%E4%B8%80%E6%AC%A1%E5%87%BA%E7%8E%B0%E7%9A%84%E7%B4%A2%E5%BC%95%0A%20%20%20%20for%20i%20in%20range%28m%29%3A%0A%20%20%20%20%20%20%20%20counter%5Bi%20%2B%201%5D%20%2B%3D%20counter%5Bi%5D%0A%20%20%20%20%23%204.%20%E5%80%92%E5%BA%8F%E9%81%8D%E5%8E%86%20nums%20%EF%BC%8C%E5%B0%86%E5%90%84%E5%85%83%E7%B4%A0%E5%A1%AB%E5%85%A5%E7%BB%93%E6%9E%9C%E6%95%B0%E7%BB%84%20res%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E6%95%B0%E7%BB%84%20res%20%E7%94%A8%E4%BA%8E%E8%AE%B0%E5%BD%95%E7%BB%93%E6%9E%9C%0A%20%20%20%20n%20%3D%20len%28nums%29%0A%20%20%20%20res%20%3D%20%5B0%5D%20*%20n%0A%20%20%20%20for%20i%20in%20range%28n%20-%201,%20-1,%20-1%29%3A%0A%20%20%20%20%20%20%20%20num%20%3D%20nums%5Bi%5D%0A%20%20%20%20%20%20%20%20res%5Bcounter%5Bnum%5D%20-%201%5D%20%3D%20num%20%20%23%20%E5%B0%86%20num%20%E6%94%BE%E7%BD%AE%E5%88%B0%E5%AF%B9%E5%BA%94%E7%B4%A2%E5%BC%95%E5%A4%84%0A%20%20%20%20%20%20%20%20counter%5Bnum%5D%20-%3D%201%20%20%23%20%E4%BB%A4%E5%89%8D%E7%BC%80%E5%92%8C%E8%87%AA%E5%87%8F%201%20%EF%BC%8C%E5%BE%97%E5%88%B0%E4%B8%8B%E6%AC%A1%E6%94%BE%E7%BD%AE%20num%20%E7%9A%84%E7%B4%A2%E5%BC%95%0A%20%20%20%20%23%20%E4%BD%BF%E7%94%A8%E7%BB%93%E6%9E%9C%E6%95%B0%E7%BB%84%20res%20%E8%A6%86%E7%9B%96%E5%8E%9F%E6%95%B0%E7%BB%84%20nums%0A%20%20%20%20for%20i%20in%20range%28n%29%3A%0A%20%20%20%20%20%20%20%20nums%5Bi%5D%20%3D%20res%5Bi%5D%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B1,%200,%201,%202,%200,%204,%200,%202,%202,%204%5D%0A%20%20%20%20counting_sort%28nums%29%0A%20%20%20%20print%28f%22%E8%AE%A1%E6%95%B0%E6%8E%92%E5%BA%8F%E5%AE%8C%E6%88%90%E5%90%8E%20nums%20%3D%20%7Bnums%7D%22%29&codeDivHeight=472&codeDivWidth=350&cumulative=false&curInstr=4&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe></div>
|
||||
<div style="margin-top: 5px;"><a href="https://pythontutor.com/iframe-embed.html#code=def%20counting_sort%28nums%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22%E8%AE%A1%E6%95%B0%E6%8E%92%E5%BA%8F%22%22%22%0A%20%20%20%20%23%20%E5%AE%8C%E6%95%B4%E5%AE%9E%E7%8E%B0%EF%BC%8C%E5%8F%AF%E6%8E%92%E5%BA%8F%E5%AF%B9%E8%B1%A1%EF%BC%8C%E5%B9%B6%E4%B8%94%E6%98%AF%E7%A8%B3%E5%AE%9A%E6%8E%92%E5%BA%8F%0A%20%20%20%20%23%201.%20%E7%BB%9F%E8%AE%A1%E6%95%B0%E7%BB%84%E6%9C%80%E5%A4%A7%E5%85%83%E7%B4%A0%20m%0A%20%20%20%20m%20%3D%20max%28nums%29%0A%20%20%20%20%23%202.%20%E7%BB%9F%E8%AE%A1%E5%90%84%E6%95%B0%E5%AD%97%E7%9A%84%E5%87%BA%E7%8E%B0%E6%AC%A1%E6%95%B0%0A%20%20%20%20%23%20counter%5Bnum%5D%20%E4%BB%A3%E8%A1%A8%20num%20%E7%9A%84%E5%87%BA%E7%8E%B0%E6%AC%A1%E6%95%B0%0A%20%20%20%20counter%20%3D%20%5B0%5D%20*%20%28m%20%2B%201%29%0A%20%20%20%20for%20num%20in%20nums%3A%0A%20%20%20%20%20%20%20%20counter%5Bnum%5D%20%2B%3D%201%0A%20%20%20%20%23%203.%20%E6%B1%82%20counter%20%E7%9A%84%E5%89%8D%E7%BC%80%E5%92%8C%EF%BC%8C%E5%B0%86%E2%80%9C%E5%87%BA%E7%8E%B0%E6%AC%A1%E6%95%B0%E2%80%9D%E8%BD%AC%E6%8D%A2%E4%B8%BA%E2%80%9C%E5%B0%BE%E7%B4%A2%E5%BC%95%E2%80%9D%0A%20%20%20%20%23%20%E5%8D%B3%20counter%5Bnum%5D-1%20%E6%98%AF%20num%20%E5%9C%A8%20res%20%E4%B8%AD%E6%9C%80%E5%90%8E%E4%B8%80%E6%AC%A1%E5%87%BA%E7%8E%B0%E7%9A%84%E7%B4%A2%E5%BC%95%0A%20%20%20%20for%20i%20in%20range%28m%29%3A%0A%20%20%20%20%20%20%20%20counter%5Bi%20%2B%201%5D%20%2B%3D%20counter%5Bi%5D%0A%20%20%20%20%23%204.%20%E5%80%92%E5%BA%8F%E9%81%8D%E5%8E%86%20nums%20%EF%BC%8C%E5%B0%86%E5%90%84%E5%85%83%E7%B4%A0%E5%A1%AB%E5%85%A5%E7%BB%93%E6%9E%9C%E6%95%B0%E7%BB%84%20res%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E6%95%B0%E7%BB%84%20res%20%E7%94%A8%E4%BA%8E%E8%AE%B0%E5%BD%95%E7%BB%93%E6%9E%9C%0A%20%20%20%20n%20%3D%20len%28nums%29%0A%20%20%20%20res%20%3D%20%5B0%5D%20*%20n%0A%20%20%20%20for%20i%20in%20range%28n%20-%201,%20-1,%20-1%29%3A%0A%20%20%20%20%20%20%20%20num%20%3D%20nums%5Bi%5D%0A%20%20%20%20%20%20%20%20res%5Bcounter%5Bnum%5D%20-%201%5D%20%3D%20num%20%20%23%20%E5%B0%86%20num%20%E6%94%BE%E7%BD%AE%E5%88%B0%E5%AF%B9%E5%BA%94%E7%B4%A2%E5%BC%95%E5%A4%84%0A%20%20%20%20%20%20%20%20counter%5Bnum%5D%20-%3D%201%20%20%23%20%E4%BB%A4%E5%89%8D%E7%BC%80%E5%92%8C%E8%87%AA%E5%87%8F%201%20%EF%BC%8C%E5%BE%97%E5%88%B0%E4%B8%8B%E6%AC%A1%E6%94%BE%E7%BD%AE%20num%20%E7%9A%84%E7%B4%A2%E5%BC%95%0A%20%20%20%20%23%20%E4%BD%BF%E7%94%A8%E7%BB%93%E6%9E%9C%E6%95%B0%E7%BB%84%20res%20%E8%A6%86%E7%9B%96%E5%8E%9F%E6%95%B0%E7%BB%84%20nums%0A%20%20%20%20for%20i%20in%20range%28n%29%3A%0A%20%20%20%20%20%20%20%20nums%5Bi%5D%20%3D%20res%5Bi%5D%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B1,%200,%201,%202,%200,%204,%200,%202,%202,%204%5D%0A%20%20%20%20counting_sort%28nums%29%0A%20%20%20%20print%28f%22%E8%AE%A1%E6%95%B0%E6%8E%92%E5%BA%8F%E5%AE%8C%E6%88%90%E5%90%8E%20nums%20%3D%20%7Bnums%7D%22%29&codeDivHeight=800&codeDivWidth=600&cumulative=false&curInstr=4&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false" target="_blank" rel="noopener noreferrer">Full Screen ></a></div>
|
||||
|
||||
## 11.9.3 Algorithm characteristics
|
||||
|
||||
- **Time complexity is $O(n + m)$, non-adaptive sort**: Involves traversing `nums` and `counter`, both using linear time. Generally, $n \gg m$, and the time complexity tends towards $O(n)$.
|
||||
|
||||
@@ -72,9 +72,9 @@ In the code implementation, we used the sift-down function `sift_down()` from th
|
||||
|
||||
```python title="heap_sort.py"
|
||||
def sift_down(nums: list[int], n: int, i: int):
|
||||
"""堆的长度为 n ,从节点 i 开始,从顶至底堆化"""
|
||||
"""Heap length is n, start heapifying node i, from top to bottom"""
|
||||
while True:
|
||||
# 判断节点 i, l, r 中值最大的节点,记为 ma
|
||||
# Determine the largest node among i, l, r, noted as ma
|
||||
l = 2 * i + 1
|
||||
r = 2 * i + 2
|
||||
ma = i
|
||||
@@ -82,75 +82,42 @@ In the code implementation, we used the sift-down function `sift_down()` from th
|
||||
ma = l
|
||||
if r < n and nums[r] > nums[ma]:
|
||||
ma = r
|
||||
# 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出
|
||||
# If node i is the largest or indices l, r are out of bounds, no further heapification needed, break
|
||||
if ma == i:
|
||||
break
|
||||
# 交换两节点
|
||||
# Swap two nodes
|
||||
nums[i], nums[ma] = nums[ma], nums[i]
|
||||
# 循环向下堆化
|
||||
# Loop downwards heapification
|
||||
i = ma
|
||||
|
||||
def heap_sort(nums: list[int]):
|
||||
"""堆排序"""
|
||||
# 建堆操作:堆化除叶节点以外的其他所有节点
|
||||
"""Heap sort"""
|
||||
# Build heap operation: heapify all nodes except leaves
|
||||
for i in range(len(nums) // 2 - 1, -1, -1):
|
||||
sift_down(nums, len(nums), i)
|
||||
# 从堆中提取最大元素,循环 n-1 轮
|
||||
# Extract the largest element from the heap and repeat for n-1 rounds
|
||||
for i in range(len(nums) - 1, 0, -1):
|
||||
# 交换根节点与最右叶节点(交换首元素与尾元素)
|
||||
# Swap the root node with the rightmost leaf node (swap the first element with the last element)
|
||||
nums[0], nums[i] = nums[i], nums[0]
|
||||
# 以根节点为起点,从顶至底进行堆化
|
||||
# Start heapifying the root node, from top to bottom
|
||||
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;
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{siftDown}
|
||||
|
||||
/* 堆排序 */
|
||||
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);
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{heapSort}
|
||||
```
|
||||
|
||||
=== "Java"
|
||||
|
||||
```java title="heap_sort.java"
|
||||
/* 堆的长度为 n ,从节点 i 开始,从顶至底堆化 */
|
||||
/* Heap length is n, start heapifying node i, from top to bottom */
|
||||
void siftDown(int[] nums, int n, int i) {
|
||||
while (true) {
|
||||
// 判断节点 i, l, r 中值最大的节点,记为 ma
|
||||
// Determine the largest node among i, l, r, noted as ma
|
||||
int l = 2 * i + 1;
|
||||
int r = 2 * i + 2;
|
||||
int ma = i;
|
||||
@@ -158,31 +125,31 @@ In the code implementation, we used the sift-down function `sift_down()` from th
|
||||
ma = l;
|
||||
if (r < n && nums[r] > nums[ma])
|
||||
ma = r;
|
||||
// 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出
|
||||
// If node i is the largest or indices l, r are out of bounds, no further heapification needed, break
|
||||
if (ma == i)
|
||||
break;
|
||||
// 交换两节点
|
||||
// Swap two nodes
|
||||
int temp = nums[i];
|
||||
nums[i] = nums[ma];
|
||||
nums[ma] = temp;
|
||||
// 循环向下堆化
|
||||
// Loop downwards heapification
|
||||
i = ma;
|
||||
}
|
||||
}
|
||||
|
||||
/* 堆排序 */
|
||||
/* Heap sort */
|
||||
void heapSort(int[] nums) {
|
||||
// 建堆操作:堆化除叶节点以外的其他所有节点
|
||||
// Build heap operation: heapify all nodes except leaves
|
||||
for (int i = nums.length / 2 - 1; i >= 0; i--) {
|
||||
siftDown(nums, nums.length, i);
|
||||
}
|
||||
// 从堆中提取最大元素,循环 n-1 轮
|
||||
// Extract the largest element from the heap and repeat for n-1 rounds
|
||||
for (int i = nums.length - 1; i > 0; i--) {
|
||||
// 交换根节点与最右叶节点(交换首元素与尾元素)
|
||||
// Swap the root node with the rightmost leaf node (swap the first element with the last element)
|
||||
int tmp = nums[0];
|
||||
nums[0] = nums[i];
|
||||
nums[i] = tmp;
|
||||
// 以根节点为起点,从顶至底进行堆化
|
||||
// Start heapifying the root node, from top to bottom
|
||||
siftDown(nums, i, 0);
|
||||
}
|
||||
}
|
||||
@@ -191,429 +158,81 @@ In the code implementation, we used the sift-down function `sift_down()` from th
|
||||
=== "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;
|
||||
}
|
||||
}
|
||||
[class]{heap_sort}-[func]{SiftDown}
|
||||
|
||||
/* 堆排序 */
|
||||
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);
|
||||
}
|
||||
}
|
||||
[class]{heap_sort}-[func]{HeapSort}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
||||
```go title="heap_sort.go"
|
||||
/* 堆的长度为 n ,从节点 i 开始,从顶至底堆化 */
|
||||
func siftDown(nums *[]int, n, i int) {
|
||||
for true {
|
||||
// 判断节点 i, l, r 中值最大的节点,记为 ma
|
||||
l := 2*i + 1
|
||||
r := 2*i + 2
|
||||
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)[i], (*nums)[ma] = (*nums)[ma], (*nums)[i]
|
||||
// 循环向下堆化
|
||||
i = ma
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{siftDown}
|
||||
|
||||
/* 堆排序 */
|
||||
func heapSort(nums *[]int) {
|
||||
// 建堆操作:堆化除叶节点以外的其他所有节点
|
||||
for i := len(*nums)/2 - 1; i >= 0; i-- {
|
||||
siftDown(nums, len(*nums), i)
|
||||
}
|
||||
// 从堆中提取最大元素,循环 n-1 轮
|
||||
for i := len(*nums) - 1; i > 0; i-- {
|
||||
// 交换根节点与最右叶节点(交换首元素与尾元素)
|
||||
(*nums)[0], (*nums)[i] = (*nums)[i], (*nums)[0]
|
||||
// 以根节点为起点,从顶至底进行堆化
|
||||
siftDown(nums, i, 0)
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{heapSort}
|
||||
```
|
||||
|
||||
=== "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
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{siftDown}
|
||||
|
||||
/* 堆排序 */
|
||||
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 nums.indices.dropFirst().reversed() {
|
||||
// 交换根节点与最右叶节点(交换首元素与尾元素)
|
||||
nums.swapAt(0, i)
|
||||
// 以根节点为起点,从顶至底进行堆化
|
||||
siftDown(nums: &nums, n: i, i: 0)
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{heapSort}
|
||||
```
|
||||
|
||||
=== "JS"
|
||||
|
||||
```javascript title="heap_sort.js"
|
||||
/* 堆的长度为 n ,从节点 i 开始,从顶至底堆化 */
|
||||
function siftDown(nums, n, i) {
|
||||
while (true) {
|
||||
// 判断节点 i, l, r 中值最大的节点,记为 ma
|
||||
let l = 2 * i + 1;
|
||||
let r = 2 * i + 2;
|
||||
let 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[i], nums[ma]] = [nums[ma], nums[i]];
|
||||
// 循环向下堆化
|
||||
i = ma;
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{siftDown}
|
||||
|
||||
/* 堆排序 */
|
||||
function heapSort(nums) {
|
||||
// 建堆操作:堆化除叶节点以外的其他所有节点
|
||||
for (let i = Math.floor(nums.length / 2) - 1; i >= 0; i--) {
|
||||
siftDown(nums, nums.length, i);
|
||||
}
|
||||
// 从堆中提取最大元素,循环 n-1 轮
|
||||
for (let i = nums.length - 1; i > 0; i--) {
|
||||
// 交换根节点与最右叶节点(交换首元素与尾元素)
|
||||
[nums[0], nums[i]] = [nums[i], nums[0]];
|
||||
// 以根节点为起点,从顶至底进行堆化
|
||||
siftDown(nums, i, 0);
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{heapSort}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="heap_sort.ts"
|
||||
/* 堆的长度为 n ,从节点 i 开始,从顶至底堆化 */
|
||||
function siftDown(nums: number[], n: number, i: number): void {
|
||||
while (true) {
|
||||
// 判断节点 i, l, r 中值最大的节点,记为 ma
|
||||
let l = 2 * i + 1;
|
||||
let r = 2 * i + 2;
|
||||
let 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[i], nums[ma]] = [nums[ma], nums[i]];
|
||||
// 循环向下堆化
|
||||
i = ma;
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{siftDown}
|
||||
|
||||
/* 堆排序 */
|
||||
function heapSort(nums: number[]): void {
|
||||
// 建堆操作:堆化除叶节点以外的其他所有节点
|
||||
for (let i = Math.floor(nums.length / 2) - 1; i >= 0; i--) {
|
||||
siftDown(nums, nums.length, i);
|
||||
}
|
||||
// 从堆中提取最大元素,循环 n-1 轮
|
||||
for (let i = nums.length - 1; i > 0; i--) {
|
||||
// 交换根节点与最右叶节点(交换首元素与尾元素)
|
||||
[nums[0], nums[i]] = [nums[i], nums[0]];
|
||||
// 以根节点为起点,从顶至底进行堆化
|
||||
siftDown(nums, i, 0);
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{heapSort}
|
||||
```
|
||||
|
||||
=== "Dart"
|
||||
|
||||
```dart title="heap_sort.dart"
|
||||
/* 堆的长度为 n ,从节点 i 开始,从顶至底堆化 */
|
||||
void siftDown(List<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;
|
||||
// 交换两节点
|
||||
int temp = nums[i];
|
||||
nums[i] = nums[ma];
|
||||
nums[ma] = temp;
|
||||
// 循环向下堆化
|
||||
i = ma;
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{siftDown}
|
||||
|
||||
/* 堆排序 */
|
||||
void heapSort(List<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--) {
|
||||
// 交换根节点与最右叶节点(交换首元素与尾元素)
|
||||
int tmp = nums[0];
|
||||
nums[0] = nums[i];
|
||||
nums[i] = tmp;
|
||||
// 以根节点为起点,从顶至底进行堆化
|
||||
siftDown(nums, i, 0);
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{heapSort}
|
||||
```
|
||||
|
||||
=== "Rust"
|
||||
|
||||
```rust title="heap_sort.rs"
|
||||
/* 堆的长度为 n ,从节点 i 开始,从顶至底堆化 */
|
||||
fn sift_down(nums: &mut [i32], n: usize, mut i: usize) {
|
||||
loop {
|
||||
// 判断节点 i, l, r 中值最大的节点,记为 ma
|
||||
let l = 2 * i + 1;
|
||||
let r = 2 * i + 2;
|
||||
let mut 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;
|
||||
}
|
||||
// 交换两节点
|
||||
let temp = nums[i];
|
||||
nums[i] = nums[ma];
|
||||
nums[ma] = temp;
|
||||
// 循环向下堆化
|
||||
i = ma;
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{sift_down}
|
||||
|
||||
/* 堆排序 */
|
||||
fn heap_sort(nums: &mut [i32]) {
|
||||
// 建堆操作:堆化除叶节点以外的其他所有节点
|
||||
for i in (0..=nums.len() / 2 - 1).rev() {
|
||||
sift_down(nums, nums.len(), i);
|
||||
}
|
||||
// 从堆中提取最大元素,循环 n-1 轮
|
||||
for i in (1..=nums.len() - 1).rev() {
|
||||
// 交换根节点与最右叶节点(交换首元素与尾元素)
|
||||
let tmp = nums[0];
|
||||
nums[0] = nums[i];
|
||||
nums[i] = tmp;
|
||||
// 以根节点为起点,从顶至底进行堆化
|
||||
sift_down(nums, i, 0);
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{heap_sort}
|
||||
```
|
||||
|
||||
=== "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;
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{siftDown}
|
||||
|
||||
/* 堆排序 */
|
||||
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);
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{heapSort}
|
||||
```
|
||||
|
||||
=== "Kotlin"
|
||||
|
||||
```kotlin title="heap_sort.kt"
|
||||
/* 堆的长度为 n ,从节点 i 开始,从顶至底堆化 */
|
||||
fun siftDown(nums: IntArray, n: Int, li: Int) {
|
||||
var i = li
|
||||
while (true) {
|
||||
// 判断节点 i, l, r 中值最大的节点,记为 ma
|
||||
val l = 2 * i + 1
|
||||
val 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
|
||||
// 交换两节点
|
||||
val temp = nums[i]
|
||||
nums[i] = nums[ma]
|
||||
nums[ma] = temp
|
||||
// 循环向下堆化
|
||||
i = ma
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{siftDown}
|
||||
|
||||
/* 堆排序 */
|
||||
fun heapSort(nums: IntArray) {
|
||||
// 建堆操作:堆化除叶节点以外的其他所有节点
|
||||
for (i in nums.size / 2 - 1 downTo 0) {
|
||||
siftDown(nums, nums.size, i)
|
||||
}
|
||||
// 从堆中提取最大元素,循环 n-1 轮
|
||||
for (i in nums.size - 1 downTo 1) {
|
||||
// 交换根节点与最右叶节点(交换首元素与尾元素)
|
||||
val temp = nums[0]
|
||||
nums[0] = nums[i]
|
||||
nums[i] = temp
|
||||
// 以根节点为起点,从顶至底进行堆化
|
||||
siftDown(nums, i, 0)
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{heapSort}
|
||||
```
|
||||
|
||||
=== "Ruby"
|
||||
|
||||
```ruby title="heap_sort.rb"
|
||||
### 堆的长度为 n ,从节点 i 开始,从顶至底堆化 ###
|
||||
def sift_down(nums, n, i)
|
||||
while true
|
||||
# 判断节点 i, l, r 中值最大的节点,记为 ma
|
||||
l = 2 * i + 1
|
||||
r = 2 * i + 2
|
||||
ma = i
|
||||
ma = l if l < n && nums[l] > nums[ma]
|
||||
ma = r if r < n && nums[r] > nums[ma]
|
||||
# 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出
|
||||
break if ma == i
|
||||
# 交换两节点
|
||||
nums[i], nums[ma] = nums[ma], nums[i]
|
||||
# 循环向下堆化
|
||||
i = ma
|
||||
end
|
||||
end
|
||||
[class]{}-[func]{sift_down}
|
||||
|
||||
### 堆排序 ###
|
||||
def heap_sort(nums)
|
||||
# 建堆操作:堆化除叶节点以外的其他所有节点
|
||||
(nums.length / 2 - 1).downto(0) do |i|
|
||||
sift_down(nums, nums.length, i)
|
||||
end
|
||||
# 从堆中提取最大元素,循环 n-1 轮
|
||||
(nums.length - 1).downto(1) do |i|
|
||||
# 交换根节点与最右叶节点(交换首元素与尾元素)
|
||||
nums[0], nums[i] = nums[i], nums[0]
|
||||
# 以根节点为起点,从顶至底进行堆化
|
||||
sift_down(nums, i, 0)
|
||||
end
|
||||
end
|
||||
[class]{}-[func]{heap_sort}
|
||||
```
|
||||
|
||||
=== "Zig"
|
||||
@@ -624,11 +243,6 @@ In the code implementation, we used the sift-down function `sift_down()` from th
|
||||
[class]{}-[func]{heapSort}
|
||||
```
|
||||
|
||||
??? pythontutor "Code Visualization"
|
||||
|
||||
<div style="height: 549px; width: 100%;"><iframe class="pythontutor-iframe" src="https://pythontutor.com/iframe-embed.html#code=def%20sift_down%28nums%3A%20list%5Bint%5D,%20n%3A%20int,%20i%3A%20int%29%3A%0A%20%20%20%20%22%22%22%E5%A0%86%E7%9A%84%E9%95%BF%E5%BA%A6%E4%B8%BA%20n%20%EF%BC%8C%E4%BB%8E%E8%8A%82%E7%82%B9%20i%20%E5%BC%80%E5%A7%8B%EF%BC%8C%E4%BB%8E%E9%A1%B6%E8%87%B3%E5%BA%95%E5%A0%86%E5%8C%96%22%22%22%0A%20%20%20%20while%20True%3A%0A%20%20%20%20%20%20%20%20%23%20%E5%88%A4%E6%96%AD%E8%8A%82%E7%82%B9%20i,%20l,%20r%20%E4%B8%AD%E5%80%BC%E6%9C%80%E5%A4%A7%E7%9A%84%E8%8A%82%E7%82%B9%EF%BC%8C%E8%AE%B0%E4%B8%BA%20ma%0A%20%20%20%20%20%20%20%20l%20%3D%202%20*%20i%20%2B%201%0A%20%20%20%20%20%20%20%20r%20%3D%202%20*%20i%20%2B%202%0A%20%20%20%20%20%20%20%20ma%20%3D%20i%0A%20%20%20%20%20%20%20%20if%20l%20%3C%20n%20and%20nums%5Bl%5D%20%3E%20nums%5Bma%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20ma%20%3D%20l%0A%20%20%20%20%20%20%20%20if%20r%20%3C%20n%20and%20nums%5Br%5D%20%3E%20nums%5Bma%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20ma%20%3D%20r%0A%20%20%20%20%20%20%20%20%23%20%E8%8B%A5%E8%8A%82%E7%82%B9%20i%20%E6%9C%80%E5%A4%A7%E6%88%96%E7%B4%A2%E5%BC%95%20l,%20r%20%E8%B6%8A%E7%95%8C%EF%BC%8C%E5%88%99%E6%97%A0%E9%A1%BB%E7%BB%A7%E7%BB%AD%E5%A0%86%E5%8C%96%EF%BC%8C%E8%B7%B3%E5%87%BA%0A%20%20%20%20%20%20%20%20if%20ma%20%3D%3D%20i%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20break%0A%20%20%20%20%20%20%20%20%23%20%E4%BA%A4%E6%8D%A2%E4%B8%A4%E8%8A%82%E7%82%B9%0A%20%20%20%20%20%20%20%20nums%5Bi%5D,%20nums%5Bma%5D%20%3D%20nums%5Bma%5D,%20nums%5Bi%5D%0A%20%20%20%20%20%20%20%20%23%20%E5%BE%AA%E7%8E%AF%E5%90%91%E4%B8%8B%E5%A0%86%E5%8C%96%0A%20%20%20%20%20%20%20%20i%20%3D%20ma%0A%0Adef%20heap_sort%28nums%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22%E5%A0%86%E6%8E%92%E5%BA%8F%22%22%22%0A%20%20%20%20%23%20%E5%BB%BA%E5%A0%86%E6%93%8D%E4%BD%9C%EF%BC%9A%E5%A0%86%E5%8C%96%E9%99%A4%E5%8F%B6%E8%8A%82%E7%82%B9%E4%BB%A5%E5%A4%96%E7%9A%84%E5%85%B6%E4%BB%96%E6%89%80%E6%9C%89%E8%8A%82%E7%82%B9%0A%20%20%20%20for%20i%20in%20range%28len%28nums%29%20//%202%20-%201,%20-1,%20-1%29%3A%0A%20%20%20%20%20%20%20%20sift_down%28nums,%20len%28nums%29,%20i%29%0A%20%20%20%20%23%20%E4%BB%8E%E5%A0%86%E4%B8%AD%E6%8F%90%E5%8F%96%E6%9C%80%E5%A4%A7%E5%85%83%E7%B4%A0%EF%BC%8C%E5%BE%AA%E7%8E%AF%20n-1%20%E8%BD%AE%0A%20%20%20%20for%20i%20in%20range%28len%28nums%29%20-%201,%200,%20-1%29%3A%0A%20%20%20%20%20%20%20%20%23%20%E4%BA%A4%E6%8D%A2%E6%A0%B9%E8%8A%82%E7%82%B9%E4%B8%8E%E6%9C%80%E5%8F%B3%E5%8F%B6%E8%8A%82%E7%82%B9%EF%BC%88%E4%BA%A4%E6%8D%A2%E9%A6%96%E5%85%83%E7%B4%A0%E4%B8%8E%E5%B0%BE%E5%85%83%E7%B4%A0%EF%BC%89%0A%20%20%20%20%20%20%20%20nums%5B0%5D,%20nums%5Bi%5D%20%3D%20nums%5Bi%5D,%20nums%5B0%5D%0A%20%20%20%20%20%20%20%20%23%20%E4%BB%A5%E6%A0%B9%E8%8A%82%E7%82%B9%E4%B8%BA%E8%B5%B7%E7%82%B9%EF%BC%8C%E4%BB%8E%E9%A1%B6%E8%87%B3%E5%BA%95%E8%BF%9B%E8%A1%8C%E5%A0%86%E5%8C%96%0A%20%20%20%20%20%20%20%20sift_down%28nums,%20i,%200%29%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B4,%201,%203,%201,%205,%202%5D%0A%20%20%20%20heap_sort%28nums%29%0A%20%20%20%20print%28%22%E5%A0%86%E6%8E%92%E5%BA%8F%E5%AE%8C%E6%88%90%E5%90%8E%20nums%20%3D%22,%20nums%29&codeDivHeight=472&codeDivWidth=350&cumulative=false&curInstr=5&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe></div>
|
||||
<div style="margin-top: 5px;"><a href="https://pythontutor.com/iframe-embed.html#code=def%20sift_down%28nums%3A%20list%5Bint%5D,%20n%3A%20int,%20i%3A%20int%29%3A%0A%20%20%20%20%22%22%22%E5%A0%86%E7%9A%84%E9%95%BF%E5%BA%A6%E4%B8%BA%20n%20%EF%BC%8C%E4%BB%8E%E8%8A%82%E7%82%B9%20i%20%E5%BC%80%E5%A7%8B%EF%BC%8C%E4%BB%8E%E9%A1%B6%E8%87%B3%E5%BA%95%E5%A0%86%E5%8C%96%22%22%22%0A%20%20%20%20while%20True%3A%0A%20%20%20%20%20%20%20%20%23%20%E5%88%A4%E6%96%AD%E8%8A%82%E7%82%B9%20i,%20l,%20r%20%E4%B8%AD%E5%80%BC%E6%9C%80%E5%A4%A7%E7%9A%84%E8%8A%82%E7%82%B9%EF%BC%8C%E8%AE%B0%E4%B8%BA%20ma%0A%20%20%20%20%20%20%20%20l%20%3D%202%20*%20i%20%2B%201%0A%20%20%20%20%20%20%20%20r%20%3D%202%20*%20i%20%2B%202%0A%20%20%20%20%20%20%20%20ma%20%3D%20i%0A%20%20%20%20%20%20%20%20if%20l%20%3C%20n%20and%20nums%5Bl%5D%20%3E%20nums%5Bma%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20ma%20%3D%20l%0A%20%20%20%20%20%20%20%20if%20r%20%3C%20n%20and%20nums%5Br%5D%20%3E%20nums%5Bma%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20ma%20%3D%20r%0A%20%20%20%20%20%20%20%20%23%20%E8%8B%A5%E8%8A%82%E7%82%B9%20i%20%E6%9C%80%E5%A4%A7%E6%88%96%E7%B4%A2%E5%BC%95%20l,%20r%20%E8%B6%8A%E7%95%8C%EF%BC%8C%E5%88%99%E6%97%A0%E9%A1%BB%E7%BB%A7%E7%BB%AD%E5%A0%86%E5%8C%96%EF%BC%8C%E8%B7%B3%E5%87%BA%0A%20%20%20%20%20%20%20%20if%20ma%20%3D%3D%20i%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20break%0A%20%20%20%20%20%20%20%20%23%20%E4%BA%A4%E6%8D%A2%E4%B8%A4%E8%8A%82%E7%82%B9%0A%20%20%20%20%20%20%20%20nums%5Bi%5D,%20nums%5Bma%5D%20%3D%20nums%5Bma%5D,%20nums%5Bi%5D%0A%20%20%20%20%20%20%20%20%23%20%E5%BE%AA%E7%8E%AF%E5%90%91%E4%B8%8B%E5%A0%86%E5%8C%96%0A%20%20%20%20%20%20%20%20i%20%3D%20ma%0A%0Adef%20heap_sort%28nums%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22%E5%A0%86%E6%8E%92%E5%BA%8F%22%22%22%0A%20%20%20%20%23%20%E5%BB%BA%E5%A0%86%E6%93%8D%E4%BD%9C%EF%BC%9A%E5%A0%86%E5%8C%96%E9%99%A4%E5%8F%B6%E8%8A%82%E7%82%B9%E4%BB%A5%E5%A4%96%E7%9A%84%E5%85%B6%E4%BB%96%E6%89%80%E6%9C%89%E8%8A%82%E7%82%B9%0A%20%20%20%20for%20i%20in%20range%28len%28nums%29%20//%202%20-%201,%20-1,%20-1%29%3A%0A%20%20%20%20%20%20%20%20sift_down%28nums,%20len%28nums%29,%20i%29%0A%20%20%20%20%23%20%E4%BB%8E%E5%A0%86%E4%B8%AD%E6%8F%90%E5%8F%96%E6%9C%80%E5%A4%A7%E5%85%83%E7%B4%A0%EF%BC%8C%E5%BE%AA%E7%8E%AF%20n-1%20%E8%BD%AE%0A%20%20%20%20for%20i%20in%20range%28len%28nums%29%20-%201,%200,%20-1%29%3A%0A%20%20%20%20%20%20%20%20%23%20%E4%BA%A4%E6%8D%A2%E6%A0%B9%E8%8A%82%E7%82%B9%E4%B8%8E%E6%9C%80%E5%8F%B3%E5%8F%B6%E8%8A%82%E7%82%B9%EF%BC%88%E4%BA%A4%E6%8D%A2%E9%A6%96%E5%85%83%E7%B4%A0%E4%B8%8E%E5%B0%BE%E5%85%83%E7%B4%A0%EF%BC%89%0A%20%20%20%20%20%20%20%20nums%5B0%5D,%20nums%5Bi%5D%20%3D%20nums%5Bi%5D,%20nums%5B0%5D%0A%20%20%20%20%20%20%20%20%23%20%E4%BB%A5%E6%A0%B9%E8%8A%82%E7%82%B9%E4%B8%BA%E8%B5%B7%E7%82%B9%EF%BC%8C%E4%BB%8E%E9%A1%B6%E8%87%B3%E5%BA%95%E8%BF%9B%E8%A1%8C%E5%A0%86%E5%8C%96%0A%20%20%20%20%20%20%20%20sift_down%28nums,%20i,%200%29%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B4,%201,%203,%201,%205,%202%5D%0A%20%20%20%20heap_sort%28nums%29%0A%20%20%20%20print%28%22%E5%A0%86%E6%8E%92%E5%BA%8F%E5%AE%8C%E6%88%90%E5%90%8E%20nums%20%3D%22,%20nums%29&codeDivHeight=800&codeDivWidth=600&cumulative=false&curInstr=5&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false" target="_blank" rel="noopener noreferrer">Full Screen ></a></div>
|
||||
|
||||
## 11.7.2 Algorithm characteristics
|
||||
|
||||
- **Time complexity is $O(n \log n)$, non-adaptive sort**: The heap creation uses $O(n)$ time. Extracting the largest element from the heap takes $O(\log n)$ time, looping for $n - 1$ rounds.
|
||||
|
||||
@@ -33,50 +33,38 @@ Example code is as follows:
|
||||
|
||||
```python title="insertion_sort.py"
|
||||
def insertion_sort(nums: list[int]):
|
||||
"""插入排序"""
|
||||
# 外循环:已排序区间为 [0, i-1]
|
||||
"""Insertion sort"""
|
||||
# Outer loop: sorted range is [0, i-1]
|
||||
for i in range(1, len(nums)):
|
||||
base = nums[i]
|
||||
j = i - 1
|
||||
# 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置
|
||||
# Inner loop: insert base into the correct position within the sorted range [0, i-1]
|
||||
while j >= 0 and nums[j] > base:
|
||||
nums[j + 1] = nums[j] # 将 nums[j] 向右移动一位
|
||||
nums[j + 1] = nums[j] # Move nums[j] to the right by one position
|
||||
j -= 1
|
||||
nums[j + 1] = base # 将 base 赋值到正确位置
|
||||
nums[j + 1] = base # Assign base to the correct position
|
||||
```
|
||||
|
||||
=== "C++"
|
||||
|
||||
```cpp title="insertion_sort.cpp"
|
||||
/* 插入排序 */
|
||||
void insertionSort(vector<int> &nums) {
|
||||
// 外循环:已排序区间为 [0, i-1]
|
||||
for (int i = 1; i < nums.size(); i++) {
|
||||
int base = nums[i], j = i - 1;
|
||||
// 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置
|
||||
while (j >= 0 && nums[j] > base) {
|
||||
nums[j + 1] = nums[j]; // 将 nums[j] 向右移动一位
|
||||
j--;
|
||||
}
|
||||
nums[j + 1] = base; // 将 base 赋值到正确位置
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{insertionSort}
|
||||
```
|
||||
|
||||
=== "Java"
|
||||
|
||||
```java title="insertion_sort.java"
|
||||
/* 插入排序 */
|
||||
/* Insertion sort */
|
||||
void insertionSort(int[] nums) {
|
||||
// 外循环:已排序区间为 [0, i-1]
|
||||
// Outer loop: sorted range is [0, i-1]
|
||||
for (int i = 1; i < nums.length; i++) {
|
||||
int base = nums[i], j = i - 1;
|
||||
// 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置
|
||||
// Inner loop: insert base into the correct position within the sorted range [0, i-1]
|
||||
while (j >= 0 && nums[j] > base) {
|
||||
nums[j + 1] = nums[j]; // 将 nums[j] 向右移动一位
|
||||
nums[j + 1] = nums[j]; // Move nums[j] to the right by one position
|
||||
j--;
|
||||
}
|
||||
nums[j + 1] = base; // 将 base 赋值到正确位置
|
||||
nums[j + 1] = base; // Assign base to the correct position
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -84,216 +72,69 @@ Example code is as follows:
|
||||
=== "C#"
|
||||
|
||||
```csharp title="insertion_sort.cs"
|
||||
/* 插入排序 */
|
||||
void InsertionSort(int[] nums) {
|
||||
// 外循环:已排序区间为 [0, i-1]
|
||||
for (int i = 1; i < nums.Length; i++) {
|
||||
int bas = nums[i], j = i - 1;
|
||||
// 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置
|
||||
while (j >= 0 && nums[j] > bas) {
|
||||
nums[j + 1] = nums[j]; // 将 nums[j] 向右移动一位
|
||||
j--;
|
||||
}
|
||||
nums[j + 1] = bas; // 将 base 赋值到正确位置
|
||||
}
|
||||
}
|
||||
[class]{insertion_sort}-[func]{InsertionSort}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
||||
```go title="insertion_sort.go"
|
||||
/* 插入排序 */
|
||||
func insertionSort(nums []int) {
|
||||
// 外循环:已排序区间为 [0, i-1]
|
||||
for i := 1; i < len(nums); i++ {
|
||||
base := nums[i]
|
||||
j := i - 1
|
||||
// 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置
|
||||
for j >= 0 && nums[j] > base {
|
||||
nums[j+1] = nums[j] // 将 nums[j] 向右移动一位
|
||||
j--
|
||||
}
|
||||
nums[j+1] = base // 将 base 赋值到正确位置
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{insertionSort}
|
||||
```
|
||||
|
||||
=== "Swift"
|
||||
|
||||
```swift title="insertion_sort.swift"
|
||||
/* 插入排序 */
|
||||
func insertionSort(nums: inout [Int]) {
|
||||
// 外循环:已排序区间为 [0, i-1]
|
||||
for i in nums.indices.dropFirst() {
|
||||
let base = nums[i]
|
||||
var j = i - 1
|
||||
// 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置
|
||||
while j >= 0, nums[j] > base {
|
||||
nums[j + 1] = nums[j] // 将 nums[j] 向右移动一位
|
||||
j -= 1
|
||||
}
|
||||
nums[j + 1] = base // 将 base 赋值到正确位置
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{insertionSort}
|
||||
```
|
||||
|
||||
=== "JS"
|
||||
|
||||
```javascript title="insertion_sort.js"
|
||||
/* 插入排序 */
|
||||
function insertionSort(nums) {
|
||||
// 外循环:已排序区间为 [0, i-1]
|
||||
for (let i = 1; i < nums.length; i++) {
|
||||
let base = nums[i],
|
||||
j = i - 1;
|
||||
// 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置
|
||||
while (j >= 0 && nums[j] > base) {
|
||||
nums[j + 1] = nums[j]; // 将 nums[j] 向右移动一位
|
||||
j--;
|
||||
}
|
||||
nums[j + 1] = base; // 将 base 赋值到正确位置
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{insertionSort}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="insertion_sort.ts"
|
||||
/* 插入排序 */
|
||||
function insertionSort(nums: number[]): void {
|
||||
// 外循环:已排序区间为 [0, i-1]
|
||||
for (let i = 1; i < nums.length; i++) {
|
||||
const base = nums[i];
|
||||
let j = i - 1;
|
||||
// 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置
|
||||
while (j >= 0 && nums[j] > base) {
|
||||
nums[j + 1] = nums[j]; // 将 nums[j] 向右移动一位
|
||||
j--;
|
||||
}
|
||||
nums[j + 1] = base; // 将 base 赋值到正确位置
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{insertionSort}
|
||||
```
|
||||
|
||||
=== "Dart"
|
||||
|
||||
```dart title="insertion_sort.dart"
|
||||
/* 插入排序 */
|
||||
void insertionSort(List<int> nums) {
|
||||
// 外循环:已排序区间为 [0, i-1]
|
||||
for (int i = 1; i < nums.length; i++) {
|
||||
int base = nums[i], j = i - 1;
|
||||
// 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置
|
||||
while (j >= 0 && nums[j] > base) {
|
||||
nums[j + 1] = nums[j]; // 将 nums[j] 向右移动一位
|
||||
j--;
|
||||
}
|
||||
nums[j + 1] = base; // 将 base 赋值到正确位置
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{insertionSort}
|
||||
```
|
||||
|
||||
=== "Rust"
|
||||
|
||||
```rust title="insertion_sort.rs"
|
||||
/* 插入排序 */
|
||||
fn insertion_sort(nums: &mut [i32]) {
|
||||
// 外循环:已排序区间为 [0, i-1]
|
||||
for i in 1..nums.len() {
|
||||
let (base, mut j) = (nums[i], (i - 1) as i32);
|
||||
// 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置
|
||||
while j >= 0 && nums[j as usize] > base {
|
||||
nums[(j + 1) as usize] = nums[j as usize]; // 将 nums[j] 向右移动一位
|
||||
j -= 1;
|
||||
}
|
||||
nums[(j + 1) as usize] = base; // 将 base 赋值到正确位置
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{insertion_sort}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
|
||||
```c title="insertion_sort.c"
|
||||
/* 插入排序 */
|
||||
void insertionSort(int nums[], int size) {
|
||||
// 外循环:已排序区间为 [0, i-1]
|
||||
for (int i = 1; i < size; i++) {
|
||||
int base = nums[i], j = i - 1;
|
||||
// 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置
|
||||
while (j >= 0 && nums[j] > base) {
|
||||
// 将 nums[j] 向右移动一位
|
||||
nums[j + 1] = nums[j];
|
||||
j--;
|
||||
}
|
||||
// 将 base 赋值到正确位置
|
||||
nums[j + 1] = base;
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{insertionSort}
|
||||
```
|
||||
|
||||
=== "Kotlin"
|
||||
|
||||
```kotlin title="insertion_sort.kt"
|
||||
/* 插入排序 */
|
||||
fun insertionSort(nums: IntArray) {
|
||||
//外循环: 已排序元素为 1, 2, ..., n
|
||||
for (i in nums.indices) {
|
||||
val base = nums[i]
|
||||
var j = i - 1
|
||||
// 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置
|
||||
while (j >= 0 && nums[j] > base) {
|
||||
nums[j + 1] = nums[j] // 将 nums[j] 向右移动一位
|
||||
j--
|
||||
}
|
||||
nums[j + 1] = base // 将 base 赋值到正确位置
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{insertionSort}
|
||||
```
|
||||
|
||||
=== "Ruby"
|
||||
|
||||
```ruby title="insertion_sort.rb"
|
||||
### 插入排序 ###
|
||||
def insertion_sort(nums)
|
||||
n = nums.length
|
||||
# 外循环:已排序区间为 [0, i-1]
|
||||
for i in 1...n
|
||||
base = nums[i]
|
||||
j = i - 1
|
||||
# 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置
|
||||
while j >= 0 && nums[j] > base
|
||||
nums[j + 1] = nums[j] # 将 nums[j] 向右移动一位
|
||||
j -= 1
|
||||
end
|
||||
nums[j + 1] = base # 将 base 赋值到正确位置
|
||||
end
|
||||
end
|
||||
[class]{}-[func]{insertion_sort}
|
||||
```
|
||||
|
||||
=== "Zig"
|
||||
|
||||
```zig title="insertion_sort.zig"
|
||||
// 插入排序
|
||||
fn insertionSort(nums: []i32) void {
|
||||
// 外循环:已排序区间为 [0, i-1]
|
||||
var i: usize = 1;
|
||||
while (i < nums.len) : (i += 1) {
|
||||
var base = nums[i];
|
||||
var j: usize = i;
|
||||
// 内循环:将 base 插入到已排序区间 [0, i-1] 中的正确位置
|
||||
while (j >= 1 and nums[j - 1] > base) : (j -= 1) {
|
||||
nums[j] = nums[j - 1]; // 将 nums[j] 向右移动一位
|
||||
}
|
||||
nums[j] = base; // 将 base 赋值到正确位置
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{insertionSort}
|
||||
```
|
||||
|
||||
??? pythontutor "Code Visualization"
|
||||
|
||||
<div style="height: 513px; width: 100%;"><iframe class="pythontutor-iframe" src="https://pythontutor.com/iframe-embed.html#code=def%20insertion_sort%28nums%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F%22%22%22%0A%20%20%20%20%23%20%E5%A4%96%E5%BE%AA%E7%8E%AF%EF%BC%9A%E5%B7%B2%E6%8E%92%E5%BA%8F%E5%8C%BA%E9%97%B4%E4%B8%BA%20%5B0,%20i-1%5D%0A%20%20%20%20for%20i%20in%20range%281,%20len%28nums%29%29%3A%0A%20%20%20%20%20%20%20%20base%20%3D%20nums%5Bi%5D%0A%20%20%20%20%20%20%20%20j%20%3D%20i%20-%201%0A%20%20%20%20%20%20%20%20%23%20%E5%86%85%E5%BE%AA%E7%8E%AF%EF%BC%9A%E5%B0%86%20base%20%E6%8F%92%E5%85%A5%E5%88%B0%E5%B7%B2%E6%8E%92%E5%BA%8F%E5%8C%BA%E9%97%B4%20%5B0,%20i-1%5D%20%E4%B8%AD%E7%9A%84%E6%AD%A3%E7%A1%AE%E4%BD%8D%E7%BD%AE%0A%20%20%20%20%20%20%20%20while%20j%20%3E%3D%200%20and%20nums%5Bj%5D%20%3E%20base%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20nums%5Bj%20%2B%201%5D%20%3D%20nums%5Bj%5D%20%20%23%20%E5%B0%86%20nums%5Bj%5D%20%E5%90%91%E5%8F%B3%E7%A7%BB%E5%8A%A8%E4%B8%80%E4%BD%8D%0A%20%20%20%20%20%20%20%20%20%20%20%20j%20-%3D%201%0A%20%20%20%20%20%20%20%20nums%5Bj%20%2B%201%5D%20%3D%20base%20%20%23%20%E5%B0%86%20base%20%E8%B5%8B%E5%80%BC%E5%88%B0%E6%AD%A3%E7%A1%AE%E4%BD%8D%E7%BD%AE%0A%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B4,%201,%203,%201,%205,%202%5D%0A%20%20%20%20insertion_sort%28nums%29%0A%20%20%20%20print%28%22%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F%E5%AE%8C%E6%88%90%E5%90%8E%20nums%20%3D%22,%20nums%29&codeDivHeight=472&codeDivWidth=350&cumulative=false&curInstr=4&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe></div>
|
||||
<div style="margin-top: 5px;"><a href="https://pythontutor.com/iframe-embed.html#code=def%20insertion_sort%28nums%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F%22%22%22%0A%20%20%20%20%23%20%E5%A4%96%E5%BE%AA%E7%8E%AF%EF%BC%9A%E5%B7%B2%E6%8E%92%E5%BA%8F%E5%8C%BA%E9%97%B4%E4%B8%BA%20%5B0,%20i-1%5D%0A%20%20%20%20for%20i%20in%20range%281,%20len%28nums%29%29%3A%0A%20%20%20%20%20%20%20%20base%20%3D%20nums%5Bi%5D%0A%20%20%20%20%20%20%20%20j%20%3D%20i%20-%201%0A%20%20%20%20%20%20%20%20%23%20%E5%86%85%E5%BE%AA%E7%8E%AF%EF%BC%9A%E5%B0%86%20base%20%E6%8F%92%E5%85%A5%E5%88%B0%E5%B7%B2%E6%8E%92%E5%BA%8F%E5%8C%BA%E9%97%B4%20%5B0,%20i-1%5D%20%E4%B8%AD%E7%9A%84%E6%AD%A3%E7%A1%AE%E4%BD%8D%E7%BD%AE%0A%20%20%20%20%20%20%20%20while%20j%20%3E%3D%200%20and%20nums%5Bj%5D%20%3E%20base%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20nums%5Bj%20%2B%201%5D%20%3D%20nums%5Bj%5D%20%20%23%20%E5%B0%86%20nums%5Bj%5D%20%E5%90%91%E5%8F%B3%E7%A7%BB%E5%8A%A8%E4%B8%80%E4%BD%8D%0A%20%20%20%20%20%20%20%20%20%20%20%20j%20-%3D%201%0A%20%20%20%20%20%20%20%20nums%5Bj%20%2B%201%5D%20%3D%20base%20%20%23%20%E5%B0%86%20base%20%E8%B5%8B%E5%80%BC%E5%88%B0%E6%AD%A3%E7%A1%AE%E4%BD%8D%E7%BD%AE%0A%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B4,%201,%203,%201,%205,%202%5D%0A%20%20%20%20insertion_sort%28nums%29%0A%20%20%20%20print%28%22%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F%E5%AE%8C%E6%88%90%E5%90%8E%20nums%20%3D%22,%20nums%29&codeDivHeight=800&codeDivWidth=600&cumulative=false&curInstr=4&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false" target="_blank" rel="noopener noreferrer">Full Screen ></a></div>
|
||||
|
||||
## 11.4.2 Algorithm characteristics
|
||||
|
||||
- **Time complexity is $O(n^2)$, adaptive sorting**: In the worst case, each insertion operation requires $n - 1$, $n-2$, ..., $2$, $1$ loops, summing up to $(n - 1) n / 2$, thus the time complexity is $O(n^2)$. In the case of ordered data, the insertion operation will terminate early. When the input array is completely ordered, insertion sort achieves the best time complexity of $O(n)$.
|
||||
|
||||
@@ -65,13 +65,13 @@ The implementation of merge sort is shown in the following code. Note that the i
|
||||
|
||||
```python title="merge_sort.py"
|
||||
def merge(nums: list[int], left: int, mid: int, right: int):
|
||||
"""合并左子数组和右子数组"""
|
||||
# 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right]
|
||||
# 创建一个临时数组 tmp ,用于存放合并后的结果
|
||||
"""Merge left subarray and right subarray"""
|
||||
# Left subarray interval is [left, mid], right subarray interval is [mid+1, right]
|
||||
# Create a temporary array tmp to store the merged results
|
||||
tmp = [0] * (right - left + 1)
|
||||
# 初始化左子数组和右子数组的起始索引
|
||||
# Initialize the start indices of the left and right subarrays
|
||||
i, j, k = left, mid + 1, 0
|
||||
# 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中
|
||||
# While both subarrays still have elements, compare and copy the smaller element into the temporary array
|
||||
while i <= mid and j <= right:
|
||||
if nums[i] <= nums[j]:
|
||||
tmp[k] = nums[i]
|
||||
@@ -80,7 +80,7 @@ The implementation of merge sort is shown in the following code. Note that the i
|
||||
tmp[k] = nums[j]
|
||||
j += 1
|
||||
k += 1
|
||||
# 将左子数组和右子数组的剩余元素复制到临时数组中
|
||||
# Copy the remaining elements of the left and right subarrays into the temporary array
|
||||
while i <= mid:
|
||||
tmp[k] = nums[i]
|
||||
i += 1
|
||||
@@ -89,107 +89,71 @@ The implementation of merge sort is shown in the following code. Note that the i
|
||||
tmp[k] = nums[j]
|
||||
j += 1
|
||||
k += 1
|
||||
# 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间
|
||||
# Copy the elements from the temporary array tmp back to the original array nums at the corresponding interval
|
||||
for k in range(0, len(tmp)):
|
||||
nums[left + k] = tmp[k]
|
||||
|
||||
def merge_sort(nums: list[int], left: int, right: int):
|
||||
"""归并排序"""
|
||||
# 终止条件
|
||||
"""Merge sort"""
|
||||
# Termination condition
|
||||
if left >= right:
|
||||
return # 当子数组长度为 1 时终止递归
|
||||
# 划分阶段
|
||||
mid = (left + right) // 2 # 计算中点
|
||||
merge_sort(nums, left, mid) # 递归左子数组
|
||||
merge_sort(nums, mid + 1, right) # 递归右子数组
|
||||
# 合并阶段
|
||||
return # Terminate recursion when subarray length is 1
|
||||
# Partition stage
|
||||
mid = (left + right) // 2 # Calculate midpoint
|
||||
merge_sort(nums, left, mid) # Recursively process the left subarray
|
||||
merge_sort(nums, mid + 1, right) # Recursively process the right subarray
|
||||
# Merge stage
|
||||
merge(nums, left, mid, right)
|
||||
```
|
||||
|
||||
=== "C++"
|
||||
|
||||
```cpp title="merge_sort.cpp"
|
||||
/* 合并左子数组和右子数组 */
|
||||
void merge(vector<int> &nums, int left, int mid, int right) {
|
||||
// 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right]
|
||||
// 创建一个临时数组 tmp ,用于存放合并后的结果
|
||||
vector<int> tmp(right - left + 1);
|
||||
// 初始化左子数组和右子数组的起始索引
|
||||
int i = left, j = mid + 1, k = 0;
|
||||
// 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中
|
||||
while (i <= mid && j <= right) {
|
||||
if (nums[i] <= nums[j])
|
||||
tmp[k++] = nums[i++];
|
||||
else
|
||||
tmp[k++] = nums[j++];
|
||||
}
|
||||
// 将左子数组和右子数组的剩余元素复制到临时数组中
|
||||
while (i <= mid) {
|
||||
tmp[k++] = nums[i++];
|
||||
}
|
||||
while (j <= right) {
|
||||
tmp[k++] = nums[j++];
|
||||
}
|
||||
// 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间
|
||||
for (k = 0; k < tmp.size(); k++) {
|
||||
nums[left + k] = tmp[k];
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{merge}
|
||||
|
||||
/* 归并排序 */
|
||||
void mergeSort(vector<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);
|
||||
}
|
||||
[class]{}-[func]{mergeSort}
|
||||
```
|
||||
|
||||
=== "Java"
|
||||
|
||||
```java title="merge_sort.java"
|
||||
/* 合并左子数组和右子数组 */
|
||||
/* Merge left subarray and right subarray */
|
||||
void merge(int[] nums, int left, int mid, int right) {
|
||||
// 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right]
|
||||
// 创建一个临时数组 tmp ,用于存放合并后的结果
|
||||
// Left subarray interval is [left, mid], right subarray interval is [mid+1, right]
|
||||
// Create a temporary array tmp to store the merged results
|
||||
int[] tmp = new int[right - left + 1];
|
||||
// 初始化左子数组和右子数组的起始索引
|
||||
// Initialize the start indices of the left and right subarrays
|
||||
int i = left, j = mid + 1, k = 0;
|
||||
// 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中
|
||||
// While both subarrays still have elements, compare and copy the smaller element into the temporary array
|
||||
while (i <= mid && j <= right) {
|
||||
if (nums[i] <= nums[j])
|
||||
tmp[k++] = nums[i++];
|
||||
else
|
||||
tmp[k++] = nums[j++];
|
||||
}
|
||||
// 将左子数组和右子数组的剩余元素复制到临时数组中
|
||||
// Copy the remaining elements of the left and right subarrays into the temporary array
|
||||
while (i <= mid) {
|
||||
tmp[k++] = nums[i++];
|
||||
}
|
||||
while (j <= right) {
|
||||
tmp[k++] = nums[j++];
|
||||
}
|
||||
// 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间
|
||||
// Copy the elements from the temporary array tmp back to the original array nums at the corresponding interval
|
||||
for (k = 0; k < tmp.length; k++) {
|
||||
nums[left + k] = tmp[k];
|
||||
}
|
||||
}
|
||||
|
||||
/* 归并排序 */
|
||||
/* Merge sort */
|
||||
void mergeSort(int[] nums, int left, int right) {
|
||||
// 终止条件
|
||||
// Termination condition
|
||||
if (left >= right)
|
||||
return; // 当子数组长度为 1 时终止递归
|
||||
// 划分阶段
|
||||
int mid = (left + right) / 2; // 计算中点
|
||||
mergeSort(nums, left, mid); // 递归左子数组
|
||||
mergeSort(nums, mid + 1, right); // 递归右子数组
|
||||
// 合并阶段
|
||||
return; // Terminate recursion when subarray length is 1
|
||||
// Partition stage
|
||||
int mid = (left + right) / 2; // Calculate midpoint
|
||||
mergeSort(nums, left, mid); // Recursively process the left subarray
|
||||
mergeSort(nums, mid + 1, right); // Recursively process the right subarray
|
||||
// Merge stage
|
||||
merge(nums, left, mid, right);
|
||||
}
|
||||
```
|
||||
@@ -197,547 +161,91 @@ The implementation of merge sort is shown in the following code. Note that the i
|
||||
=== "C#"
|
||||
|
||||
```csharp title="merge_sort.cs"
|
||||
/* 合并左子数组和右子数组 */
|
||||
void Merge(int[] nums, int left, int mid, int right) {
|
||||
// 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right]
|
||||
// 创建一个临时数组 tmp ,用于存放合并后的结果
|
||||
int[] tmp = new int[right - left + 1];
|
||||
// 初始化左子数组和右子数组的起始索引
|
||||
int i = left, j = mid + 1, k = 0;
|
||||
// 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中
|
||||
while (i <= mid && j <= right) {
|
||||
if (nums[i] <= nums[j])
|
||||
tmp[k++] = nums[i++];
|
||||
else
|
||||
tmp[k++] = nums[j++];
|
||||
}
|
||||
// 将左子数组和右子数组的剩余元素复制到临时数组中
|
||||
while (i <= mid) {
|
||||
tmp[k++] = nums[i++];
|
||||
}
|
||||
while (j <= right) {
|
||||
tmp[k++] = nums[j++];
|
||||
}
|
||||
// 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间
|
||||
for (k = 0; k < tmp.Length; ++k) {
|
||||
nums[left + k] = tmp[k];
|
||||
}
|
||||
}
|
||||
[class]{merge_sort}-[func]{Merge}
|
||||
|
||||
/* 归并排序 */
|
||||
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);
|
||||
}
|
||||
[class]{merge_sort}-[func]{MergeSort}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
||||
```go title="merge_sort.go"
|
||||
/* 合并左子数组和右子数组 */
|
||||
func merge(nums []int, left, mid, right int) {
|
||||
// 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right]
|
||||
// 创建一个临时数组 tmp ,用于存放合并后的结果
|
||||
tmp := make([]int, right-left+1)
|
||||
// 初始化左子数组和右子数组的起始索引
|
||||
i, j, k := left, mid+1, 0
|
||||
// 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中
|
||||
for i <= mid && j <= right {
|
||||
if nums[i] <= nums[j] {
|
||||
tmp[k] = nums[i]
|
||||
i++
|
||||
} else {
|
||||
tmp[k] = nums[j]
|
||||
j++
|
||||
}
|
||||
k++
|
||||
}
|
||||
// 将左子数组和右子数组的剩余元素复制到临时数组中
|
||||
for i <= mid {
|
||||
tmp[k] = nums[i]
|
||||
i++
|
||||
k++
|
||||
}
|
||||
for j <= right {
|
||||
tmp[k] = nums[j]
|
||||
j++
|
||||
k++
|
||||
}
|
||||
// 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间
|
||||
for k := 0; k < len(tmp); k++ {
|
||||
nums[left+k] = tmp[k]
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{merge}
|
||||
|
||||
/* 归并排序 */
|
||||
func mergeSort(nums []int, left, right int) {
|
||||
// 终止条件
|
||||
if left >= right {
|
||||
return
|
||||
}
|
||||
// 划分阶段
|
||||
mid := (left + right) / 2
|
||||
mergeSort(nums, left, mid)
|
||||
mergeSort(nums, mid+1, right)
|
||||
// 合并阶段
|
||||
merge(nums, left, mid, right)
|
||||
}
|
||||
[class]{}-[func]{mergeSort}
|
||||
```
|
||||
|
||||
=== "Swift"
|
||||
|
||||
```swift title="merge_sort.swift"
|
||||
/* 合并左子数组和右子数组 */
|
||||
func merge(nums: inout [Int], left: Int, mid: Int, right: Int) {
|
||||
// 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right]
|
||||
// 创建一个临时数组 tmp ,用于存放合并后的结果
|
||||
var tmp = Array(repeating: 0, count: right - left + 1)
|
||||
// 初始化左子数组和右子数组的起始索引
|
||||
var i = left, j = mid + 1, k = 0
|
||||
// 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中
|
||||
while i <= mid, j <= right {
|
||||
if nums[i] <= nums[j] {
|
||||
tmp[k] = nums[i]
|
||||
i += 1
|
||||
} else {
|
||||
tmp[k] = nums[j]
|
||||
j += 1
|
||||
}
|
||||
k += 1
|
||||
}
|
||||
// 将左子数组和右子数组的剩余元素复制到临时数组中
|
||||
while i <= mid {
|
||||
tmp[k] = nums[i]
|
||||
i += 1
|
||||
k += 1
|
||||
}
|
||||
while j <= right {
|
||||
tmp[k] = nums[j]
|
||||
j += 1
|
||||
k += 1
|
||||
}
|
||||
// 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间
|
||||
for k in tmp.indices {
|
||||
nums[left + k] = tmp[k]
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{merge}
|
||||
|
||||
/* 归并排序 */
|
||||
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)
|
||||
}
|
||||
[class]{}-[func]{mergeSort}
|
||||
```
|
||||
|
||||
=== "JS"
|
||||
|
||||
```javascript title="merge_sort.js"
|
||||
/* 合并左子数组和右子数组 */
|
||||
function merge(nums, left, mid, right) {
|
||||
// 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right]
|
||||
// 创建一个临时数组 tmp ,用于存放合并后的结果
|
||||
const tmp = new Array(right - left + 1);
|
||||
// 初始化左子数组和右子数组的起始索引
|
||||
let i = left,
|
||||
j = mid + 1,
|
||||
k = 0;
|
||||
// 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中
|
||||
while (i <= mid && j <= right) {
|
||||
if (nums[i] <= nums[j]) {
|
||||
tmp[k++] = nums[i++];
|
||||
} else {
|
||||
tmp[k++] = nums[j++];
|
||||
}
|
||||
}
|
||||
// 将左子数组和右子数组的剩余元素复制到临时数组中
|
||||
while (i <= mid) {
|
||||
tmp[k++] = nums[i++];
|
||||
}
|
||||
while (j <= right) {
|
||||
tmp[k++] = nums[j++];
|
||||
}
|
||||
// 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间
|
||||
for (k = 0; k < tmp.length; k++) {
|
||||
nums[left + k] = tmp[k];
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{merge}
|
||||
|
||||
/* 归并排序 */
|
||||
function mergeSort(nums, left, right) {
|
||||
// 终止条件
|
||||
if (left >= right) return; // 当子数组长度为 1 时终止递归
|
||||
// 划分阶段
|
||||
let mid = Math.floor((left + right) / 2); // 计算中点
|
||||
mergeSort(nums, left, mid); // 递归左子数组
|
||||
mergeSort(nums, mid + 1, right); // 递归右子数组
|
||||
// 合并阶段
|
||||
merge(nums, left, mid, right);
|
||||
}
|
||||
[class]{}-[func]{mergeSort}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="merge_sort.ts"
|
||||
/* 合并左子数组和右子数组 */
|
||||
function merge(nums: number[], left: number, mid: number, right: number): void {
|
||||
// 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right]
|
||||
// 创建一个临时数组 tmp ,用于存放合并后的结果
|
||||
const tmp = new Array(right - left + 1);
|
||||
// 初始化左子数组和右子数组的起始索引
|
||||
let i = left,
|
||||
j = mid + 1,
|
||||
k = 0;
|
||||
// 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中
|
||||
while (i <= mid && j <= right) {
|
||||
if (nums[i] <= nums[j]) {
|
||||
tmp[k++] = nums[i++];
|
||||
} else {
|
||||
tmp[k++] = nums[j++];
|
||||
}
|
||||
}
|
||||
// 将左子数组和右子数组的剩余元素复制到临时数组中
|
||||
while (i <= mid) {
|
||||
tmp[k++] = nums[i++];
|
||||
}
|
||||
while (j <= right) {
|
||||
tmp[k++] = nums[j++];
|
||||
}
|
||||
// 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间
|
||||
for (k = 0; k < tmp.length; k++) {
|
||||
nums[left + k] = tmp[k];
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{merge}
|
||||
|
||||
/* 归并排序 */
|
||||
function mergeSort(nums: number[], left: number, right: number): void {
|
||||
// 终止条件
|
||||
if (left >= right) return; // 当子数组长度为 1 时终止递归
|
||||
// 划分阶段
|
||||
let mid = Math.floor((left + right) / 2); // 计算中点
|
||||
mergeSort(nums, left, mid); // 递归左子数组
|
||||
mergeSort(nums, mid + 1, right); // 递归右子数组
|
||||
// 合并阶段
|
||||
merge(nums, left, mid, right);
|
||||
}
|
||||
[class]{}-[func]{mergeSort}
|
||||
```
|
||||
|
||||
=== "Dart"
|
||||
|
||||
```dart title="merge_sort.dart"
|
||||
/* 合并左子数组和右子数组 */
|
||||
void merge(List<int> nums, int left, int mid, int right) {
|
||||
// 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right]
|
||||
// 创建一个临时数组 tmp ,用于存放合并后的结果
|
||||
List<int> tmp = List.filled(right - left + 1, 0);
|
||||
// 初始化左子数组和右子数组的起始索引
|
||||
int i = left, j = mid + 1, k = 0;
|
||||
// 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中
|
||||
while (i <= mid && j <= right) {
|
||||
if (nums[i] <= nums[j])
|
||||
tmp[k++] = nums[i++];
|
||||
else
|
||||
tmp[k++] = nums[j++];
|
||||
}
|
||||
// 将左子数组和右子数组的剩余元素复制到临时数组中
|
||||
while (i <= mid) {
|
||||
tmp[k++] = nums[i++];
|
||||
}
|
||||
while (j <= right) {
|
||||
tmp[k++] = nums[j++];
|
||||
}
|
||||
// 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间
|
||||
for (k = 0; k < tmp.length; k++) {
|
||||
nums[left + k] = tmp[k];
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{merge}
|
||||
|
||||
/* 归并排序 */
|
||||
void mergeSort(List<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);
|
||||
}
|
||||
[class]{}-[func]{mergeSort}
|
||||
```
|
||||
|
||||
=== "Rust"
|
||||
|
||||
```rust title="merge_sort.rs"
|
||||
/* 合并左子数组和右子数组 */
|
||||
fn merge(nums: &mut [i32], left: usize, mid: usize, right: usize) {
|
||||
// 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right]
|
||||
// 创建一个临时数组 tmp ,用于存放合并后的结果
|
||||
let tmp_size = right - left + 1;
|
||||
let mut tmp = vec![0; tmp_size];
|
||||
// 初始化左子数组和右子数组的起始索引
|
||||
let (mut i, mut j, mut k) = (left, mid + 1, 0);
|
||||
// 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中
|
||||
while i <= mid && j <= right {
|
||||
if nums[i] <= nums[j] {
|
||||
tmp[k] = nums[i];
|
||||
i += 1;
|
||||
} else {
|
||||
tmp[k] = nums[j];
|
||||
j += 1;
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
// 将左子数组和右子数组的剩余元素复制到临时数组中
|
||||
while i <= mid {
|
||||
tmp[k] = nums[i];
|
||||
k += 1;
|
||||
i += 1;
|
||||
}
|
||||
while j <= right {
|
||||
tmp[k] = nums[j];
|
||||
k += 1;
|
||||
j += 1;
|
||||
}
|
||||
// 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间
|
||||
for k in 0..tmp_size {
|
||||
nums[left + k] = tmp[k];
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{merge}
|
||||
|
||||
/* 归并排序 */
|
||||
fn merge_sort(nums: &mut [i32], left: usize, right: usize) {
|
||||
// 终止条件
|
||||
if left >= right {
|
||||
return; // 当子数组长度为 1 时终止递归
|
||||
}
|
||||
|
||||
// 划分阶段
|
||||
let mid = (left + right) / 2; // 计算中点
|
||||
merge_sort(nums, left, mid); // 递归左子数组
|
||||
merge_sort(nums, mid + 1, right); // 递归右子数组
|
||||
|
||||
// 合并阶段
|
||||
merge(nums, left, mid, right);
|
||||
}
|
||||
[class]{}-[func]{merge_sort}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
|
||||
```c title="merge_sort.c"
|
||||
/* 合并左子数组和右子数组 */
|
||||
void merge(int *nums, int left, int mid, int right) {
|
||||
// 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right]
|
||||
// 创建一个临时数组 tmp ,用于存放合并后的结果
|
||||
int tmpSize = right - left + 1;
|
||||
int *tmp = (int *)malloc(tmpSize * sizeof(int));
|
||||
// 初始化左子数组和右子数组的起始索引
|
||||
int i = left, j = mid + 1, k = 0;
|
||||
// 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中
|
||||
while (i <= mid && j <= right) {
|
||||
if (nums[i] <= nums[j]) {
|
||||
tmp[k++] = nums[i++];
|
||||
} else {
|
||||
tmp[k++] = nums[j++];
|
||||
}
|
||||
}
|
||||
// 将左子数组和右子数组的剩余元素复制到临时数组中
|
||||
while (i <= mid) {
|
||||
tmp[k++] = nums[i++];
|
||||
}
|
||||
while (j <= right) {
|
||||
tmp[k++] = nums[j++];
|
||||
}
|
||||
// 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间
|
||||
for (k = 0; k < tmpSize; ++k) {
|
||||
nums[left + k] = tmp[k];
|
||||
}
|
||||
// 释放内存
|
||||
free(tmp);
|
||||
}
|
||||
[class]{}-[func]{merge}
|
||||
|
||||
/* 归并排序 */
|
||||
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);
|
||||
}
|
||||
[class]{}-[func]{mergeSort}
|
||||
```
|
||||
|
||||
=== "Kotlin"
|
||||
|
||||
```kotlin title="merge_sort.kt"
|
||||
/* 合并左子数组和右子数组 */
|
||||
fun merge(nums: IntArray, left: Int, mid: Int, right: Int) {
|
||||
// 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right]
|
||||
// 创建一个临时数组 tmp ,用于存放合并后的结果
|
||||
val tmp = IntArray(right - left + 1)
|
||||
// 初始化左子数组和右子数组的起始索引
|
||||
var i = left
|
||||
var j = mid + 1
|
||||
var k = 0
|
||||
// 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中
|
||||
while (i <= mid && j <= right) {
|
||||
if (nums[i] <= nums[j])
|
||||
tmp[k++] = nums[i++]
|
||||
else
|
||||
tmp[k++] = nums[j++]
|
||||
}
|
||||
// 将左子数组和右子数组的剩余元素复制到临时数组中
|
||||
while (i <= mid) {
|
||||
tmp[k++] = nums[i++]
|
||||
}
|
||||
while (j <= right) {
|
||||
tmp[k++] = nums[j++]
|
||||
}
|
||||
// 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间
|
||||
for (l in tmp.indices) {
|
||||
nums[left + l] = tmp[l]
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{merge}
|
||||
|
||||
/* 归并排序 */
|
||||
fun mergeSort(nums: IntArray, left: Int, right: Int) {
|
||||
// 终止条件
|
||||
if (left >= right) return // 当子数组长度为 1 时终止递归
|
||||
// 划分阶段
|
||||
val mid = (left + right) / 2 // 计算中点
|
||||
mergeSort(nums, left, mid) // 递归左子数组
|
||||
mergeSort(nums, mid + 1, right) // 递归右子数组
|
||||
// 合并阶段
|
||||
merge(nums, left, mid, right)
|
||||
}
|
||||
[class]{}-[func]{mergeSort}
|
||||
```
|
||||
|
||||
=== "Ruby"
|
||||
|
||||
```ruby title="merge_sort.rb"
|
||||
### 合并左子数组和右子数组 ###
|
||||
def merge(nums, left, mid, right)
|
||||
# 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right]
|
||||
# 创建一个临时数组 tmp,用于存放合并后的结果
|
||||
tmp = Array.new(right - left + 1, 0)
|
||||
# 初始化左子数组和右子数组的起始索引
|
||||
i, j, k = left, mid + 1, 0
|
||||
# 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中
|
||||
while i <= mid && j <= right
|
||||
if nums[i] <= nums[j]
|
||||
tmp[k] = nums[i]
|
||||
i += 1
|
||||
else
|
||||
tmp[k] = nums[j]
|
||||
j += 1
|
||||
end
|
||||
k += 1
|
||||
end
|
||||
# 将左子数组和右子数组的剩余元素复制到临时数组中
|
||||
while i <= mid
|
||||
tmp[k] = nums[i]
|
||||
i += 1
|
||||
k += 1
|
||||
end
|
||||
while j <= right
|
||||
tmp[k] = nums[j]
|
||||
j += 1
|
||||
k += 1
|
||||
end
|
||||
# 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间
|
||||
(0...tmp.length).each do |k|
|
||||
nums[left + k] = tmp[k]
|
||||
end
|
||||
end
|
||||
[class]{}-[func]{merge}
|
||||
|
||||
### 归并排序 ###
|
||||
def merge_sort(nums, left, right)
|
||||
# 终止条件
|
||||
# 当子数组长度为 1 时终止递归
|
||||
return if left >= right
|
||||
# 划分阶段
|
||||
mid = (left + right) / 2 # 计算中点
|
||||
merge_sort(nums, left, mid) # 递归左子数组
|
||||
merge_sort(nums, mid + 1, right) # 递归右子数组
|
||||
# 合并阶段
|
||||
merge(nums, left, mid, right)
|
||||
end
|
||||
[class]{}-[func]{merge_sort}
|
||||
```
|
||||
|
||||
=== "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;
|
||||
}
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{merge}
|
||||
|
||||
// 归并排序
|
||||
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);
|
||||
}
|
||||
[class]{}-[func]{mergeSort}
|
||||
```
|
||||
|
||||
??? pythontutor "Code Visualization"
|
||||
|
||||
<div style="height: 549px; width: 100%;"><iframe class="pythontutor-iframe" src="https://pythontutor.com/iframe-embed.html#code=def%20merge%28nums%3A%20list%5Bint%5D,%20left%3A%20int,%20mid%3A%20int,%20right%3A%20int%29%3A%0A%20%20%20%20%22%22%22%E5%90%88%E5%B9%B6%E5%B7%A6%E5%AD%90%E6%95%B0%E7%BB%84%E5%92%8C%E5%8F%B3%E5%AD%90%E6%95%B0%E7%BB%84%22%22%22%0A%20%20%20%20%23%20%E5%B7%A6%E5%AD%90%E6%95%B0%E7%BB%84%E5%8C%BA%E9%97%B4%E4%B8%BA%20%5Bleft,%20mid%5D,%20%E5%8F%B3%E5%AD%90%E6%95%B0%E7%BB%84%E5%8C%BA%E9%97%B4%E4%B8%BA%20%5Bmid%2B1,%20right%5D%0A%20%20%20%20%23%20%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AA%E4%B8%B4%E6%97%B6%E6%95%B0%E7%BB%84%20tmp%20%EF%BC%8C%E7%94%A8%E4%BA%8E%E5%AD%98%E6%94%BE%E5%90%88%E5%B9%B6%E5%90%8E%E7%9A%84%E7%BB%93%E6%9E%9C%0A%20%20%20%20tmp%20%3D%20%5B0%5D%20*%20%28right%20-%20left%20%2B%201%29%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%B7%A6%E5%AD%90%E6%95%B0%E7%BB%84%E5%92%8C%E5%8F%B3%E5%AD%90%E6%95%B0%E7%BB%84%E7%9A%84%E8%B5%B7%E5%A7%8B%E7%B4%A2%E5%BC%95%0A%20%20%20%20i,%20j,%20k%20%3D%20left,%20mid%20%2B%201,%200%0A%20%20%20%20%23%20%E5%BD%93%E5%B7%A6%E5%8F%B3%E5%AD%90%E6%95%B0%E7%BB%84%E9%83%BD%E8%BF%98%E6%9C%89%E5%85%83%E7%B4%A0%E6%97%B6%EF%BC%8C%E8%BF%9B%E8%A1%8C%E6%AF%94%E8%BE%83%E5%B9%B6%E5%B0%86%E8%BE%83%E5%B0%8F%E7%9A%84%E5%85%83%E7%B4%A0%E5%A4%8D%E5%88%B6%E5%88%B0%E4%B8%B4%E6%97%B6%E6%95%B0%E7%BB%84%E4%B8%AD%0A%20%20%20%20while%20i%20%3C%3D%20mid%20and%20j%20%3C%3D%20right%3A%0A%20%20%20%20%20%20%20%20if%20nums%5Bi%5D%20%3C%3D%20nums%5Bj%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20tmp%5Bk%5D%20%3D%20nums%5Bi%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%2B%3D%201%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20tmp%5Bk%5D%20%3D%20nums%5Bj%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20j%20%2B%3D%201%0A%20%20%20%20%20%20%20%20k%20%2B%3D%201%0A%20%20%20%20%23%20%E5%B0%86%E5%B7%A6%E5%AD%90%E6%95%B0%E7%BB%84%E5%92%8C%E5%8F%B3%E5%AD%90%E6%95%B0%E7%BB%84%E7%9A%84%E5%89%A9%E4%BD%99%E5%85%83%E7%B4%A0%E5%A4%8D%E5%88%B6%E5%88%B0%E4%B8%B4%E6%97%B6%E6%95%B0%E7%BB%84%E4%B8%AD%0A%20%20%20%20while%20i%20%3C%3D%20mid%3A%0A%20%20%20%20%20%20%20%20tmp%5Bk%5D%20%3D%20nums%5Bi%5D%0A%20%20%20%20%20%20%20%20i%20%2B%3D%201%0A%20%20%20%20%20%20%20%20k%20%2B%3D%201%0A%20%20%20%20while%20j%20%3C%3D%20right%3A%0A%20%20%20%20%20%20%20%20tmp%5Bk%5D%20%3D%20nums%5Bj%5D%0A%20%20%20%20%20%20%20%20j%20%2B%3D%201%0A%20%20%20%20%20%20%20%20k%20%2B%3D%201%0A%20%20%20%20%23%20%E5%B0%86%E4%B8%B4%E6%97%B6%E6%95%B0%E7%BB%84%20tmp%20%E4%B8%AD%E7%9A%84%E5%85%83%E7%B4%A0%E5%A4%8D%E5%88%B6%E5%9B%9E%E5%8E%9F%E6%95%B0%E7%BB%84%20nums%20%E7%9A%84%E5%AF%B9%E5%BA%94%E5%8C%BA%E9%97%B4%0A%20%20%20%20for%20k%20in%20range%280,%20len%28tmp%29%29%3A%0A%20%20%20%20%20%20%20%20nums%5Bleft%20%2B%20k%5D%20%3D%20tmp%5Bk%5D%0A%0A%0Adef%20merge_sort%28nums%3A%20list%5Bint%5D,%20left%3A%20int,%20right%3A%20int%29%3A%0A%20%20%20%20%22%22%22%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F%22%22%22%0A%20%20%20%20%23%20%E7%BB%88%E6%AD%A2%E6%9D%A1%E4%BB%B6%0A%20%20%20%20if%20left%20%3E%3D%20right%3A%0A%20%20%20%20%20%20%20%20return%20%20%23%20%E5%BD%93%E5%AD%90%E6%95%B0%E7%BB%84%E9%95%BF%E5%BA%A6%E4%B8%BA%201%20%E6%97%B6%E7%BB%88%E6%AD%A2%E9%80%92%E5%BD%92%0A%20%20%20%20%23%20%E5%88%92%E5%88%86%E9%98%B6%E6%AE%B5%0A%20%20%20%20mid%20%3D%20%28left%20%2B%20right%29%20//%202%20%20%23%20%E8%AE%A1%E7%AE%97%E4%B8%AD%E7%82%B9%0A%20%20%20%20merge_sort%28nums,%20left,%20mid%29%20%20%23%20%E9%80%92%E5%BD%92%E5%B7%A6%E5%AD%90%E6%95%B0%E7%BB%84%0A%20%20%20%20merge_sort%28nums,%20mid%20%2B%201,%20right%29%20%20%23%20%E9%80%92%E5%BD%92%E5%8F%B3%E5%AD%90%E6%95%B0%E7%BB%84%0A%20%20%20%20%23%20%E5%90%88%E5%B9%B6%E9%98%B6%E6%AE%B5%0A%20%20%20%20merge%28nums,%20left,%20mid,%20right%29%0A%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B7,%203,%202,%206,%200,%201,%205,%204%5D%0A%20%20%20%20merge_sort%28nums,%200,%20len%28nums%29%20-%201%29%0A%20%20%20%20print%28%22%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F%E5%AE%8C%E6%88%90%E5%90%8E%20nums%20%3D%22,%20nums%29&codeDivHeight=472&codeDivWidth=350&cumulative=false&curInstr=5&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe></div>
|
||||
<div style="margin-top: 5px;"><a href="https://pythontutor.com/iframe-embed.html#code=def%20merge%28nums%3A%20list%5Bint%5D,%20left%3A%20int,%20mid%3A%20int,%20right%3A%20int%29%3A%0A%20%20%20%20%22%22%22%E5%90%88%E5%B9%B6%E5%B7%A6%E5%AD%90%E6%95%B0%E7%BB%84%E5%92%8C%E5%8F%B3%E5%AD%90%E6%95%B0%E7%BB%84%22%22%22%0A%20%20%20%20%23%20%E5%B7%A6%E5%AD%90%E6%95%B0%E7%BB%84%E5%8C%BA%E9%97%B4%E4%B8%BA%20%5Bleft,%20mid%5D,%20%E5%8F%B3%E5%AD%90%E6%95%B0%E7%BB%84%E5%8C%BA%E9%97%B4%E4%B8%BA%20%5Bmid%2B1,%20right%5D%0A%20%20%20%20%23%20%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AA%E4%B8%B4%E6%97%B6%E6%95%B0%E7%BB%84%20tmp%20%EF%BC%8C%E7%94%A8%E4%BA%8E%E5%AD%98%E6%94%BE%E5%90%88%E5%B9%B6%E5%90%8E%E7%9A%84%E7%BB%93%E6%9E%9C%0A%20%20%20%20tmp%20%3D%20%5B0%5D%20*%20%28right%20-%20left%20%2B%201%29%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%B7%A6%E5%AD%90%E6%95%B0%E7%BB%84%E5%92%8C%E5%8F%B3%E5%AD%90%E6%95%B0%E7%BB%84%E7%9A%84%E8%B5%B7%E5%A7%8B%E7%B4%A2%E5%BC%95%0A%20%20%20%20i,%20j,%20k%20%3D%20left,%20mid%20%2B%201,%200%0A%20%20%20%20%23%20%E5%BD%93%E5%B7%A6%E5%8F%B3%E5%AD%90%E6%95%B0%E7%BB%84%E9%83%BD%E8%BF%98%E6%9C%89%E5%85%83%E7%B4%A0%E6%97%B6%EF%BC%8C%E8%BF%9B%E8%A1%8C%E6%AF%94%E8%BE%83%E5%B9%B6%E5%B0%86%E8%BE%83%E5%B0%8F%E7%9A%84%E5%85%83%E7%B4%A0%E5%A4%8D%E5%88%B6%E5%88%B0%E4%B8%B4%E6%97%B6%E6%95%B0%E7%BB%84%E4%B8%AD%0A%20%20%20%20while%20i%20%3C%3D%20mid%20and%20j%20%3C%3D%20right%3A%0A%20%20%20%20%20%20%20%20if%20nums%5Bi%5D%20%3C%3D%20nums%5Bj%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20tmp%5Bk%5D%20%3D%20nums%5Bi%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%2B%3D%201%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20tmp%5Bk%5D%20%3D%20nums%5Bj%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20j%20%2B%3D%201%0A%20%20%20%20%20%20%20%20k%20%2B%3D%201%0A%20%20%20%20%23%20%E5%B0%86%E5%B7%A6%E5%AD%90%E6%95%B0%E7%BB%84%E5%92%8C%E5%8F%B3%E5%AD%90%E6%95%B0%E7%BB%84%E7%9A%84%E5%89%A9%E4%BD%99%E5%85%83%E7%B4%A0%E5%A4%8D%E5%88%B6%E5%88%B0%E4%B8%B4%E6%97%B6%E6%95%B0%E7%BB%84%E4%B8%AD%0A%20%20%20%20while%20i%20%3C%3D%20mid%3A%0A%20%20%20%20%20%20%20%20tmp%5Bk%5D%20%3D%20nums%5Bi%5D%0A%20%20%20%20%20%20%20%20i%20%2B%3D%201%0A%20%20%20%20%20%20%20%20k%20%2B%3D%201%0A%20%20%20%20while%20j%20%3C%3D%20right%3A%0A%20%20%20%20%20%20%20%20tmp%5Bk%5D%20%3D%20nums%5Bj%5D%0A%20%20%20%20%20%20%20%20j%20%2B%3D%201%0A%20%20%20%20%20%20%20%20k%20%2B%3D%201%0A%20%20%20%20%23%20%E5%B0%86%E4%B8%B4%E6%97%B6%E6%95%B0%E7%BB%84%20tmp%20%E4%B8%AD%E7%9A%84%E5%85%83%E7%B4%A0%E5%A4%8D%E5%88%B6%E5%9B%9E%E5%8E%9F%E6%95%B0%E7%BB%84%20nums%20%E7%9A%84%E5%AF%B9%E5%BA%94%E5%8C%BA%E9%97%B4%0A%20%20%20%20for%20k%20in%20range%280,%20len%28tmp%29%29%3A%0A%20%20%20%20%20%20%20%20nums%5Bleft%20%2B%20k%5D%20%3D%20tmp%5Bk%5D%0A%0A%0Adef%20merge_sort%28nums%3A%20list%5Bint%5D,%20left%3A%20int,%20right%3A%20int%29%3A%0A%20%20%20%20%22%22%22%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F%22%22%22%0A%20%20%20%20%23%20%E7%BB%88%E6%AD%A2%E6%9D%A1%E4%BB%B6%0A%20%20%20%20if%20left%20%3E%3D%20right%3A%0A%20%20%20%20%20%20%20%20return%20%20%23%20%E5%BD%93%E5%AD%90%E6%95%B0%E7%BB%84%E9%95%BF%E5%BA%A6%E4%B8%BA%201%20%E6%97%B6%E7%BB%88%E6%AD%A2%E9%80%92%E5%BD%92%0A%20%20%20%20%23%20%E5%88%92%E5%88%86%E9%98%B6%E6%AE%B5%0A%20%20%20%20mid%20%3D%20%28left%20%2B%20right%29%20//%202%20%20%23%20%E8%AE%A1%E7%AE%97%E4%B8%AD%E7%82%B9%0A%20%20%20%20merge_sort%28nums,%20left,%20mid%29%20%20%23%20%E9%80%92%E5%BD%92%E5%B7%A6%E5%AD%90%E6%95%B0%E7%BB%84%0A%20%20%20%20merge_sort%28nums,%20mid%20%2B%201,%20right%29%20%20%23%20%E9%80%92%E5%BD%92%E5%8F%B3%E5%AD%90%E6%95%B0%E7%BB%84%0A%20%20%20%20%23%20%E5%90%88%E5%B9%B6%E9%98%B6%E6%AE%B5%0A%20%20%20%20merge%28nums,%20left,%20mid,%20right%29%0A%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B7,%203,%202,%206,%200,%201,%205,%204%5D%0A%20%20%20%20merge_sort%28nums,%200,%20len%28nums%29%20-%201%29%0A%20%20%20%20print%28%22%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F%E5%AE%8C%E6%88%90%E5%90%8E%20nums%20%3D%22,%20nums%29&codeDivHeight=800&codeDivWidth=600&cumulative=false&curInstr=5&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false" target="_blank" rel="noopener noreferrer">Full Screen ></a></div>
|
||||
|
||||
## 11.6.2 Algorithm characteristics
|
||||
|
||||
- **Time complexity of $O(n \log n)$, non-adaptive sort**: The division creates a recursion tree of height $\log n$, with each layer merging a total of $n$ operations, resulting in an overall time complexity of $O(n \log n)$.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -34,44 +34,44 @@ Additionally, we need to slightly modify the counting sort code to allow sorting
|
||||
|
||||
```python title="radix_sort.py"
|
||||
def digit(num: int, exp: int) -> int:
|
||||
"""获取元素 num 的第 k 位,其中 exp = 10^(k-1)"""
|
||||
# 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算
|
||||
"""Get the k-th digit of element num, where exp = 10^(k-1)"""
|
||||
# Passing exp instead of k can avoid repeated expensive exponentiation here
|
||||
return (num // exp) % 10
|
||||
|
||||
def counting_sort_digit(nums: list[int], exp: int):
|
||||
"""计数排序(根据 nums 第 k 位排序)"""
|
||||
# 十进制的位范围为 0~9 ,因此需要长度为 10 的桶数组
|
||||
"""Counting sort (based on nums k-th digit)"""
|
||||
# Decimal digit range is 0~9, therefore need a bucket array of length 10
|
||||
counter = [0] * 10
|
||||
n = len(nums)
|
||||
# 统计 0~9 各数字的出现次数
|
||||
# Count the occurrence of digits 0~9
|
||||
for i in range(n):
|
||||
d = digit(nums[i], exp) # 获取 nums[i] 第 k 位,记为 d
|
||||
counter[d] += 1 # 统计数字 d 的出现次数
|
||||
# 求前缀和,将“出现个数”转换为“数组索引”
|
||||
d = digit(nums[i], exp) # Get the k-th digit of nums[i], noted as d
|
||||
counter[d] += 1 # Count the occurrence of digit d
|
||||
# Calculate prefix sum, converting "occurrence count" into "array index"
|
||||
for i in range(1, 10):
|
||||
counter[i] += counter[i - 1]
|
||||
# 倒序遍历,根据桶内统计结果,将各元素填入 res
|
||||
# Traverse in reverse, based on bucket statistics, place each element into 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
|
||||
j = counter[d] - 1 # Get the index j for d in the array
|
||||
res[j] = nums[i] # Place the current element at index j
|
||||
counter[d] -= 1 # Decrease the count of d by 1
|
||||
# Use result to overwrite the original array nums
|
||||
for i in range(n):
|
||||
nums[i] = res[i]
|
||||
|
||||
def radix_sort(nums: list[int]):
|
||||
"""基数排序"""
|
||||
# 获取数组的最大元素,用于判断最大位数
|
||||
"""Radix sort"""
|
||||
# Get the maximum element of the array, used to determine the maximum number of digits
|
||||
m = max(nums)
|
||||
# 按照从低位到高位的顺序遍历
|
||||
# Traverse from the lowest to the highest digit
|
||||
exp = 1
|
||||
while exp <= m:
|
||||
# 对数组元素的第 k 位执行计数排序
|
||||
# Perform counting sort on the k-th digit of array elements
|
||||
# k = 1 -> exp = 1
|
||||
# k = 2 -> exp = 10
|
||||
# 即 exp = 10^(k-1)
|
||||
# i.e., exp = 10^(k-1)
|
||||
counting_sort_digit(nums, exp)
|
||||
exp *= 10
|
||||
```
|
||||
@@ -79,102 +79,62 @@ Additionally, we need to slightly modify the counting sort code to allow sorting
|
||||
=== "C++"
|
||||
|
||||
```cpp title="radix_sort.cpp"
|
||||
/* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */
|
||||
int digit(int num, int exp) {
|
||||
// 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算
|
||||
return (num / exp) % 10;
|
||||
}
|
||||
[class]{}-[func]{digit}
|
||||
|
||||
/* 计数排序(根据 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];
|
||||
}
|
||||
[class]{}-[func]{countingSortDigit}
|
||||
|
||||
/* 基数排序 */
|
||||
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);
|
||||
}
|
||||
[class]{}-[func]{radixSort}
|
||||
```
|
||||
|
||||
=== "Java"
|
||||
|
||||
```java title="radix_sort.java"
|
||||
/* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */
|
||||
/* Get the k-th digit of element num, where exp = 10^(k-1) */
|
||||
int digit(int num, int exp) {
|
||||
// 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算
|
||||
// Passing exp instead of k can avoid repeated expensive exponentiation here
|
||||
return (num / exp) % 10;
|
||||
}
|
||||
|
||||
/* 计数排序(根据 nums 第 k 位排序) */
|
||||
/* Counting sort (based on nums k-th digit) */
|
||||
void countingSortDigit(int[] nums, int exp) {
|
||||
// 十进制的位范围为 0~9 ,因此需要长度为 10 的桶数组
|
||||
// Decimal digit range is 0~9, therefore need a bucket array of length 10
|
||||
int[] counter = new int[10];
|
||||
int n = nums.length;
|
||||
// 统计 0~9 各数字的出现次数
|
||||
// Count the occurrence of digits 0~9
|
||||
for (int i = 0; i < n; i++) {
|
||||
int d = digit(nums[i], exp); // 获取 nums[i] 第 k 位,记为 d
|
||||
counter[d]++; // 统计数字 d 的出现次数
|
||||
int d = digit(nums[i], exp); // Get the k-th digit of nums[i], noted as d
|
||||
counter[d]++; // Count the occurrence of digit d
|
||||
}
|
||||
// 求前缀和,将“出现个数”转换为“数组索引”
|
||||
// Calculate prefix sum, converting "occurrence count" into "array index"
|
||||
for (int i = 1; i < 10; i++) {
|
||||
counter[i] += counter[i - 1];
|
||||
}
|
||||
// 倒序遍历,根据桶内统计结果,将各元素填入 res
|
||||
// Traverse in reverse, based on bucket statistics, place each element into 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
|
||||
int j = counter[d] - 1; // Get the index j for d in the array
|
||||
res[j] = nums[i]; // Place the current element at index j
|
||||
counter[d]--; // Decrease the count of d by 1
|
||||
}
|
||||
// 使用结果覆盖原数组 nums
|
||||
// Use result to overwrite the original array nums
|
||||
for (int i = 0; i < n; i++)
|
||||
nums[i] = res[i];
|
||||
}
|
||||
|
||||
/* 基数排序 */
|
||||
/* Radix sort */
|
||||
void radixSort(int[] nums) {
|
||||
// 获取数组的最大元素,用于判断最大位数
|
||||
// Get the maximum element of the array, used to determine the maximum number of digits
|
||||
int m = Integer.MIN_VALUE;
|
||||
for (int num : nums)
|
||||
if (num > m)
|
||||
m = num;
|
||||
// 按照从低位到高位的顺序遍历
|
||||
// Traverse from the lowest to the highest digit
|
||||
for (int exp = 1; exp <= m; exp *= 10) {
|
||||
// 对数组元素的第 k 位执行计数排序
|
||||
// Perform counting sort on the k-th digit of array elements
|
||||
// k = 1 -> exp = 1
|
||||
// k = 2 -> exp = 10
|
||||
// 即 exp = 10^(k-1)
|
||||
// i.e., exp = 10^(k-1)
|
||||
countingSortDigit(nums, exp);
|
||||
}
|
||||
}
|
||||
@@ -183,616 +143,113 @@ Additionally, we need to slightly modify the counting sort code to allow sorting
|
||||
=== "C#"
|
||||
|
||||
```csharp title="radix_sort.cs"
|
||||
/* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */
|
||||
int Digit(int num, int exp) {
|
||||
// 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算
|
||||
return (num / exp) % 10;
|
||||
}
|
||||
[class]{radix_sort}-[func]{Digit}
|
||||
|
||||
/* 计数排序(根据 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];
|
||||
}
|
||||
}
|
||||
[class]{radix_sort}-[func]{CountingSortDigit}
|
||||
|
||||
/* 基数排序 */
|
||||
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);
|
||||
}
|
||||
}
|
||||
[class]{radix_sort}-[func]{RadixSort}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
||||
```go title="radix_sort.go"
|
||||
/* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */
|
||||
func digit(num, exp int) int {
|
||||
// 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算
|
||||
return (num / exp) % 10
|
||||
}
|
||||
[class]{}-[func]{digit}
|
||||
|
||||
/* 计数排序(根据 nums 第 k 位排序) */
|
||||
func countingSortDigit(nums []int, exp int) {
|
||||
// 十进制的位范围为 0~9 ,因此需要长度为 10 的桶数组
|
||||
counter := make([]int, 10)
|
||||
n := len(nums)
|
||||
// 统计 0~9 各数字的出现次数
|
||||
for i := 0; i < n; i++ {
|
||||
d := digit(nums[i], exp) // 获取 nums[i] 第 k 位,记为 d
|
||||
counter[d]++ // 统计数字 d 的出现次数
|
||||
}
|
||||
// 求前缀和,将“出现个数”转换为“数组索引”
|
||||
for i := 1; i < 10; i++ {
|
||||
counter[i] += counter[i-1]
|
||||
}
|
||||
// 倒序遍历,根据桶内统计结果,将各元素填入 res
|
||||
res := make([]int, n)
|
||||
for i := n - 1; i >= 0; i-- {
|
||||
d := digit(nums[i], exp)
|
||||
j := counter[d] - 1 // 获取 d 在数组中的索引 j
|
||||
res[j] = nums[i] // 将当前元素填入索引 j
|
||||
counter[d]-- // 将 d 的数量减 1
|
||||
}
|
||||
// 使用结果覆盖原数组 nums
|
||||
for i := 0; i < n; i++ {
|
||||
nums[i] = res[i]
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{countingSortDigit}
|
||||
|
||||
/* 基数排序 */
|
||||
func radixSort(nums []int) {
|
||||
// 获取数组的最大元素,用于判断最大位数
|
||||
max := math.MinInt
|
||||
for _, num := range nums {
|
||||
if num > max {
|
||||
max = num
|
||||
}
|
||||
}
|
||||
// 按照从低位到高位的顺序遍历
|
||||
for exp := 1; max >= exp; exp *= 10 {
|
||||
// 对数组元素的第 k 位执行计数排序
|
||||
// k = 1 -> exp = 1
|
||||
// k = 2 -> exp = 10
|
||||
// 即 exp = 10^(k-1)
|
||||
countingSortDigit(nums, exp)
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{radixSort}
|
||||
```
|
||||
|
||||
=== "Swift"
|
||||
|
||||
```swift title="radix_sort.swift"
|
||||
/* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */
|
||||
func digit(num: Int, exp: Int) -> Int {
|
||||
// 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算
|
||||
(num / exp) % 10
|
||||
}
|
||||
[class]{}-[func]{digit}
|
||||
|
||||
/* 计数排序(根据 nums 第 k 位排序) */
|
||||
func countingSortDigit(nums: inout [Int], exp: Int) {
|
||||
// 十进制的位范围为 0~9 ,因此需要长度为 10 的桶数组
|
||||
var counter = Array(repeating: 0, count: 10)
|
||||
// 统计 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: nums.count)
|
||||
for i in nums.indices.reversed() {
|
||||
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]
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{countingSortDigit}
|
||||
|
||||
/* 基数排序 */
|
||||
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)
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{radixSort}
|
||||
```
|
||||
|
||||
=== "JS"
|
||||
|
||||
```javascript title="radix_sort.js"
|
||||
/* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */
|
||||
function digit(num, exp) {
|
||||
// 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算
|
||||
return Math.floor(num / exp) % 10;
|
||||
}
|
||||
[class]{}-[func]{digit}
|
||||
|
||||
/* 计数排序(根据 nums 第 k 位排序) */
|
||||
function countingSortDigit(nums, exp) {
|
||||
// 十进制的位范围为 0~9 ,因此需要长度为 10 的桶数组
|
||||
const counter = new Array(10).fill(0);
|
||||
const n = nums.length;
|
||||
// 统计 0~9 各数字的出现次数
|
||||
for (let i = 0; i < n; i++) {
|
||||
const d = digit(nums[i], exp); // 获取 nums[i] 第 k 位,记为 d
|
||||
counter[d]++; // 统计数字 d 的出现次数
|
||||
}
|
||||
// 求前缀和,将“出现个数”转换为“数组索引”
|
||||
for (let i = 1; i < 10; i++) {
|
||||
counter[i] += counter[i - 1];
|
||||
}
|
||||
// 倒序遍历,根据桶内统计结果,将各元素填入 res
|
||||
const res = new Array(n).fill(0);
|
||||
for (let i = n - 1; i >= 0; i--) {
|
||||
const d = digit(nums[i], exp);
|
||||
const j = counter[d] - 1; // 获取 d 在数组中的索引 j
|
||||
res[j] = nums[i]; // 将当前元素填入索引 j
|
||||
counter[d]--; // 将 d 的数量减 1
|
||||
}
|
||||
// 使用结果覆盖原数组 nums
|
||||
for (let i = 0; i < n; i++) {
|
||||
nums[i] = res[i];
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{countingSortDigit}
|
||||
|
||||
/* 基数排序 */
|
||||
function radixSort(nums) {
|
||||
// 获取数组的最大元素,用于判断最大位数
|
||||
let m = Number.MIN_VALUE;
|
||||
for (const num of nums) {
|
||||
if (num > m) {
|
||||
m = num;
|
||||
}
|
||||
}
|
||||
// 按照从低位到高位的顺序遍历
|
||||
for (let exp = 1; exp <= m; exp *= 10) {
|
||||
// 对数组元素的第 k 位执行计数排序
|
||||
// k = 1 -> exp = 1
|
||||
// k = 2 -> exp = 10
|
||||
// 即 exp = 10^(k-1)
|
||||
countingSortDigit(nums, exp);
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{radixSort}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="radix_sort.ts"
|
||||
/* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */
|
||||
function digit(num: number, exp: number): number {
|
||||
// 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算
|
||||
return Math.floor(num / exp) % 10;
|
||||
}
|
||||
[class]{}-[func]{digit}
|
||||
|
||||
/* 计数排序(根据 nums 第 k 位排序) */
|
||||
function countingSortDigit(nums: number[], exp: number): void {
|
||||
// 十进制的位范围为 0~9 ,因此需要长度为 10 的桶数组
|
||||
const counter = new Array(10).fill(0);
|
||||
const n = nums.length;
|
||||
// 统计 0~9 各数字的出现次数
|
||||
for (let i = 0; i < n; i++) {
|
||||
const d = digit(nums[i], exp); // 获取 nums[i] 第 k 位,记为 d
|
||||
counter[d]++; // 统计数字 d 的出现次数
|
||||
}
|
||||
// 求前缀和,将“出现个数”转换为“数组索引”
|
||||
for (let i = 1; i < 10; i++) {
|
||||
counter[i] += counter[i - 1];
|
||||
}
|
||||
// 倒序遍历,根据桶内统计结果,将各元素填入 res
|
||||
const res = new Array(n).fill(0);
|
||||
for (let i = n - 1; i >= 0; i--) {
|
||||
const d = digit(nums[i], exp);
|
||||
const j = counter[d] - 1; // 获取 d 在数组中的索引 j
|
||||
res[j] = nums[i]; // 将当前元素填入索引 j
|
||||
counter[d]--; // 将 d 的数量减 1
|
||||
}
|
||||
// 使用结果覆盖原数组 nums
|
||||
for (let i = 0; i < n; i++) {
|
||||
nums[i] = res[i];
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{countingSortDigit}
|
||||
|
||||
/* 基数排序 */
|
||||
function radixSort(nums: number[]): void {
|
||||
// 获取数组的最大元素,用于判断最大位数
|
||||
let m = Number.MIN_VALUE;
|
||||
for (const num of nums) {
|
||||
if (num > m) {
|
||||
m = num;
|
||||
}
|
||||
}
|
||||
// 按照从低位到高位的顺序遍历
|
||||
for (let exp = 1; exp <= m; exp *= 10) {
|
||||
// 对数组元素的第 k 位执行计数排序
|
||||
// k = 1 -> exp = 1
|
||||
// k = 2 -> exp = 10
|
||||
// 即 exp = 10^(k-1)
|
||||
countingSortDigit(nums, exp);
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{radixSort}
|
||||
```
|
||||
|
||||
=== "Dart"
|
||||
|
||||
```dart title="radix_sort.dart"
|
||||
/* 获取元素 _num 的第 k 位,其中 exp = 10^(k-1) */
|
||||
int digit(int _num, int exp) {
|
||||
// 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算
|
||||
return (_num ~/ exp) % 10;
|
||||
}
|
||||
[class]{}-[func]{digit}
|
||||
|
||||
/* 计数排序(根据 nums 第 k 位排序) */
|
||||
void countingSortDigit(List<int> nums, int exp) {
|
||||
// 十进制的位范围为 0~9 ,因此需要长度为 10 的桶数组
|
||||
List<int> counter = List<int>.filled(10, 0);
|
||||
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
|
||||
List<int> res = List<int>.filled(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];
|
||||
}
|
||||
[class]{}-[func]{countingSortDigit}
|
||||
|
||||
/* 基数排序 */
|
||||
void radixSort(List<int> nums) {
|
||||
// 获取数组的最大元素,用于判断最大位数
|
||||
// dart 中 int 的长度是 64 位的
|
||||
int m = -1 << 63;
|
||||
for (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);
|
||||
}
|
||||
[class]{}-[func]{radixSort}
|
||||
```
|
||||
|
||||
=== "Rust"
|
||||
|
||||
```rust title="radix_sort.rs"
|
||||
/* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */
|
||||
fn digit(num: i32, exp: i32) -> usize {
|
||||
// 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算
|
||||
return ((num / exp) % 10) as usize;
|
||||
}
|
||||
[class]{}-[func]{digit}
|
||||
|
||||
/* 计数排序(根据 nums 第 k 位排序) */
|
||||
fn counting_sort_digit(nums: &mut [i32], exp: i32) {
|
||||
// 十进制的位范围为 0~9 ,因此需要长度为 10 的桶数组
|
||||
let mut counter = [0; 10];
|
||||
let n = nums.len();
|
||||
// 统计 0~9 各数字的出现次数
|
||||
for i in 0..n {
|
||||
let d = digit(nums[i], exp); // 获取 nums[i] 第 k 位,记为 d
|
||||
counter[d] += 1; // 统计数字 d 的出现次数
|
||||
}
|
||||
// 求前缀和,将“出现个数”转换为“数组索引”
|
||||
for i in 1..10 {
|
||||
counter[i] += counter[i - 1];
|
||||
}
|
||||
// 倒序遍历,根据桶内统计结果,将各元素填入 res
|
||||
let mut res = vec![0; n];
|
||||
for i in (0..n).rev() {
|
||||
let d = digit(nums[i], exp);
|
||||
let j = counter[d] - 1; // 获取 d 在数组中的索引 j
|
||||
res[j] = nums[i]; // 将当前元素填入索引 j
|
||||
counter[d] -= 1; // 将 d 的数量减 1
|
||||
}
|
||||
// 使用结果覆盖原数组 nums
|
||||
for i in 0..n {
|
||||
nums[i] = res[i];
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{counting_sort_digit}
|
||||
|
||||
/* 基数排序 */
|
||||
fn radix_sort(nums: &mut [i32]) {
|
||||
// 获取数组的最大元素,用于判断最大位数
|
||||
let m = *nums.into_iter().max().unwrap();
|
||||
// 按照从低位到高位的顺序遍历
|
||||
let mut exp = 1;
|
||||
while exp <= m {
|
||||
counting_sort_digit(nums, exp);
|
||||
exp *= 10;
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{radix_sort}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
|
||||
```c title="radix_sort.c"
|
||||
/* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */
|
||||
int digit(int num, int exp) {
|
||||
// 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算
|
||||
return (num / exp) % 10;
|
||||
}
|
||||
[class]{}-[func]{digit}
|
||||
|
||||
/* 计数排序(根据 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];
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{countingSortDigit}
|
||||
|
||||
/* 基数排序 */
|
||||
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);
|
||||
}
|
||||
[class]{}-[func]{radixSort}
|
||||
```
|
||||
|
||||
=== "Kotlin"
|
||||
|
||||
```kotlin title="radix_sort.kt"
|
||||
/* 获取元素 num 的第 k 位,其中 exp = 10^(k-1) */
|
||||
fun digit(num: Int, exp: Int): Int {
|
||||
// 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算
|
||||
return (num / exp) % 10
|
||||
}
|
||||
[class]{}-[func]{digit}
|
||||
|
||||
/* 计数排序(根据 nums 第 k 位排序) */
|
||||
fun countingSortDigit(nums: IntArray, exp: Int) {
|
||||
// 十进制的位范围为 0~9 ,因此需要长度为 10 的桶数组
|
||||
val counter = IntArray(10)
|
||||
val n = nums.size
|
||||
// 统计 0~9 各数字的出现次数
|
||||
for (i in 0..<n) {
|
||||
val d = digit(nums[i], exp) // 获取 nums[i] 第 k 位,记为 d
|
||||
counter[d]++ // 统计数字 d 的出现次数
|
||||
}
|
||||
// 求前缀和,将“出现个数”转换为“数组索引”
|
||||
for (i in 1..9) {
|
||||
counter[i] += counter[i - 1]
|
||||
}
|
||||
// 倒序遍历,根据桶内统计结果,将各元素填入 res
|
||||
val res = IntArray(n)
|
||||
for (i in n - 1 downTo 0) {
|
||||
val d = digit(nums[i], exp)
|
||||
val j = counter[d] - 1 // 获取 d 在数组中的索引 j
|
||||
res[j] = nums[i] // 将当前元素填入索引 j
|
||||
counter[d]-- // 将 d 的数量减 1
|
||||
}
|
||||
// 使用结果覆盖原数组 nums
|
||||
for (i in 0..<n)
|
||||
nums[i] = res[i]
|
||||
}
|
||||
[class]{}-[func]{countingSortDigit}
|
||||
|
||||
/* 基数排序 */
|
||||
fun radixSort(nums: IntArray) {
|
||||
// 获取数组的最大元素,用于判断最大位数
|
||||
var m = Int.MIN_VALUE
|
||||
for (num in nums) if (num > m) m = num
|
||||
var exp = 1
|
||||
// 按照从低位到高位的顺序遍历
|
||||
while (exp <= m) {
|
||||
// 对数组元素的第 k 位执行计数排序
|
||||
// k = 1 -> exp = 1
|
||||
// k = 2 -> exp = 10
|
||||
// 即 exp = 10^(k-1)
|
||||
countingSortDigit(nums, exp)
|
||||
exp *= 10
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{radixSort}
|
||||
```
|
||||
|
||||
=== "Ruby"
|
||||
|
||||
```ruby title="radix_sort.rb"
|
||||
### 获取元素 num 的第 k 位,其中 exp = 10^(k-1) ###
|
||||
def digit(num, exp)
|
||||
# 转入 exp 而非 k 可以避免在此重复执行昂贵的次方计算
|
||||
(num / exp) % 10
|
||||
end
|
||||
[class]{}-[func]{digit}
|
||||
|
||||
### 计数排序(根据 nums 第 k 位排序)###
|
||||
def counting_sort_digit(nums, exp)
|
||||
# 十进制的位范围为 0~9 ,因此需要长度为 10 的桶数组
|
||||
counter = Array.new(10, 0)
|
||||
n = nums.length
|
||||
# 统计 0~9 各数字的出现次数
|
||||
for i in 0...n
|
||||
d = digit(nums[i], exp) # 获取 nums[i] 第 k 位,记为 d
|
||||
counter[d] += 1 # 统计数字 d 的出现次数
|
||||
end
|
||||
# 求前缀和,将“出现个数”转换为“数组索引”
|
||||
(1...10).each { |i| counter[i] += counter[i - 1] }
|
||||
# 倒序遍历,根据桶内统计结果,将各元素填入 res
|
||||
res = Array.new(n, 0)
|
||||
for i in (n - 1).downto(0)
|
||||
d = digit(nums[i], exp)
|
||||
j = counter[d] - 1 # 获取 d 在数组中的索引 j
|
||||
res[j] = nums[i] # 将当前元素填入索引 j
|
||||
counter[d] -= 1 # 将 d 的数量减 1
|
||||
end
|
||||
# 使用结果覆盖原数组 nums
|
||||
(0...n).each { |i| nums[i] = res[i] }
|
||||
end
|
||||
[class]{}-[func]{counting_sort_digit}
|
||||
|
||||
### 基数排序 ###
|
||||
def radix_sort(nums)
|
||||
# 获取数组的最大元素,用于判断最大位数
|
||||
m = nums.max
|
||||
# 按照从低位到高位的顺序遍历
|
||||
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
|
||||
end
|
||||
end
|
||||
[class]{}-[func]{radix_sort}
|
||||
```
|
||||
|
||||
=== "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);
|
||||
}
|
||||
[class]{}-[func]{digit}
|
||||
|
||||
// 计数排序(根据 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];
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{countingSortDigit}
|
||||
|
||||
// 基数排序
|
||||
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);
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{radixSort}
|
||||
```
|
||||
|
||||
??? pythontutor "Code Visualization"
|
||||
|
||||
<div style="height: 549px; width: 100%;"><iframe class="pythontutor-iframe" src="https://pythontutor.com/iframe-embed.html#code=def%20digit%28num%3A%20int,%20exp%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%E8%8E%B7%E5%8F%96%E5%85%83%E7%B4%A0%20num%20%E7%9A%84%E7%AC%AC%20k%20%E4%BD%8D%EF%BC%8C%E5%85%B6%E4%B8%AD%20exp%20%3D%2010%5E%28k-1%29%22%22%22%0A%20%20%20%20%23%20%E4%BC%A0%E5%85%A5%20exp%20%E8%80%8C%E9%9D%9E%20k%20%E5%8F%AF%E4%BB%A5%E9%81%BF%E5%85%8D%E5%9C%A8%E6%AD%A4%E9%87%8D%E5%A4%8D%E6%89%A7%E8%A1%8C%E6%98%82%E8%B4%B5%E7%9A%84%E6%AC%A1%E6%96%B9%E8%AE%A1%E7%AE%97%0A%20%20%20%20return%20%28num%20//%20exp%29%20%25%2010%0A%0Adef%20counting_sort_digit%28nums%3A%20list%5Bint%5D,%20exp%3A%20int%29%3A%0A%20%20%20%20%22%22%22%E8%AE%A1%E6%95%B0%E6%8E%92%E5%BA%8F%EF%BC%88%E6%A0%B9%E6%8D%AE%20nums%20%E7%AC%AC%20k%20%E4%BD%8D%E6%8E%92%E5%BA%8F%EF%BC%89%22%22%22%0A%20%20%20%20%23%20%E5%8D%81%E8%BF%9B%E5%88%B6%E7%9A%84%E4%BD%8D%E8%8C%83%E5%9B%B4%E4%B8%BA%200~9%20%EF%BC%8C%E5%9B%A0%E6%AD%A4%E9%9C%80%E8%A6%81%E9%95%BF%E5%BA%A6%E4%B8%BA%2010%20%E7%9A%84%E6%A1%B6%E6%95%B0%E7%BB%84%0A%20%20%20%20counter%20%3D%20%5B0%5D%20*%2010%0A%20%20%20%20n%20%3D%20len%28nums%29%0A%20%20%20%20%23%20%E7%BB%9F%E8%AE%A1%200~9%20%E5%90%84%E6%95%B0%E5%AD%97%E7%9A%84%E5%87%BA%E7%8E%B0%E6%AC%A1%E6%95%B0%0A%20%20%20%20for%20i%20in%20range%28n%29%3A%0A%20%20%20%20%20%20%20%20d%20%3D%20digit%28nums%5Bi%5D,%20exp%29%20%20%23%20%E8%8E%B7%E5%8F%96%20nums%5Bi%5D%20%E7%AC%AC%20k%20%E4%BD%8D%EF%BC%8C%E8%AE%B0%E4%B8%BA%20d%0A%20%20%20%20%20%20%20%20counter%5Bd%5D%20%2B%3D%201%20%20%23%20%E7%BB%9F%E8%AE%A1%E6%95%B0%E5%AD%97%20d%20%E7%9A%84%E5%87%BA%E7%8E%B0%E6%AC%A1%E6%95%B0%0A%20%20%20%20%23%20%E6%B1%82%E5%89%8D%E7%BC%80%E5%92%8C%EF%BC%8C%E5%B0%86%E2%80%9C%E5%87%BA%E7%8E%B0%E4%B8%AA%E6%95%B0%E2%80%9D%E8%BD%AC%E6%8D%A2%E4%B8%BA%E2%80%9C%E6%95%B0%E7%BB%84%E7%B4%A2%E5%BC%95%E2%80%9D%0A%20%20%20%20for%20i%20in%20range%281,%2010%29%3A%0A%20%20%20%20%20%20%20%20counter%5Bi%5D%20%2B%3D%20counter%5Bi%20-%201%5D%0A%20%20%20%20%23%20%E5%80%92%E5%BA%8F%E9%81%8D%E5%8E%86%EF%BC%8C%E6%A0%B9%E6%8D%AE%E6%A1%B6%E5%86%85%E7%BB%9F%E8%AE%A1%E7%BB%93%E6%9E%9C%EF%BC%8C%E5%B0%86%E5%90%84%E5%85%83%E7%B4%A0%E5%A1%AB%E5%85%A5%20res%0A%20%20%20%20res%20%3D%20%5B0%5D%20*%20n%0A%20%20%20%20for%20i%20in%20range%28n%20-%201,%20-1,%20-1%29%3A%0A%20%20%20%20%20%20%20%20d%20%3D%20digit%28nums%5Bi%5D,%20exp%29%0A%20%20%20%20%20%20%20%20j%20%3D%20counter%5Bd%5D%20-%201%20%20%23%20%E8%8E%B7%E5%8F%96%20d%20%E5%9C%A8%E6%95%B0%E7%BB%84%E4%B8%AD%E7%9A%84%E7%B4%A2%E5%BC%95%20j%0A%20%20%20%20%20%20%20%20res%5Bj%5D%20%3D%20nums%5Bi%5D%20%20%23%20%E5%B0%86%E5%BD%93%E5%89%8D%E5%85%83%E7%B4%A0%E5%A1%AB%E5%85%A5%E7%B4%A2%E5%BC%95%20j%0A%20%20%20%20%20%20%20%20counter%5Bd%5D%20-%3D%201%20%20%23%20%E5%B0%86%20d%20%E7%9A%84%E6%95%B0%E9%87%8F%E5%87%8F%201%0A%20%20%20%20%23%20%E4%BD%BF%E7%94%A8%E7%BB%93%E6%9E%9C%E8%A6%86%E7%9B%96%E5%8E%9F%E6%95%B0%E7%BB%84%20nums%0A%20%20%20%20for%20i%20in%20range%28n%29%3A%0A%20%20%20%20%20%20%20%20nums%5Bi%5D%20%3D%20res%5Bi%5D%0A%0Adef%20radix_sort%28nums%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22%E5%9F%BA%E6%95%B0%E6%8E%92%E5%BA%8F%22%22%22%0A%20%20%20%20%23%20%E8%8E%B7%E5%8F%96%E6%95%B0%E7%BB%84%E7%9A%84%E6%9C%80%E5%A4%A7%E5%85%83%E7%B4%A0%EF%BC%8C%E7%94%A8%E4%BA%8E%E5%88%A4%E6%96%AD%E6%9C%80%E5%A4%A7%E4%BD%8D%E6%95%B0%0A%20%20%20%20m%20%3D%20max%28nums%29%0A%20%20%20%20%23%20%E6%8C%89%E7%85%A7%E4%BB%8E%E4%BD%8E%E4%BD%8D%E5%88%B0%E9%AB%98%E4%BD%8D%E7%9A%84%E9%A1%BA%E5%BA%8F%E9%81%8D%E5%8E%86%0A%20%20%20%20exp%20%3D%201%0A%20%20%20%20while%20exp%20%3C%3D%20m%3A%0A%20%20%20%20%20%20%20%20%23%20%E5%AF%B9%E6%95%B0%E7%BB%84%E5%85%83%E7%B4%A0%E7%9A%84%E7%AC%AC%20k%20%E4%BD%8D%E6%89%A7%E8%A1%8C%E8%AE%A1%E6%95%B0%E6%8E%92%E5%BA%8F%0A%20%20%20%20%20%20%20%20%23%20k%20%3D%201%20-%3E%20exp%20%3D%201%0A%20%20%20%20%20%20%20%20%23%20k%20%3D%202%20-%3E%20exp%20%3D%2010%0A%20%20%20%20%20%20%20%20%23%20%E5%8D%B3%20exp%20%3D%2010%5E%28k-1%29%0A%20%20%20%20%20%20%20%20counting_sort_digit%28nums,%20exp%29%0A%20%20%20%20%20%20%20%20exp%20*%3D%2010%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%9F%BA%E6%95%B0%E6%8E%92%E5%BA%8F%0A%20%20%20%20nums%20%3D%20%5B%0A%20%20%20%20%20%20%20%20105,%0A%20%20%20%20%20%20%20%20356,%0A%20%20%20%20%20%20%20%20428,%0A%20%20%20%20%20%20%20%20348,%0A%20%20%20%20%20%20%20%20818,%0A%20%20%20%20%5D%0A%20%20%20%20radix_sort%28nums%29%0A%20%20%20%20print%28%22%E5%9F%BA%E6%95%B0%E6%8E%92%E5%BA%8F%E5%AE%8C%E6%88%90%E5%90%8E%20nums%20%3D%22,%20nums%29&codeDivHeight=472&codeDivWidth=350&cumulative=false&curInstr=6&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe></div>
|
||||
<div style="margin-top: 5px;"><a href="https://pythontutor.com/iframe-embed.html#code=def%20digit%28num%3A%20int,%20exp%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%E8%8E%B7%E5%8F%96%E5%85%83%E7%B4%A0%20num%20%E7%9A%84%E7%AC%AC%20k%20%E4%BD%8D%EF%BC%8C%E5%85%B6%E4%B8%AD%20exp%20%3D%2010%5E%28k-1%29%22%22%22%0A%20%20%20%20%23%20%E4%BC%A0%E5%85%A5%20exp%20%E8%80%8C%E9%9D%9E%20k%20%E5%8F%AF%E4%BB%A5%E9%81%BF%E5%85%8D%E5%9C%A8%E6%AD%A4%E9%87%8D%E5%A4%8D%E6%89%A7%E8%A1%8C%E6%98%82%E8%B4%B5%E7%9A%84%E6%AC%A1%E6%96%B9%E8%AE%A1%E7%AE%97%0A%20%20%20%20return%20%28num%20//%20exp%29%20%25%2010%0A%0Adef%20counting_sort_digit%28nums%3A%20list%5Bint%5D,%20exp%3A%20int%29%3A%0A%20%20%20%20%22%22%22%E8%AE%A1%E6%95%B0%E6%8E%92%E5%BA%8F%EF%BC%88%E6%A0%B9%E6%8D%AE%20nums%20%E7%AC%AC%20k%20%E4%BD%8D%E6%8E%92%E5%BA%8F%EF%BC%89%22%22%22%0A%20%20%20%20%23%20%E5%8D%81%E8%BF%9B%E5%88%B6%E7%9A%84%E4%BD%8D%E8%8C%83%E5%9B%B4%E4%B8%BA%200~9%20%EF%BC%8C%E5%9B%A0%E6%AD%A4%E9%9C%80%E8%A6%81%E9%95%BF%E5%BA%A6%E4%B8%BA%2010%20%E7%9A%84%E6%A1%B6%E6%95%B0%E7%BB%84%0A%20%20%20%20counter%20%3D%20%5B0%5D%20*%2010%0A%20%20%20%20n%20%3D%20len%28nums%29%0A%20%20%20%20%23%20%E7%BB%9F%E8%AE%A1%200~9%20%E5%90%84%E6%95%B0%E5%AD%97%E7%9A%84%E5%87%BA%E7%8E%B0%E6%AC%A1%E6%95%B0%0A%20%20%20%20for%20i%20in%20range%28n%29%3A%0A%20%20%20%20%20%20%20%20d%20%3D%20digit%28nums%5Bi%5D,%20exp%29%20%20%23%20%E8%8E%B7%E5%8F%96%20nums%5Bi%5D%20%E7%AC%AC%20k%20%E4%BD%8D%EF%BC%8C%E8%AE%B0%E4%B8%BA%20d%0A%20%20%20%20%20%20%20%20counter%5Bd%5D%20%2B%3D%201%20%20%23%20%E7%BB%9F%E8%AE%A1%E6%95%B0%E5%AD%97%20d%20%E7%9A%84%E5%87%BA%E7%8E%B0%E6%AC%A1%E6%95%B0%0A%20%20%20%20%23%20%E6%B1%82%E5%89%8D%E7%BC%80%E5%92%8C%EF%BC%8C%E5%B0%86%E2%80%9C%E5%87%BA%E7%8E%B0%E4%B8%AA%E6%95%B0%E2%80%9D%E8%BD%AC%E6%8D%A2%E4%B8%BA%E2%80%9C%E6%95%B0%E7%BB%84%E7%B4%A2%E5%BC%95%E2%80%9D%0A%20%20%20%20for%20i%20in%20range%281,%2010%29%3A%0A%20%20%20%20%20%20%20%20counter%5Bi%5D%20%2B%3D%20counter%5Bi%20-%201%5D%0A%20%20%20%20%23%20%E5%80%92%E5%BA%8F%E9%81%8D%E5%8E%86%EF%BC%8C%E6%A0%B9%E6%8D%AE%E6%A1%B6%E5%86%85%E7%BB%9F%E8%AE%A1%E7%BB%93%E6%9E%9C%EF%BC%8C%E5%B0%86%E5%90%84%E5%85%83%E7%B4%A0%E5%A1%AB%E5%85%A5%20res%0A%20%20%20%20res%20%3D%20%5B0%5D%20*%20n%0A%20%20%20%20for%20i%20in%20range%28n%20-%201,%20-1,%20-1%29%3A%0A%20%20%20%20%20%20%20%20d%20%3D%20digit%28nums%5Bi%5D,%20exp%29%0A%20%20%20%20%20%20%20%20j%20%3D%20counter%5Bd%5D%20-%201%20%20%23%20%E8%8E%B7%E5%8F%96%20d%20%E5%9C%A8%E6%95%B0%E7%BB%84%E4%B8%AD%E7%9A%84%E7%B4%A2%E5%BC%95%20j%0A%20%20%20%20%20%20%20%20res%5Bj%5D%20%3D%20nums%5Bi%5D%20%20%23%20%E5%B0%86%E5%BD%93%E5%89%8D%E5%85%83%E7%B4%A0%E5%A1%AB%E5%85%A5%E7%B4%A2%E5%BC%95%20j%0A%20%20%20%20%20%20%20%20counter%5Bd%5D%20-%3D%201%20%20%23%20%E5%B0%86%20d%20%E7%9A%84%E6%95%B0%E9%87%8F%E5%87%8F%201%0A%20%20%20%20%23%20%E4%BD%BF%E7%94%A8%E7%BB%93%E6%9E%9C%E8%A6%86%E7%9B%96%E5%8E%9F%E6%95%B0%E7%BB%84%20nums%0A%20%20%20%20for%20i%20in%20range%28n%29%3A%0A%20%20%20%20%20%20%20%20nums%5Bi%5D%20%3D%20res%5Bi%5D%0A%0Adef%20radix_sort%28nums%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22%E5%9F%BA%E6%95%B0%E6%8E%92%E5%BA%8F%22%22%22%0A%20%20%20%20%23%20%E8%8E%B7%E5%8F%96%E6%95%B0%E7%BB%84%E7%9A%84%E6%9C%80%E5%A4%A7%E5%85%83%E7%B4%A0%EF%BC%8C%E7%94%A8%E4%BA%8E%E5%88%A4%E6%96%AD%E6%9C%80%E5%A4%A7%E4%BD%8D%E6%95%B0%0A%20%20%20%20m%20%3D%20max%28nums%29%0A%20%20%20%20%23%20%E6%8C%89%E7%85%A7%E4%BB%8E%E4%BD%8E%E4%BD%8D%E5%88%B0%E9%AB%98%E4%BD%8D%E7%9A%84%E9%A1%BA%E5%BA%8F%E9%81%8D%E5%8E%86%0A%20%20%20%20exp%20%3D%201%0A%20%20%20%20while%20exp%20%3C%3D%20m%3A%0A%20%20%20%20%20%20%20%20%23%20%E5%AF%B9%E6%95%B0%E7%BB%84%E5%85%83%E7%B4%A0%E7%9A%84%E7%AC%AC%20k%20%E4%BD%8D%E6%89%A7%E8%A1%8C%E8%AE%A1%E6%95%B0%E6%8E%92%E5%BA%8F%0A%20%20%20%20%20%20%20%20%23%20k%20%3D%201%20-%3E%20exp%20%3D%201%0A%20%20%20%20%20%20%20%20%23%20k%20%3D%202%20-%3E%20exp%20%3D%2010%0A%20%20%20%20%20%20%20%20%23%20%E5%8D%B3%20exp%20%3D%2010%5E%28k-1%29%0A%20%20%20%20%20%20%20%20counting_sort_digit%28nums,%20exp%29%0A%20%20%20%20%20%20%20%20exp%20*%3D%2010%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%9F%BA%E6%95%B0%E6%8E%92%E5%BA%8F%0A%20%20%20%20nums%20%3D%20%5B%0A%20%20%20%20%20%20%20%20105,%0A%20%20%20%20%20%20%20%20356,%0A%20%20%20%20%20%20%20%20428,%0A%20%20%20%20%20%20%20%20348,%0A%20%20%20%20%20%20%20%20818,%0A%20%20%20%20%5D%0A%20%20%20%20radix_sort%28nums%29%0A%20%20%20%20print%28%22%E5%9F%BA%E6%95%B0%E6%8E%92%E5%BA%8F%E5%AE%8C%E6%88%90%E5%90%8E%20nums%20%3D%22,%20nums%29&codeDivHeight=800&codeDivWidth=600&cumulative=false&curInstr=6&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false" target="_blank" rel="noopener noreferrer">Full Screen ></a></div>
|
||||
|
||||
!!! question "Why start sorting from the least significant digit?"
|
||||
|
||||
In consecutive sorting rounds, the result of a later round will override the result of an earlier round. For example, if the result of the first round is $a < b$ and the result of the second round is $a > b$, the result of the second round will replace the first round's result. Since the significance of higher digits is greater than that of lower digits, it makes sense to sort lower digits before higher digits.
|
||||
|
||||
@@ -55,54 +55,40 @@ In the code, we use $k$ to record the smallest element within the unsorted inter
|
||||
|
||||
```python title="selection_sort.py"
|
||||
def selection_sort(nums: list[int]):
|
||||
"""选择排序"""
|
||||
"""Selection sort"""
|
||||
n = len(nums)
|
||||
# 外循环:未排序区间为 [i, n-1]
|
||||
# Outer loop: unsorted range is [i, n-1]
|
||||
for i in range(n - 1):
|
||||
# 内循环:找到未排序区间内的最小元素
|
||||
# Inner loop: find the smallest element within the unsorted range
|
||||
k = i
|
||||
for j in range(i + 1, n):
|
||||
if nums[j] < nums[k]:
|
||||
k = j # 记录最小元素的索引
|
||||
# 将该最小元素与未排序区间的首个元素交换
|
||||
k = j # Record the index of the smallest element
|
||||
# Swap the smallest element with the first element of the unsorted range
|
||||
nums[i], nums[k] = nums[k], nums[i]
|
||||
```
|
||||
|
||||
=== "C++"
|
||||
|
||||
```cpp title="selection_sort.cpp"
|
||||
/* 选择排序 */
|
||||
void selectionSort(vector<int> &nums) {
|
||||
int n = nums.size();
|
||||
// 外循环:未排序区间为 [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; // 记录最小元素的索引
|
||||
}
|
||||
// 将该最小元素与未排序区间的首个元素交换
|
||||
swap(nums[i], nums[k]);
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{selectionSort}
|
||||
```
|
||||
|
||||
=== "Java"
|
||||
|
||||
```java title="selection_sort.java"
|
||||
/* 选择排序 */
|
||||
/* Selection sort */
|
||||
void selectionSort(int[] nums) {
|
||||
int n = nums.length;
|
||||
// 外循环:未排序区间为 [i, n-1]
|
||||
// Outer loop: unsorted range is [i, n-1]
|
||||
for (int i = 0; i < n - 1; i++) {
|
||||
// 内循环:找到未排序区间内的最小元素
|
||||
// Inner loop: find the smallest element within the unsorted range
|
||||
int k = i;
|
||||
for (int j = i + 1; j < n; j++) {
|
||||
if (nums[j] < nums[k])
|
||||
k = j; // 记录最小元素的索引
|
||||
k = j; // Record the index of the smallest element
|
||||
}
|
||||
// 将该最小元素与未排序区间的首个元素交换
|
||||
// Swap the smallest element with the first element of the unsorted range
|
||||
int temp = nums[i];
|
||||
nums[i] = nums[k];
|
||||
nums[k] = temp;
|
||||
@@ -113,215 +99,61 @@ In the code, we use $k$ to record the smallest element within the unsorted inter
|
||||
=== "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]);
|
||||
}
|
||||
}
|
||||
[class]{selection_sort}-[func]{SelectionSort}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
||||
```go title="selection_sort.go"
|
||||
/* 选择排序 */
|
||||
func selectionSort(nums []int) {
|
||||
n := len(nums)
|
||||
// 外循环:未排序区间为 [i, n-1]
|
||||
for i := 0; i < n-1; i++ {
|
||||
// 内循环:找到未排序区间内的最小元素
|
||||
k := i
|
||||
for j := i + 1; j < n; j++ {
|
||||
if nums[j] < nums[k] {
|
||||
// 记录最小元素的索引
|
||||
k = j
|
||||
}
|
||||
}
|
||||
// 将该最小元素与未排序区间的首个元素交换
|
||||
nums[i], nums[k] = nums[k], nums[i]
|
||||
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{selectionSort}
|
||||
```
|
||||
|
||||
=== "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)
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{selectionSort}
|
||||
```
|
||||
|
||||
=== "JS"
|
||||
|
||||
```javascript title="selection_sort.js"
|
||||
/* 选择排序 */
|
||||
function selectionSort(nums) {
|
||||
let n = nums.length;
|
||||
// 外循环:未排序区间为 [i, n-1]
|
||||
for (let i = 0; i < n - 1; i++) {
|
||||
// 内循环:找到未排序区间内的最小元素
|
||||
let k = i;
|
||||
for (let j = i + 1; j < n; j++) {
|
||||
if (nums[j] < nums[k]) {
|
||||
k = j; // 记录最小元素的索引
|
||||
}
|
||||
}
|
||||
// 将该最小元素与未排序区间的首个元素交换
|
||||
[nums[i], nums[k]] = [nums[k], nums[i]];
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{selectionSort}
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="selection_sort.ts"
|
||||
/* 选择排序 */
|
||||
function selectionSort(nums: number[]): void {
|
||||
let n = nums.length;
|
||||
// 外循环:未排序区间为 [i, n-1]
|
||||
for (let i = 0; i < n - 1; i++) {
|
||||
// 内循环:找到未排序区间内的最小元素
|
||||
let k = i;
|
||||
for (let j = i + 1; j < n; j++) {
|
||||
if (nums[j] < nums[k]) {
|
||||
k = j; // 记录最小元素的索引
|
||||
}
|
||||
}
|
||||
// 将该最小元素与未排序区间的首个元素交换
|
||||
[nums[i], nums[k]] = [nums[k], nums[i]];
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{selectionSort}
|
||||
```
|
||||
|
||||
=== "Dart"
|
||||
|
||||
```dart title="selection_sort.dart"
|
||||
/* 选择排序 */
|
||||
void selectionSort(List<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;
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{selectionSort}
|
||||
```
|
||||
|
||||
=== "Rust"
|
||||
|
||||
```rust title="selection_sort.rs"
|
||||
/* 选择排序 */
|
||||
fn selection_sort(nums: &mut [i32]) {
|
||||
if nums.is_empty() {
|
||||
return;
|
||||
}
|
||||
let n = nums.len();
|
||||
// 外循环:未排序区间为 [i, n-1]
|
||||
for i in 0..n - 1 {
|
||||
// 内循环:找到未排序区间内的最小元素
|
||||
let mut k = i;
|
||||
for j in i + 1..n {
|
||||
if nums[j] < nums[k] {
|
||||
k = j; // 记录最小元素的索引
|
||||
}
|
||||
}
|
||||
// 将该最小元素与未排序区间的首个元素交换
|
||||
nums.swap(i, k);
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{selection_sort}
|
||||
```
|
||||
|
||||
=== "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;
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{selectionSort}
|
||||
```
|
||||
|
||||
=== "Kotlin"
|
||||
|
||||
```kotlin title="selection_sort.kt"
|
||||
/* 选择排序 */
|
||||
fun selectionSort(nums: IntArray) {
|
||||
val n = nums.size
|
||||
// 外循环:未排序区间为 [i, n-1]
|
||||
for (i in 0..<n - 1) {
|
||||
var k = i
|
||||
// 内循环:找到未排序区间内的最小元素
|
||||
for (j in i + 1..<n) {
|
||||
if (nums[j] < nums[k])
|
||||
k = j // 记录最小元素的索引
|
||||
}
|
||||
// 将该最小元素与未排序区间的首个元素交换
|
||||
val temp = nums[i]
|
||||
nums[i] = nums[k]
|
||||
nums[k] = temp
|
||||
}
|
||||
}
|
||||
[class]{}-[func]{selectionSort}
|
||||
```
|
||||
|
||||
=== "Ruby"
|
||||
|
||||
```ruby title="selection_sort.rb"
|
||||
### 选择排序 ###
|
||||
def selection_sort(nums)
|
||||
n = nums.length
|
||||
# 外循环:未排序区间为 [i, n-1]
|
||||
for i in 0...(n - 1)
|
||||
# 内循环:找到未排序区间内的最小元素
|
||||
k = i
|
||||
for j in (i + 1)...n
|
||||
if nums[j] < nums[k]
|
||||
k = j # 记录最小元素的索引
|
||||
end
|
||||
end
|
||||
# 将该最小元素与未排序区间的首个元素交换
|
||||
nums[i], nums[k] = nums[k], nums[i]
|
||||
end
|
||||
end
|
||||
[class]{}-[func]{selection_sort}
|
||||
```
|
||||
|
||||
=== "Zig"
|
||||
@@ -330,11 +162,6 @@ In the code, we use $k$ to record the smallest element within the unsorted inter
|
||||
[class]{}-[func]{selectionSort}
|
||||
```
|
||||
|
||||
??? pythontutor "Code Visualization"
|
||||
|
||||
<div style="height: 531px; width: 100%;"><iframe class="pythontutor-iframe" src="https://pythontutor.com/iframe-embed.html#code=def%20selection_sort%28nums%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22%E9%80%89%E6%8B%A9%E6%8E%92%E5%BA%8F%22%22%22%0A%20%20%20%20n%20%3D%20len%28nums%29%0A%20%20%20%20%23%20%E5%A4%96%E5%BE%AA%E7%8E%AF%EF%BC%9A%E6%9C%AA%E6%8E%92%E5%BA%8F%E5%8C%BA%E9%97%B4%E4%B8%BA%20%5Bi,%20n-1%5D%0A%20%20%20%20for%20i%20in%20range%28n%20-%201%29%3A%0A%20%20%20%20%20%20%20%20%23%20%E5%86%85%E5%BE%AA%E7%8E%AF%EF%BC%9A%E6%89%BE%E5%88%B0%E6%9C%AA%E6%8E%92%E5%BA%8F%E5%8C%BA%E9%97%B4%E5%86%85%E7%9A%84%E6%9C%80%E5%B0%8F%E5%85%83%E7%B4%A0%0A%20%20%20%20%20%20%20%20k%20%3D%20i%0A%20%20%20%20%20%20%20%20for%20j%20in%20range%28i%20%2B%201,%20n%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20nums%5Bj%5D%20%3C%20nums%5Bk%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20k%20%3D%20j%20%20%23%20%E8%AE%B0%E5%BD%95%E6%9C%80%E5%B0%8F%E5%85%83%E7%B4%A0%E7%9A%84%E7%B4%A2%E5%BC%95%0A%20%20%20%20%20%20%20%20%23%20%E5%B0%86%E8%AF%A5%E6%9C%80%E5%B0%8F%E5%85%83%E7%B4%A0%E4%B8%8E%E6%9C%AA%E6%8E%92%E5%BA%8F%E5%8C%BA%E9%97%B4%E7%9A%84%E9%A6%96%E4%B8%AA%E5%85%83%E7%B4%A0%E4%BA%A4%E6%8D%A2%0A%20%20%20%20%20%20%20%20nums%5Bi%5D,%20nums%5Bk%5D%20%3D%20nums%5Bk%5D,%20nums%5Bi%5D%0A%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B4,%201,%203,%201,%205,%202%5D%0A%20%20%20%20selection_sort%28nums%29%0A%20%20%20%20print%28%22%E9%80%89%E6%8B%A9%E6%8E%92%E5%BA%8F%E5%AE%8C%E6%88%90%E5%90%8E%20nums%20%3D%22,%20nums%29&codeDivHeight=472&codeDivWidth=350&cumulative=false&curInstr=4&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe></div>
|
||||
<div style="margin-top: 5px;"><a href="https://pythontutor.com/iframe-embed.html#code=def%20selection_sort%28nums%3A%20list%5Bint%5D%29%3A%0A%20%20%20%20%22%22%22%E9%80%89%E6%8B%A9%E6%8E%92%E5%BA%8F%22%22%22%0A%20%20%20%20n%20%3D%20len%28nums%29%0A%20%20%20%20%23%20%E5%A4%96%E5%BE%AA%E7%8E%AF%EF%BC%9A%E6%9C%AA%E6%8E%92%E5%BA%8F%E5%8C%BA%E9%97%B4%E4%B8%BA%20%5Bi,%20n-1%5D%0A%20%20%20%20for%20i%20in%20range%28n%20-%201%29%3A%0A%20%20%20%20%20%20%20%20%23%20%E5%86%85%E5%BE%AA%E7%8E%AF%EF%BC%9A%E6%89%BE%E5%88%B0%E6%9C%AA%E6%8E%92%E5%BA%8F%E5%8C%BA%E9%97%B4%E5%86%85%E7%9A%84%E6%9C%80%E5%B0%8F%E5%85%83%E7%B4%A0%0A%20%20%20%20%20%20%20%20k%20%3D%20i%0A%20%20%20%20%20%20%20%20for%20j%20in%20range%28i%20%2B%201,%20n%29%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20nums%5Bj%5D%20%3C%20nums%5Bk%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20k%20%3D%20j%20%20%23%20%E8%AE%B0%E5%BD%95%E6%9C%80%E5%B0%8F%E5%85%83%E7%B4%A0%E7%9A%84%E7%B4%A2%E5%BC%95%0A%20%20%20%20%20%20%20%20%23%20%E5%B0%86%E8%AF%A5%E6%9C%80%E5%B0%8F%E5%85%83%E7%B4%A0%E4%B8%8E%E6%9C%AA%E6%8E%92%E5%BA%8F%E5%8C%BA%E9%97%B4%E7%9A%84%E9%A6%96%E4%B8%AA%E5%85%83%E7%B4%A0%E4%BA%A4%E6%8D%A2%0A%20%20%20%20%20%20%20%20nums%5Bi%5D,%20nums%5Bk%5D%20%3D%20nums%5Bk%5D,%20nums%5Bi%5D%0A%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20nums%20%3D%20%5B4,%201,%203,%201,%205,%202%5D%0A%20%20%20%20selection_sort%28nums%29%0A%20%20%20%20print%28%22%E9%80%89%E6%8B%A9%E6%8E%92%E5%BA%8F%E5%AE%8C%E6%88%90%E5%90%8E%20nums%20%3D%22,%20nums%29&codeDivHeight=800&codeDivWidth=600&cumulative=false&curInstr=4&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false" target="_blank" rel="noopener noreferrer">Full Screen ></a></div>
|
||||
|
||||
## 11.2.1 Algorithm characteristics
|
||||
|
||||
- **Time complexity of $O(n^2)$, non-adaptive sort**: There are $n - 1$ rounds in the outer loop, with the unsorted interval length starting at $n$ in the first round and decreasing to $2$ in the last round, i.e., the outer loops contain $n$, $n - 1$, $\dots$, $3$, $2$ inner loops respectively, summing up to $\frac{(n - 1)(n + 2)}{2}$.
|
||||
|
||||
Reference in New Issue
Block a user