T:
题目描述
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
- 暴力搜索法
直接看代码,简洁明了。
code:
public int InversePairs(int [] array) {
int nums = 0;
for (int i = 1; i < array.length; i++) {
for (int j = 0; j < i; j++) {
if (array[j] > array[i]) {
nums ++;
}
}
}
return nums;
}
如果只是单纯的从暴力搜索的角度去解决该问题,貌似简单了点儿,且这种情况下的耗时为
看到讨论版的代码,很多人说用归并排序,对于这个排序,我也忘得差不多了,重新学习!
- more efficiency methods
先复习一下归并排序…
具体的算法思想不说了,给个链接:http://blog.csdn.net/morewindows/article/details/6678165/
code:
package niuke.sward2offer.inversePairs;
/**
* 归并排序,递归方式
*
* date: 2015.11.11 22:00
* @author SSS
*
*/
public class Solution1 {
/**
* 将两个有序数组合并为一个新数组,耗时O(n)
*
* @param array // 共用一个数组,即都在数组array上进行两个数组片段的重新排序
* @param aStart
* @param aEnd
* @param bStart
* @param bEnd
* @param newArray
*/
public void merge(int []array, int aStart, int aEnd, int bStart, int bEnd, int []newArray) {
int i = aStart;
int j = bStart;
int count = 0; // 新数组的下标
while (i <= aEnd && j <= bEnd) {
if (array[i] < array[j]) {
newArray[count ++] = array[i ++];
}
else {
newArray[count ++] = array[j ++];
}
}
while (i <= aEnd) {
newArray[count++] = array[i++];
}
while (j <= bEnd) {
newArray[count++] = array[j++];
}
/**
* 因为是要在下标为aStart和bEnd之间进行排序,所以将要有序的数列按对应下标覆盖array中原有的数据。
*/
for (int k = 0; k < bEnd - aStart + 1; k++) {
array[aStart + k] = newArray[k];
}
}
/**
* 归并排序的主体部分
* @param array
* @param start
* @param end
* @param newArray
*/
public void mergeSort(int []array, int start, int end, int []newArray) {
if (start < end) {
// 将下标为start和end这一部分的子数组从中间分为左、右两个部分
int mid = (start + end) / 2;
mergeSort(array, start, mid, newArray); // 认为左边有序
mergeSort(array, mid + 1, end, newArray); // 认为右边有序
this.merge(array, start, mid, mid + 1, end, newArray);
}
}
public static void main(String []args) {
int []array = {6, 3, 5, 9, 1, 8, 2, 7, 10, 4};
int []newArray = new int[array.length];
Solution1 solution1 = new Solution1();
solution1.mergeSort(array, 0, array.length - 1, newArray);
for (int i : newArray) {
System.out.println(i);
}
}
}
那么,能不能从这种方法的排序中得到点儿启发,从而算出原始数组中的逆序对呢?
归并排序的最底层,是从一个个的单个数字开始合并的,两个单个数字合并为一个二维有序数组,两个二维有序数组合并为一个有序的四维有序数组,……
归并排序的核心在于将两个有序数组合并为一个有序数组,代码如下:
/**
* 将两个有序数组合并为一个新数组,耗时O(n)
*
* @param array // 共用一个数组,即都在数组array上进行两个数组片段的重新排序
* @param aStart
* @param aEnd
* @param bStart
* @param bEnd
* @param newArray
*/
public void merge(int []array, int aStart, int aEnd, int bStart, int bEnd, int []newArray) {
int i = aStart;
int j = bStart;
int count = 0; // 新数组的下标
while (i <= aEnd && j <= bEnd) {
if (array[i] < array[j]) {
newArray[count ++] = array[i ++];
}
else {
newArray[count ++] = array[j ++];
}
}
while (i <= aEnd) {
newArray[count++] = array[i++];
}
while (j <= bEnd) {
newArray[count++] = array[j++];
}
/**
* 因为是要在下标为aStart和bEnd之间进行排序,所以将要有序的数列按对应下标覆盖array中原有的数据。
*/
for (int k = 0; k < bEnd - aStart + 1; k++) {
array[aStart + k] = newArray[k];
}
}
比如将两个有序数组:
假设
所以,在归并排序的过程中求解逆序对的方法如下:设置一个全局变量,用于统计每次调用merge(…)函数时存在的逆序对。
code:
package niuke.sward2offer.inversePairs;
/**
* 在归并排序上的改进,可求得数组中的逆序对。
*
* date:2015.11.11 22:40
* @author SSS
*
*/
public class Solution2 {
private int inversePairsCount = 0;
public int InversePairs(int [] array) {
int []newArray = new int[array.length];
this.mergeSort(array, 0, array.length - 1, newArray);
return inversePairsCount;
}
/**
* 将两个有序数组合并为一个新数组,耗时O(n)
*
* @param array // 共用一个数组,即都在数组array上进行两个数组片段的重新排序
* @param aStart
* @param aEnd
* @param bStart
* @param bEnd
* @param newArray
*/
public void merge(int []array, int aStart, int aEnd, int bStart, int bEnd, int []newArray) {
int i = aStart;
int j = bStart;
int count = 0; // 新数组的下标
while (i <= aEnd && j <= bEnd) {
if (array[i] > array[j]) {
newArray[count ++] = array[j ++];
inversePairsCount += aEnd - i + 1; // 将该轮合并当中出现的逆序对统计出来
}
else {
newArray[count ++] = array[i ++];
}
}
while (i <= aEnd) {
newArray[count++] = array[i++];
}
while (j <= bEnd) {
newArray[count++] = array[j++];
}
/**
* 因为是要在下标为aStart和bEnd之间进行排序,所以将要有序的数列按对应下标覆盖array中原有的数据。
*/
for (int k = 0; k < bEnd - aStart + 1; k++) {
array[aStart + k] = newArray[k];
}
}
/**
* 归并排序的主体部分
* @param array
* @param start
* @param end
* @param newArray
*/
public void mergeSort(int []array, int start, int end, int []newArray) {
if (start < end) {
// 将下标为start和end这一部分的子数组从中间分为左、右两个部分
int mid = (start + end) / 2;
mergeSort(array, start, mid, newArray); // 认为左边有序
mergeSort(array, mid + 1, end, newArray); // 认为右边有序
this.merge(array, start, mid, mid + 1, end, newArray);
}
}
public static void main(String []args) {
int []array = {5, 3, 4, 1};
Solution2 solution2 = new Solution2();
System.out.println("nums = " + solution2.InversePairs(array));
}
}