今天要解的題目是 Sort Colors(排序顏色)。題目給我們一個陣列 nums
,這個陣列裡包含 0、1 和 2,分別代表紅色、白色和藍色。現在要求我們將這些顏色按照紅、白、藍的順序排序。
這題的限制是不能使用內建的排序函數,因此我們需要想辦法在 O(n) 的時間內完成這個排序操作。而這個問題的經典解法是「三路快排」,也稱為荷蘭國旗問題。接下來我們來看看具體的解法吧!
難度:Medium
題目描述:
Given an array nums
with n
objects colored red, white, or blue, sort them in-place so that objects of the same color are adjacent, with the colors in the order red, white, and blue.
We will use the integers 0
, 1
, and 2
to represent the color red, white, and blue, respectively.
You must solve this problem without using the library's sort function.
給定一個只包含 0
(紅色)、1
(白色)、2
(藍色)的陣列,要求將這些顏色按照紅、白、藍的順序進行排序,並且要求原地排序,即不使用額外的空間。
範例 1:
輸入: nums = [2, 0, 2, 1, 1, 0]
輸出: [0, 0, 1, 1, 2, 2]
範例 2:
輸入: nums = [2, 0, 1]
輸出: [0, 1, 2]
這道題的核心解法是使用三路快排(Three-way partitioning)來將陣列中的數字分為三個部分:0
的部分、1
的部分和 2
的部分。我們用三個指針來實現這個操作,並通過掃描一次陣列,完成排序。
具體思路:
三個指針:
left
指向應該放 0
的位置。right
指向應該放 2
的位置。i
用來遍歷陣列,掃描整個陣列。操作邏輯:
nums[i]
是 0
時,將 nums[i]
和 nums[left]
交換,然後 i
和 left
同時向右移動。nums[i]
是 2
時,將 nums[i]
和 nums[right]
交換,然後 right
向左移動,但 i
位置暫時不動,因為交換後 i
位置的數字還需要進行處理。nums[i]
是 1
時,只需要將 i
向右移動。停止條件:
i
超過 right
時,整個排序結束。function sortColors(nums: number[]): void {
let left = 0; // 指向放 0 的位置
let right = nums.length - 1; // 指向放 2 的位置
let i = 0; // 用來遍歷陣列
while (i <= right) {
if (nums[i] === 0) {
// 當遇到 0,將其放到 left 位置
[nums[i], nums[left]] = [nums[left], nums[i]]; // 交換
left++; // left 向右移動
i++; // i 向右移動
} else if (nums[i] === 2) {
// 當遇到 2,將其放到 right 位置
[nums[i], nums[right]] = [nums[right], nums[i]]; // 交換
right--; // right 向左移動
// i 不移動,因為交換過來的元素還需要再檢查
} else {
// 當遇到 1,只需要跳過
i++;
}
}
}
三路快排的概念:
left
指向 0
的區域,right
指向 2
的區域,而中間的區域則由 1
組成。交換的操作:
0
時,將其與 left
位置的元素交換,這樣 left
指針向右移動,0
就被放到正確的位置。2
時,將其與 right
位置的元素交換,然後將 right
指針向左移動,2
被放到正確的位置。1
時,因為它本身在中間區域,不需要交換,我們只需要繼續移動 i
。注意 i
和 right
的關係:
nums[i]
和 nums[right]
時,i
不會立即移動,因為交換過來的數字可能還需要進一步處理。這種處理方式利用了三路快排的思想,將問題分為三個區域處理,能夠在 O(n) 的時間內完成排序,同時只使用常數級別的額外空間。這是一種非常高效的解法,能夠保證我們不需要再進行額外的掃描或排序操作。
0
、1
和 2
。這道題是否讓你感受到三路快排的神奇呢?
學會這個技巧後,對於類似需要分區的問題,你就可以輕鬆應對了!
期待下次我們一起挑戰更多有趣的題目吧!💪🌟