【问题标题】:Merge sorted arrays - Efficient solution合并排序数组 - 高效的解决方案
【发布时间】:2010-07-23 20:01:16
【问题描述】:

这里的目标是将多个已经排序的数组合并成一个结果数组。

我写了以下解决方案,想知道是否有办法改进解决方案

/*
    Goal is to merge all sorted arrays
*/
void mergeAll(const vector< vector<int> >& listOfIntegers,  vector<int>& result)
{

    int totalNumbers = listOfIntegers.size();
    vector<int> curpos;
    int currow = 0 , minElement , foundMinAt = 0;
    curpos.reserve(totalNumbers);

    // Set the current position that was travered to 0 in all the array elements
    for ( int i = 0; i < totalNumbers; ++i)
    {
        curpos.push_back(0);
    }

    for ( ; ; )
    {
        /*  Find the first minimum 
            Which is basically the first element in the array that hasn't been fully traversed
        */

        for ( currow = 0 ; currow < totalNumbers ; ++currow)
        {
            if ( curpos[currow] < listOfIntegers[currow].size() )
            {
                minElement = listOfIntegers[currow][curpos[currow] ];
                foundMinAt = currow;
                break;
            }
        }
        /* If all the elements were traversed in all the arrays, then no further work needs to be done */
        if ( !(currow < totalNumbers ) )
            break;
        /* 
            Traverse each of the array and find out the first available minimum value
        */
        for ( ;currow < totalNumbers; ++currow)
        {
            if ( listOfIntegers[currow][curpos[currow] ] < minElement )
            {
                minElement = listOfIntegers[currow][curpos[currow] ];
                foundMinAt = currow;
            }
        }
        /* 
            Store the minimum into the resultant array 
            and increment the element traversed
        */
        result.push_back(minElement);
        ++curpos[foundMinAt];
    }
}

对应的main是这样的。

int main()
{
    vector< vector<int> > myInt;
    vector<int> result;

    myInt.push_back(vector<int>() );
    myInt.push_back(vector<int>() );
    myInt.push_back(vector<int>() );

    myInt[0].push_back(10);
    myInt[0].push_back(12);
    myInt[0].push_back(15);


    myInt[1].push_back(20);
    myInt[1].push_back(21);
    myInt[1].push_back(22);

    myInt[2].push_back(14);
    myInt[2].push_back(17);
    myInt[2].push_back(30);

    mergeAll(myInt,result);

    for ( int i = 0; i < result.size() ; ++i)
    {
        cout << result[i] << endl;
    }
}

【问题讨论】:

  • 这似乎和stackoverflow.com/questions/886178/…是同一个问题
  • 也许我遗漏了什么,但这不是std::set_union吗?
  • 它将是 "n - 1" set_union s,这可能比一次构建它的效率低。

标签: c++ algorithm stl


【解决方案1】:

您可以概括合并排序算法并使用多个指针。最初,它们都指向每个数组的开头。您在优先级队列中维护这些指针(按它们指向的值)排序。在每一步中,您删除O(log n) 中堆中的最小元素(n 是数组的数量)。然后输出提取的指针指向的元素。现在你在一个位置增加这个指针,如果你没有到达数组的末尾,重新插入O(log n)的优先级队列。以这种方式进行,直到堆不为空。如果总共有 m 个元素,则复杂度为O(m log n)。元素以这种方式按排序顺序输出。

【讨论】:

  • 对我来说听起来很不错!我刚刚验证了堆支持的优先级队列的性能实际上只是 log n。
【解决方案2】:

也许我误解了这个问题......我觉得我误解了你的解决方案。

也就是说,也许这个答案完全不合时宜,没有帮助。

但是,尤其是您已经使用的vectors 和push_back 的数量,为什么不直接使用std::sort

#include <algorithm>
void mergeAll(const vector<vector<int>> &origList, vector<int> &resultList)
{
    for(int i = 0; i < origList.size(); ++i)
    {
        resultList.insert(resultList.end(), origList[i].begin(), origList[i].end());
    }
    std::sort(resultList.begin(), resultList.end());
}

如果这与您要查找的内容完全不同,我深表歉意。但这是我理解问题和解决方案的方式。

std::sortO(N log (N)) http://www.cppreference.com/wiki/stl/algorithm/sort 中运行

