Leetcode 75 Sort Colors
這道題目當然有比較容易想到的方法:首先遍歷一遍陣列,分別統計0,1,2的個數,然後按照順序填寫在陣列相應位置就行了。
這個方法顯然需要遍歷兩次陣列,有沒有遍歷一次就可以的方法呢?
聯想到我們在quick sort的時候選取pivot並且partition的思路,我們這裡也可以借鑑一下。
不過,知其然,也要知其所以然,一開始按照partition的思路寫下了程式,卻怎麼也通不過,仔細研究了一下,才發現對quick sort的理解不夠深刻啊。
這裡首先說一下partition的思路。我們首先隨機選取一個pivot,並且將其放在某一個位置(一般為start位置),隨後用兩個指標。quicksortp1指標從start+1的位置開始,指向第一個不比pivot小的元素,quicksortp2指標也從start+1的位置開始,指向當前我們遍歷的位置。所以,quicksortp2一定是大於等於quicksortp1的。每當quicksortp2遇到一個元素,就和pivot進行比較,如果比pivot小,那麼就交換quicksortp1和quicksortp2指向的元素。(重點1)這樣遍歷完陣列之後,交換pivot和quicksortp1-1位置的元素(因為quicksortp1指向第一個不比pivot小的元素,那麼quicksortp1-1指向的元素一定比pivot小,這樣就可以把pivot放在中間,達到二分的目的了——pivot左邊的元素都比它小,右邊的元素都不比它小)
這裡,重點來了。參考(重點1)位置,為什麼交換quicksortp1和quicksortp2元素之後,就能保證pivot一定在中間呢?
道理的確很簡單,因為我們是“二分”如果一個元素不比pivot小,那麼就一定大於等於pivot,所以,quicksortp1指標的含義不僅僅是指向第一個不比pivot小的元素,更重要的意義在於:它是一個分界線,分開了比pivot小和不比pivot小的元素。
那麼回到我們這個題目中來。如果我們想遍歷一次陣列,那麼我們就應該設定三個指標。第一個p1指向0元素的結尾,第二個p2指向2元素的開始,第三個p3用來遍歷整個陣列。那麼我們應該怎樣做?首先,p1和p3指向陣列開始,p2指向陣列結尾。如果p3指向的位置是一個0,那麼交換p1和p3指向的元素。如果該元素是2,那麼交換p2和p3指向的元素。每次p3都自增1,這樣遍歷完整個陣列(實際上,遍歷到p2位置就可以),我們的任務就做完了。
按照這個思路寫出來程式,竟然有錯誤!為什麼?
我們先回頭看一下quick sort中的做法。為什麼每次交換之後,我們能夠放心大膽的讓quicksortp2繼續向前遍歷?因為我們知道,我們現在是二分陣列,如果當前元素比pivot小,那麼交換過來的元素,就一定大於等於pivot!
再看看leetcode這道題目,我們有這樣的保證嗎?答案是沒有。比如p3和p1交換完畢之後,交換的元素是什麼?有兩種可能:一種是1,一種是2。所以,這裡我們就要判斷一下交換過來的元素是什麼,因為沒有辦法保證當前交換完畢的元素,一定符合我們的需要。換句話說,我們交換完畢之後,還要判斷一下當前元素是什麼。如果是2的話,就繼續交換。
那麼這樣可就複雜了,我們不如換一個思路:還是用三個指標,但是我們的定義變一下。p1指向1元素的開始,作為0和1的分界點。p2指向1元素的結尾,作為1和2的分界點。p3用來遍歷。
遍歷的時候採取這樣的策略:如果p3指向的元素為0,那麼交換p3和p1位置的元素。因為剛剛的定義,所以我們總是能夠保證交換過來的元素是1,那這樣的話我們就p1++,p3++。如果p3指向的元素是2,那麼我們交換p2和p3指向的元素,p2--,但是p3不變,因為我們不知道交換過來的是什麼元素。
這樣遍歷一邊陣列之後,就能夠得到我們想要的結果了。
class Solution { public: #define RED 0 #define WHITE 1 #define BLUE 2 void sortColors(int A[], int n) { if (A == NULL) return; int redIndex = 0; //points to the first one that may be not red int blueIndex = n - 1; //points to the first one that may be not blue int index = 0; while (index <= blueIndex) { if (A[index] == RED) { swap(A[index], A[redIndex]); redIndex++; index++; //cannot be less than redIndex } else if (A[index] == BLUE) { swap(A[index], A[blueIndex]); blueIndex--; } else index++; } } };