LeetCode No.75

顏色分類

給定一個(gè)包含紅色、白色和藍(lán)色,一共 n 個(gè)元素的數(shù)組,原地**對它們進(jìn)行排序,使得相同顏色的元素相鄰,并按照紅色、白色、藍(lán)色順序排列。

此題中,我們使用整數(shù) 0、 1 和 2 分別表示紅色、白色和藍(lán)色。

示例:

輸入:** [2,0,2,1,1,0]
輸出: [0,0,1,1,2,2]</pre>

進(jìn)階:

  • 一個(gè)直觀的解決方案是使用計(jì)數(shù)排序的兩趟掃描算法。
    首先,迭代計(jì)算出0、1 和 2 元素的個(gè)數(shù),然后按照0、1、2的排序,重寫當(dāng)前數(shù)組。
  • 你能想出一個(gè)僅使用常數(shù)空間的一趟掃描算法嗎?

題目分析

  1. 相同元素相鄰,換句話說即分區(qū)存放,一個(gè)顏色一個(gè)區(qū)域,但因?yàn)檫@里顏色使用0 ,1,2表示,即對一個(gè)012的無序序列進(jìn)行排序,變成000...111....2222的順序。

正常排序算法都適用,只不過這里注意僅有三種可能的順序值,可以有特殊做法。

  1. 對于一個(gè)元素,我們將他分區(qū)時(shí),肯定是插在區(qū)域的末尾,換句話說,我們只要保留每個(gè)區(qū)域的末尾下標(biāo),插入的時(shí)候交換過去就可以了。

三個(gè)區(qū)域,其實(shí)我們只要確定了兩個(gè)區(qū)域即可確定第三個(gè)區(qū)域,因?yàn)?區(qū)域在最左邊,2區(qū)域在最右邊,方便程序定位,因此取0 2 的末尾指針保留下來。

  1. 確定了雙指針確定區(qū)域,那么怎么進(jìn)行遍歷處理呢?確定了分類移動(dòng)的位置,即cur遍歷每一位的數(shù)值,分類處理遇到0放左邊,遇到2放右邊,02放好了,則1的區(qū)域也放好了。
針對cur的分類處理:
  1. 是0,則和0的flag ,left交換值,同時(shí)left++。此時(shí)cur獲取的是之前的left交換過來的值。我們知道我們換過去的處理好了,那么這個(gè)換過來的還需要處理嗎?目前還不知道,先看2的情況的處理。

  2. 是2,則和2的flag,right交換,同時(shí)right--。同樣,此時(shí)cur獲取了交換過來的值,由于這個(gè)值必然是在cur右邊(因?yàn)樗窃璻ight,即cur的右邊界),則cur肯定沒有遍歷過這個(gè)值。
    同時(shí)因?yàn)樗赾ur右邊,它也不可能是交換過去的,因?yàn)榻粨Q過去的值肯定都在0 2 區(qū)域里了。
    因此這個(gè)值是野生的,所以cur在此停留,繼續(xù)處理這個(gè)交換過來的值。

  3. 是1,因?yàn)槲覀儼? 2 排序好,1的區(qū)域自然就出來了,因此1直接不處理,過就好了

  4. 那么看看0的情況交換過來的值?首先這個(gè)值的位置關(guān)系,即原left是在cur左邊還是右邊呢?

    只有當(dāng)left會在cur右邊時(shí),left指向的才可能是cur沒處理過的,但此時(shí)cur在left內(nèi)部?那cur就是把一個(gè)0換到了外邊,把一個(gè)亂序的換進(jìn)了0的內(nèi)部,內(nèi)鬼毫無意義。

    當(dāng)left在cur左邊時(shí),即left指向的必定經(jīng)過了cur的洗禮,而且它必不是原來在后面被換到前面的,因?yàn)榻粨Q的肯定在left內(nèi)部去了。所以它原值就是在那,且經(jīng)過處理不用動(dòng),因此從left換到現(xiàn)在的cur之后,也是同樣的處理,也不用動(dòng)它,cur++拜拜嘞。

  5. 當(dāng)cur遇到right,即到達(dá)2的邊界,則說明不需要處理了,結(jié)束。

題解代碼

class Solution {
public:
    void sortColors(vector<int>& nums) 
    {
        int left=0;
        int right=nums.size()-1;
        int cur=0;
        while(cur<=right)
        {   
            if(nums[cur]==0)//這個(gè)數(shù)字是0,移動(dòng)0區(qū)域內(nèi),
            {
                swap(nums[cur],nums[left]);
                //假如cur>left,left交換過來的值肯定已經(jīng)被處理過了,但是處理時(shí)產(chǎn)生交換,原left指向的,一定是不用處理的嗎?
                //jeromememory:準(zhǔn)確的說 cur 如果 與 p0 不是指向同一個(gè)索引,那 cur 指向的索引值如果發(fā)生交換,那交換過來的一定是 1(原因是只有當(dāng)遍歷過的節(jié)點(diǎn)有1,p0 和 cur 才不會同步),而 如果索引是 1 剛好也就不用有任何操作,所以可以直接++。
                //假如cur==left==0,沒啥好說的,下一個(gè)
                //假如cur<left, 可能嗎?cur++的機(jī)會 >= left++的機(jī)會
                left++;
            }
            else if(nums[cur]==2)//這個(gè)數(shù)字是2,移到2區(qū)域內(nèi)
            {
                swap(nums[cur],nums[right]);
                right--;
                //交換過來的值,是右邊過來的,cur沒處理過,因此還需要對這個(gè)位置處理,--抵消++,位置不變
                cur--;
            }
            cur++;
        }
    }
};
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容