分治算法的简单应用

1.要求2n个数的中位数,采用分治策略。每次划分后,都会去掉一半的数,只剩原来一半的数。
递推关系式为:T(n)=T(n/2)+O(1),由master定理,可得时间复杂度为O(logn)。

#include<cstdio>
#include<iostream>
#define N 1000000
using namespace std;

int a[N],b[N];
int i,n;
int getMin(int x,int y)//比较得到较小的数
{
    if (x<y) return x;
    return y;
}
int getMax(int x,int y)//比较得到较大的数
{
    if (x>y) return x;
    return y;
}

double getMedian(int arr[],int n)//得到中位数
{
    if (n&1==1)//若为奇数个,中位数为arr[n/2];若为偶数个,中位数为(arr[(n/2)-1]+arr[n/2])/2
    {
        return arr[n/2];
    }
    else return (arr[(n/2)-1]+arr[n/2])/2.0;
}

double CalMedian(int a[],int b[],int n)
{
    double m1,m2;
    if (n==1)//若最后只剩2个数,中位数即为两个数的平均数
        return (a[0]+b[0])/2.0;
    if (n==2)//若最后只剩4个数,中位数即为(getMax(a[0],b[0])+getMin(a[1],b[1]))/2
    {
        return (getMax(a[0],b[0])+getMin(a[1],b[1]))/2.0;
    }
    m1=getMedian(a,n);
    m2=getMedian(b,n);
    if (m1==m2)//若两个数组中位数相等,直接返回m1
        return m1;
    if (m1<m2)//若m1<m2,n为偶数,搜索范围为(a+n/2-1,b);n为奇数,搜索范围为(a+n/2,b);
    {
        if (n&1==0)
            return CalMedian(a+n/2-1,b,n/2+1);
        else
            return CalMedian(a+n/2,b,n/2+1);
    }
    else
    {
        if (n&1==0)
            return CalMedian(b+n/2-1,a,n/2+1);
        else
            return CalMedian(b+n/2,a,n/2+1);
    }

}

int main()
{
    printf("输入数组长度n的大小:");
    scanf("%d",&n);
    printf("\n请如输入第一个数组的值:");
    for (i=0;i<n;i++)
    {
        cin>>a[i];
    }
    printf("\n请输入第二个数组的值:");
    for (i=0;i<n;i++)
    {
        cin>>b[i];
    }
    printf("\n两个数组的中位数是:");
    cout<<CalMedian(a,b,n)<<endl;
    return 0;
}

2.要求逆序数,采用分治策略。每次划分后,都会去掉一半的数。总逆序数=左半边逆序数+右半边逆序+两半边分别排序后的逆序数。 若需要排序A[p…q],则可以对半分成A[p…r]和A[r…q],然后将这有序的两部分合并。合并过程的时间复杂度为O(n)。
递推关系式为:T(n)=2T(n/2)+O(n),由master定理,可得时间复杂度
为O(nlogn)。

#include<cstdio>
#include<iostream>
#define N 1000000
using namespace std;

int a[N],l[N],r[N],temp[N];
int i,n;

int mergeSort(int a[],int low,int high)
{
    int mi=(high-low)/2;
    int llen=(mi-low)+1;//左边的长度
    int rlen=high-mi;//右边的长度
    int i,j;
    //memset(l,0,sizeof(l));
    //memset(r,0,sizeof(r));
    for (i=0;i<llen;i++)
    {
        l[i]=a[low+i];//记录左边的数
    }
    for (i=mi+1;i<=high;i++)
    {
        r[i-mi-1]=a[i];//记录右边的数
    }
    int ret=0;
    int k=low;
    i=0,j=0;
    while(i<llen && j<rlen)//两个指针扫描比较大小,归并排序
    {
        if (l[i]<=r[j])
            temp[k++]=l[i++];
        else
        {
            temp[k++]=r[j++];
            ret+=llen-i;
        }

    }
    while(i<llen)//处理剩余的数
    {
        temp[k++]=l[i++];
    }
    while(j<rlen)
    {
        temp[k++]=r[j++];
    }
    return ret;
}


int CalReverseOrder(int a[],int low,int high)//计算逆序数
{
    int ans=0;
    if (low>=high)
    {
        return 0;
    }
    int mi=(low+high)/2;
    ans+=CalReverseOrder(a,low,mi);//计算(low,mi)范围内的逆序数
    ans+=CalReverseOrder(a,mi+1,high);//计算(mi+1,high)范围内的逆序数
    ans+=mergeSort(a,low,high);//再加上归并排序后增加的逆序数
    return ans;
}

int main()
{
    printf("输入数组长度n的大小:");
    scanf("%d",&n);
    printf("\n请输入数组的值:");
    for (i=0;i<n;i++)
    {
        cin>>a[i];
    }

    printf("\n逆序对的个数是:");
    cout<<CalReverseOrder(a,0,n-1)<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值