Проблемы с кучами

Недавно я разместил здесь вопрос о stackoverflow проблемы временной сложности с multimap, и я получил несколько замечательных ответы, которые отсылали меня к использованию куч, которые, как ни странно, я вообще не использовал до сих пор. Я создал новую программу, переписанную с использованием minheap и maxheap. Она отлично работала, поскольку была намного быстрее, чем любая другая программа, которую я применил для решения этой задачи. Единственная проблема заключалась в том, что время от времени он выдавал неправильные ответы. Я вернулся и сделал много отладки. Я понял, что проблема была в организации моей кучи. Он не сортировался и не распределялся, как я думал, с использованием push_heap и pop_heap с операциями сравнения. Кроме того, когда я пытался запустить программу в Visual Studio, я в конечном итоге видел много ошибок утверждений, выбрасываемых туда. Я попытался прочитать больше о кучах и их методах на сайтах cplusplus.com и cppreference.com. Я думаю, что, возможно, я что-то неправильно понимаю и поэтому сталкиваюсь с дальнейшими проблемами.

Первое, что меня смущает, это push_heap. Я понимаю это следующим образом: push_heap имеет два аргумента и по по умолчанию выдвигает наименьшее значение на позицию last-1. Он делает это только в том случае, если первый аргумент меньше второго аргумента, в противном случае он остается прежним. в основном он поддерживает порядок обычной кучи. Третий необязательный аргумент — это оператор сравнения, который можно использовать как Greater(), который затем помещает больший элемент в позицию last-1.

Что не имеет смысла, так это то, что если у меня происходит динамическая вставка или удаление чисел в векторе, у меня возникают проблемы с сохранением этого порядка. Если бы я хотел, чтобы вектор был в порядке возрастания, я бы использовал большую операцию, чтобы продолжать нажимать кучу, чтобы значения росли. Но это сбивает с толку, когда вы впервые смотрите на метод push_heap, потому что он очень похож на некоторые другие функции алгоритма, которые работают в диапазоне чисел, например:

 std::unique (myvector.begin(), myvector.end(), myfunction); 

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

После того, как я обнаружил, что push_heap на самом деле не поддерживает сортировку моего вектора, и мне пришлось сохранить сортировку моего вектора, чтобы использовать двоичный поиск. Я использовал sort_heap, но это замедлило работу программы настолько, что она была недостаточно быстрой.

Кроме того, я обнаружил, что иногда push_heap в странных случаях выдает ошибку недействительной кучи.

как например:

   push_heap(v.begin(), v.end(), greater<int>());  

с вектором 755, 98, 55, 22

вы увидите после push_heap:

     22, 98, 55, 755

но допустим, что у вас было 22, 98, 55, 755

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

но иногда я попробую push_heap:

887, 52, 44, 22

и он скажет

      'invalid heap' 

или если я попытаюсь: 22, 52, 44, 887, вместо того, чтобы просто вернуть false и двигаться дальше, это сломается с

'invalid heap'

это также иногда происходит с pop_heap.

Почему я получаю недопустимую кучу? это потому, что все кучи должны быть в порядке убывания?

РЕДАКТИРОВАТЬ: я нашел это на cplusplus.com, который, я думаю, отвечает на один вопрос:

The element with the highest value is always pointed by first. The order of the other elements depends on the particular implementation, but it is consistent throughout all heap-related functions of this header.


person user1066524    schedule 25.03.2013    source источник
comment
Я бы сказал, забудьте о push_heap и используйте std::priority_queue. приоритетная очередь работает так же, как и куча, но ее проще использовать и понимать.   -  person Mooing Duck    schedule 25.03.2013
comment
Priority_queue было бы здорово, за исключением моей проблемы, мне нужно иметь возможность удалить один элемент, что priority_queue не позволит вам сделать. но кроме этого, priority_queue — достойный выбор.   -  person user1066524    schedule 25.03.2013
comment
std::priority_queue имеет pop и top для просмотра и удаления наибольшего элемента. Если это не то, что вам нужно, то и вся эта куча вам не нужна, так как они работают на точно одних и тех же концепциях.   -  person Mooing Duck    schedule 25.03.2013


Ответы (1)


