Как еще оптимизировать эту структуру данных?
меня недавно попросили построить структуру данных, которая поддерживает четыре операции, а именно:
- Push: добавление элемента в DS.
- Pop: удалите последний нажатый элемент.
- Find_max: найдите максимальный элемент из сохраненных элементов.
- Pop_max: удалите максимальный элемент из DS.
элементы-целые числа.
вот решение I предложил:
- берем стопку.
- храните в нем пару элементов. Пара должна быть (element, max_so_far), где элемент является элементом в этом индексе и max_so_far является максимальным значимым элементом, замеченным до сих пор.
- при нажатии элемента в стек, проверить max_so_far самого верхнего элемента стека. Если текущее число больше этого, поместите текущую пару max_so_far значение как значение текущего элемента, иначе сохраните предыдущий max_so_far. Это означает, что толчок будет просто
O(1)
операции. - на
pop
, просто вытащите элемент из стека. Опять же, эта операцияO(1)
. - на
Find_max
, возвращает значение max_so_far самого верхнего элемента в стеке. Опять,O(1)
. - Popping элемент max будет включать в себя прохождение через стек и явно удаление элемента max и отталкивание элементов поверх него после выделения нового max_so_far значения. Это было бы линейно.
меня попросили улучшить его, но я не смог.
С точки зрения временной сложности, общее время может быть улучшено, если все операции происходят в O(logn)
, Я думаю. Как это сделать, я не могу понять.
4 ответов
одним из подходов было бы хранить указатели на элементы в дважды связанном списке, а также в max-heap структуры данных (сортировка по значению).
каждый элемент будет хранить свою позицию в дважды связанном списке, а также в max-куче.
в этом случае все ваши операции потребуют O(1) времени в дважды связанном списке, плюс O(log(n)) времени в структуре данных кучи.
один из способов получить операции O(log n)-time-замять две структуры данных, в этом случае дважды связанный список и очередь приоритетов (куча сопряжения-хороший выбор) . У нас есть структура узлов, как
struct Node {
Node *previous, *next; // doubly linked list
Node **back, *child, *sibling; // pairing heap
int value;
} list_head, *heap_root;
теперь, чтобы нажать, мы нажимаем в обеих структурах. Для find_max мы возвращаем значение корня связывающей кучи. Чтобы pop или pop_max, мы выскакиваем из соответствующей структуры данных, а затем используем другие указатели узлов для удаления в другой структуре данных.
обычно, когда вам нужно найти элементы по качеству A (значение), а также по качеству B (порядок вставки), вы начинаете глазеть на структуру данных, которая фактически имеет две структуры данных внутри, которые ссылаются друг на друга или иным образом чередуются.
например: две карты, которые являются ключами качества A и качества B, значения которых являются общим указателем на структуру, содержащую итераторы, возвращающиеся к обеим картам, и значение. Тогда у вас есть log( n), чтобы найти элемент через качество и стирание - ~O (logn) для удаления двух итераторов с любой карты.
struct hybrid {
struct value {
std::map<std::string, std::shared_ptr<value>>::iterator name_iter;
std::map<int, std::shared_ptr<value>>::iterator height_iter;
mountain value;
};
std::map<std::string, std::shared_ptr<value>> name_map;
std::map<int, std::shared_ptr<value>> height_map;
mountain& find_by_name(std::string s) {return name_map[s]->value;}
mountain& find_by_height(int height h) {return height_map[s]->value;}
void erase_by_name(std::string s) {
value& v = name_map[s];
name_map.erase(v.name_iter);
height_iter.erase(v.height_iter); //note that this invalidates the reference v
}
};
однако в вашем случае вы можете сделать даже лучше, чем этот O(logn), так как вам нужны только "самые последние" и "следующие самые высокие". Чтобы сделать" pop highest " быстрым, вам нужен быстрый способ обнаружения следующего максимума, а это означает, что его необходимо предварительно вычислить при insert. Чтобы найти положение "высота" относительно остальных, вам нужна какая-то карта. Чтобы сделать" pop most recent " быстрым, вам нужен быстрый способ чтобы обнаружить следующий самый последний, но это тривиально рассчитано. Я бы рекомендовал создать карту или кучу узлов, где ключи-это значение для поиска max, а значения-указатель на следующее Самое последнее значение. Это дает вам o(logn) insert, O(1) find most recent, O(1) или O (logn) find maximum value(в зависимости от реализации) и ~o (logn) erasure по любому индексу.
еще один способ сделать это:-
создать max-heap с элементами. Таким образом мы сможем получить/удалить максимальный элемент за O(1) операций. Наряду с этим мы можем поддерживать указатель на последний выталкиваемый элемент.И насколько я знаю, delete in heaps можно построить в O (log n).