数组中的逆序对
数组中的逆序对
http://www.nowcoder.com/questionTerminal/96bd6684e04a44eb80e6a68efc0ec6c5
剑指offer35 数组中的逆序对
问题分析
暴力是第一个想法,但是一定是行不通的,那么必须想办法去解决一个复杂度问题,既然是逆序对,那么就会想到归并排序的想法。
归并排序
- 将数组等分成两份,并且不断等分,直到只有一个元素的时候,那么不再进行等分,也就是达到 left(数组的左边起点) == mid(等分的点),直接return,不再分解
- 等分的每一份是要进行排序的,比如现在等分的数据是[6,9],[10,8],我们按照升序排列,那么结果是[6,9],[9,10] 才是我们想要的结果
- 合并阶段,如何合并呢,我们需要辅助数组temp来记录正确的顺序,还是上面的例子,我们需要三个索引来完成这个工作。
- p1为[6,9]的起点,p2为[8,10]的起点,mid为[6,9,10,8]划分两份时的切割点。
- 如果array[p1]>array[p2],那么temp存array[p2]的值,然后p2++,向后移动索引。
- 反之,也是一样的,temp存array[p1]的值,p1++,向后移动索引
- 最后一步,循环把temp覆盖到原来数组的位置
基于归并继续分析
我们看到了合并阶段的过程,那么p1,p2索引到的值的比较,也正是逆序对的比较,那么在这个阶段,我们需要给总和加上这个数,可以在代码中看到。
需要理解的一点是,由于先进行数组的划分,再进行排序,那么到排序阶段是可以理解划分两半的数组为是有序的。
代码
private int res = 0 ; //记录统计结果 public int InversePairs(int [] array) { Merge_Array(array,0,array.length-1); return res ; } public void Merge_Array(int[] array,int origin,int end){ if (origin>=end){ //只有一个值吗,不再进行归并 return; } int mid = (origin + end) / 2 ; //左归并 Merge_Array(array,origin,mid); //右归并 Merge_Array(array,mid+1,end); // 排序统计 Merge(array,origin,end,mid); } public void Merge(int[] array,int orgin,int end,int mid){ int[] temp = new int[end-orgin+1] ; //辅助数组 int i = 0 ; //temp数组添加新结果,向后移动 int p1 = orgin ; // 左边数组的起点 int p2 = mid+1 ; // 右边数组的起点 // p1 , p2 比较哪个小把哪个放到temp while (p1<=mid && p2 <=end){ if (array[p1]<=array[p2]){ temp[i++] = array[p1++] ; }else { temp[i++] = array[p2++] ; this.res = (this.res + mid - p1 + 1) % 1000000007; /** * 数组A【7,8,9,10,11,12】 * 数组B【1,2,3,4,5,6】 * * 如果A[pa] > B[pb] * 就说明 mid-pa+1个逆序数对 */ } } if (p1>mid){ //说明左边全部添加到temp中,继续添加右边 while (p2<=end){ temp[i++] = array[p2++] ; } } if (p2>end){ //说明右边全部添加到temp中,继续添加左边 while (p1<=mid){ temp[i++] = array[p1++] ; } } //在原数组中用有序的值覆盖掉原来无序的值 for (int j=0;j<temp.length;j++){ array[orgin+j] = temp[j] ; } }