分治法 求 逆序對數 的個數 時間複雜度為O(n*logn)
阿新 • • 發佈:2019-01-28
思路:
分治法 歸併排序的過程中,有一步是從左右兩個陣列中,每次都取出小的那個元素放到tmp[]陣列中
右邊的陣列其實就是原陣列中位於右側的元素。當不取左側的元素而取右側的元素時,說明左側剩下的元素均比右側的第一個元素大,即均能構成一個逆序對。假設現在左側剩餘n個元素,則逆序對數+n。
另外,如果當所有右側的元素都取完,但是左側仍然有元素剩餘時,左側剩餘的元素已經在之前的運算中加到了逆序對中,不需要再新增一次
下面給出 歸併排序 和 求逆序對數 兩份程式碼:
code1:
歸併排序
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n; int a[20]; void query(int a[], int first, int mid, int last, int tmp[]){ int i = first, j = mid+1; int k = 0; while(i <= mid && j <= last){ if(a[i] < a[j]) tmp[k++] = a[i++]; else tmp[k++] = a[j++]; } while(i <= mid){ tmp[k++] = a[i++]; } while(j <= last){ tmp[k++] = a[j++]; } for(int id = 0; id < k; id++){ a[first + id] = tmp[id]; } } void merge_sort(int* a, int L, int R, int* tmp){ if(L < R){ int M = L + (R-L)/2; merge_sort(a,L,M,tmp); merge_sort(a,M+1,R,tmp); query(a,L,M,R,tmp); } } int main(){ scanf("%d",&n); for(int i = 0; i < n; i++){ scanf("%d",&a[i]); } int tmp[20]; merge_sort(a,0,n-1,tmp); for(int i = 0; i < n; i++){ printf("%d ",a[i]); } printf("\n"); return 0; }
code2:
求逆序對數:
cnt 表示逆序對數的個數
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n; int a[20]; int cnt; void query(int a[], int first, int mid, int last, int tmp[]){ int i = first, j = mid+1; int k = 0; while(i <= mid && j <= last){ if(a[i] <= a[j]) tmp[k++] = a[i++]; else{ tmp[k++] = a[j++]; cnt += mid-i+1; } } while(i <= mid){ tmp[k++] = a[i++]; } while(j <= last){ tmp[k++] = a[j++]; } for(int id = 0; id < k; id++){ a[first + id] = tmp[id]; } } void merge_sort(int* a, int L, int R, int* tmp){ if(L < R){ int M = L + (R-L)/2; merge_sort(a,L,M,tmp); merge_sort(a,M+1,R,tmp); query(a,L,M,R,tmp); } } int main(){ scanf("%d",&n); for(int i = 0; i < n; i++){ scanf("%d",&a[i]); } int tmp[20]; cnt = 0; merge_sort(a,0,n-1,tmp); for(int i = 0; i < n; i++){ printf("%d ",a[i]); } printf("cnt = %d\n",cnt); printf("\n"); return 0; }
LRJ給的程式碼不好理解....