如何使用合并排序删除重复项?

时间:2009-11-15 20:11:04

标签: c algorithm mergesort

我使用递归合并排序来排序链接列表,但在合并排序期间我想删除重复项。任何人都知道如何实现这一目标?

我正在使用C代码。

6 个答案:

答案 0 :(得分:8)

在合并排序中,您需要重复应用以下规则:(/ p>)

  • 找到每个输入列表顶部的较小/最少的项目,如果存在平局则选择任何最低项目
  • 从列表中删除该项目
  • 将其添加到您的输出列表

要删除重复项,您只需稍微修改一下规则:

  • 找到每个输入列表顶部的较小/最少的项目,如果存在平局则选择任何最低项目
  • 从列表中删除该项目
  • 如果它与您添加到输出列表中的最后一项相同,则将其丢弃
  • 否则,将其添加到您的输出列表

这将确保输出列表中没有两个连续项目相同,并且其中的项目是有序的,这就是您所追求的。

答案 1 :(得分:7)

要使用合并排序删除重复项,您将忽略在合并过程中重复的元素。

答案 2 :(得分:1)

考虑mergesort中的合并功能。

在合并过程中,您当然要将元素相互比较。

说服自己,如果你要合并2个排序列表A和B,并且如果两个列表包含相同的值x,那么发生两个相同的元素将与一个相比较另一个。如果你想要一个证明,我的方法是表明如果有两个相同的元素比较的情况,那么一个或两个列表实际上是未排序的。通过矛盾证明,宝贝!

因此,您可以轻松检测到两个列表中有两个相同元素被合并的情况。

接下来,说服自己如果两个列表中有两个相同的元素而不是刚刚合并,最终它们将合并在一起,并且将检测到相同的元素。这基本上是归纳的证据---如果没有别的,显然最后的合并(将长度为n / 2和n / 2的排序列表合并到长度为n的最终列表中)将把这些元素组合在一起。

最后,如果你递归到n = 1或n = 0的情况,说服自己不能存在具有两个相同元素的单数列表。这又是归纳法,因为当然任何较大的列表都必须首先在第一个大段所描述的“过滤”过程中存活下来。

如果您确信这三件事,那么Steven或Tim的解决方案显然非常合适。

答案 3 :(得分:1)

使用Collection Iterators而不仅仅是vector来更新我的原始答案,使用一些更通用的代码。

// merge a sort collection
template<typename CollectionT>
void mergeCollection(CollectionT & collection, CollectionT & tmpCollection,
        typename CollectionT::iterator first, typename CollectionT::iterator mid,
        typename CollectionT::iterator last) {
    using IteratorType = typename CollectionT::iterator;

    IteratorType left = first;
    IteratorType  leftEnd = mid;

    IteratorType temp = tmpCollection.begin();
    auto const distance = std::distance(collection.begin(), first);
    std::advance(temp, distance);

    IteratorType right = mid;
    IteratorType rightEnd = last;

    // finger matching algo left and right
    while (left != leftEnd && right != rightEnd) {
        // this first if block here for equals is what does your duplicate removal magic
        if (*left == *right) {
            *temp++ = *left++;
            *temp++ = *right++;  // ++right for non-duplicate
        }
        else if (*left < *right) {
            *temp++ = *left++;
        }
        else {
            *temp++ = *right++;
        }
    }

    // copy rest of left
    while (left != leftEnd) {
        *temp++ = *left++;
    }

    // copy rest of right
    while (right != rightEnd) {
        *temp++ = *right++;
    }

    collection = tmpCollection;
}

template<typename CollectionT>
void mergeSortCollection(CollectionT & collection, CollectionT & tmpCollection, typename CollectionT::iterator first, typename CollectionT::iterator last) {

    auto const distance = std::distance(first, last);
    if(distance > 1) {
        // get mid iterator
        auto mid = first;
        std::advance(mid, distance / 2);
        mergeSortCollection(collection, tmpCollection, first, mid);
        mergeSortCollection(collection, tmpCollection, mid, last);
        mergeCollection(collection, tmpCollection, first, mid, last);
    }
}

template<typename CollectionT>
void mergeSortCollection(CollectionT & collection) {
    CollectionT tmpCollection {collection};
    mergeSortCollection(collection, tmpCollection, collection.begin(), collection.end());
}

}

一些测试代码:

