分治方法实现求一个数组的逆序数

输入:一个一维整型数组,数组大小为n

输出:这个数组的所存的数列的逆序数

要求:可以处理相等的数值

分析:这个问题很容易解决,最简单的方法是:依次求这个数组中每一个数的逆序数,这种方法的时间负责度是O(n*n)。我们探讨效率更高一点的方法,用分治法解决这个问题,可以将时间负责度降低至O(n*log(n))。

分治方法解决这个题目的思路是:首先求前n/2个数的逆序数,然后求后n/2个数的逆序数,最后在排序的过程中合并前后两部分之间的逆序数。

此问题分治方法的边界条件是:当子数列只有一个元素时,将它的逆序数设置为0;

此问题分治方法用递归方法实现,递归的次数为O(log(n)),每个递归中合并前后两部分子数列的时间复杂度是O(n)。在O(n)时间内合并前后两个子数列,并调整其中各个元素的逆序数,用到以中比较巧妙的方法。下面的程序中理解逆序数是这样的:假如一个数的逆序数为m,则表示它后面有m个按顺序应该在它前面的数。

 

下面的C语言代码可供参考:

#include
#include

/* 数组a是存储原始数列的数组,数组a_temp是为了排序而设置的辅助数组,数组inverse_num用来存储对应原始数列数组中每个元素的逆序数(m个比自己小的数排到了自己的后面),inverse_num_temp是为了响应排序时的元素位置调整而设置的逆序数数组的辅助数组,b表示需要求逆序数的数组内的范围的起点(begin),e表示需要求逆序数的数组内的范围的终点(end)。

void inverse_numbers(int* a,int* a_temp,int* inverse_num,int* inverse_num_temp,int b,int e)
{
        if(b==e)
        {
                inverse_num[b]=0;
                return;
        }
        int mid=b+(e-b)/2;
        int b_p=b,mid_p=mid+1;  //b_p和mid_p是在排序是用到的,指示子数列走到了哪个位置
        int p_in_temp=b;
        int add_invers_num=0; //add_invers_num在排序过程中动态变化,表示当前为止有几个比自己小的数排到了自己后面
        inverse_numbers(a,a_temp,inverse_num,inverse_num_temp,b,mid); //求前一部分数列的逆序数
        inverse_numbers(a,a_temp,inverse_num,inverse_num_temp,mid+1,e);  //求后一部分数列的逆序数

        while((b_p<=mid)&&(mid_p<=e))
        {
                if(a[b_p]<=a[mid_p])
                {
                        a_temp[p_in_temp]=a[b_p];
                        inverse_num_temp[p_in_temp]=inverse_num[b_p]+add_invers_num;  //排序时,逆序数等于自己之前的逆序数加上有多少个比自己小的数排到了自己后面(这些数已经这次排序过程中排到自己前面)
                        p_in_temp++;
                        b_p++;
                }
                else
                {
                        a_temp[p_in_temp]=a[mid_p];
                        inverse_num_temp[p_in_temp]=inverse_num[mid_p];
                        p_in_temp++;
                        mid_p++;
                        add_invers_num++;
                }
        }
        if(b_p<=mid)
        {
                while(b_p<=mid)
                {
                        a_temp[p_in_temp]=a[b_p];
                        inverse_num_temp[p_in_temp]=inverse_num[b_p]+add_invers_num;
                        p_in_temp++;
                        b_p++;
                }
        }
        else
                while(mid_p<=e)
                {
                        a_temp[p_in_temp]=a[mid_p];
                        inverse_num_temp[p_in_temp]=inverse_num[mid_p];
                        p_in_temp++;
                        mid_p++;
                }
        while(b<=e)  //把辅助数组内的信息复制到原数组
        {
                a[b]=a_temp[b];
                inverse_num[b]=inverse_num_temp[b];
                b++;
        }
}

int calculate_inverse_numbers(int* arr,int n)
{
        int i,inverse_nums=0;
        int* a_temp=(int*)malloc(n*sizeof(int));
        int* inverse_num=(int*)malloc(n*sizeof(int));
        int* inverse_num_temp=(int*)malloc(n*sizeof(int));

        inverse_numbers(arr,a_temp,inverse_num,inverse_num_temp,0,n-1);
        for(i=0;i
        {
                inverse_nums=inverse_nums+inverse_num[i];
        }

        free(a_temp);
        free(inverse_num);
        free(inverse_num_temp);

        return inverse_nums;
}
int main(int argc,char* argv[])
{
        int i,inverse_numbers_of_array;
        int size;
        printf("please input the size of the array:\n");
        scanf("%d",&size);

        int* array=(int*)malloc(size*sizeof(int));
        for(i=0;i
                scanf("%d",array+i);

        inverse_numbers_of_array=calculate_inverse_numbers(array,size);
        printf("the inverse numbers of the array is %d\n",inverse_numbers_of_array);
        free(array);
        return 0;
}

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值