合併兩個有序的子序,要求空間複雜度為O 1

2022-08-20 17:27:12 字數 3323 閱讀 7254

陣列al[0,mid-1]和al[mid,num-1]是各自有序的,對陣列al[0,num-1]的兩個子有序段進行merge,得到al[0,num-1]整體有序。要求空間複雜度為o(1)。注:al[i]元素是支援'

先是琢磨良久,是否可能時間複雜度為o(n),因為這讓我想起了,左旋轉陣列的相關的演算法。深陷下去後,突然什麼光一閃。。。

果斷放棄,使用最簡單的演算法,幾分鐘筆墨,錯改調。(上次就是如此深陷泥潭而不可自拔):

1

void swap(int *a, int *b)29

10void func(int a, int

n)11

18 i = 0

;19 j = n/2;20

21while(jj)

2227

else

2834 i++;

35 j++;36}

37}3839 }

演算法簡單,最壞的時間複雜度為o(n^2)。

紅色的區域是已排好的,綠色為前一子序,紫紅為後子序;比較前後子序的最小元素,小的放入紅色區域。

如果i的小,只需要往前一步;否則i與j的元素互換,各自向前一步;但此時導致前子序不再有序,通過調整恢復有序即可;

之前所說的調整恢復其實就是乙個右移了一位,每當j中有元素較小時,就需要前移到紅色區域中,這樣導致了要移位;

試想j的後面有幾個連續的元素都比i 小,這樣每次就會導致j-i個移動元素;所以改動一下,沒有每次都移動,等到j開始比i大的時候,

例如:1 6 9 12 2 3 4 15

1、i=0;j=4;a[i]2、i=1; j=4; a[i]>a[j] 所以交換為 12 9 12 6 3 4 15; i++;j++

3、i=2; j=5; a[i]>a[j] 所以交換為 12

3 12 6

9 4 15; i++; j++

4、i=3; j=6; a[i]2 312 6 9 4 15,所以這時藍色部分就要一次右移兩位(兩個連續的小),移位後1 2 36 9 12

4 15

5、之後,沿襲剛才的思想,繼續下去。。。

這樣只是稍微的優化一下而已。又是一番折騰

1

void reverse(int a, int i, intj)2

8for (;i)912

}1314void rightmove(int a, int i, int j, int

k)15

2021 reverse(a, i, j-k);

22 reverse(a, j-k+1

, j);

23reverse(a, i, j);24}

2526

void func1(int a, int

n)27

38while(ia[i])

3943

44 rightmove(a, i+1, j-1, j-tmp);

45 i++;46}

47 }

結果看了看標準答案,華麗地把哥的秒了:

1/*2

陣列a[begin, mid] 和 a[mid+1, end]是各自有序的,對兩個子段進行merge得到a[begin , end]的有序陣列。 要求空間複雜度為o(1)

3方案:

41、兩個有序段位a和b,a在前,b緊接在a後面,找到a的第乙個大於b[0]的數a[i], a[0...i-1]相當於merge後的有效段,在b中找到第乙個大於a[i]的數b[j],

5對陣列a[i...j-1]迴圈右移j-k位,使a[i...j-1]陣列的前面部分有序

62、如此迴圈merge

73、迴圈右移通過先子段反轉再整體反轉的方式進行,複雜度是o(l), l是需要迴圈移動的子段的長度8*/

9 #include10

using

namespace

std;

1112

void reverse(int *a , int begin , int end ) //

反轉13

17void rotateright(int *a , int begin , int end , int k) //

迴圈右移

1825

26//

將有序陣列a[begin...mid] 和 a[mid+1...end] 進行歸併排序

27void merge(int *a , int begin , int

end )

2843}44

45void mergesort(int *a , int begin , int

end )

4654

55int main(void)56

;59int a = ;

6061 n = sizeof(a)/sizeof(int

);62

63 mergesort(a , 0 , n - 1

);64

65for(i = 0 ; i < n ; ++i)

66 cout

6768

return0;

69 }

咋一看,和哥的像,太像了;可是演算法的思想的是兩碼事。(思考:1 3 5 7 2 4 6 8,我的演算法則將要退化)

答案的思想高明要與結合了,歸併(分治)的思想。繼續考慮剛才的例子:

1 6 9 12 2 3 4 15

1、遞迴到底,將陣列分每個元素為乙個子串行,顯然有序

2、退上一層,兩個一組,1 6 | 9 12 | 2 3 | 4 15,依然有序

3、退上一層,四個一組,1 6 9 12 | 2 3 4 15

4、退上一層,8個一組,1 2 3 4 6 12 15

坑爹啊~這不是和哥的演算法一樣麼。。。。其實是這樣的當只有偶數個的時候,就退化為第二個演算法了。

因為其每次的分組剛好是有序的,只有退回至最上層的時候,才需要真正移位。

再考慮 1 ,6 ,9 , 2 ,3,4 ,15

1、遞迴到底,將陣列分每個元素為乙個子串行,顯然有序

2、退上一層,兩個一組,1 6 | 9  2 | 3 4 | 15=>1 6 | 2 9 | 3 4 | 15

3、退上一層,四個一組,1 2 6 9 | 3 4 15

4、退上一層,7個一組,1 2 3 4 6 9 15

其實回想起來,沒見得演算法三很好,而且使用了兩個遞迴。至於效率上,隱約感覺演算法三稍比演算法二的好;

合併兩個有序子陣列

設子陣列a 0 k 1 和a k,n 1 已經排好序 0 k n 1 設計乙個合併這兩個子陣列的排序好的陣列a 0 n 1 的 演算法。要求演算法最壞情況下所用的計算時間為o n 且只用到o 1 的輔助空間。解答此題可以用向右 左 迴圈換位合併的思想,先用二分搜尋查詢a 0 在陣列a k n 1 中...

pta兩個有序鍊錶的合併 21 合併兩個有序鍊錶

分別遍歷兩個鍊錶,把數放到列表中,運用sort方法。再用尾插法,遍歷列表,建立新的有序鍊錶。class solution def mergetwolists self,l1 listnode,l2 listnode listnode sum def list2num node while node ...

合併兩個有序序列

將兩個有序的數列,合併成乙個有序的序列 package toyprogram this class is used for author dlf 460795365 qq.com version 1.0,2016年9月13日 下午3 39 42 public class aboutarrayl in...