LeetCode—75—Sort Colors(三路快排思想應用)
題目
Given an array 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.
Here, we will use the integers 0, 1, and 2 to represent the color red, white, and blue respectively.
Note: You are not suppose to use the library’s sort function for this problem.
Example:
Input: [2,0,2,1,1,0]
Output: [0,0,1,1,2,2]
翻譯
給定一個數組,包含n個元素,n個元素被隨機標記了三種顏色的一種,紅色,白色,藍色。現在原地排序,使相同顏色的元素相鄰,按照紅色,白色,藍色的順序排列。在這裡我們分別用0,1,2。代表三種顏色。
###解題思路
通過題意可知,這個陣列有其特殊性,特點就是陣列只包含三種元素。所以首先提供一種暴力的解法。就是分別統計每個元素在陣列中出現了多少次。然後根據元素在陣列中出現的次數,將陣列重新賦值。
程式碼如下:
// 75. Sort Colors
// https://leetcode.com/problems/sort-colors/description/
//
// 計數排序的思路
// 對整個陣列遍歷了兩遍,第一遍統計各個元素個數,第二遍將陣列重新賦值
// 時間複雜度: O(n)
// 空間複雜度: O(k), k為元素的取值範圍
public class Solution1 {
public void sortColors(int[] nums) {
int[] count = {0, 0, 0}; // 存放0, 1, 2三個元素的頻率
for(int i = 0 ; i < nums.length ; i ++){
assert nums[i] >= 0 && nums[ i] <= 2;
count[nums[i]] ++;
}
int index = 0;
for(int i = 0 ; i < count[0] ; i ++)
nums[index++] = 0;
for(int i = 0 ; i < count[1] ; i ++)
nums[index++] = 1;
for(int i = 0 ; i < count[2] ; i ++)
nums[index++] = 2;
}
}
優化解法
在這裡我們可以採用三路快排的思想,什麼是三路快排呢,三路快排就是在陣列中選擇一個標定點,然後以這個標定點為依據,將陣列分為三部分,小於標定點的資料,等於標定點的資料,和大於標定點的資料。然後在小於標定點和大於標定點的區域繼續採用三路快排的思想排序,就是一個遞迴呼叫的過程。對於本題,因為只有三種元素,所以一次呼叫就完成了整個排序過程。我們先來看幾張圖。
在這裡我們新定義兩個索引zero,two。當我們遍歷陣列時,將0元素放到[0…zero]區間,將為2的元素放到區間[zero…n-1]中。
由上圖可知,此時我們遍歷到第i個元素。第i個元素值為2,我們就將第i個元素的值放到two-1的位置。同時將two-1位置的元素放到i的位置。就是交換這兩個位置的元素。因為找出為2的元素增加了一個,所以two要減一。而此時i是不變的,因為交換過來的資料是啥仍然是未知的。同理可以分析出當第i個元素如果是0和1要應該怎麼處理。具體情況請看程式碼。
// 75. Sort Colors
// https://leetcode.com/problems/sort-colors/description/
//
// 三路快速排序的思想
// 對整個陣列只遍歷了一遍
// 時間複雜度: O(n)
// 空間複雜度: O(1)
public class Solution2 {
public void sortColors(int[] nums) {
// [0...zero] == 0,初始情況zero為-1,此時區間無效。
//如果初始值為1,那就是說區間中第一個元素是0,
//而第一個元素未知,所以這裡初始值設為-1;
int zero = -1;
int two = nums.length; // [two...n-1] == 2,同理初始區間是無效區間
for(int i = 0 ; i < two ; ){
if(nums[i] == 1)
i ++;
else if (nums[i] == 2)
swap(nums, i, --two); //two先自減
else{ // nums[i] == 0
assert nums[i] == 0;
swap(nums, ++zero, i++); //zero先自增
}
}
}
private void swap(int[] nums, int i, int j){
int t = nums[i];
nums[i]= nums[j];
nums[j] = t;
}
}
更多內容歡迎大家關注