【讨论】:

  • 我不喜欢排序。合并是我热衷的。想知道是否有更快的方法来合并元素。
  • 合并排序也平均运行O(N log (N))en.wikipedia.org/wiki/Merge_sort#Analysis 我认为这两种解决方案在性能方面都没有太大的不同。但是,复杂性、稳定性和维护可能会受到影响。我将通过使用 std 函数来简化这一步,并继续在其他地方寻找我的性能提升。
【解决方案3】:

我在互联网上看到了一些合并两个排序数组的解决方案,但其中大多数都非常麻烦。我更改了一些逻辑以提供我能想到的最短版本:

void merge(const int list1[], int size1, const int list2[], int size2, int list3[]) {

    // Declaration & Initialization
    int index1 = 0, index2 = 0, index3 = 0;

    // Loop untill both arrays have reached their upper bound.
    while (index1 < size1 || index2 < size2) {

        // Make sure the first array hasn't reached 
        // its upper bound already and make sure we 
        // don't compare outside bounds of the second 
        // array.
        if ((list1[index1] <= list2[index2] && index1 < size1) || index2 >= size2) {
            list3[index3] = list1[index1];
            index1++;
        }
        else {
            list3[index3] = list2[index2];
            index2++;
        }
        index3++;
    }
}

【讨论】:

    【解决方案4】:

    如果您想利用多线程,那么一个相当好的解决方案是一次只合并 2 个列表。

    假设你有 9 个列表。

    将列表 0 与 1 合并。 将列表 2 与 3 合并。 将列表 4 与 5 合并。 将列表 6 与 7 合并。

    这些可以同时执行。

    然后:

    将列表 0&1 与 2&3 合并 将列表 4&5 与 6&7 合并

    同样,这些可以同时执行。

    然后将列表 0,1,2&3 与列表 4,5,6&7 合并

    最后将列表 0,1,2,3,4,5,6&7 与列表 8 合并。

    工作完成。

    我不确定它的复杂性,但它似乎是显而易见的解决方案,并且在某种程度上确实具有多线程的好处。

    【讨论】:

      【解决方案5】:

      考虑上面评论中链接的此答案中的优先级队列实现:Merging 8 sorted lists in c++, which algorithm should I use

      这是 O(n lg m) 时间(其中 n = 项目总数,m = 列表数)。

      【讨论】:

        【解决方案6】:

        您所需要的只是两个指针(或只是 int 索引计数器),检查数组 A 和 B 之间的最小值,将值复制到结果列表中,并递增最小值所在数组的指针。如果一个源数组中的元素用完,请将第二个数组的剩余部分复制到结果中,然后就完成了。

        编辑: 您可以轻松地将其扩展为 N 个数组。

        编辑: 不要简单地将其扩展到 N 个数组:-)。一次做两个。傻我。

        【讨论】:

        • 就像 Mad 所说的,他正在合并任意数量的数组。
        • 该方法被简单地扩展为任意数量的数组。 “你只需要 n 个指针,检查所有 n 个数组之间的最小值,...”
        • 这使得复杂度为 O(NM²),其中 N 是最长数组的长度,M 是数组的数量。 Goz 的解决方案只运行 O(NM log M)。
        • Nabb,你就是那个男人,但你能详细说明你是怎么看到的吗?
        【解决方案7】:

        如果您将 非常多 个向量合并在一起,那么您可以通过使用一种树来确定哪个向量包含最小元素来加快性能。这对于您的应用程序可能不是必需的,但如果是,请发表评论,我会尝试解决。

        【讨论】:

        • 其实,堆就是你要找的东西。
        【解决方案8】:

        您可以将它们全部放入一个多重集合中。这将为您处理排序。

        【讨论】:

        • 这是一个非常糟糕的主意。首先,多重集很慢,因此您不妨将所有列表复制到另一个向量中并 std::sort 该向量,这样会快得多。其次,这只比log totalElements &lt; numberOfLists合并更好。
        • 谁在乎它是否“慢”?你为什么认为它需要快速燃烧?问题中没有任何内容说明这是针对高频交易,或者输入向量是数十亿个元素。那是明显的过早优化。
        • @user275455 我认为关键在于将所有向量简单地连接成一个向量,然后进行排序,这将与多重集一样简单和快速。
        • @userN:用户要求一个有效的实现,我们尝试给他一个。如果他想要一个简单的实现,他就不必问我们了。
        猜你喜欢
        • 2013-08-22
        • 2014-06-08
        • 2018-01-21
        • 1970-01-01
        • 1970-01-01
        • 2016-11-14
        • 1970-01-01
        • 2022-01-18
        • 1970-01-01
        相关资源
        最近更新 更多