1. 程式人生 > 實用技巧 >【LeetCode/LintCode】 題解丨谷歌高頻題:兩個排序陣列的中位數排顏色 II

【LeetCode/LintCode】 題解丨谷歌高頻題:兩個排序陣列的中位數排顏色 II

給定一個有n個物件(包括k種不同的顏色,並按照1到k進行編號)的陣列,將物件進行分類使相同顏色的物件相鄰,並按照1,2,...k的順序進行排序。

  1. 不能使用程式碼庫中的排序函式來解決這個問題
  2. ​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. 遞迴函式設定四個引數,序列需要處理區間的左右端點和處理的顏色區間
  2. 根據給定的顏色區間決定中間的顏色
  3. 將小於等於中間的顏色的就放到左邊,大於中間顏色的就放到右邊
  4. 遞迴左右兩半,直到顏色區間長度等於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 ;
    }
}

更多題解參考:

九章演算法 - 幫助更多中國人找到好工作,矽谷頂尖IT企業工程師實時線上授課為你傳授面試技巧