【问题标题】:Counting inversion using merge sort使用归并排序计算反转
【发布时间】:2016-07-22 21:02:34
【问题描述】:

我知道 Stack 中有很多这样的实现,但是我遇到了一个我无法处理的问题。

首先我在 khanacademy 使用 javascript 实现了合并排序,然后我将代码重写为 C++,并尝试计算数组中的反转数。

我尽我所能,并花了一个小时试图了解我做错了什么。我确实在堆栈中搜索了另一个实现并尝试更正我的代码。不幸的是,我不知道我做错了什么。我认为我计算了每次反转。在此先感谢您在了解问题所在方面提供的帮助。

我的代码:

int lowhalflength(int p, int q)
{
    return q - p + 1;
}

int highhalflength(int q, int r)
{
    return r - q;
}


int merge(int array[], int p, int q, int r, int lowhalf[], int highhalf[])
{
    int k = p;
    int i;
    int j;
    int count = 0;
    for (int i = 0; k <= q; i++ , k++)
    {
        lowhalf[i] = array[k];
    }
    for (int i = 0; k <= r; i++ , k++)
    {
        highhalf[i] = array[k];
    }

    k = p;
    i = 0;
    j = 0;

    while (i <= (q - p) && j <= r - (q + 1))
    {
        if (lowhalf[i] <= highhalf[j])
        {
            array[k] = lowhalf[i];
            i++;
        }
        else
        {
            array[k] = highhalf[j];
            j++;
            count += q - 1;
        }

        k++;
    }

    while (i < lowhalflength(p, q))
    {
        array[k] = lowhalf[i];
        k++;
        i++;
    }

    while (j < highhalflength(q, r))
    {
        array[k] = highhalf[j];
        k++;
        j++;
    }


    return count;
}

mergeSort 函数:

int mergeSort(int array[], int p, int r)
{
    int q = ((p + r) / 2);
    int* lowhalf = new int[lowhalflength(p, q)];
    int* highhalf = new int[highhalflength(q, r)];

    int count = 0;
    if (p < r)
    {
        q = ((p + r) / 2);
        count = mergeSort(array, p, q);
        count += mergeSort(array, q + 1, r);
        count += merge(array, p, q, r, lowhalf, highhalf);
    }
    delete[] lowhalf;
    delete[] highhalf;
    return count;
}

对于数组 [10, 9, 8, 7, 6, 5, 4, 3, 2, 1],输出是 46,而应该是 45。

编辑: 答案是把下面这行q-1改成q+j-k.我自己找的但是不知道怎么解释。任何提示或证明为什么它是正确的都是非常可取的。

【问题讨论】:

  • 可能是学习如何使用调试器单步调试代码并观察每一步后变量值变化的良好起点。
  • 你最初在哪里调用mergeSort?
  • @o_weisman 我像这样使用mergeSort。如果我在数组中有 10 个元素,我会调用 cout
  • 你的算法和我的有点不同,但count += q - 1 不应该更像count = (q - p) - i 吗?假设我正确理解了您的索引(可以肯定的是,这方面的机会很小)。 ?仅供参考,我常用的算法将所有中等范围的计算都隐藏起来,以支持指针数学。 something like this。从根本上讲,累积值是每当您从围栏的右侧移动某物时,从中点到下方攀爬左侧索引的距离就是该累积值。
  • @WhozCraig 不幸的是它没有给出正确的输出。;(

标签: c++ recursion mergesort divide-and-conquer


【解决方案1】:

您可以使用我的代码来计算反转对,并且您的合并函数应该以更有效的方式如下所示:

int merge(int *array, int lower, int mid, int upper) {

    // Initialisation of the sizes of two subarrays and subarrays also.
    int left_array_size = mid - lower + 1;
    int right_array_size = upper - mid;
    int left_array[left_array_size], right_array[right_array_size];

    int j = 0;
    for (int i = lower; i <= mid; i++) {
        left_array[j++] = array[i];
    }
    j = 0;
    for (int i = mid + 1; i <= upper; i++) {
        right_array[j++] = array[i];
    }

    // Performing merging in a non-increasing manner and count inversion pairs..
    int i = 0, k;
    j = 0;
    int resultIntermediate = 0;
    for (k = lower; k <= upper; ) {
        if (left_array[i] <= right_array[j]) {
            array[k++] = left_array[i++];
            if (i >= left_array_size)   break;
        }
        else {
            array[k++] = right_array[j++];

            // If a element in left_array_size is greater than an element from
            // right_array_size then rest of all other elements will also be
            // greater than that element of right_array_size because both
            // subarrays are sorted in non-decreasing order.
            resultIntermediate += left_array_size - i;

            if (j >= right_array_size)  break;
        }
    } //end of for loop.


    // Performing merging if i or j doesn't reach to its
    // maximum value i.e. size of the subarrays.
    while (i < left_array_size) {
        array[k++] = left_array[i++];
    }
    while (j < right_array_size) {
        array[k++] = right_array[j++];
    }

    // Returning the result...
    return resultIntermediate;

} //end of the merge function.

以及计算反转对的功能

int countInversionPair(int *array, int lower, int upper) {
    int count_inv_pair = 0;
    // Do recusion untill the problem / array can be subdevided.
    if (lower < upper) {

        // Partition the Array into two subproblems.
        int mid = (lower + upper) / 2;

        // Call the countInversionPair() function for these two
        // subarrays / subproblems recursively to count number of
        // inversion for these subproblems / subarrays.
        count_inv_pair = countInversionPair(array, lower, mid);
        count_inv_pair += countInversionPair(array, mid + 1, upper);

        // Merge these two subarrays into a sigle array
        count_inv_pair += merge(array, lower, mid, upper);
    }
    return count_inv_pair;
}

现在您可以通过从 main 调用函数来获取反转对的数量:

int count_inv_pair = countInversionPair(array, 0, size - 1);

现在你会得到你的答案..

【讨论】:

  • 非常感谢您的回答,但我不想只是复制您的工作,我想从我的代码中获取它而不是复制您的代码。
  • @Charllie 态度很好,但要更好更快地学习一些东西,不要只是复制我的代码。将我的“合并”功能与您的“合并”功能进行比较,并尝试看看您错在哪里......这是在编码方面专门学习一些东西的最佳方法..
  • 我认为我真的很接近从我的代码中获得正确的值,但我仍然缺少一些东西。
  • 感谢您的回答。
【解决方案2】:

非常感谢你们所有人,尤其是@Shiv 和@WhozCraig,你们给了我解决问题的方法。答案是将q-1改为q+j-k

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-11-20
    • 2014-08-04
    • 2014-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多