Дерево с O(1) доступом по ID
От: Hоmunculus  
Дата: 09.01.26 09:47
Оценка:
У меня есть дерево. То есть нулевой узел с дочерними узлами и каждый дочерний содержит свои дочерние и так далее
При создании ноды пользователю отдается unsigned int ID. И вот по этому ID нужен доступ за константное время. Идеально было бы как-то отразить дерево в вектор и чтоб ID был номером элемента в векторе. Но как-то криво выходит. Две структуры для одного и того же. Криво.
Еще вариант, что ID — это собственно сам указатель, но не нравится он мне
Какие есть варианты?
Отредактировано 09.01.2026 9:51 Hоmunculus . Предыдущая версия .
Re: Дерево с O(1) доступом по ID
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 09.01.26 10:06
Оценка:
Здравствуйте, Hоmunculus, Вы писали:

H>У меня есть дерево. То есть нулевой узел с дочерними узлами и каждый дочерний содержит свои дочерние и так далее

H>При создании ноды пользователю отдается unsigned int ID. И вот по этому ID нужен доступ за константное время. Идеально было бы как-то отразить дерево в вектор и чтоб ID был номером элемента в векторе. Но как-то криво выходит. Две структуры для одного и того же. Криво.
H>Еще вариант, что ID — это собственно сам указатель, но не нравится он мне
H>Какие есть варианты?

std::unordered_map?
Маньяк Робокряк колесит по городу
Re[2]: Дерево с O(1) доступом по ID
От: Hоmunculus  
Дата: 09.01.26 10:08
Оценка:
Здравствуйте, Marty, Вы писали:

M>std::unordered_map?


ну там константа не всегда. Учитывая, что узлов, конечно, не миллионы, а меньше тысячи, то скорее всего более-менее решение, но вдруг что-то интереснее уже придумали.
Re[3]: Дерево с O(1) доступом по ID
От: sergii.p  
Дата: 09.01.26 11:58
Оценка:
Здравствуйте, Hоmunculus, Вы писали:

H>Здравствуйте, Marty, Вы писали:


M>>std::unordered_map?


H>ну там константа не всегда.


там есть метод reserve. Так что можно сказать, что всегда константа
Re[4]: Дерево с O(1) доступом по ID
От: T4r4sB Россия  
Дата: 09.01.26 16:51
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>Здравствуйте, Hоmunculus, Вы писали:


H>>Здравствуйте, Marty, Вы писали:


M>>>std::unordered_map?


H>>ну там константа не всегда.


SP>там есть метод reserve. Так что можно сказать, что всегда константа


Коллизии еще бывают
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[5]: Дерево с O(1) доступом по ID
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 09.01.26 18:30
Оценка:
Здравствуйте, T4r4sB, Вы писали:

TB>Коллизии еще бывают


Сложность всё равно константная. Вряд ли у ТС подразумевалось именно константное время (не более XX микро/наносекунд), он, скорее всего, не совсем точно выразился
Маньяк Робокряк колесит по городу
Re[6]: Дерево с O(1) доступом по ID
От: Hоmunculus  
Дата: 09.01.26 18:32
Оценка:
Здравствуйте, Marty, Вы писали:

M>Здравствуйте, T4r4sB, Вы писали:


TB>>Коллизии еще бывают


M>Сложность всё равно константная. Вряд ли у ТС подразумевалось именно константное время (не более XX микро/наносекунд), он, скорее всего, не совсем точно выразился


O(1) однозначно значит независимость от количества элементов. И если их станет миллиард, чтобы это не привело к просадкам. Разумеется, это не значит одно и то же количество миллисекунд на любой платформе
Re[7]: Дерево с O(1) доступом по ID
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 09.01.26 18:38
Оценка:
Здравствуйте, Hоmunculus, Вы писали:

TB>>>Коллизии еще бывают


M>>Сложность всё равно константная. Вряд ли у ТС подразумевалось именно константное время (не более XX микро/наносекунд), он, скорее всего, не совсем точно выразился


H>O(1) однозначно значит независимость от количества элементов. И если их станет миллиард, чтобы это не привело к просадкам. Разумеется, это не значит одно и то же количество миллисекунд на любой платформе


Именно это я и имел в виду. В хэш мапе, даже если есть коллизия это перебор нескольких элементов, и это всё равно O(1). Проблема может быть с рехэш при увеличении размера, но это можно решить, если это прям проблема.
Маньяк Робокряк колесит по городу
Re[6]: Дерево с O(1) доступом по ID
От: T4r4sB Россия  
Дата: 09.01.26 18:50
Оценка: +2
Здравствуйте, Marty, Вы писали:

M>Сложность всё равно константная.


В среднем да, в худшем случае нет
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[7]: Дерево с O(1) доступом по ID
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 09.01.26 19:17
Оценка:
Здравствуйте, T4r4sB, Вы писали:

