【LeetCode/LintCode】 題解丨谷歌高頻題:兩個排序陣列的中位數排顏色 II
給定一個有n個物件(包括k種不同的顏色,並按照1到k進行編號)的陣列,將物件進行分類使相同顏色的物件相鄰,並按照1,2,...k的順序進行排序。
- 不能使用程式碼庫中的排序函式來解決這個問題
- k <= n
線上評測地址:LintCode 領釦
樣例1
輸入:
[3,2,2,1,4]
4
輸出:
[1,2,2,3,4]
樣例2
輸入:
[2,1,1,2,2]
2
輸出:
[1,1,2,2,2]
演算法一:分治法
運使用rainbowSort,或者說是改動過的quickSort,運用的是分治的思想,不斷將當前需要處理的序列分成兩個更小的序列處理。
演算法思路
- 思路與quickSort大致相同,每次選定一箇中間的顏色,這個中間的顏色用給出的k來決定,將小於等於中間的顏色的就放到左邊,大於中間顏色的就放到右邊,然後分別再遞迴左右兩半。
程式碼思路
- 遞迴函式設定四個引數,序列需要處理區間的左右端點和處理的顏色區間
- 根據給定的顏色區間決定中間的顏色
- 將小於等於中間的顏色的就放到左邊,大於中間顏色的就放到右邊
- 遞迴左右兩半,直到顏色區間長度等於1
複雜度分析
N為序列長度,K為顏色數
- 空間複雜度:O(1)
- 時間複雜度:O(NlogK)
- 每次是對K分成左右進行遞迴,因此有logK層,每層遞迴遍歷到整個序列,長度為N
public class Solution { /* * @param colors: A list of integer * @param k: An integer * @return: nothing */ public void sortColors2(int[] colors, int k) { if (colors == null || colors.length < 2) { return; } sort(colors, 0, colors.length - 1, 1, k); } private void sort(int[] colors, int start, int end, int colorFrom, int colorTo) { //若處理區間長度為小於等於1或顏色區間長度為1,則不需要再進行處理 if (start >= end || colorFrom == colorTo) { return; } //設定左右指標以及中間的顏色 int left = start; int right = end; int colorMid = colorFrom + (colorTo - colorFrom) / 2; while (left <= right) { //找到左側大於中間顏色的位置 while (left <= right && colors[left] <= colorMid) { left++; } //找到右側小於等於中間顏色的位置 while (left <= right && colors[right] > colorMid) { right--; } //交換左右指標指向的顏色 if (left <= right) { int temp = colors[left]; colors[left] = colors[right]; colors[right] = temp; } } //繼續遞迴處理左右兩半序列 sort(colors, start, right, colorFrom, colorMid); sort(colors, left, end, colorMid + 1, colorTo); } }
演算法二:計數排序(counting sort)
- 題目要求不使用額外的陣列,一種方法是使用彩虹排序(rainbow sort),但是這樣雖然做到了沒有使用額外的空間,但是代價是時間複雜度變成了O(N logK),那麼是否有方法做到時間和空間的雙贏呢?
- 我們重新考慮計數排序(counting sort),這裡我們需要注意到顏色肯定是1-k,那麼k一定小於n,我們是否可以用colors自己本身這個陣列作為count陣列呢?
- 下面我們介紹一種不佔用大量額外空間的計數排序的寫法。
演算法思路
- 我們用負數代表數字出現的次數,例如colors[i]=-cnt表示數字i出現了cnt次
程式碼思路
- 我們從左往右遍歷colors陣列
- 若colors[i] > 0且colors[colors[i]] < 0,那麼colors[colors[i]] -= 1
- 若colors[i] > 0且colors[colors[i]] > 0,那麼先用臨時變數temp存下colors[i],將colors[colors[i]]賦值給colors[i],再將colors[temp] = -1
> 注意此時i指標並不需要指向下一個位置,因為交換過來的值還未進行計數
- 若colors[i] < 0,跳過
- 倒著輸出每種顏色
- 另外注意陣列下標是從0開始,為了避免n==k導致陣列越界的情況,本題中colors[i]對應的計數位為colors[colors[i] - 1]
複雜度分析
NN表示colors陣列長度
- 空間複雜度:O(1)
- 時間複雜度:O(N)
public class Solution {
/**
* @param colors: A list of integer
* @param k: An integer
* @return: nothing
*/
public void sortColors2(int[] colors, int k) {
int len = colors.length;
if(len <= 0) {
return;
}
int index = 0;
while(index < len) {
int temp = colors[index] - 1;
//遇到計數位,跳過
if(colors[index] <= 0){
index++;
}
else {
//已經作為計數位
if(colors[temp] <= 0) {
colors[temp]--;
colors[index] = 0;
index++;
}
//還未被作為計數位使用
else {
swap(colors[index], colors[temp]);
colors[temp] = -1;
}
}
}
//倒著輸出
int i = len - 1;
while(k > 0) {
for(int j = 0; j>colors[k-1]; j--) {
colors[i--] = k;
}
k--;
}
}
public void swap(int[] colors , int a , int b){
int temp = colors[a];
colors[a] = colors[b];
colors[b] = temp;
return ;
}
}
更多題解參考: