Пользовательский вектор сортировки пары на основе их значений

У меня есть следующий вектор:

std::vector< std::pair<int, int> > vectorOfPairs

со следующими пунктами:

0, 1
0, 2
1, 4
2, 3
3, 4
4, 5
5, 6

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

0, 1
1, 4
4, 5
5, 6
0, 2
2, 3
3, 4

Я не знаю, достаточно ли этого, я добавлю изображение, показывающее, что я пытаюсь сделать:

введите описание изображения здесь

Я чувствую, что должен использовать sort с каким-то компаратором, но я потерялся прямо здесь:

std::sort(vectorOfPairs.begin(), vectorOfPairs.end(), MyComparator);

bool MyComparator(pair<int, int> a, pair<int, int> b) {
    if(some_kind_of_comparison){
        return true;
    } 
    return false;
}

Я новичок с С++, и если кто-то может помочь мне с псевдокодом, как это сделать, я буду очень благодарен.

Ответы

Ответ 1

Вы можете думать об этой проблеме как о проблеме графа. Каждая из ваших пар представляет собой ребро в ориентированном графе. Например, пара (0, 2) означает "там ребро от node 0 до node 2", а пара (2, 5) означает "там ребро от node 2 до node 5."

Если вы так думаете об этом, серия ребер, где второй элемент каждой пары соответствует первому элементу следующей пары, соответствует пути в графе. Например, отсортированное упорядочение, которое вы указали, имеет в нем два пути: 0 → 1 → 4 → 5 → 6 и 0 → 2 → 3 → 4. Следовательно, проблема, которую вы пытаетесь решаем следующее: как вы разбиваете ребра в графе отдельно на наименьшее число непересекающихся границ пути?. После того как вы это разрешите, вы можете вывести эти пути в любом порядке, d хотел бы составить упорядоченное упорядочение в соответствии с тем, что вы пытаетесь сделать.

Вы не можете решить эту проблему с помощью std::sort. В качестве примера предположим, что у вас есть ребра (0, 1), (0, 2), (2, 3) и (1, 3). В этом случае оба этих порядка действительны:

(0, 1)          (0, 2)
(1, 3)          (2, 3)
(0, 2)          (0, 1)
(2, 3)          (1, 3)

Это проблема. Поскольку (0, 1) предшествует (0, 2) в первом порядке и (0, 2) предшествует (0, 1) во втором порядке, единственный способ, которым компаратор может быть строгим слабым порядком, - это если (0, 1 ) и (0, 2) несравнимы. Это означает, что при любом упорядоченном упорядочении все элементы между (0, 1) и (0, 2) (включительно) также должны быть несравнимы из-за транзитивности несравнимости. Другими словами, мы должны иметь возможность принимать любое упорядочение, переставлять элементы между (0, 1) и (0, 2) (включительно) и возвращать новый порядок. Это означало бы, что это должно быть правильное упорядочение, хотя это не потому, что существует гораздо лучшее решение:

(0, 1)          (0, 1)
(1, 3)   -->    (0, 2)
(0, 2)          (1, 3)
(2, 3)          (2, 3)

Поэтому нет способа решить эту проблему с помощью std::sort.

Я не уверен, что лучший способ решить это. Это похоже на проблему потока, но я не уверен, как ее настроить. Если я что-нибудь придумаю, я обновлю этот ответ. Спасибо за публикацию чего-то интересного!

Ответ 2

Я бы не использовал std:: sort для этого. Позвольте мне объяснить, почему.

1) Ваш выбор зависит от информации обо всех участниках, которые нужно сортировать, а не о попарном сравнении. В вашем примере причина [0,1] предшествует [4,5] - наличие [1,4] в списке. Если бы у вас вместо этого было [5,0] в списке, это подразумевало бы [0,1], ПОСЛЕ [4,5]. Хуже того, если оба находятся в списке, у вас нет четкой основы для выбора, который должен быть первым.

2) Ваш метод сортировки не определен. Вы, например, не объяснили, почему [0,1] должно появиться до [0,2], а не после. Аналогично, если у вас есть [[0,1], [1,2], [1,3]], то нет способа узнать, должны ли быть [1,2] или [1,3] вторыми.

Еще одно важное соображение. Похоже, что вы можете заниматься какой-то проблемой/связыванием путей. Возможно, ваша структура данных плохо подходит для вашей проблемы в целом. Это просто наблюдение, но, возможно, стоит подумать.

Ответ 3

@templatetypedef предложения велики. Подумав об этом, это скорее похоже на алгоритм планирования, чем алгоритм сортировки. В частности, это похоже на алгоритм лифта, подобный автономному расписанию (т.е. Все упорядоченные приходы известны при запуске планирования времени) с ограничением, что в любой момент можно использовать только одну задачу. Другими словами, лифт будет двигаться только в одном направлении, пока он не спросит верхний запрошенный пол. После этого он опустится до самого нижнего запрошенного пола и перейдет к следующему запрошенному началу.

Я предполагаю, что порядок элементов в списке соответствует приходу запросов.

Это показано на рисунке ниже. введите описание изображения здесь

Если приведенные выше предположения верны, псевдокод для этого будет выглядеть следующим образом:

