【问题标题】:std::sort that also keeps track of number of unique entries at each levelstd::sort 还跟踪每个级别的唯一条目数
【发布时间】:2013-05-07 22:55:01
【问题描述】:

假设我有一个 std::vector。假设向量包含数字。让我们来看看这个 std::vector

1,3,5,4,3,4,5,1,6,3

std::sort<std::less<int>> will sort this into

1,1,3,3,3,4,4,5,5,6,

我将如何修改排序,以便在排序的同时,它还计算同一级别的数字数量。所以说除了排序,还会编译下面的字典[level is also int]

std::map<level, int>

<1, 2>
<2, 3>
<3, 2>
<4, 2>
<5, 1>
<6, 1>

所以有 2 个 1、3 个 3、2 个 4,等等。

我 [认为] 我需要这个的原因是因为我不想对向量进行排序,然后再次计算每个级别的重复数。一次完成似乎更快?

谢谢大家! bjskishore123 是最接近我所要求的内容,但所有的回答都让我受益匪浅。再次感谢。

【问题讨论】:

  • 您可以通过对元素使用 std::count 来单独创建地图。或遍历排序结果以自行构建地图。无需使排序过程复杂化。即使增强排序部分,映射所需的操作数也不应该减少。
  • 一步完成可能并不快,两遍可能最快。
  • “一次完成似乎更快。”您甚至都没有一次性排序,因此这作为基线并不准确。您可以在 O(NlogN + N) 中执行此操作,相当于 O(NlogN),只需单遍扫描排序列表,递增出现对向量并在移动到排序列表中的新值时添加新对.我认为您不太可能得到比这更快的解决方案,特别是因为sort() 真的不在乎存在多少“多少”东西;相反,它只关心 ordering。但是,如果您只关心计数,则不需要地图或集合。

标签: c++ algorithm std


【解决方案1】:

而不是使用向量,

在一个一个存储数字的同时,使用std::multiset容器

它按排序顺序在内部存储。

在存储每个数字时,使用地图来跟踪每个数字的出现次数。

map<int, int> m;

每次添加一个数字都做

m[num]++; 

因此,尽管您需要在 map 中迭代以获取每个出现次数,但无需再通过一次来计算出现次数。

================================================ ===============================

以下是不推荐的替代解决方案。 按照您的要求提供使用 STD::SORT 的方式

下面的代码使用比较函数来计算出现次数。

#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;

struct Elem
{
    int index;
    int num;
};

std::map<int, int> countMap; //Count map
std::map<int, bool> visitedMap;
bool compare(Elem a, Elem b)
{
    if(visitedMap[a.index] == false)
    {
        visitedMap[a.index] = true;
        countMap[a.num]++;
    }
    if(visitedMap[b.index] == false)
    {
        visitedMap[b.index] = true;
        countMap[b.num]++;
    }
    return a.num < b.num;
}

int main()
{
    vector<Elem> v;
    Elem e[5] = {{0, 10}, {1, 20}, {2, 30}, {3, 10}, {4, 20} };
    for(size_t i = 0; i < 5; i++)
        v.push_back(e[i]);

    std::sort(v.begin(), v.end(), compare);

    for(map<int, int>::iterator it = countMap.begin(); it != countMap.end(); it++)
        cout<<"Element : "<<it->first<<" occurred "<<it->second<<" times"<<endl;
} 

输出:

Element : 10 occurred 2 times
Element : 20 occurred 2 times
Element : 30 occurred 1 times

【讨论】:

  • 你的算法复杂度是O(n*logn²)
  • @Ian:哪一个?第一还是第二?
  • 排序+地图插入。您将遍历向量 n * log(n) 次进行排序。每次访问向量时,都会进行一次地图插入,即 O(logn)。我认为由此产生的复杂性是 O(n * logn²)
  • 不是O(n log n) + O(log n)
  • 我不这么认为。在排序时每次访问向量时都会进行映射插入,最多完成 n * logn 次 - O(nlogn) * O(logn) = O(nlogn²)。 O(n log n) + O(log n) 如果向量是有序的并且地图填充在不同的循环中,情况将是这样。但正如你所说,真的不建议使用你的实现。从理论上讲,它比排序 + 计数更无效:O(n log n) + O(n))。
【解决方案2】:

正如@bjskishore123 所说,您可以使用地图来保证您的套装的正确顺序。作为奖励,您将拥有一个优化的搜索结构(当然是地图)。

在地图中插入/搜索需要 O(log(n)) 时间,而遍历向量是 O(n)。因此,算法是 O(n*log(n))。这与任何需要比较元素的排序算法的复杂性相同:例如,合并排序或快速排序。

这是一个示例代码:

int tmp[] = {5,5,5,5,5,5,2,2,2,2,7,7,7,7,1,1,1,1,6,6,6,2,2,2,8,8,8,5,5};
std::vector<int> values(tmp, tmp + sizeof(tmp) / sizeof(tmp[0]));
std::map<int, int> map_values;
for_each(values.begin(), values.end(), [&](int value)
{
    map_values[value]++;
});

for(std::map<int, int>::iterator it = map_values.begin();  it != map_values.end(); it++)
{
    std::cout << it->first << ": " << it->second << "times";
}

输出:

1: 4times
2: 7times
5: 8times
6: 3times
7: 4times
8: 3times

【讨论】:

    【解决方案3】:

    我认为你不能一次完成。假设您提供了自己的自定义comparator 进行排序,以某种方式尝试计算重复项。

    但是,您可以在排序器中捕获的唯一内容当前正在比较的两个元素的值(可能是参考但无关紧要 /强>。您没有其他信息,因为std::sort 不会将任何其他信息传递给分拣机。

    现在std::sort 的工作方式将继续交换元素,直到它们到达排序向量中的正确位置。这意味着单个成员可以多次发送到分拣机,因此无法准确计数。 您可以计算某个元素和与其相等的所有其他值被移动了多少次,但不能准确计算其中有多少。

    【讨论】:

    • 是的,使用 std::sort 可能是不可能的。
    • @Ian:这是可能的,但必须像我的第二个解决方案一样使用大量不必要的存储和低效的算法。
    • @bjskishore123 当然可以。我并不是想说这完全不可能。我只是想说这并不是真正的“一次通过”,您按排序计数。当然,如果你加倍努力,这是可能的。
    【解决方案4】:

    如果您有很多重复项,完成此任务的最快方法可能是首先使用哈希映射计算重复项,即O(n),然后对映射进行排序,即O(m log m),其中m是唯一值的数量。

    类似这样的东西(在 c++11 中):

    #include <algorithm>
    #include <unordered_map>
    #include <utility>
    #include <vector>
    
    std::vector<std::pair<int, int>> uniqsort(const std::vector<int>& v) {
      std::unordered_map<int, int> count;
      for (auto& val : v) ++count[val];
      std::vector<std::pair<int, int>> result(count.begin(), count.end());
      std::sort(result.begin(), result.end());
      return result;
    }
    

    主题有很多变化,具体取决于您的需要。例如,也许您甚至不需要对结果进行排序;也许只有计数图就足够了。或者,您可能希望结果是从 int 到 int 的排序映射,在这种情况下,您可以构建一个常规的 std::map,而不是。 (那就是O(n log m)。)或者你可能知道一些使它们排序更快的值(比如它们是已知范围内的小整数。)等等。

    【讨论】:

    • 感谢大家的慷慨帮助!
    猜你喜欢
    • 2019-09-03
    • 1970-01-01
    • 2023-03-04
    • 1970-01-01
    • 2015-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多