【问题标题】:Merge sort remove duplicates合并排序删除重复项
【发布时间】:2013-05-02 06:47:17
【问题描述】:

我正在尝试通过合并排序对数组进行排序,并在排序时删除我认为相等的元素。我递归地调用合并排序然后合并。

我到了这一点,发现ac重复

a b | c d

我根据某些标准确定我想要哪一个,然后选择 c。我增加右手计数器和左手计数器并比较b和d。说我选d,然后我选b。我希望我的最终列表只有元素

c d b  

但是,在下一次递归调用中发生了什么,startend 分别为 0 和 3,因此 d 在下一次调用时在数组中列出了两次。合并过程使用的数组是:

c d b d

这里是代码。提前致谢。

private static void merge(int[] data, int start, int mid, int end)
{
    int firstCopied=0;
    int secondCopied=0;
    int index=0;
    int length=end-start+1;

    int[] temp = new int[end-start+1];
    int firstSize=mid-start+1;
    int secondSize=end-mid;

    while(firstCopied < firstSize && secondCopied < secondSize)
    {
        if(data[start+firstCopied] < data[mid+1+secondCopied])
        {
            temp[index++] = data[start+firstCopied];
            firstCopied++;
        }

        else if(data[start+firstCopied] > data[mid+1+secondCopied])
        {
            temp[index++] = data[mid+1+secondCopied];
            secondCopied++;
        }

        else if(data[start+firstCopied]==data[mid+1+secondCopied])
        {
            boolean result = PickOne();

            if(result)
            {
                temp[index++] = data[start+firstCopied];
            }
            else
            {
                temp[index++] = data[mid+1+secondCopied];
            }

            firstCopied++;
            secondCopied++;
            length--;
        }
    }
    while(firstCopied < firstSize)
    {
        temp[index++] = data[start+firstCopied];
        firstCopied++;
    }

    while(secondCopied < secondSize)
    {
        temp[index++] = data[mid+1+secondCopied];
        secondCopied++;
    }

    for(int i=0; i<length; i++)
    {
        data[start+i]=temp[i];
    }

}

【问题讨论】:

  • PickOne() 是做什么的?
  • 在我看来,mergesort 已经足够复杂了,无需交织特殊用途的代码来删除其中的重复项。我建议使用两个单独的功能:首先对数据进行合并排序,然后删除重复项,这在排序后的数据中可能是连续的,因此很容易找到。
  • 您已标记此 C 和 C++,但 private static void ...int[] temp = new int[end-start+1]; 表明这是另一种语言。您实际使用的是哪种语言?
  • 带有重复删除的合并排序修改的工作示例(在 Delphi 中):stackoverflow.com/questions/12673633/…
  • 感谢您的回复。我将其标记为mergeSort,但有人更改了它。这种语言是Java。我已经编写了代码来对数组进行后处理,但有人告诉我在合并时删除 dups。我还没有看到任何代码,只是模糊的建议“不要将重复项添加到您的列表中”。这不是问题,问题在于 mergeSort 是递归的,您无法更改它在每一步中考虑的数组的“块”。必须有办法做到这一点。

标签: java sorting mergesort


【解决方案1】:

C++ 标准库的理念是使用做好一件事的算法。最好遵循这种方法,因为它会产生更多可重用的代码。

例如这是一个合并排序草图,然后调用std::unique

template<typename BiDirIt>
void merge_sort(BiDirIt first, BiDirIt last)
{
    auto const N = std::distance(first, last);
    if (N < 2) return;

    // sort each part individually, then merge back in-place
    auto middle = first + N / 2;
    merge_sort(first, middle);
    merge_sort(middle, last);
    std::inplace_merge(first, middle, last);
}    

int data[] = { /* your data */ };
merge_sort(std::begin(data), std::end(data));

auto it = std::unique(std::begin(data), std::end(data));
for (auto ut = std::begin(data); ut != it; ++ut) {
    // process unique data
}

如果您的数据位于 std::vector 而不是 C 数组中,您可以调用 v.erase(v.begin(), it); 来实际擦除非唯一数据。

【讨论】:

  • 我很惊讶您使用自定义代码进行排序并依赖默认值来删除唯一元素。我宁愿反过来做,因为std::sortstd::stable_sort 应该给出相同的结果,但故意在两个相等的元素之间进行选择(如PickOne 大概是这样)不是@987654321 工作的一部分@.
  • @MvG 我写了一个自定义的merge_sort 来展示用递归+std::inplace_merge 编写它是多么容易,而不是作为std::stable_sort 的替代品。也许我忽略了PickOne 选择标准,但我回答的要点是让一种算法做一件事。
【解决方案2】:

您的merge 从概念上改变了数组的长度。但实际上没有截断data 的代码。我建议您返回length(而不是void)并使用一些最后的后处理步骤将数据截断为最终长度,或者至少避免打印那些过去的元素。

【讨论】:

    【解决方案3】:

    首先确保 [start, mid] 和 [mid + 1, end] 中的元素已排序且唯一。 否则,代码运行后将存在重复项。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-03-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-24
      • 2010-12-16
      • 2013-12-03
      相关资源
      最近更新 更多