使用分治法计算逆序数——算法

/*-----------------------------------------------
  名称:Use divide-and-couquer count the inversion 分治法计算逆序数 
  编写:MISaD
  博客:http://blog.csdn.net/misadd 
  日期:2014.10.25
  修改:2014.10.25
  		主要错误在于1.错误估计了逆序数的大小,先使用了long
		  			2.merge中错误计算了逆序数个数 
  内容:1.读入文件流
  		2.子程序1,sorted-and-count,将数组递归分为两部分从而分别计算每一部分的逆序数 。 
		3.子程序2,merge-and-count,使用归并排序计算两部分组合的逆序数。 
------------------------------------------------*/
#include <iostream>
#include <fstream>// 读入文件流
#include <vector>//创建vector数组
using namespace std;

long long CountInversion(vector<long long>&vec);
long long merge(vector<long long>& vec,vector<long long>& v1,vector<long long>& v2);

int main()
{
	ifstream fin("IntegerArray.txt");
	vector<long long> init;
	long long temp;
	
	while(fin>>temp){
		init.push_back(temp); 
	}
	
	long long  dataSize = init.size() ;
	cout<<"Data Size:"<<dataSize<<endl;
	
	long long x = CountInversion(init);
	
	cout<<x<<endl;
	
	return 0;
	
}

long long CountInversion(vector<long long>&vec)
{
	long long n = vec.size();
	if( 1 == n ) 
		return 0;
	long long result = 0;
	long long mid = n/2;
	
	vector<long long> v1,v2;
	long long k = 0;
	while(k < n){
		if(k < mid){
			v1.push_back(vec[k]);
			k++;
		}
		else{
			v2.push_back(vec[k]);
			k++; 
		}
	}
	
	long long leftNum,rightNum,splitNum;
	leftNum = CountInversion(v1);
	rightNum = CountInversion(v2);
	splitNum = merge(vec,v1,v2);
	
	result = leftNum+rightNum+splitNum;
	
	return result;
}

long long  merge(vector<long long>& vec,vector<long long>& v1,vector<long long>& v2)
{
	long long n1 = v1.size();
	long long n2 = v2.size();
	long long n = n1 + n2;
	long long num = 0;//split-inversion number
	
	long long p1 = 0,p2 = 0;
	long long k = 0;
	while(k < n && p1 < n1 && p2 < n2)
	{
		if(v1[p1] > v2[p2]){
			vec[k] = v2[p2];
			num +=n1-p1;
			p2++;
		}
		else{
			vec[k] =v1[p1];
			p1++;
		}
		k++;
	}
	
	while(p1 < n1){
		vec[k] = v1[p1];
		p1++;
		k++;
	}
	while(p2 < n2){
		vec[k] = v2[p2];
		p2++;
		k++;
	}
	
	return num;
	
}


首先讲一下基本的原理

首先用递归不断的将数组分成v1(0,mid-1)和v2(mid,n)两部分,具体计算逆序数则是通过归并排序来实现的,那么归并排序怎么实现计算逆序数的功能呢?

我们不妨假设v1,v2是两个分别已经按从小到达排好序的数组,所以如果v1[p1]>v2[p2],那么v1中所有从p1开始到mid-1的数都可以与v2[p2]这一个元素是逆序组,所以通过归并排序可以省时省力的计算每一部分中的逆序数,最后只要将各部分的逆序数相加,就可以得到最终结果。


具体写程序的时候有一些问题,首先是读入文件流的时候有下面几种方法

while(fin>>temp)
{
    init.push_back(temp);
}
for(int p = 0; p < 100000; p++) {
    fin>>temp;
    init.push_back(temp);
}
但是不能这样

while(!fin.eof()) {
       fin>>temp;
       init.push_back(temp); 
}
因为fin读到文件末尾的时候,再次进行读写才会发现已经到了文件的末尾无法读取了,因此才会返回true;

比如说读到了最后一个数据了,此时仍然为false;当再次读入时候发现不能再读了,这时候才会返回true结束循环,然而这时已经将最后一个数据写入了两次。


然后应用vector数组的优势是比较灵活,push_back(x)的作用是将x依次放入数组;

size()作用是表示数组的大小;


两个子函数的作用在思路中已经做了简单的描述再次不想多说,然后我还想说一下数据大小;

因为我做的是1-100000中所有的数的任意排序的逆序数,所以是一个超出了2^31次,所以用long是不行的,所以改成了long long;当然也可以用double,但是直接输出的话会输出科学计数法,所以需要用

cout.setf(ios_base::fixed,ios_base::floatfield);
当然还看到有大神是这么用的

char str[20];
sprintf(str,"%.8lf",x);//将格式化后的x存到字符串str中 (将浮点型转换成字符串) 
printf("%s",str);//不转换会按科学计数法输出 
简言之就是讲一个double的数字符串化之后输出。


也许是第一次写这样的程序,略显生疏,希望通过以后学习能够有更多的进步,因为是在coursera上学算法,全英文课也着实坑爹了点,只能说继续努力吧!!相信自己。我看行!希望有大神可以多提意见,一定认真学习改进。




  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值