#数据结构与算法学习笔记#剑指Offer33:数组中的逆序对 + 归并排序 + 测试用例(Java、C/C++)

版权声明:本文为博主NJU_ChopinXBP原创文章,发表于CSDN,仅供交流学习使用,转载请私信或评论联系,未经博主允许不得转载。感谢您的评论与点赞。 https://blog.csdn.net/qq_20304723/article/details/83055616

2018.10.15

这道题也是一道时间空间效率题,表面上很容易能够想出复杂度为o(n^{2})的算法。可以用归并排序的思想将其优化成o(nlogn)。

1.创建一个归并排序的排序数组copy。

2.将数组拆分为左右两个部分,逆序总个数=左半部分逆序对个数+右半部分逆序对个数+跨左右的逆序对个数

3.递归求解左半部分和右半部分

4.对于跨左右的逆序对个数计算,将左半部分尾指针lefttail对应值依次与右半部分尾指针righttail对应值进行比较,若lefttail对应值大于righttail对应值,则逆序对个数为右半部分头指针至尾指针的元素个数,即存在(righttail - righthead + 1)个逆序对。(前提是左右部分已经分别递增排序)。

5.计算过程中依次将尾指针对应值比较较大者自右向左依次放入整段copy和原序列中,使其排成一段递增排序序列。

最终可以递归求解出总的逆序对个数,同时也将原序列归并排序为递增序列。计算过程中需要注意题目附加条件,防止数值过大进行取余操作。


题目描述

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007

输入描述:

题目保证输入的数组中没有的相同的数字

数据范围:

对于%50的数据,size<=10^4

对于%75的数据,size<=10^5

对于%100的数据,size<=2*10^5


Java实现:

/**
 * 
 * @author ChopinXBP
 * 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。
 * 输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
 * 
 */



public class InversePairs_34 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
//		int[] array = {7, 5, 6, 4};
		int[] array = {1,2,3,4,5,6,7,0};
		System.out.println(InversePairs(array));
	}
	
	//模拟归并排序操作,复杂度o(nlogn)
    public static int InversePairs(int [] array) {
    	if(array == null) return -1;
    	
    	int head = 0;
    	int tail = array.length - 1;
    	int[] copy = new int[array.length];		//记录归并排序后的数组
    	
    	int count = Solution(array, copy, head, tail);
    	
    	return count;
    }
    
    public static int Solution(int[] array, int[] copy, int head, int tail){
    	if(head == tail){
    		copy[head] = array[head];
    		return 0;
    	}
    	
    	int lefttail = (head + tail) / 2;			//左半部分尾指针
    	int righthead = lefttail + 1;				//右半部分头指针
    	int righttail = tail;						//右半部分尾指针
    	
    	int leftcount = Solution(array, copy, head, lefttail) % 1000000007;
    	int rightcount = Solution(array, copy, righthead, tail) % 1000000007;
    	
    	//将左半部分尾指针对应值依次与右半部分尾指针对应值进行比较,若lefttail对应值大于righttail对应值,则存在(righttail - righthead + 1)个逆序对
    	//计算过程中依次将尾指针对应值比较较大者自右向左依次放入整段copy和原序列中,使其排成一段递增排序序列。
    	int midcount = 0;
    	int indexCopy = tail;
    	while(lefttail >= head && righttail >= righthead){
    		if(array[lefttail] > array[righttail]){
    			midcount += righttail - righthead + 1;
    			copy[indexCopy--] = array[lefttail--];
    			if(midcount >= 1000000007) midcount %= 1000000007;			//防止数值过大先求余
    		}else{
    			copy[indexCopy--] = array[righttail--];
    		}
    	}
    	
    	while(lefttail >= head){
    		copy[indexCopy--] = array[lefttail--]; 
    	}
    	while(righttail >= righthead){
    		copy[indexCopy--] = array[righttail--];
    	}
    	for(int i = head; i <= tail; i++){
    		array[i] = copy[i];
    	}
    	
    	return (leftcount + rightcount + midcount) % 1000000007;
    }

}

C++实现示例: 

class Solution {
public:
    int InversePairs(vector<int> data)
    {
        if(data.empty() || data.size()==0)
            return 0;
        int length=data.size();
        vector<int> copy;
        for(auto iter=data.begin();iter!=data.end();iter++)
            copy.push_back(*iter);
        //for(int i=0;i<length-1;i++)
           // copy[i]=data[i];
        long long count=0;
        count=InversePairsCore(data,copy,0,length-1);
        return count%1000000007;
    }
    long long InversePairsCore(vector<int>& data,vector<int>& copy,int start,int end)
    {
        if(start==end)
        {
            copy[start]=data[start];
            return 0;
        }
        int length=(end-start)>>2;
        long long left;
        left=InversePairsCore(copy,data,start,start+length);
        long long right;
        right=InversePairsCore(copy,data,start+length+1,end);
        int i=start+length;
        int j=end;
        int indexCopy=end;
        long long count=0;
        while(i>=start && j>=start+length+1)
        {
            if(data[i]>data[j])
            {
                copy[indexCopy--]=data[i--];
                count+=j-start-length;
            }
            else
            {
                copy[indexCopy--]=data[j--];
            }
        }
        for(;i>=start;--i)
            copy[indexCopy--]=data[i];
        for(;j>=start+length+1;--j)
            copy[indexCopy--]=data[j];
        return left+right+count;
    }
};

测试代码:

// ====================测试代码====================
void Test(char* testName, int* data, int length, int expected)
{
    if(testName != NULL)
        printf("%s begins: ", testName);

    if(InversePairs(data, length) == expected)
        printf("Passed.\n");
    else
        printf("Failed.\n");
}

void Test1()
{
    int data[] = {1, 2, 3, 4, 7, 6, 5};
    int expected = 3;

    Test("Test1", data, sizeof(data) / sizeof(int), expected);
}

// 递减排序数组
void Test2()
{
    int data[] = {6, 5, 4, 3, 2, 1};
    int expected = 15;

    Test("Test2", data, sizeof(data) / sizeof(int), expected);
}

// 递增排序数组
void Test3()
{
    int data[] = {1, 2, 3, 4, 5, 6};
    int expected = 0;

    Test("Test3", data, sizeof(data) / sizeof(int), expected);
}

// 数组中只有一个数字
void Test4()
{
    int data[] = {1};
    int expected = 0;

    Test("Test4", data, sizeof(data) / sizeof(int), expected);
}


// 数组中只有两个数字,递增排序
void Test5()
{
    int data[] = {1, 2};
    int expected = 0;

    Test("Test5", data, sizeof(data) / sizeof(int), expected);
}

// 数组中只有两个数字,递减排序
void Test6()
{
    int data[] = {2, 1};
    int expected = 1;

    Test("Test6", data, sizeof(data) / sizeof(int), expected);
}

// 数组中有相等的数字
void Test7()
{
    int data[] = {1, 2, 1, 2, 1};
    int expected = 3;

    Test("Test7", data, sizeof(data) / sizeof(int), expected);
}

void Test8()
{
    int expected = 0;

    Test("Test8", NULL, 0, expected);
}

int _tmain(int argc, _TCHAR* argv[])
{
    Test1();
    Test2();
    Test3();
    Test4();
    Test5();
    Test6();
    Test7();
    Test8();

    return 0;
}


#Coding一小时,Copying一秒钟。留个言点个赞呗,谢谢你#

猜你喜欢

转载自blog.csdn.net/qq_20304723/article/details/83055616