数组中的逆序对

题目

在数组中的两个数字假设前面一个数字大于后面的数字。则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
比如:{7,5,6,4},一共存在5个逆序对,各自是(7,6)(7,5)(7,4)(6,4)和(5,4)

思路

本来是毫无思路的,一般习惯了暴力破解,剑指offer后面的归并排序的思想。让我想到归并排序是好多年前写过的(忘了......果然出来混总是要还的)。

归并排序的思想是利用分治法。将一个非常大的问题,缩小成一个非常小的问题,将非常长的数组。终于递归分成两个数进行比較。

逆序对刚好能够利用这一点。当剩两个数的时候。推断前面的是否大于后面的,假设大于后面的。那么就是一个逆序对,并且后面的一般都是排好序的。所以我们让指针从后面開始往前遍历,仅仅要前半部分的数据大于后半部分的最后一个,那么就会大于全部后半部分的。

如图:

(图片来自:http://blog.csdn.net/cjf_iceking/article/details/7920153)

第一层排好序后,回到第二层。这时候我们推断95 和84的大小。95>84。就说明95大于84之前全部的,所以此时的逆序对数就是84加上之前全部的元素个数。

然后计算完毕后,反着放入新数组。这样后边就不会再次累积了,这事实上借用了归并排序的思想。

代码

public static int InversePair(int[] data,int start,int end,int[] temp)
	{
		int count=0;  //累积逆序对的总数
		if(start < end)
		{
 
		   int mid = (end+start)/2;
		
		   count +=  InversePair(data, start, mid ,temp);
		   count +=  InversePair(data,mid+1,end,temp);
		   count += getInversePair(data,start,mid,end,temp);
		  
		}    
		
		return count;
	}
	
	
	
	private static int getInversePair(int[] data, int start,int mid, int end,int[] temp) 
	{
		int i=mid;    //前半部分的末尾
		int count=0; 
		int j = end;  //后半部分的末尾
		int index=0;
		while(i >=start && j>mid)
		{
			if(data[i] > data[j])
			{
				temp[index++] = data[i--];
				count += j-mid;  //j之前的全部元素都是逆序对数
				
			}
			else
			{
				temp[index++] = data[j--];
				 
			}
			
		}
		
		while(i>=start)
		{
			temp[index++] = data[i--];
		 
		}
		
		while(j>mid)
		{
			temp[index++] = data[j--];
			 
		}
		//将其排好序的放入原数组
		for(int k=0;k<index;k++)
		{
			data[end-k] = temp[k];
		}
	 
	 	 
		
		return count;
	}




原文地址:https://www.cnblogs.com/yjbjingcha/p/7267956.html