【问题标题】:How do I change merge sort for calculate distance of an array from sorted array?如何更改合并排序以计算数组与排序数组的距离?
【发布时间】:2018-10-21 20:42:06
【问题描述】:

注意:这个问题不同于倒数问题。

一个数组与已排序数组的距离定义为:

 dist(A)=Sumation(j-i) for any i<j that A[i]>A[j]

。简单地说,它可以在 O(n^2) 中计算。我的问题是如何更改合并排序以计算 O(n log n) 中的距离? 例如:

input: 5 2 3 4 1
output: 16

我不想计算反转! 这个函数不同于倒置。

input: 5 2 3 4 1
dist(A): 16
inversions(A): 7

【问题讨论】:

  • 你需要计算反转。搜索一下,有很多类似的主题。
  • 我不想计算倒置。请再检查一下功能!
  • 好的,重新打开。也许方法可能很接近。

标签: algorithm sorting mergesort


【解决方案1】:

如果你知道如何通过归并排序来计算一个数组的inversion个数,你会通过改变几行代码来解决这个问题。

注意细节。

// struct to store the values
//
struct pa {
    int value, index;
};

// The merge step
// A is the left part, while B is the right part
// a_cnt is the number of elements in A
void merge(pa A[], pa B[], int a_cnt, int b_cnt) {
    int st = 0;
    int tmp_sum = 0;
    for (int i = 0; i < a_cnt; ++i) {
        while (st < b_cnt && B[st].value < A[i].value) {
            tmp_sum += B[st].index;
            st++;
        }
        ans += (tmp_sum - st * A[i].index)
    }

    // origional merge code here
}

【讨论】:

  • 我不想计算倒置。请再检查一个问题。
  • 哦,我想你误解了我的意思……我想你可以通过使用归并排序计算反转来获得一些想法。
  • 谢谢。我知道倒置问题的计数是多少并考虑一下。但我不知道这个新功能。
  • 您应该在原始数组中记录每个值的索引。在合并步骤中,左侧部分和右侧部分排序良好。这时候可以知道左边的i和右边的j的对,如果A[i] > A[j],就可以求逆,加( index(j) - index(i)) 你的答案。
  • 在合并步骤中,我们不会检查所有差异。所以我不明白它对我们有什么帮助?
【解决方案2】:

您可以通过在排序序列中一个一个地插入数字并维护一个后缀和数组来解决这个问题。以下是它的工作原理:

对于数组中的每个元素,都有一对。一个包含数字本身,另一个包含它的索引。现在创建另一个数组并尝试将其插入排序序列中。

例如:[(5, 1), (2, 2), (3, 3), (4, 4), (1, 5)]

插入 5

[(5, 1)]

插入 2

[(2, 2), (5, 1)] => 贡献 (1*2 - 1) = > 1

插入 3

[(2, 2), (3, 3), (5, 1)] => 贡献 (1*3 - 1) => 2

插入 4

[(2, 2), (3, 3), (4, 4), (5, 1)] => 贡献 (1*4 - 1) => 3

插入 1

[(1, 5), (2, 2), (3, 3), (4, 4), (5, 1)] => 贡献 (5 * 4 - (10) ) => 10

将所有贡献相加,你得到 16

时间复杂度:O(N * log N)

【讨论】:

    【解决方案3】:

    这可以在二叉搜索树的帮助下轻松完成。 您需要维护右子树中存在的节点索引的总和以及右子树中存在的节点数。因此,每当您插入一个新节点并且它向任何节点的左侧移动时,距离都会更新

     `(no of nodes in right subtree* index of val which is to be inserted) - sum of indices of nodes present in right subtree)`
    

    让我们来看看输入 5, 2, 3, 4, 1

    第一个节点的 val 为 5,距离现在为 0;

    插入2后的情况

    sizeOfRightSubTree : 1 index: 1 sumOfIndicesOnRight: 0 inserted: 2, distance: 1

    插入3后

    sizeOfRightSubTree : 1 index: 2 sumOfIndicesOnRight: 0 inserted: 3, distance: 3

    插入4后

    sizeOfRightSubTree : 1 index: 3 sumOfIndicesOnRight: 0 inserted: 4, distance: 6

    插入 1 后。注意它必须向左移动两次才能到达最终位置,因此距离会更新两次。 sizeOfRightSubTree : 1 index: 4 sumOfIndicesOnRight: 0 sizeOfRightSubTree : 3 index: 4 sumOfIndicesOnRight: 6 inserted: 1, distance: 16

    下面是java代码

    public class DistanceFromSortedArray
    {
     class Node {
        int val;
        Node left;
        Node right;
        int index;
        int sumOfIndicesOnRight;
        int sizeOfRightSubTree;
    
    
        Node(int num, int index)
        {
            this.val = num;
            this.index = index;
            sizeOfRightSubTree = 1;
            sumOfIndicesOnRight = index;
        }
    
    
        void addIndexToRight(int index)
        {
            sizeOfRightSubTree++;
            sumOfIndicesOnRight += index;
        }
    
    
        int distance(int index)
        {
            return sizeOfRightSubTree*index - sumOfIndicesOnRight;
        }
    }
    
    private Node head;
    private int distance;
    
    public int calculate(int[] nums){
        head = null;
        distance = 0;
        for(int i=0; i<nums.length; i++){
            insert(nums[i], i);
        }
        return distance;
    }
    
    
    private void insert(int num, int index)
    {
        Node toInsert = new Node(num, index);
        if(head == null){
            head = toInsert;
            return;
        }
    
        Node current = head;
        Node previous = null;
        while (current != null){
            previous = current;
            if(current.val > num){
                distance += current.distance(index);
                current = current.left;
            }
            else {
                current.addIndexToRight(index);
                current = current.right;
            }
        }
    
    
        if(previous.val > num){
            previous.left = toInsert;
        }
        else {
            previous.right = toInsert;
        }
    
    }
    }
    

    这里有几个测试用例

    @Test
    public void calculate()
    {
        int[] nums = {5, 2, 3, 4, 1};
        assertEquals(16, new DistanceFromSortedArray().calculate(nums));
    }
    
    
    @Test
    public void reverseCalculate()
    {
        int[] nums = {5, 4, 3, 2, 1};
        assertEquals(20, new DistanceFromSortedArray().calculate(nums));
    }
    
    @Test
    public void SizeTwoCalculate()
    {
        int[] nums = {4, 5};
        assertEquals(0, new DistanceFromSortedArray().calculate(nums));
    
         int [] nums2 = {5, 4};
        assertEquals(1, new DistanceFromSortedArray().calculate(nums2));
    }
    
    
    @Test
    public void twistedCalculate()
    {
        int[] nums = {8, 3, 6, 5, 7, 1};
        assertEquals(26, new DistanceFromSortedArray().calculate(nums));
    }
    
    @Test
    public void AllSameCalculate()
    {
        int[] nums = {1, 1, 1, 1, 1, 1};
        assertEquals(0, new DistanceFromSortedArray().calculate(nums));
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-04
      • 1970-01-01
      • 1970-01-01
      • 2013-11-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多