Гуру тест про порядок элементов в иерархии
Есть некоторый иерархический справочник, иерархия элементов. У каждого элемента есть поле «Позиция» (может быть дробным и отрицательным). Элементы в пределах одного родителя должны быть пронумерованы в поле «Позиция» по порядку с единицы с шагом один: 1, 2, … N
Таким образом, например, чтобы поменять порядок элементов с позициями 2 и 3 мы можем элементу с позицией 2 установить позицию 2,5 и вызвать процедуру исправления порядка.
Не буду давать подсказки и наводящие мысли, просто жду от вас идей оптимальной реализации алгоритма проверки и изменения порядка. Это гуру-тест, который, кстати, можно давать на собеседованиях.
Пример: в группе с названием Алкоголь есть элементы с позициями:
- Пиво 1
- Водка 2
- Эль 3
Я меняю:
- Пиво 2.5
- Водка 2
- Эль 3
Система должна пересчитать по всей номенклатуре и сделать:
- Водка 1
- Пиво 2
- Эль 3
Причем найти отклонения и сделать изменения максимально эффективно.
По сути можно упростить вопрос — как запросом найти группы, где порядок 1, 2, … N нарушен?
Вот еще пример:
Есть группа Алкоголь 1, в ней элементы:
Водка 2
Пиво 2.5
Эль 3
Есть группа Мясное 2, в ней элементы:
Сосиски 3
Ветчина 5
Есть группа Напитки 3, в ней элементы:
Квас 1
Сок 2
Нужно по этому пример найти запросом группы, где нарушен порядок 1, 2, N.
Правильный ответ: Алкоголь, Мясное.
Неправильные ответы: Напитки, Корень.
Обсуждение задачи на Мисте. 1сники не справились, задал задачу на Хабре.
Для решения задачи поиска групп, в которых нарушен порядок элементов, можно использовать SQL-запрос, который будет анализировать позиции элементов в каждой группе. Основная идея заключается в том, чтобы найти группы, где хотя бы один элемент имеет позицию, которая не соответствует порядку 1, 2, …, N.
SQL-запрос для поиска групп с нарушенным порядком
sql
Copy
SELECT DISTINCT parent_id
FROM (
SELECT
parent_id,
position,
ROW_NUMBER() OVER (PARTITION BY parent_id ORDER BY position) AS expected_position
FROM elements
) AS subquery
WHERE position expected_position;
Объяснение SQL-запроса
Подзапрос:
Используется функция ROW_NUMBER(), чтобы назначить ожидаемую позицию (expected_position) для каждого элемента в пределах его родительской группы.
PARTITION BY parent_id разбивает данные на группы по родительскому идентификатору.
ORDER BY position сортирует элементы в каждой группе по их текущей позиции.
Основной запрос:
Сравнивает текущую позицию (position) с ожидаемой позицией (expected_position).
Если они не совпадают, это означает, что порядок нарушен.
DISTINCT parent_id используется для вывода уникальных идентификаторов групп, где обнаружены нарушения.
Пример данных
Предположим, у нас есть таблица elements со следующими данными:
id parent_id name position
1 1 Водка 2
2 1 Пиво 2.5
3 1 Эль 3
4 2 Сосиски 3
5 2 Ветчина 5
6 3 Квас 1
7 3 Сок 2
Результат запроса
Запрос вернет:
parent_id
1
2
Это означает, что группы с parent_id = 1 (Алкоголь) и parent_id = 2 (Мясное) имеют нарушения в порядке элементов.
Сложность (Big O)
Временная сложность:
ROW_NUMBER() имеет сложность O(N log N) из-за сортировки.
Основной запрос проверяет каждую строку, поэтому его сложность O(N).
Общая сложность: O(N log N).
Пространственная сложность:
Зависит от реализации базы данных, но в целом O(N) для хранения промежуточных данных.
C++ Реализация
Если требуется реализация на C++, то можно использовать следующий подход:
cpp
Copy
#include
#include
#include
#include
struct Element {
int id;
int parent_id;
std::string name;
double position;
};
void find_groups_with_invalid_order(const std::vector& elements) {
std::map<int, std::vector> grouped_elements;
// Группируем элементы по parent_id
for (const auto& elem : elements) {
grouped_elements[elem.parent_id].push_back(elem);
}
// Проверяем каждую группу
for (const auto& group : grouped_elements) {
const auto& group_elements = group.second;
std::vector positions;
// Собираем позиции
for (const auto& elem : group_elements) {
positions.push_back(elem.position);
}
// Сортируем позиции
std::vector sorted_positions = positions;
std::sort(sorted_positions.begin(), sorted_positions.end());
// Проверяем, совпадают ли позиции с отсортированными
bool is_valid = true;
for (size_t i = 0; i < positions.size(); ++i) {
if (positions[i] != sorted_positions[i]) {
is_valid = false;
break;
}
}
// Если порядок нарушен, выводим parent_id
if (!is_valid) {
std::cout << "Группа с parent_id = " << group.first << " имеет нарушенный порядок." << std::endl;
}
}
}
int main() {
std::vector elements = {
{1, 1, «Водка», 2},
{2, 1, «Пиво», 2.5},
{3, 1, «Эль», 3},
{4, 2, «Сосиски», 3},
{5, 2, «Ветчина», 5},
{6, 3, «Квас», 1},
{7, 3, «Сок», 2}
};
find_groups_with_invalid_order(elements);
return 0;
}
Объяснение C++ кода
Группировка элементов:
Элементы группируются по parent_id с использованием std::map.
Проверка порядка:
Для каждой группы собираются позиции и сортируются.
Сравниваются исходные позиции с отсортированными.
Вывод результата:
Если обнаружено несоответствие, выводится parent_id группы.
Сложность (Big O)
Временная сложность:
Группировка: O(N).
Сортировка: O(N log N).
Проверка: O(N).
Общая сложность: O(N log N).
Пространственная сложность:
O(N) для хранения групп и позиций.
Этот подход эффективно решает задачу и может быть легко адаптирован для работы с реальными данными.
Хорошо, но сложно. Есть запрос попроще и побыстрее соответственно.