分而治之篇-逆序对计数问题

问题描述

定义:逆序对:

在 int 数组 array 中,i < j && array[i] > array[j] 则为一个逆序对

input:输入一个 int 数组,返回数组中的逆序对个数

算法简介

一、暴力枚举法

毫无技巧,两层循环遍历,时间复杂度为 O(n^2)

二、分而治之法

时间复杂度为 O(nlogn)

二分拆分原数组,算出左边数组的逆序对个数 s 1 s_1 s1,右边数组的逆序对个数 s 2 s_2 s2 ,因合并而产生的逆序对个数 s 3 s_3 s3,则原数组的逆序对个数:
s = s 1 + s 2 + s 3 s = s_1 + s_2 + s_3 s=s1+s2+s3

1、 s 1   s 2 s_1 \space s_2 s1 s2 计算函数

int CountInver(int left, int right, int array[]) :

用于递归二分计算 s 1   s 2 s_1\space s_2 s1 s2 ,用 left、right、mid 区分左右数组的开始和结尾

当 left == right 的时候返回

2、 s 3 s_3 s3 计算函数

int MergeCount(int left, int mid, int right, int input[]) :

用于计算 s 3 s_3 s3,若采用朴素的比较法:两层循环遍历,则时间复杂度不减反增

想法:若 array[left, mid] 和 array[mid + 1, right] 都是有序的,则合并寻找逆序对的复杂度仅为 O(n) ,而想让左右数组都是有序的也是可以简单实现的,类似于二分排序的思想

该算法关键在于 s 3 s_3 s3 求解的优化——利用二分排序思想

代码实现

// 此程序解决逆序对计数问题
// 逆序对:在int数组Array中,i<j && Array[i] > Array[j] 则为一个逆序对
#include <iostream>
#include <cstdio>
using namespace std;

int CountInver(int left, int right, int array[]);
int MergeCount(int left, int mid, int right, int input[]);

int main(void) {
    int Array[100] = {0};   // 原数组, 简单起见不要超过100
    int capacity;           // 数组容量
    cout << "输入数组的容量:";
    cin >> capacity;
    for(int i=0; i < capacity; i++) cin >> Array[i];

// 暴力枚举法
// 时间复杂度 O(n^2)

    int InverTimes1 = 0;
    for(int i=0;i<capacity;i++) {
        for(int j=i+1;j<capacity;j++) {
            if(Array[i] > Array[j]) InverTimes1++;
        }
    }
    cout << InverTimes1 << endl;

// 分而治之法
// 拆分原数组,算出左半部分逆序对个数s1,右半部分逆序对个数s2,合并起来的逆序对个数s3,相加为总逆序对个数
// 重点在于优化s3计算算法,采用二路归并排序使其成为有序数组,在合并过程中得到s3
// 时间复杂度 O(nlogn)

    int InverTimes2;
    InverTimes2 = CountInver(0, capacity - 1, Array);
    cout << InverTimes2 << endl;

    return 0;
}

int MergeCount(int left, int mid, int right, int input[]) {
    int tool[right - left + 1], i = 0;   // tool as the cache 
    int loc1 = left;                     // loc1 as the begin of left array
    int loc2 = mid + 1;                  // loc2 as the begin of right array
    int s3 = 0;                          // add s3 in the MergeCount
    while(i < right - left + 1) {
        if(input[loc1] < input[loc2]) 
            tool[i++] = input[loc1++]; 
        else {
            tool[i++] = input[loc2++]; 
            s3 += mid - loc1 + 1;        // find inverse times
        }
        if(loc1 > mid) {
            while(loc2 <= right) 
                tool[i++] = input[loc2++];
            break;
        } else if(loc2 > right) {
            while(loc1 <= mid)
                tool[i++] = input[loc1++];
            break;
        }
    }
    for(int i=0;i<right - left + 1;i++)  // put tool[] into the input[]
        input[i+left] = tool[i];
    return s3;
}

int CountInver(int left, int right, int array[]) {
    if(left == right) return 0;
    else {
        int s1, s2, s3;
        int mid = (left + right) / 2;
        s1 = CountInver(left, mid, array);
        s2 = CountInver(mid + 1, right, array);
        s3 = MergeCount(left, mid, right, array);
        return s1 + s2 + s3;
    }
}
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值