Как еще оптимизировать эту структуру данных?

меня недавно попросили построить структуру данных, которая поддерживает четыре операции, а именно:

  1. Push: добавление элемента в DS.
  2. Pop: удалите последний нажатый элемент.
  3. Find_max: найдите максимальный элемент из сохраненных элементов.
  4. Pop_max: удалите максимальный элемент из DS.

элементы-целые числа.

вот решение I предложил:

  1. берем стопку.
  2. храните в нем пару элементов. Пара должна быть (element, max_so_far), где элемент является элементом в этом индексе и max_so_far является максимальным значимым элементом, замеченным до сих пор.
  3. при нажатии элемента в стек, проверить max_so_far самого верхнего элемента стека. Если текущее число больше этого, поместите текущую пару max_so_far значение как значение текущего элемента, иначе сохраните предыдущий max_so_far. Это означает, что толчок будет просто O(1) операции.
  4. на pop, просто вытащите элемент из стека. Опять же, эта операция O(1).
  5. на Find_max, возвращает значение max_so_far самого верхнего элемента в стеке. Опять,O(1).
  6. 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).