std::sort, который также отслеживает количество уникальных записей на каждом уровне.

Скажем, у меня есть 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,

Как бы мне изменить сортировку, чтобы она одновременно сортировала и вычисляла количество чисел на том же уровне. Итак, скажем, в дополнение к сортировке он также скомпилирует следующий словарь [уровень также int]

std::map<level, int>

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

так что есть 2 1, 3 3, 2 4 и так далее.

Причина, по которой я [думаю], что мне это нужно, заключается в том, что я не хочу сортировать вектор, ТОГДА еще раз вычислять количество дубликатов на каждом уровне. Кажется, быстрее сделать это за один проход?

Спасибо вам всем! bjskishore123 ближе всего к тому, о чем я спрашивал, но все ответы меня многому научили. Спасибо еще раз.


person user1676605    schedule 13.05.2013    source источник
comment
Вы можете создать карту отдельно, используя std::count для элементов. или повторите результаты сортировки, чтобы построить карту самостоятельно. Не нужно усложнять процесс сортировки. Количество операций, необходимых для карты, нельзя уменьшить, даже если вы улучшите часть сортировки.   -  person taocp    schedule 13.05.2013
comment
Вероятно, это не быстрее сделать это за один шаг, вероятно, быстрее всего сделать два прохода.   -  person Mooing Duck    schedule 14.05.2013
comment
Кажется, быстрее сделать это за один проход. Вы даже не выполняете сортировку за один проход, так что это не точно в качестве базового уровня. Вы можете сделать это за O(NlogN + N), что эквивалентно O(NlogN), просто просканировав отсортированный список за один проход, увеличив вектор пары вхождений и добавив новую пару при переходе к новому значению в отсортированном списке. . Я думаю, маловероятно, что вы получите более быстрое решение, тем более что sort() действительно все равно, сколько чего-то присутствует; скорее, он заботится только о порядке. Но вам не нужна карта ИЛИ набор, если все, о чем вы заботитесь, это подсчеты.   -  person WhozCraig    schedule 14.05.2013


Ответы (4)


Как заявил @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
person Ian Medeiros    schedule 13.05.2013

Я не думаю, что вы можете сделать это за один проход. Допустим, вы предоставляете свой собственный comparator для сортировки, который каким-то образом пытается подсчитать дубликаты.

Однако единственное, что вы можете зафиксировать в сортировщике, это значение (может быть ссылка, но не имеет значения) текущих двух сравниваемых элементов . У вас нет другой информации, потому что std::sort больше ничего не передает сортировщику.

Теперь, как работает std::sort, элементы будут меняться местами, пока они не достигнут нужного места в отсортированном векторе. Это означает, что один элемент может быть отправлен сортировщику несколько раз, что делает невозможным точный подсчет. Вы можете подсчитать, сколько раз определенный элемент и все другие значения, равные ему, были перемещены, но не можете точно определить, сколько из них там находится.

person stardust    schedule 13.05.2013
comment
Да, используя std::sort, это, вероятно, невозможно. - person Ian Medeiros; 14.05.2013
comment
@Ian: Это возможно, но нужно использовать много ненужного хранилища и неэффективного алгоритма, как мое второе решение. - person bjskishore123; 14.05.2013
comment
@bjskishore123 Да, конечно. Я не пытался сказать, что это невозможно полностью. Я просто пытался сказать, что на самом деле это не один проход, когда вы учитываете сортировку. Конечно, это возможно, если вы сделаете все возможное. - person stardust; 14.05.2013

Вместо использования вектора

При сохранении номеров по одному используйте контейнер std::multiset

Он хранится внутри в отсортированном порядке.

При сохранении каждого числа используйте карту, чтобы отслеживать количество вхождений каждого числа.

map<int, int> m;

Каждый раз, когда добавляется число, выполните

m[num]++; 

Таким образом, нет необходимости в другом проходе для подсчета количества вхождений, хотя вам нужно выполнить итерацию по карте, чтобы получить количество каждого вхождения.

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

НИЖЕ ПРЕДСТАВЛЯЕТСЯ АЛЬТЕРНАТИВНОЕ РЕШЕНИЕ, КОТОРОЕ НЕ РЕКОМЕНДУЕТСЯ . ДАВАТЬ ЭТО, КАК ВЫ ПРОСИЛИ, СПОСОБ ИСПОЛЬЗОВАНИЯ 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
person bjskishore123    schedule 13.05.2013
comment
Сложность вашего алгоритма O(n*logn²) - person Ian Medeiros; 14.05.2013
comment
@ Ян: Какой? первый или второй? - person bjskishore123; 14.05.2013
comment
Сортировка + вставка карты. Вы будете проходить вектор n * log (n) раз для сортировки. Каждый раз, когда вы обращаетесь к вектору, выполняется вставка карты, это O (logn). Я думаю, что полученная сложность равна O (n * logn²) - person Ian Medeiros; 14.05.2013
comment
Разве это не O (n log n) + O (log n) - person bjskishore123; 14.05.2013
comment
Я так не думаю. Вставка карты выполняется при каждом обращении к вектору при упорядочении, что выполняется n * logn раз - O(nlogn) * O(logn) = O(nlogn²). O(n log n) + O(log n) было бы, если бы вектор был упорядочен, а карта заполнялась в разных циклах. Но, как вы сказали, действительно не рекомендуется использовать вашу реализацию. Теоретически это более неэффективно, чем: упорядочение + подсчет: O (n log n) + O (n)). - person Ian Medeiros; 14.05.2013
comment
Вставка карты происходит только один раз для каждого вхождения элемента. Посещенная карта проверяется перед обновлением карты подсчета. Таким образом, карта не обновляется для каждого доступа к вектору. - person bjskishore123; 14.05.2013
comment
Вы не считаете доступ к VisitMap? Это также O (logn). Вы делаете доступ к 3 картам в худшем случае.... - person Ian Medeiros; 14.05.2013
comment
о, да. мы можем заменить посещенную карту простым логическим массивом. - person bjskishore123; 14.05.2013

Если у вас много дубликатов, самый быстрый способ выполнить эту задачу, вероятно, состоит в том, чтобы сначала подсчитать дубликаты с помощью хэш-карты, то есть O(n), а затем отсортировать карту, то есть O(m log m), где m — количество уникальных значений.

Что-то вроде этого (в С++ 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).) Или, может быть, вы знаете что-то о значениях, которые ускоряют их сортировку (например, тот факт, что они представляют собой небольшие целые числа в известном диапазоне). И так далее.

person rici    schedule 13.05.2013
comment
Спасибо всем за вашу щедрую помощь! - person user1676605; 14.05.2013