... push_heap имеет два аргумента и по умолчанию помещает наименьшее значение в позицию last-1. Он делает это только в том случае, если первый аргумент меньше второго аргумента, в противном случае он остается прежним.

Неа. Если ваше хранилище представляет собой вектор v и в настоящее время является кучей (как создано с помощью make_heap), вы должны вызвать

v.push_back(new_item);
push_heap(v.begin(), v.end());

чтобы добавить новый элемент. См., например, здесь или здесь.

Учтите, что push_heap действительно берет диапазон [begin, end-1) (который уже требуется для выполнения инварианта кучи) и добавленный элемент в end-1 (что может и не быть), и перемещает вверх последний элемент до тех пор, пока инвариант кучи не будет восстановлен для всего [begin, end). Алгоритм объясняется здесь.


Обнаружив, что push_heap на самом деле не поддерживает сортировку моего вектора...

Кучи не сортируются. У них есть ограничение порядка (свойство кучи), которое конкретно и преднамеренно слабее, чем при полной сортировке.

Если вы хотите выполнить бинарный поиск, вам нужен полностью отсортированный контейнер, а преобразование вашей кучи в один с использованием sort_heap каждый раз будет медленным и разрушительным: ваш контейнер больше не будет кучей после того, как вы вызовете это, и вы можете не используйте его как один.


Теперь о вашем редактировании: кучи не должны располагаться в порядке убывания. Максимальная куча находится в порядке убывания (с самым большим элементом впереди), а минимальная куча — в порядке возрастания (с самым маленьким элементом впереди).

По по умолчанию в стандартной библиотеке создается мини-куча с использованием operator< для сравнения. Вместо этого, чтобы создать максимальную кучу, вы просто передаете std::greater<int> или что-то еще в качестве (необязательного) последнего аргумента.

person Useless    schedule 25.03.2013
comment
Когда вы говорите, что отсортировано кучей, вы имеете в виду, что я вызвал make_heap перед выполнением push_heap, или вы имеете в виду, что на самом деле нужно использовать функцию heap_sort? - person user1066524; 25.03.2013
comment
make_heap превращает ваш вектор в кучу, и для работы push_heap она должна быть кучей. Если вы heap_sort это сделаете, то инвариант кучи будет нарушен. Прочитайте последнюю ссылку (на википедию), если вы не понимаете, как должна выглядеть куча. - person Useless; 25.03.2013
comment
но действительно ли происходит какое-либо перемещение только с использованием push_heap? или мне придется вручную создать это вместе с использованием push_heap? - person user1066524; 25.03.2013
comment
да, push_heap перемещает последний элемент (который вы только что добавили в конец, поэтому он может нарушить свойство кучи) до тех пор, пока свойство кучи не будет восстановлено. Итак, он прыгает в вашей куче, пока снова не станет действительным. - person Useless; 25.03.2013
comment
хорошо, так что это имеет смысл. поэтому обычно в куче наибольшее значение — это первый элемент, а наименьшее значение — последний элемент... но промежуточные элементы не обязательно будут в правильном порядке, верно? Я спрашиваю об этом, потому что если я хочу выполнить бинарный поиск, мне придется отсортировать это с помощью heap_sort. - person user1066524; 25.03.2013
comment
@user1066524 user1066524: В максимальной куче первый элемент будет самым большим, но наименьший не обязательно будет последним. - person Dave S; 25.03.2013
comment
хорошо, теперь все это начинает иметь больше смысла. Таким образом, вы можете использовать сравнение по умолчанию, чтобы найти max_element в vec[0], или большее сравнение, чтобы найти min_element в vec[0]? и sort_heap для быстрой сортировки кучи для использования в бинарном поиске? но вы должны использовать временную кучу, чтобы инвариант кучи не был нарушен. - person user1066524; 25.03.2013
comment
@user1066524 user1066524 Как я уже сказал, промежуточные элементы расположены в правильном порядке для кучи, потому что они удовлетворяют свойству кучи. Они не отсортированы. И для получения максимума и минимума вы почти достигли цели, но я думаю, что у вас все наоборот. - person Useless; 25.03.2013
comment
о, подождите, вы правы, push_heap(v.begin(), v.end(), Greater‹int›()) отодвинет max_element на задний план, а если бы я использовал less‹int›, это отодвинуло бы min_element к назад. - person user1066524; 25.03.2013