M>>Сложность всё равно константная.


TB>В среднем да, в худшем случае нет


Скорее всего, ТСу нужна средняя сложность, вряд ли он делает систему жесткого реального времени. А резервирование/рехеширование можно делать по мере необходимости при вставке, или настроить max_load_factor, чтобы рехеши пореже происходили

Практические рекомендации

Всегда используйте reserve(), если знаете примерный размер

Оптимальный max_load_factor зависит от использования:

Для частых поисков: 0.7-0.8

Для экономии памяти: 1.0-1.5

Для минимизации рехеширования: 2.0-3.0

Профилируйте для нахождения оптимальных параметров

Рассмотрите альтернативные контейнеры, если рехеширование критично

Маньяк Робокряк колесит по городу
Отредактировано 09.01.2026 20:31 Marty . Предыдущая версия .
Re[8]: Дерево с O(1) доступом по ID
От: T4r4sB Россия  
Дата: 09.01.26 19:39
Оценка:
Здравствуйте, Marty, Вы писали:

M>А резервирование/рехеширование можно делать по мере необходимости при вставке, или настроить max_load_factor, чтобы рехеши пореже происходили


Рехеши не помогут от коллизий

M>Скорее всего, ТСу нужна средняя сложность, вряд ли он делает систему жесткого реального времени.


Это да. В обычных прикладных задачах о коллизиях никто не задумывается, нет смысла.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[9]: Дерево с O(1) доступом по ID
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 09.01.26 19:47
Оценка: +1 :)
Здравствуйте, T4r4sB, Вы писали:

M>>А резервирование/рехеширование можно делать по мере необходимости при вставке, или настроить max_load_factor, чтобы рехеши пореже происходили


TB>Рехеши не помогут от коллизий


При плохой хэш функции оно конечно может выродится в O(N), но на практике скорее будет O(1)/O(2)/O(3) или около того


M>>Скорее всего, ТСу нужна средняя сложность, вряд ли он делает систему жесткого реального времени.


TB>Это да. В обычных прикладных задачах о коллизиях никто не задумывается, нет смысла.


Стандартного std::hash обычно хватает, чтобы коллизий было приемлемое количество
Маньяк Робокряк колесит по городу
Re: Дерево с O(1) доступом по ID
От: BlackEric http://black-eric.lj.ru
Дата: 09.01.26 20:29
Оценка: 84 (2)
Здравствуйте, Hоmunculus, Вы писали:


Можно попробовать как-то так. Массив узлов + массив указателей.

struct Node {
    std::vector<size_t> children;
    // ... другие данные узла
};

class Tree {
private:
    std::vector<Node> nodes;           // Все узлы хранятся здесь
    std::vector<size_t> free_ids;      // Список свободных ID
    size_t next_id = 0;
    
public:
    // Создание узла возвращает ID
    size_t createNode() {
        size_t id;
        if (!free_ids.empty()) {
            id = free_ids.back();
            free_ids.pop_back();
        } else {
            id = next_id++;
            if (id >= nodes.size()) {
                nodes.resize(id + 1);
            }
        }
        return id;
    }
    
    // Доступ за O(1)
    Node& getNode(size_t id) {
        return nodes[id];
    }
    
