分而治之算法(MergeSort Algo)计算反转次数

Posted

技术标签:

【中文标题】分而治之算法(MergeSort Algo)计算反转次数【英文标题】:Divide and Conquer Algorithm (MergeSort Algo) calculating number of inversions 【发布时间】:2021-03-11 23:19:32 【问题描述】:

我正在尝试解决计算问题所在的反转次数的问题:

`序列的反转 ????0, ????1, . . . , ????????−1 是一对索引 0 ≤ ???? ??????????。在某种意义上,序列的反转次数衡量了如何 关闭序列将被排序。例如,一个排序的(在非降序 order) 序列完全不包含反转,而在降序排序的序列中 顺序任意两个元素构成一个反转(总共 ??????(???? − 1)/2 倒置)。

示例输入是: 6 9 8 7 3 2 1

输出将是: 15 `

现在,我正在尝试合并排序算法,这个想法是每当我看到 nextNo 时。大于prevNo.我将添加最初为 0 的计数。

这是合并算法:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class MergeSort 

static void Merge(int arr[],int l,int m, int r)

    int n1 = m-l+1;
    int n2 = r-m;

    int L[] = new int[n1];
    int R[] = new int[n2];

    for(int i=0;i<n1;i++)
        L[i] = arr[i+l];
    for(int i=0;i<n2;i++)
        R[i] = arr[m+1+i];

    int i=0,j=0;

    int k=l;

    while(i<n1&&j<n2)
        if(L[i]<R[j])
            arr[k]=L[i];
            i++;
        
        else
            arr[k]=R[j];
            j++;
        
        k++;
    

    while(i<n1)
        arr[k] =L[i];
        i++;
        k++;
    

    while(j<n2)
        arr[k] =R[j];
        j++;
        k++;
    

   

static void MergeSortBasic(int arr[],int l,int r) 

    if(l<r)
        int m = (l+r)/2;

        MergeSortBasic(arr,l,m);
        MergeSortBasic(arr,m+1,r);

        Merge(arr,l,m,r);
    
   

public static void main(String[] args) 
    QuickSortAlgo.FastScanner scanner = new QuickSortAlgo.FastScanner(System.in);
    int n = scanner.nextInt();
    int[] a = new int[n];
    for (int i = 0; i < n; i++) 
        a[i] = scanner.nextInt();
    
  MergeSortBasic(a,0,n-1);
    for (int i = 0; i < n; i++) 
        System.out.print(a[i] + " ");
    


static class FastScanner 
    BufferedReader br;
    StringTokenizer st;

    FastScanner(InputStream stream) 
        try 
            br = new BufferedReader(new InputStreamReader(stream));
         catch (Exception e) 
            e.printStackTrace();
        
    

    String next() 
        while (st == null || !st.hasMoreTokens()) 
            try 
                st = new StringTokenizer(br.readLine());
             catch (IOException e) 
                e.printStackTrace();
            
        
        return st.nextToken();
    

    int nextInt() 
        return Integer.parseInt(next());
    

   

这我明白,但这是我遇到的这个问题的解决方案,我无法理解。请有人帮我理解这个算法的解决方案,尤其是中点/平均部分。

解决方案:

import java.util.*;

public class Inversions 
private static long merge(int[] a, int[] b, int left, int ave, int right) 
    int i = left, j = ave, k = left;
    long inv_count = 0;
    while (i <= ave - 1 && j <= right) 
        if (a[i] <= a[j]) 
            b[k] = a[i];
            i++;
         else 
            b[k] = a[j];
            inv_count += ave - i;
            j++;
        
        k++;
    
    while (i <= ave - 1) 
        b[k] = a[i];
        i++;
        k++;
    
    while (j <= right) 
        b[k] = a[j];
        j++;
        k++;
    
    for (i = left; i <= right; i++) 
        a[i] = b[i];
    
    return inv_count;


private static long getNumberOfInversions(int[] a, int[] b, int left, int right) 
    long inv_count = 0;
    if (right <= left) 
        return inv_count;
    
    int ave = left + (right - left) / 2;
    inv_count += getNumberOfInversions(a, b, left, ave);
    inv_count += getNumberOfInversions(a, b, ave + 1, right);
    inv_count += merge(a, b, left, ave + 1, right);
    return inv_count;


public static void main(String[] args) 
    Scanner scanner = new Scanner(System.in);
    int n = scanner.nextInt();
    int[] a = new int[n];
    for (int i = 0; i < n; i++) 
        a[i] = scanner.nextInt();
    
    int[] b = new int[n];
    System.out.println(getNumberOfInversions(a, b, 0, a.length - 1));


我的问题是为什么我们有

inv_count += ave - i;

而不是简单地:

inv_count++;

这两个程序有什么区别??这个 ave 变量是如何工作的?另外,知道我将来如何有效地学习这一点吗?

【问题讨论】:

尝试找出一个由 4 个元素组成的未排序数组会发生什么。 我的问题是你如何从 mergeSort 到这个解决方案!!这可以正常工作,但如何找出这个解决方案对不起,我是算法的新手。 【参考方案1】:

为什么:inv_count += ave - i;

被合并的两个子数组已经从先前的递归中排序(或子数组大小为 1 个元素的最终情况)。每次发现右子数组中的一个元素小于左子数组中的当前元素(a[j]

【讨论】:

至于如何到达,写一个归并排序。在合并排序中添加一个循环以计算在合并期间发现的反转。然后通过计算循环会找到多少东西来简化该循环。瞧!

以上是关于分而治之算法(MergeSort Algo)计算反转次数的主要内容,如果未能解决你的问题,请参考以下文章

排序算法之归并排序

Python 超出递归限制

难以思考分而治之的方法

使用分而治之找到最长的递增子序列

排序算法之 '归并排序'

归并排序和快速排序