輸入乙個整數陣列,調整陣列中數字的順序,使得所有奇數字於陣列的前半部分,所有偶數字於陣列的後半部分。要求時間複雜度為o(n)。
最容易想到的辦法是從頭掃瞄這個陣列,每碰到乙個偶數,拿出這個數字,並把位於這個數字後面的所有數字往前挪動一位。挪完之後在陣列的末尾有乙個空位,然後把該偶數放入這個空位。由於每碰到乙個偶數,需要移動o(n)個數字,所以這種方法總的時間複雜度是o(n^2),不符合題目要求。
事實上,若把奇數看做是小的數,偶數看做是大的數,那麼按照題目所要求的奇數放在前面偶數放在後面,就相當於小數放在前面大數放在後面,聯想到快速排序中的partition過程,不就是通過乙個主元把整個陣列分成大小兩個部分麼,小於主元的小數放在前面,大於主元的大數放在後面。
而partition過程有以下兩種實現:
類似這個partition過程,奇偶排序問題也可以分別借鑑partition的兩種實現解決。
為何?比如partition的實現一中,如果最終是為了讓整個序列元素從小到大排序,那麼頭指標理應指向的就是小數,而尾指標理應指向的就是大數,故當頭指標指的是大數且尾指標指的是小數的時候就不正常,此時就當交換。
解法一借鑑partition的實現一,我們可以考慮維護兩個指標,乙個指標指向陣列的第乙個數字,我們稱之為頭指標,向右移動;乙個指標指向最後乙個數字,稱之為尾指標,向左移動。
這樣,兩個指標分別從陣列的頭部和尾部向陣列的中間移動,如果第乙個指標指向的數字是偶數而第二個指標指向的數字是奇數,我們就交換這兩個數字。
因為按照題目要求,最終是為了讓奇數排在陣列的前面,偶數排在陣列的後面,所以頭指標理應指向的就是奇數,尾指標理應指向的就是偶數,故當頭指標指向的是偶數且尾指標指向的是奇數時,我們就當立即交換它們所指向的數字。
思路有了,接下來,寫**實現:
//判斷是否為奇數
public
static
boolean isodd(int
number)
/** 解法一
* 借鑑partition的實現一,我們可以考慮維護兩個指標,乙個指標指向陣列的第乙個數字,我們稱之為頭指標,向右移動;乙個指標指向最後乙個數字,稱之為尾指標,向左移動。
* 這樣,兩個指標分別從陣列的頭部和尾部向陣列的中間移動,如果第乙個指標指向的數字是偶數而第二個指標指向的數字是奇數,我們就交換這兩個數字。
* 因為按照題目要求,最終是為了讓奇數排在陣列的前面,偶數排在陣列的後面,所以頭指標理應指向的就是奇數,尾指標理應指向的就是偶數,故當頭指標指向的是偶數且尾指標指向的是奇數時,我們就當立即交換它們所指向的數字。
*/public
static
void solution1(int
arr)
else
if(!isodd(arr[j]))
else
}}
解法二我們先來看看快速排序partition過程的第二種實現是具體怎樣的乙個原理。
partition分治過程,每一趟排序的過程中,選取的主元都會把整個陣列排列成一大一小的序列,繼而遞迴排序完整個陣列。如下偽**所示:
partition(a, p, r)
1 x ← a[r]
2 i ← p - 1
3 for j ← p to r - 1
4 do if a[j] ≤ x
5 then i ← i + 1
6 exchange a[i] <-> a[j]
7 exchange a[i + 1] <-> a[r]
8 return i + 1
舉個例子如下:現要對陣列data = 進行快速排序,為了表述方便,令i指向陣列頭部前乙個位置,j指向陣列頭部元素,j在前,i在後,雙雙從左向右移動。
① j 指向元素2時,i 也指向元素2,2與2互換不變
i p/j
2 8 7 1 3 5 6 4(主元)
② 於是j 繼續後移,直到指向了1,1 <= 4,於是i++,i 指向8,故j 所指元素1 與 i 所指元素8 位置互換:
i j
2 1 7 8 3 5 6 4
③ j 繼續後移,指到了元素3,3 <= 4,於是同樣i++,i 指向7,故j 所指元素3 與 i 所指元素7 位置互換:
i j
2 1 3 8 7 5 6 4
④ j 一路後移,沒有再碰到比主元4小的元素:
i j
2 1 3 8 7 5 6 4
⑤ 最後,a[i + 1] <-> a[r],即8與4交換,所以,陣列最終變成了如下形式:
2 1 3 4 7 5 6 8
這樣,快速排序第一趟完成。就這樣,4把整個陣列分成了倆部分,2 1 3,7 5 6 8,再遞迴對這兩部分分別進行排序。
借鑑partition的上述實現,我們也可以維護兩個指標i和j,乙個指標指向陣列的第乙個數的前乙個位置,我們稱之為後指標i,向右移動;乙個指標指向陣列第乙個數,稱之為前指標j,也向右移動,且前指標j先向右移動。如果前指標j指向的數字是奇數,則令i指標向右移動一位,然後交換i和j指標所各自指向的數字。
因為按照題目要求,最終是為了讓奇數排在陣列的前面,偶數排在陣列的後面,所以i指標理應指向的就是奇數,j指標理應指向的就是偶數,所以,當j指標指向的是奇數時,不正常,我們就當讓i++,然後交換i和j指標所各自指向的數字。
/** 借鑑partition的上述實現,我們也可以維護兩個指標i和j,
* 乙個指標指向陣列的第乙個數的前乙個位置,我們稱之為後指標i,向右移動;
* 乙個指標指向陣列第乙個數,稱之為前指標j,也向右移動,且前指標j先向右移動。
* 如果前指標j指向的數字是奇數,則令i指標向右移動一位,然後交換i和j指標所各自指向的數字。
* 因為按照題目要求,最終是為了讓奇數排在陣列的前面,偶數排在陣列的後面,所以i指標理應指向的就是奇數,j指標理應指向的就是偶數,所以,當j指標指向的是奇數時,不正常,我們就當讓i++,然後交換i和j指標所各自指向的數字。
*/public
static
void solution2(int
arr)
}}
程式設計之法 面試和演算法心得 筆記
一 字串翻轉 三步反轉法,先將兩部分分別反轉,然後再整體反轉。abcdef defabc 1 cbadef 旋轉前一部分 2 cbafed 旋轉後一部分 3 defabc 整個旋轉 那麼將 i am a student.studnet.a am i 也類似。二 字串的包含 a abcxyzlmnop...
程式設計之法 面試和演算法心得 最近公共祖先LCA問題
小結 暴力 tarjan演算法 是乙個找強連通分量的演算法。dfs 並查集,每次將兩個節點對的最近公共祖先的查詢儲存起來,然後dfs更新一次。複雜度 o n q q為查詢個數 rmq 沒看 但前面暴力求a i,j 最小值的位子可以考慮一下 1.普通的o n 3 2.動態規劃將為o n 2 線段樹 用...
程式設計之法 面試和演算法心得(最大連續子陣列和)
輸入乙個整形陣列,陣列裡有正數也有負數。陣列中連續的乙個或多個整數組成乙個子陣列,每個子陣列都有乙個和。求所有子陣列的和的最大值,要求時間複雜度為o n 例如輸入的陣列為1,2,3,10,4,7,2,5,和最大的子陣列為3,10,4,7,2,因此輸出為該子陣列的和18。求乙個陣列的最大子陣列和,我想...