1. Create two helper maps:
2. LeftKeyPairMap containing all tuples (leftValue, Pair) e.g. (0, (0,1)), (0,(0,2)) ...
3. PairIndexMap containing all tuples (Pair, Index) e.g. ((0,1),0), ((0,2),1) ...
4. Initialize an empty schedule
5. Add first input element to schedule and mark it as visited
6. Start input search at index = 1
7. Repeat while schedule size != input list {
8.   lastElementInSchedule = shedule.get(index - 1);
9.   Check if LeftKeyPairMap contains the an entry with key: lastElementInSchedule.rightElem
10.   if (a pair is present and it is not yet marked visited) {
11.      add pair to schedule
12.      mark pair as visited
13.      increment index
14.   } else {
15.     find min univisited index (identified as the non-consecutive gap in visited entries
16.     add the univisited pair to schedule
17.     increment index
18.   }
19. } // End Loop

Ответ 4

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

typedef std::vector<pair<int,int>> PVect
PVect mysort(const PVect& in){
    PVect result;
    // first element is the same ?
    result.push_back(in[0]);
    // add the next one
    for (int i=1;i<in.size();i++){
        if (in[i].second() == result[0].first()){
             result.push_back(in[i]);
        }
    }
    /* ... */
    return result;
}

Этот код определенно не хорош, но он предназначен только для того, чтобы вести вас в правильном направлении.

Ответ 5

Вы не можете использовать std::sort(), поскольку для этого требуется строгий слабый порядок. Я бы скопировал вектор в std::multi_map, а затем вернул их в отсортированном порядке:

std::multi_map<int,int> values( vectorOfPairs.begin(), vectorOfPairs.end() );
vectorOfPairs.clear();
for( auto iter = values.begin(); iter != values.end(); ) {
    vectorOfPairs.push_back( *iter );
    values.erase( iter );
    iter = values.find( vectorOfPairs.back().second );
    if( iter == values.end() ) iter = values.begin();
}

Ответ 6

Если у вас есть доступ к полному полному вектору с самого начала, вы можете попробовать своего рода сортировку:

  • просмотрите полный список и выберите нужный элемент в начале и замените его на позицию 1.

  • Пока сохраняются элементы: отсканируйте оставшиеся элементы и выберите то, что вы хотите, и замените их на следующую позицию.

Это будет работать, но его временная сложность равна n, поэтому может быть медленным, если вектор очень большой.

Ответ 7

Довольно поздний пост. но может помочь кому-то. Этот код предназначен для списка пар, которые нужно заказывать непрерывно.

Это даст следующий порядок

0, 1
1, 4
4, 5
5, 6

Но нет

0, 1
1, 4
4, 5
5, 6
0, 2
2, 3
3, 4

Так,

  1. если у нас есть (1,2), у нас не должно быть (2,1) (1 не должно быть видно снова)
  2. список пар должен иметь одну начальную точку и одну конечную точку

Нам нужна отправная точка для этого. Я выполнил следующие шаги.

  1. Создание карты с именем originalMap, в которой каждая пара хранит первое значение в качестве ключа и второе значение в качестве значения

  2. Создание другой карты с именем reverseMap, в которой каждая пара хранит второе значение в качестве ключа и первое значение в качестве значения

  3. Итерация по исходной карте: Если какое-либо из значений ключа в originalMap также не является ключом в reverseMap, это будет отправной точкой, поскольку начальная точка не будет вторым значением любой из пар во входных данных b. Если исходная точка (цикл присутствует) или более одного такого значения не найдены, в пути происходит разъединение, и мы можем заключить, что непрерывная последовательность не может быть сформирована из заданного входного значения и вызовет исключение

  4. Если найдена правильная начальная точка, исходная карта может быть повторена с этой точки, пока все пары не будут найдены в порядке

public static List<Pair<Integer, Integer>> sortSegments (List<Pair<Integer, Integer>> segments){
        LinkedList<Pair<Integer, Integer>> resultList = new LinkedList<Pair<Integer,Integer>>();

        if(segments.size() == 0) return resultList;
        HashMap<Integer, Integer> originalMap = new HashMap<>();
        HashMap<Integer, Integer> reverseMap = new HashMap<>();

        for(Pair<Integer, Integer> segment : segments) originalMap.put(segment.getKey(), segment.getValue());  // original pairs 
        for(Pair<Integer, Integer> segment : segments) reverseMap.put(segment.getValue(), segment.getKey());   // reversed pairs

        System.out.println("The original input order: " + originalMap);
        System.out.println("The reverse order: " + reverseMap);

        List<Integer> startingPoints = new ArrayList<>();
        for (Map.Entry<Integer, Integer> entry: originalMap.entrySet()) { // if an element from original is not in reverse then thats 
            if(!reverseMap.containsKey(entry.getKey())) startingPoints.add(entry.getKey()); // the first to go into the output resultList
        }

        Integer pairFirst = null;
        if(startingPoints.size() != 1) {            // if there are multiple / NO starting points are identified then there is no
            throw new IllegalArgumentException("Invalid input segments"); // contiguous path for the segments to be arranged
        } else {
            pairFirst = startingPoints.get(0);
            Integer pairSecond = originalMap.get(pairFirst); 
            while(pairSecond != null) {
                resultList.add(new Pair<Integer, Integer>(pairFirst, pairSecond));
                pairFirst = pairSecond;
                pairSecond = originalMap.get(pairFirst);
            }
        }

        return resultList;
    }