《演算法設計與分析》迴圈右移合併陣列
阿新 • • 發佈:2018-12-11
題目:有已經排好序的陣列arr[0:k-1]和arr[k:n-1],現在將兩個數組合併成一個大的有序陣列。要求:時間複雜度最壞的情況下為0(n),空間複雜的度為o(1)。
之前看過《演算法筆記》上面將遞迴時,有個two pointers思想,講的正好是怎麼將兩個有序陣列在時間複雜度為O(n)的情況下合併成一個大的有序陣列。但是這種思想所用到的空間複雜度為o(n),本題中就不適用。
筆者的想法是選擇arr[i:k-1]和arr[j:n-1](初始時i=0,j=k)中最小的數,如果該數在arr[j:n-1]中,那麼該數必定時arr[j],交換arr[i]與arr[j],則(如果)該數在arr[i:k-1]中,直接I++即可。這種想法,由於還要將arr[j:n-1]在進行排序,最壞情況下的時間複雜度相比於o(n)增加到o(nlogn)。筆者想不出了什麼高效的演算法,於是偷偷看解答書。?
《演算法設計與分析習題解答》中提供了一種思想----->迴圈右移陣列方法。書中講的這個方法的大概思想如下:在arr[0:k-1]中找出arr[i](i範圍是0~k-1)在arr[k:n-1]中的位置(二分查詢法),記為pos,在計算pos到k的距離,則從i到pos一直往復迴圈,次數為距離。
程式碼如下可能不好懂,但一定要認真理解。
#include <bits/stdc++.h> using namespace std; //演算法實現:用o(1)的空間複雜度,時間複雜度最壞為o(n)的情況下 //合併同一個陣列的兩個排好序的部分,將其變成一個大的有序陣列 const int maxn = 1e5+10; int arr[maxn]; int binarySearch(int x,int left,int right) { int mid; while(left <= right) { mid = (right-left)/2+left; if(arr[mid] == x) return mid; if(arr[mid] > x) right = mid-1; else left = mid+1; } if(arr[mid] == x) return mid; else return mid-1; } void shiftRight(int k,int s,int e) //k是移動次數,s是開始迴圈移動的地方,e是結束迴圈移動的地方 { for(int i=0; i<k; i++) { int tmp = arr[e]; for(int j=e; j>s; j--) arr[j] = arr[j-1]; arr[s] = tmp; } } void mer(int k,int n) { int i=0,j=k; while(i<j && j<n) { int pos = binarySearch(arr[i],j,n-1); shiftRight(pos-j+1,i,pos); j = pos+1; i = i + pos - j +2; } } int main() { int n,i,k; cin>>n; for(i=0; i<n; i++) { cin>>arr[i]; if(arr[i] < arr[i-1]) { k = i; } } mer(k,n); for(i=0; i<n; i++) cout<<arr[i]<<" "; return 0; }