namespace { 
    template<typename It>
    auto printCollection =
            [](std::ostream& out, It const begin, It const end, std::string const & message = "") {
                using ValueType = typename std::iterator_traits<It>::value_type;
                out << message;
                std::copy(begin, end, std::ostream_iterator<ValueType>(out, ", "));
                out << std::endl;
            };
}

    TEST(Sort, MergeSortCollectionVector) {
        std::vector<int32_t> before = { 83, 86, 77, 15, 93, 35, 86, 92, 49, 21 };
        std::vector<int32_t> original = before;
        std::vector<int32_t> after = { 15, 21, 35, 49, 77, 83, 86, 86, 92, 93 };

        printCollection<decltype(before.begin())>(std::cout, before.begin(), before.end(), "BEFORE sort: ");
        mergeSortCollection(before);

        printCollection<decltype(before.begin())>(std::cout, before.begin(), before.end(), "AFTER sort: ");
        EXPECT_EQ(after, before);
        EXPECT_NE(original, before);
    }

    TEST(Sort, MergeSortCollectionList) {
        std::list<int32_t> before = { 83, 86, 77, 15, 93, 35, 86, 92, 49, 21 };
        std::list<int32_t> original = before;
        std::list<int32_t> after = { 15, 21, 35, 49, 77, 83, 86, 86, 92, 93 };

        printCollection<decltype(before.begin())>(std::cout, before.begin(), before.end(), "BEFORE sort: ");
        mergeSortCollection(before);

        printCollection<decltype(before.begin())>(std::cout, before.begin(), before.end(), "AFTER sort: ");
        EXPECT_EQ(after, before);
        EXPECT_NE(original, before);
    }

正如其他人指出的那样,您需要对merge流程进行一些修改以满足您的需求。以下是修改后的merge()函数供您参考(原文为here

function merge(left,right)
var list result
while length(left) > 0 and length(right) > 0
    if first(left) < first(right)    // <--- change from <= to <
        append first(left) to result
        left = rest(left)
    else if first(left) > first(right)
        append first(right) to result
        right = rest(right)
    else        // <----- added case to remove duplicated items
        append first(right) to result
        left = rest(left)
        right = rest(right)
    end

end while
if length(left) > 0 
    append left to result
else  
    append right to result
return result

答案 4 :(得分:1)

使用C ++但你可以使用数组代替C

的向量
#include <iostream>
#include <vector>

// merge 2 arrays using a temp array
void merge (std::vector<int>& v, std::vector<int>& tmpArray, int left, int center, int right ) 
{
  int leftPos = left;
  int leftEnd = center;

  int tmpPos = leftPos;

  int rightEnd = right;
  int rightPos = center + 1;

  // finger matching algo left and right
  while ( leftPos <= leftEnd && rightPos <= rightEnd )
  {
     // this first if block here for equals is what does your duplicate removal magic
    if ( v[leftPos] == v[rightPos] )
    {
      tmpArray[tmpPos++] = std::move(v[leftPos++]);
      ++rightPos;
    }
    else if ( v[leftPos] < v[rightPos] )
      tmpArray[tmpPos++] = std::move(v[leftPos++]);
    else
      tmpArray[tmpPos++] = std::move(v[rightPos++]);
  }

  // copy rest of left
  while ( leftPos <= leftEnd )
    tmpArray[tmpPos++] = std::move(v[leftPos++]);

  // copy rest of right
  while ( rightPos <= rightEnd )
    tmpArray[tmpPos++] = std::move(v[rightPos++]);

  // copy tmp array back to array
  int numElements = right - left  + 1;
  for ( int i = 0; i < numElements; ++i, --rightEnd)
    v[rightEnd]=std::move(tmpArray[rightEnd]);
}

void mergeSort ( std::vector<int>& v, std::vector<int>& tmpArray, int left, int right )
{
  if ( left < right )
  {
    auto center = left + (right - left)/2;
    mergeSort(v, tmpArray, left, center);
    mergeSort(v, tmpArray, center+1, right);
    merge ( v, tmpArray, left, center, right );
  }
}

void mergeSort (std::vector<int>& v)
{
  int sz = v.size();
  std::vector<int> tmpArray ( sz, 0 );
  mergeSort (v, tmpArray, 0, sz-1);
}

int main ()
{
  std::vector<int> v { 7,8,6,5,4,3,9,12,14,17,21,1,-2,-3,-3,-3,-9,10,11 };
  mergeSort ( v );
  for ( auto&i : v)
    std::cout << i << " " ;
  std::cout << std::endl;
}

答案 5 :(得分:0)

或者只是使用任何排序,当它完成时,扫描排序列表并删除重复的元素(它们自然会彼此相邻)