    void deleteNode(size_t id) {
        // Очищаем узел
        nodes[id].children.clear();
        // Добавляем ID в список свободных
        free_ids.push_back(id);
    }
https://github.com/BlackEric001
Re[8]: Дерево с O(1) доступом по ID
От: T4r4sB Россия  
Дата: 09.01.26 20:37
Оценка:
Здравствуйте, Marty, Вы писали:

M>Оптимальный max_load_factor зависит от использования:

M>...
M>Для экономии памяти: 1.0-1.5
M>Для минимизации рехеширования: 2.0-3.0

А вот тут я чёт не понял
Если мы экономим память, то адресация должна быть открытая, никаких нахрен связных списков, а тогда заполнение в принципе не может достигать 1.0
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[9]: Дерево с O(1) доступом по ID
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 09.01.26 20:51
Оценка:
Здравствуйте, T4r4sB, Вы писали:

TB>А вот тут я чёт не понял

TB>Если мы экономим память, то адресация должна быть открытая, никаких нахрен связных списков, а тогда заполнение в принципе не может достигать 1.0

А вот тут я тебя не понял. Я не спец по устройству std::unordered_map, но, как я понимаю, чем меньше корзин, тем меньше требуется памяти, т.е., количество корзин влияет на размер занимаемой памяти больше, чем размер самих корзин.
Маньяк Робокряк колесит по городу
Re[10]: Дерево с O(1) доступом по ID
От: T4r4sB Россия  
Дата: 09.01.26 21:06
Оценка:
Здравствуйте, Marty, Вы писали:

M>Здравствуйте, T4r4sB, Вы писали:


TB>>А вот тут я чёт не понял

TB>>Если мы экономим память, то адресация должна быть открытая, никаких нахрен связных списков, а тогда заполнение в принципе не может достигать 1.0

M>А вот тут я тебя не понял. Я не спец по устройству std::unordered_map, но, как я понимаю, чем меньше корзин, тем меньше требуется памяти, т.е., количество корзин влияет на размер занимаемой памяти больше, чем размер самих корзин.




Тут я вижу 2 случая
Элементы мапы имеют небольшой размер ну там это поды до 50 байт каждый
Тогда накладные расходы на хранение корзин, разбросанных по куче, настолько велики, что нет смысла говорить об экономии памяти, и открытая адресация с заполнением 90% выиграет по памяти
Если элементы мапы жирные то уже пофиг сколько корзин.
А еще в стдшной мапе все элементы живут в одном односвязном списке, а для корзин просто массив итераторов на начало блока с нужным хешем, то есть увеличивая процент заполнения ты просто эеономишь на длине этого массива указателей
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[11]: Дерево с O(1) доступом по ID
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 09.01.26 21:11
Оценка:
Здравствуйте, T4r4sB, Вы писали:

TB>Тут я вижу 2 случая

TB>Элементы мапы имеют небольшой размер ну там это поды до 50 байт каждый
TB>Тогда накладные расходы на хранение корзин, разбросанных по куче, настолько велики, что нет смысла говорить об экономии памяти, и открытая адресация с заполнением 90% выиграет по памяти
TB>Если элементы мапы жирные то уже пофиг сколько корзин.
TB>А еще в стдшной мапе все элементы живут в одном односвязном списке, а для корзин просто массив итераторов на начало блока с нужным хешем, то есть увеличивая процент заполнения ты просто эеономишь на длине этого массива указателей

Я не очень понял, что такое "открытая адресация"?
Маньяк Робокряк колесит по городу
Re[2]: Дерево с O(1) доступом по ID
От: SaZ  
Дата: 09.01.26 21:39
Оценка:
Здравствуйте, Marty, Вы писали:

M>Здравствуйте, Hоmunculus, Вы писали:


H>>У меня есть дерево. То есть нулевой узел с дочерними узлами и каждый дочерний содержит свои дочерние и так далее

H>>При создании ноды пользователю отдается unsigned int ID. И вот по этому ID нужен доступ за константное время. Идеально было бы как-то отразить дерево в вектор и чтоб ID был номером элемента в векторе. Но как-то криво выходит. Две структуры для одного и того же. Криво.
H>>Еще вариант, что ID — это собственно сам указатель, но не нравится он мне
H>>Какие есть варианты?

M>std::unordered_map?


Без примерного понимания размера, реальных тестов и того, как мапа создаётся — не всегда. Во многих случаях, особенно на небольших наборах данных map / flat_map будет (внезапно) быстрее из-за особенностей работы кэша, даже при теоретической логарифмической сложности. Особенно на всяких embedded где нет L3 кэша.
Re[3]: Дерево с O(1) доступом по ID
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 09.01.26 21:57
Оценка:
Здравствуйте, SaZ, Вы писали:

M>>std::unordered_map?


SaZ>Без примерного понимания размера, реальных тестов и того, как мапа создаётся — не всегда. Во многих случаях, особенно на небольших наборах данных map / flat_map будет (внезапно) быстрее из-за особенностей работы кэша, даже при теоретической логарифмической сложности. Особенно на всяких embedded где нет L3 кэша.


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

ЗЫ А в эмбеде обычно нет вообще никакого кеша.
Маньяк Робокряк колесит по городу
Re: Дерево с O(1) доступом по ID
От: Pzz Россия https://github.com/alexpevzner
Дата: 09.01.26 22:18
Оценка:
Здравствуйте, Hоmunculus, Вы писали:

H>У меня есть дерево. То есть нулевой узел с дочерними узлами и каждый дочерний содержит свои дочерние и так далее

H>При создании ноды пользователю отдается unsigned int ID. И вот по этому ID нужен доступ за константное время. Идеально было бы как-то отразить дерево в вектор и чтоб ID был номером элемента в векторе. Но как-то криво выходит. Две структуры для одного и того же. Криво.
H>Еще вариант, что ID — это собственно сам указатель, но не нравится он мне
H>Какие есть варианты?

Ничего не понятно. Звучит так, что у тебя есть дерево, построенное по какому-то там своему ключу, и есть еще дополнительный ключ, который ID, и по нему тоже надо организовать поиск.

Но если у тебя два ключа, и по обоим надо искать эффективно, то волей-неволей продётся строить две параллельные структуры, и поддерживать синхронность между ними (что само по себе неприятно).

Или же ты спрашиваешь, как организовать быстрый поиск по unsigned int?
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.