1. 程式人生 > >經典演算法 之 子陣列換位問題

經典演算法 之 子陣列換位問題

子陣列換位問題

設a[0:n-1]是一個有n個元素的陣列,k(0<=k<=n-1)是一個非負整數。
試設計一個演算法將子陣列a[0:k]與a[k+1,n-1]換位。要求演算法在最壞情況下耗時O(n),且只用到O(1)的輔助空間。

初步思考:最簡單的方法就是迴圈(n-k-1)次,將a陣列的末尾數字插入到a[0]之前。

具體做法:
(1) 首先開闢一個額外空間temp用於存放每一次a陣列的末尾資料。
(2) temp <- a[n-1]
(3) 將a[0: n-2] 每個資料都依次向後移動一位賦值給a[1: n-1]。
(4) a[0] <- temp
(5) 迴圈執行(2) -(4) 步 (n-k+1)次。

代價分析: 時間代價—— O((n-1)*(n-k+1)) 即O(n^2)數量級;空間代價: O(1)

我們仔細想想還有沒有更快的辦法呢?試想一下,如果a[0 : k] 與 a[k+1 : n-1] 正好長度相等,則可以直接一一對應交換即可。 當然,這道題的難點就在於k並不一定是a陣列的中間位置。即便如此,但是仍然可以交換:

採用分治演算法

當v[k]左邊子陣列的長度等於右邊的子陣列長度時,直接將兩個子陣列對應的元素互換即可
當左邊子陣列長度小於右邊子陣列長度時,將左邊子陣列與右邊子陣列右邊的等長子陣列對換,再對結果遞迴呼叫對換函式
當右邊子陣列長度小於左邊子陣列長度時,將右邊子陣列與左邊子陣列左邊的等長子陣列對換,再對結果遞迴呼叫對換函式

通過分析,可知只需要利用儲存元素對換時的交換空間即可,空間複雜度為O(1),子陣列對換時時間複雜度不會超過O(n)

程式碼如下:

//交換陣列的兩段大小相等的範圍的對應資料  
//a[low1] <->a[low2]  a[low1+1]<->a[low2+1]  ... a[high1] <-> a[high2]  
void swap(int a[],int low1,int high1,int low2,int high2){  

    int temp;  
    while(low1<=high1){  
        temp=a
[low1]; a[low1]=a[low2]; a[low2]=temp; low1++; low2++; } } //利用分治演算法, 每次選擇最小的陣列進行換位 void patition(int a[], int low, int k, int high){ if(low<high){ if((k-low+1)==(high-k)) swap(a,low,k,k+1,high); else if((k-low+1)<(high-k)){ swap(a,low,k,low+high-k,high); patition(a,low,k,low+high-k-1); } else{ swap(a,low,high+low-k-1,k+1,high); patition(a,high+low-k,k,high); } } }

以下以陣列1,2,| 3,4,5,6,7陣列中交換1、2和3、4、5、6、7兩個陣列塊為例分析程式執行過程:

初始陣列:1,2,| 3,4,5,6,7
此時: low=0 k=1 high=6
執行: patition(a,0,1,6);

左邊長度小於右邊
執行: swap(a,0,1,5,6), 將1、2和6、7互換 陣列變為: 6,7,3,4,5,1、2
此時: low=0 k=1 high=high-(k-low+1)=4
執行: patition(a,0,1,4);

左邊長度小於右邊
執行: swap(a,0,1,3,4), 將6、7和4、5互換 陣列變為: 4,5,3,6,7,1、2
此時: low=0 k=1 high=high-(k-low+1)=2
執行: patition(a,0,1,2);

左邊長度大於右邊
執行: swap(a,0,0,2,2), 將4和3互換
陣列變為: 3,5,4,6,7,1、2
此時: low=1 k=1 high==2
執行: patition(a,1,1,2);

左邊長度等於右邊
執行: 將4和5互換
陣列變為: 3,4,5,6,7,1、2
結束

當然,此處還有另外一種演算法:三次反轉演算法,類似線性代數中的轉置的性質,有如下演算法:
這裡寫圖片描述

演算法複雜性分析:3次反轉演算法最多用了n次陣列單元交換運算,每次交換運算需要3次元素移動。因此最壞情況下用了3n次元素移動,時間複雜性為O(n),空間複雜性為 O(1)。