归并排序的改编运用
原版归并
CPP
图解 归并排序(CPP)
JAVA
图解 归并排序(JAVA)
归并排序的思想大概就是:
将要排序的序列分成两段,对左右两段分别排序,再合并为有序序列,对于每一段等待排序的序列再次进行以上操作,直到分段到其中一段只有两个元素,再分段左右两段就是一个元素,开始进行排序,其实就是交换两个数了。从排列两个数的序列开始,合并排列好的两段序列,直到之前分的所有两段都合并好。这显然需要递归实现。想象其中一个状态,排列好了左右两段的,接下来要做的事就是合并左右两个有序段将其变为有序。
很好,把自己绕晕了
看别人画的归并排序的图解,一开始做的事情其实是分段,把整个序列分成左右两段,对于每一小段又分成左右两端,直到分的两段中只有一个元素,分段到此结束。开始对最小的两段合并,合并之后就可以回溯到上一阶分的段,直到这样回溯,把最开始分的那两段都合并了(你咋知道合并到了最开始的那两段呢?因为每分两段,之后都会有 merge(a,l,r,mid);合并的指令,只是要等到上面那两段都分段、合并好了。其实就是回溯完所有的递归函数,就完成了所有段的归并)
不知道说的是否确切,就像一个递归回溯的过程,
merge_sort(a,l,mid); merge_sort(a,mid+1,r); merge(a,l,r,mid);
只可意会不可言传
这道题呢,就是要走归并排序走过的路,对于可以分的每两段,把 对这两段合并的过程 变成…,其实是真合并,因为只有真合并才能进行下去,走完所有的路。合并的过程中计数 左段和右段 左大右小的 情况种数。
#includeusing namespace std; typedef long long int ll; int n; ll a[100009]; ll cnt=0; void merge(ll a[],int l,int r){ ll temp[100009];//用来盛放合并好的有序序列 归并排序时间复杂度为nlogn,空间换时间 int k=0; int mid=l+(r-l)/2; int i=l; int j=mid+1; while(i<=mid&&j<=r){// 82 94 // if(a[i]<=a[j]){//从小到大顺序排列 // temp[k++]=a[i++]; // } // else{ 从小到大顺序排列 ,当左边数大于右边数,左边数右侧的mid-i+1个数 都会大于这个右边数 // temp[k++]=a[j++]; // cnt+=(mid-i+1); // } if(a[i]<=a[j]){//从大到小顺序排列 temp[k++]=a[j++]; } else{ //从大到小顺序排列 ,当左边数大于右边数,右边数右侧的r-j+1个数会小于这个左边数 temp[k++]=a[i++]; cnt+=(r-j+1); } } while(i<=mid){ temp[k++]=a[i++]; } while(j<=r){ temp[k++]=a[j++]; } for(int h=0;h =r)return;//递归要有终止条件噢 int mid=l+(r-l)/2; mergeSort(a,l,mid); mergeSort(a,mid+1,r); merge(a,l,r); } int main(int argc, char** argv) { cin>>n; for(int i=0;i >a[i]; } mergeSort(a,0,n-1); // 将数组分成两半,左半边的逆序数加上右半边的逆序数, // 再加上左边取一个、右边取一个的逆序数 ,通过归并排序先为两半边排好序 cout<



