This commit is contained in:
krahets
2025-07-10 07:14:11 +08:00
parent 718e8d4a1c
commit 85ebada8d9
29 changed files with 185 additions and 168 deletions
+15 -15
View File
@@ -1161,7 +1161,7 @@ comments: true
<div style="height: 549px; width: 100%;"><iframe class="pythontutor-iframe" src="https://pythontutor.com/iframe-embed.html#code=def%20median_three%28nums%3A%20list%5Bint%5D%2C%20left%3A%20int%2C%20mid%3A%20int%2C%20right%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%E9%81%B8%E5%8F%96%E4%B8%89%E5%80%8B%E5%80%99%E9%81%B8%E5%85%83%E7%B4%A0%E7%9A%84%E4%B8%AD%E4%BD%8D%E6%95%B8%22%22%22%0A%20%20%20%20l%2C%20m%2C%20r%20%3D%20nums%5Bleft%5D%2C%20nums%5Bmid%5D%2C%20nums%5Bright%5D%0A%20%20%20%20if%20%28l%20%3C%3D%20m%20%3C%3D%20r%29%20or%20%28r%20%3C%3D%20m%20%3C%3D%20l%29%3A%0A%20%20%20%20%20%20%20%20return%20mid%20%20%23%20m%20%E5%9C%A8%20l%20%E5%92%8C%20r%20%E4%B9%8B%E9%96%93%0A%20%20%20%20if%20%28m%20%3C%3D%20l%20%3C%3D%20r%29%20or%20%28r%20%3C%3D%20l%20%3C%3D%20m%29%3A%0A%20%20%20%20%20%20%20%20return%20left%20%20%23%20l%20%E5%9C%A8%20m%20%E5%92%8C%20r%20%E4%B9%8B%E9%96%93%0A%20%20%20%20return%20right%0A%0Adef%20partition%28nums%3A%20list%5Bint%5D%2C%20left%3A%20int%2C%20right%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%E5%93%A8%E5%85%B5%E5%8A%83%E5%88%86%EF%BC%88%E4%B8%89%E6%95%B8%E5%8F%96%E4%B8%AD%E5%80%BC%EF%BC%89%22%22%22%0A%20%20%20%20%23%20%E4%BB%A5%20nums%5Bleft%5D%20%E7%82%BA%E5%9F%BA%E6%BA%96%E6%95%B8%0A%20%20%20%20med%20%3D%20median_three%28nums%2C%20left%2C%20%28left%20%2B%20right%29%20//%202%2C%20right%29%0A%20%20%20%20%23%20%E5%B0%87%E4%B8%AD%E4%BD%8D%E6%95%B8%E4%BA%A4%E6%8F%9B%E8%87%B3%E9%99%A3%E5%88%97%E6%9C%80%E5%B7%A6%E7%AB%AF%0A%20%20%20%20nums%5Bleft%5D%2C%20nums%5Bmed%5D%20%3D%20nums%5Bmed%5D%2C%20nums%5Bleft%5D%0A%20%20%20%20%23%20%E4%BB%A5%20nums%5Bleft%5D%20%E7%82%BA%E5%9F%BA%E6%BA%96%E6%95%B8%0A%20%20%20%20i%2C%20j%20%3D%20left%2C%20right%0A%20%20%20%20while%20i%20%3C%20j%3A%0A%20%20%20%20%20%20%20%20while%20i%20%3C%20j%20and%20nums%5Bj%5D%20%3E%3D%20nums%5Bleft%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20j%20-%3D%201%20%20%23%20%E5%BE%9E%E5%8F%B3%E5%90%91%E5%B7%A6%E6%89%BE%E9%A6%96%E5%80%8B%E5%B0%8F%E6%96%BC%E5%9F%BA%E6%BA%96%E6%95%B8%E7%9A%84%E5%85%83%E7%B4%A0%0A%20%20%20%20%20%20%20%20while%20i%20%3C%20j%20and%20nums%5Bi%5D%20%3C%3D%20nums%5Bleft%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%2B%3D%201%20%20%23%20%E5%BE%9E%E5%B7%A6%E5%90%91%E5%8F%B3%E6%89%BE%E9%A6%96%E5%80%8B%E5%A4%A7%E6%96%BC%E5%9F%BA%E6%BA%96%E6%95%B8%E7%9A%84%E5%85%83%E7%B4%A0%0A%20%20%20%20%20%20%20%20%23%20%E5%85%83%E7%B4%A0%E4%BA%A4%E6%8F%9B%0A%20%20%20%20%20%20%20%20nums%5Bi%5D%2C%20nums%5Bj%5D%20%3D%20nums%5Bj%5D%2C%20nums%5Bi%5D%0A%20%20%20%20%23%20%E5%B0%87%E5%9F%BA%E6%BA%96%E6%95%B8%E4%BA%A4%E6%8F%9B%E8%87%B3%E5%85%A9%E5%AD%90%E9%99%A3%E5%88%97%E7%9A%84%E5%88%86%E7%95%8C%E7%B7%9A%0A%20%20%20%20nums%5Bi%5D%2C%20nums%5Bleft%5D%20%3D%20nums%5Bleft%5D%2C%20nums%5Bi%5D%0A%20%20%20%20return%20i%20%20%23%20%E8%BF%94%E5%9B%9E%E5%9F%BA%E6%BA%96%E6%95%B8%E7%9A%84%E7%B4%A2%E5%BC%95%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%20%23%20%E4%B8%AD%E4%BD%8D%E5%9F%BA%E6%BA%96%E6%95%B8%E6%9C%80%E4%BD%B3%E5%8C%96%0A%20%20%20%20nums%20%3D%20%5B2%2C%204%2C%201%2C%200%2C%203%2C%205%5D%0A%20%20%20%20partition%28nums%2C%200%2C%20len%28nums%29%20-%201%29%0A%20%20%20%20print%28%22%E5%93%A8%E5%85%B5%E5%8A%83%E5%88%86%EF%BC%88%E4%B8%AD%E4%BD%8D%E5%9F%BA%E6%BA%96%E6%95%B8%E6%9C%80%E4%BD%B3%E5%8C%96%EF%BC%89%E5%AE%8C%E6%88%90%E5%BE%8C%20nums%20%3D%22%2C%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%20median_three%28nums%3A%20list%5Bint%5D%2C%20left%3A%20int%2C%20mid%3A%20int%2C%20right%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%E9%81%B8%E5%8F%96%E4%B8%89%E5%80%8B%E5%80%99%E9%81%B8%E5%85%83%E7%B4%A0%E7%9A%84%E4%B8%AD%E4%BD%8D%E6%95%B8%22%22%22%0A%20%20%20%20l%2C%20m%2C%20r%20%3D%20nums%5Bleft%5D%2C%20nums%5Bmid%5D%2C%20nums%5Bright%5D%0A%20%20%20%20if%20%28l%20%3C%3D%20m%20%3C%3D%20r%29%20or%20%28r%20%3C%3D%20m%20%3C%3D%20l%29%3A%0A%20%20%20%20%20%20%20%20return%20mid%20%20%23%20m%20%E5%9C%A8%20l%20%E5%92%8C%20r%20%E4%B9%8B%E9%96%93%0A%20%20%20%20if%20%28m%20%3C%3D%20l%20%3C%3D%20r%29%20or%20%28r%20%3C%3D%20l%20%3C%3D%20m%29%3A%0A%20%20%20%20%20%20%20%20return%20left%20%20%23%20l%20%E5%9C%A8%20m%20%E5%92%8C%20r%20%E4%B9%8B%E9%96%93%0A%20%20%20%20return%20right%0A%0Adef%20partition%28nums%3A%20list%5Bint%5D%2C%20left%3A%20int%2C%20right%3A%20int%29%20-%3E%20int%3A%0A%20%20%20%20%22%22%22%E5%93%A8%E5%85%B5%E5%8A%83%E5%88%86%EF%BC%88%E4%B8%89%E6%95%B8%E5%8F%96%E4%B8%AD%E5%80%BC%EF%BC%89%22%22%22%0A%20%20%20%20%23%20%E4%BB%A5%20nums%5Bleft%5D%20%E7%82%BA%E5%9F%BA%E6%BA%96%E6%95%B8%0A%20%20%20%20med%20%3D%20median_three%28nums%2C%20left%2C%20%28left%20%2B%20right%29%20//%202%2C%20right%29%0A%20%20%20%20%23%20%E5%B0%87%E4%B8%AD%E4%BD%8D%E6%95%B8%E4%BA%A4%E6%8F%9B%E8%87%B3%E9%99%A3%E5%88%97%E6%9C%80%E5%B7%A6%E7%AB%AF%0A%20%20%20%20nums%5Bleft%5D%2C%20nums%5Bmed%5D%20%3D%20nums%5Bmed%5D%2C%20nums%5Bleft%5D%0A%20%20%20%20%23%20%E4%BB%A5%20nums%5Bleft%5D%20%E7%82%BA%E5%9F%BA%E6%BA%96%E6%95%B8%0A%20%20%20%20i%2C%20j%20%3D%20left%2C%20right%0A%20%20%20%20while%20i%20%3C%20j%3A%0A%20%20%20%20%20%20%20%20while%20i%20%3C%20j%20and%20nums%5Bj%5D%20%3E%3D%20nums%5Bleft%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20j%20-%3D%201%20%20%23%20%E5%BE%9E%E5%8F%B3%E5%90%91%E5%B7%A6%E6%89%BE%E9%A6%96%E5%80%8B%E5%B0%8F%E6%96%BC%E5%9F%BA%E6%BA%96%E6%95%B8%E7%9A%84%E5%85%83%E7%B4%A0%0A%20%20%20%20%20%20%20%20while%20i%20%3C%20j%20and%20nums%5Bi%5D%20%3C%3D%20nums%5Bleft%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20i%20%2B%3D%201%20%20%23%20%E5%BE%9E%E5%B7%A6%E5%90%91%E5%8F%B3%E6%89%BE%E9%A6%96%E5%80%8B%E5%A4%A7%E6%96%BC%E5%9F%BA%E6%BA%96%E6%95%B8%E7%9A%84%E5%85%83%E7%B4%A0%0A%20%20%20%20%20%20%20%20%23%20%E5%85%83%E7%B4%A0%E4%BA%A4%E6%8F%9B%0A%20%20%20%20%20%20%20%20nums%5Bi%5D%2C%20nums%5Bj%5D%20%3D%20nums%5Bj%5D%2C%20nums%5Bi%5D%0A%20%20%20%20%23%20%E5%B0%87%E5%9F%BA%E6%BA%96%E6%95%B8%E4%BA%A4%E6%8F%9B%E8%87%B3%E5%85%A9%E5%AD%90%E9%99%A3%E5%88%97%E7%9A%84%E5%88%86%E7%95%8C%E7%B7%9A%0A%20%20%20%20nums%5Bi%5D%2C%20nums%5Bleft%5D%20%3D%20nums%5Bleft%5D%2C%20nums%5Bi%5D%0A%20%20%20%20return%20i%20%20%23%20%E8%BF%94%E5%9B%9E%E5%9F%BA%E6%BA%96%E6%95%B8%E7%9A%84%E7%B4%A2%E5%BC%95%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%20%23%20%E4%B8%AD%E4%BD%8D%E5%9F%BA%E6%BA%96%E6%95%B8%E6%9C%80%E4%BD%B3%E5%8C%96%0A%20%20%20%20nums%20%3D%20%5B2%2C%204%2C%201%2C%200%2C%203%2C%205%5D%0A%20%20%20%20partition%28nums%2C%200%2C%20len%28nums%29%20-%201%29%0A%20%20%20%20print%28%22%E5%93%A8%E5%85%B5%E5%8A%83%E5%88%86%EF%BC%88%E4%B8%AD%E4%BD%8D%E5%9F%BA%E6%BA%96%E6%95%B8%E6%9C%80%E4%BD%B3%E5%8C%96%EF%BC%89%E5%AE%8C%E6%88%90%E5%BE%8C%20nums%20%3D%22%2C%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">全螢幕觀看 ></a></div>
## 11.5.5 &nbsp; 遞迴最佳化
## 11.5.5 &nbsp; 遞迴深度最佳化
**在某些輸入下,快速排序可能佔用空間較多**。以完全有序的輸入陣列為例,設遞迴中的子陣列長度為 $m$ ,每輪哨兵劃分操作都將產生長度為 $0$ 的左子陣列和長度為 $m - 1$ 的右子陣列,這意味著每一層遞迴呼叫減少的問題規模非常小(只減少一個元素),遞迴樹的高度會達到 $n - 1$ ,此時需要佔用 $O(n)$ 大小的堆疊幀空間。
@@ -1171,7 +1171,7 @@ comments: true
```python title="quick_sort.py"
def quick_sort(self, nums: list[int], left: int, right: int):
"""快速排序(遞迴最佳化)"""
"""快速排序(遞迴深度最佳化)"""
# 子陣列長度為 1 時終止
while left < right:
# 哨兵劃分操作
@@ -1188,7 +1188,7 @@ comments: true
=== "C++"
```cpp title="quick_sort.cpp"
/* 快速排序(遞迴最佳化) */
/* 快速排序(遞迴深度最佳化) */
void quickSort(vector<int> &nums, int left, int right) {
// 子陣列長度為 1 時終止
while (left < right) {
@@ -1209,7 +1209,7 @@ comments: true
=== "Java"
```java title="quick_sort.java"
/* 快速排序(遞迴最佳化) */
/* 快速排序(遞迴深度最佳化) */
void quickSort(int[] nums, int left, int right) {
// 子陣列長度為 1 時終止
while (left < right) {
@@ -1230,7 +1230,7 @@ comments: true
=== "C#"
```csharp title="quick_sort.cs"
/* 快速排序(遞迴最佳化) */
/* 快速排序(遞迴深度最佳化) */
void QuickSort(int[] nums, int left, int right) {
// 子陣列長度為 1 時終止
while (left < right) {
@@ -1251,7 +1251,7 @@ comments: true
=== "Go"
```go title="quick_sort.go"
/* 快速排序(遞迴最佳化)*/
/* 快速排序(遞迴深度最佳化)*/
func (q *quickSortTailCall) quickSort(nums []int, left, right int) {
// 子陣列長度為 1 時終止
for left < right {
@@ -1272,7 +1272,7 @@ comments: true
=== "Swift"
```swift title="quick_sort.swift"
/* 快速排序(遞迴最佳化) */
/* 快速排序(遞迴深度最佳化) */
func quickSortTailCall(nums: inout [Int], left: Int, right: Int) {
var left = left
var right = right
@@ -1295,7 +1295,7 @@ comments: true
=== "JS"
```javascript title="quick_sort.js"
/* 快速排序(遞迴最佳化) */
/* 快速排序(遞迴深度最佳化) */
quickSort(nums, left, right) {
// 子陣列長度為 1 時終止
while (left < right) {
@@ -1316,7 +1316,7 @@ comments: true
=== "TS"
```typescript title="quick_sort.ts"
/* 快速排序(遞迴最佳化) */
/* 快速排序(遞迴深度最佳化) */
quickSort(nums: number[], left: number, right: number): void {
// 子陣列長度為 1 時終止
while (left < right) {
@@ -1337,7 +1337,7 @@ comments: true
=== "Dart"
```dart title="quick_sort.dart"
/* 快速排序(遞迴最佳化) */
/* 快速排序(遞迴深度最佳化) */
void quickSort(List<int> nums, int left, int right) {
// 子陣列長度為 1 時終止
while (left < right) {
@@ -1358,7 +1358,7 @@ comments: true
=== "Rust"
```rust title="quick_sort.rs"
/* 快速排序(遞迴最佳化) */
/* 快速排序(遞迴深度最佳化) */
pub fn quick_sort(mut left: i32, mut right: i32, nums: &mut [i32]) {
// 子陣列長度為 1 時終止
while left < right {
@@ -1379,7 +1379,7 @@ comments: true
=== "C"
```c title="quick_sort.c"
/* 快速排序(遞迴最佳化) */
/* 快速排序(遞迴深度最佳化) */
void quickSortTailCall(int nums[], int left, int right) {
// 子陣列長度為 1 時終止
while (left < right) {
@@ -1404,7 +1404,7 @@ comments: true
=== "Kotlin"
```kotlin title="quick_sort.kt"
/* 快速排序(遞迴最佳化) */
/* 快速排序(遞迴深度最佳化) */
fun quickSortTailCall(nums: IntArray, left: Int, right: Int) {
// 子陣列長度為 1 時終止
var l = left
@@ -1427,7 +1427,7 @@ comments: true
=== "Ruby"
```ruby title="quick_sort.rb"
### 快速排序(遞迴最佳化)###
### 快速排序(遞迴深度最佳化)###
def quick_sort(nums, left, right)
# 子陣列長度不為 1 時遞迴
while left < right
@@ -1448,7 +1448,7 @@ comments: true
=== "Zig"
```zig title="quick_sort.zig"
// 快速排序(遞迴最佳化)
// 快速排序(遞迴深度最佳化)
fn quickSort(nums: []i32, left_: usize, right_: usize) void {
var left = left_;
var right = right_;
+4 -4
View File
@@ -8,7 +8,7 @@ comments: true
- 泡沫排序透過交換相鄰元素來實現排序。透過新增一個標誌位來實現提前返回,我們可以將泡沫排序的最佳時間複雜度最佳化到 $O(n)$ 。
- 插入排序每輪將未排序區間內的元素插入到已排序區間的正確位置,從而完成排序。雖然插入排序的時間複雜度為 $O(n^2)$ ,但由於單元操作相對較少,因此在小資料量的排序任務中非常受歡迎。
- 快速排序基於哨兵劃分操作實現排序。在哨兵劃分中,有可能每次都選取到最差的基準數,導致時間複雜度劣化至 $O(n^2)$ 。引入中位數基準數或隨機基準數可以降低這種劣化的機率。尾遞迴方法可以有效地減少遞迴深度,將空間複雜度最佳化到 $O(\log n)$ 。
- 快速排序基於哨兵劃分操作實現排序。在哨兵劃分中,有可能每次都選取到最差的基準數,導致時間複雜度劣化至 $O(n^2)$ 。引入中位數基準數或隨機基準數可以降低這種劣化的機率。透過優先遞迴較短子區間,可有效減小遞迴深度,將空間複雜度最佳化到 $O(\log n)$ 。
- 合併排序包括劃分和合並兩個階段,典型地體現了分治策略。在合併排序中,排序陣列需要建立輔助陣列,空間複雜度為 $O(n)$ ;然而排序鏈結串列的空間複雜度可以最佳化至 $O(1)$ 。
- 桶排序包含三個步驟:資料分桶、桶內排序和合並結果。它同樣體現了分治策略,適用於資料體量很大的情況。桶排序的關鍵在於對資料進行平均分配。
- 計數排序是桶排序的一個特例,它透過統計資料出現的次數來實現排序。計數排序適用於資料量大但資料範圍有限的情況,並且要求資料能夠轉換為正整數。
@@ -38,11 +38,11 @@ comments: true
再深入思考一下,如果我們選擇 `nums[right]` 為基準數,那麼正好反過來,必須先“從左往右查詢”。
**Q**:關於尾遞迴最佳化,為什麼選短的陣列能保證遞迴深度不超過 $\log n$ ?
**Q**:關於快速排序的遞迴深度最佳化,為什麼選短的陣列能保證遞迴深度不超過 $\log n$ ?
遞迴深度就是當前未返回的遞迴方法的數量。每輪哨兵劃分我們將原陣列劃分為兩個子陣列。在遞迴最佳化後,向下遞迴的子陣列長度最大為原陣列長度的一半。假設最差情況,一直為一半長度,那麼最終的遞迴深度就是 $\log n$ 。
遞迴深度就是當前未返回的遞迴方法的數量。每輪哨兵劃分我們將原陣列劃分為兩個子陣列。在遞迴深度最佳化後,向下遞迴的子陣列長度最大為原陣列長度的一半。假設最差情況,一直為一半長度,那麼最終的遞迴深度就是 $\log n$ 。
回顧原始的快速排序,我們有可能會連續地遞迴長度較大的陣列,最差情況下為 $n$、$n - 1$、$\dots$、$2$、$1$ ,遞迴深度為 $n$ 。遞迴最佳化可以避免這種情況出現。
回顧原始的快速排序,我們有可能會連續地遞迴長度較大的陣列,最差情況下為 $n$、$n - 1$、$\dots$、$2$、$1$ ,遞迴深度為 $n$ 。遞迴深度最佳化可以避免這種情況出現。
**Q**:當陣列中所有元素都相等時,快速排序的時間複雜度是 $O(n^2)$ 嗎?該如何處理這種退化情況?