Автор оригинала: FreeCodeCapm Team.
Сачин Малхотра
Представьте, что вы зарегистрированы в математическом классе на одном из самых престижных университетов мира.
У вас скоро выключите экзамен. Очевидно, вы хотите хорошо работать на экзамене.
Дело в этом университете состоит в том, что у него неуклюжий набор профессоров. Таким образом, измена действительно прост. Вы можете легко скопировать из парня, сидящего позади и впереди без пойманного.
Профессоры, чтобы взять под контроль эту проблему, придумали два решения:
- Количество студентов, сидящих в классе, никогда не исправлено. И люди, сидящие в одном классе, принимают тестовые изменения из одного теста на другое.
- Местоположение выпускается пять минут до экзамена. Установка сидения алфавита. Но поскольку ученики никогда не закреплены, и новые могут быть добавлены или старыми удалены из класса случайно, договоренность должна быть явно выпущена для студентов, чтобы узнать, где именно они должны сидеть.
Скажите, что вы один из тех ленивых студентов, которые хотят обманывать, несмотря на последствия. Пять минут до экзамена, когда договоренность о сидении освобождена, как вы узнаете, кто сидит перед вами, и кто позади как можно быстрее?
Вы не сможете обмануть, если вы не разговариваете с этими двумя людьми заранее и стратегируете, верно?
Местоположение
Таким образом, профессора выпустили договоренность о сидении для первого теста, когда-либо проводимого таким образом. Скажи, что у него были N студенты. Если бы эти студенты оставались прежними из одного теста на другое, то было бы очень легко обмануть, верно? Поскольку расположение сидения всегда делается в алфавитном порядке.
Поэтому профессора продолжают добавлять или удалять студентов из этого списка из одного теста на другой и выпустили только эти модификации перед каждым тестом. Таким образом, учащиеся могут никогда не узнать детерминистически перед тестом, который будет сидеть перед или позади них.
Рассмотрим эту проблему в алгоритмических терминах. Нам дают список N элементов, где элементы в этом случае являются именами студентов. Этот список продолжает варьироваться от одного экзамена к другому, так что новые элементы могут быть добавлены в список или существующие элементы могут быть удалены из списка.
Учитывая список модификаций в любой момент времени t и имя n, нам нужно определить элементы B и A, так что B придет прямо раньше N, а A придет сразу после n, если список должен быть отсортирован.
Теперь давайте посмотрим, какие структуры данных доступны нам и подобные этой проблеме лучше всего.
О массив, мой старый друг, ты мне поможешь?
Использование массива, кажется, довольно простой подход.
- Мы можем просто поставить все имена в выпущенном списке в массиве.
- Затем мы сортируем все имена (список выпущенных имен может быть случайным образом аранжирован) лексикографически
- И тогда мы можем найти наше имя в списке с помощью бинарной процедуры поиска. Это даст нам предшественник и преемник.
Это кажется жизнеспособным подходом для решения этой проблемы. Однако проблема, однако, заключается в том, что студенты никогда не обращаются от одного экзамена в другое. И поэтому список, который был выпущен для самого первого экзамена, будет динамически варьироваться, когда были добавлены новые студенты, и старые были удалены.
Мы можем отсортировать список в течение самого первого раза, а затем продолжать добавлять новые элементы и удаление старых соответственно двигаться вперед.
Однако сложность добавления или удаления элемента из массива является порядком O (n)
Отказ Поскольку количество студентов может быть очень большим, и мы не знаем, сколько модификаций было бы до некоторого нового теста, это займет много времени, и тест начнется, прежде чем мы сможем решить проблему. Помните, что модификации выпущены всего за пять минут до теста.
Так какая другая структура данных у нас есть там, где вставка и удаление могут быть сделаны очень быстро?
Хмммм, возможно, связанный список – мой настоящий друг в конце концов
Что касается связанного списка, он имеет свой собственный набор проблем при работе с этим типом ситуации. Первоначально нам нужно сортировать список элементов лексикографически. Поскольку это одноразовая операция, потому что она только для того, чтобы сделать только для первого экзамена, время, сделанное здесь не имеет значения.
С следующего экзамена далее выпущены только модификации. Добавление или удаление элемента из связанного списка – это постоянная работа, при условии, что мы знаем местоположение этого элемента в списке.
Нахождение элемента в связанном списке – это линейная операция времени – требуется O (n)
Отказ Я знаю, что есть концепции, такие как Скип списков , но зачем погружаться в что-то подобное, когда мы можем решить эту проблему в гораздо лучшей моде, используя другой тип структуры данных?
Введите двоичные валы поиска, новый ребенок в городе
Давайте посмотрим, как мы можем моделировать наши данные, используя двоичное дерево поиска (BST). Тогда мы посмотрим, как BST может помочь нам решить проблему, которую мы изначально решили решить.
Двоичное дерево поиска в основном является бинарным деревом с особым способом упорядочения узлов.
Для узла с ключом k каждый ключ в левом поддереве меньше, чем k и каждый ключ в правом поддереве больше, чем k .
В нашем случае ключи будут именами студентов.
Рассмотрим следующий пример, чтобы увидеть, как построен двоичное поиск. Это должно оказывать большую четкость к структуре данных.
Построение двоичного поиска недостаточно. Нам нужно убедиться, что это Сбалансированный Отказ Причина, по которой мы говорим, что двоичное поиск должно быть сбалансировано, это то, что если она не сбалансирована, то мы можем иметь что-то подобное:
Это известно как перекошенное двоичное дерево поиска. Если такая вещь происходит, то BST в основном превращается в связанный список, и это бесполезно нам. Поэтому у нас есть это понятие сохранения BST сбалансированного, так что мы не бегаем в эту проблему.
Понятие сбалансированного определяется по-разному различными подходами, как красные черные деревья или деревьев AVL. Дальнейшее объяснение этих деревьев выходит из объема этой статьи.
Возвращаясь к организации наших данных в сбалансированном BST: ключи к нашему BST будут именами студентов, а лексикографическое совпадение будет использоваться для определения структуры BST.
Предположим, что в тесте было миллион студентов. Если наше двоичное дерево поиска сбалансировано, то сложность выполнения любой операции верхняя ограничена O (log (n))
Отказ Следовательно, для 1 миллиона узлов максимальное количество отсканированных узлов было всего 14.
Это много уменьшения сложности просто путем определения данных о создании данных. Это преимущество представления данных в Сбалансированный Двоичное дерево поиска.
Основная проблема с подходом на основе массива было то, что мы не могли эффективно вставлять или удалять элемент из массива. И проблема с связанным списком подходом было то, что для нас не было эффективного способа найти элемент в связанном списке, даже если он был отсортирован.
Что касается сбалансированного двоичного дерева поиска, временная сложность для вставки, удаления или поиска элемента все ограничена O (log (n))
Отказ И это именно то, что делает эту структуру данных чрезвычайно захватывающей.
Однако мы до сих пор не решили нашу оригинальную проблему. Учитывая имя студента, мы хотим выяснить, что студент, сидящий прямо и прямо перед ними. Это сводится к поиску Заказать преемник и предшественник в заданном двоичном дереве поиска.
Обход в заказах и отсортированном порядке в BST
Интересное свойство двоичных поисковых вал.
Таким образом, преемник узла X является элементом, который приходит сразу после x в обходе в заказах по заданному BST. За нашу проблему обмана, этот преемник в порядке будет студентом, сидящим перед нами.
Предшественник узла X является элементом, который поставляется прямо перед X в обходе в заказа (или элементе, который приходит сразу после x в Reverse Traversal в заказа) над данным BST Отказ За нашу проблему обмана, этот предшественник в заказах будет студент, сидящий прямо за нами.
Преемник в порядке в BST
Существует два разных случая, которые нам нужно обращаться с нахождением преемника в порядке узла в BST.
Первый случай когда правильный ребенок существует для узла, чья преемник в порядке, мы пытаемся найти. Рассмотрим следующий пример.
Здесь мы хотели найти преемник в порядке выделенного узла 8. Так как он имеет правильный ребенок, Преемник в заказах будет самый левый узел в дереве с правым ребенком или 15, как корневой Отказ Так что узел будет 10 в этом случае.
Второй случай когда нет правого ребенка.
В этом случае преемник в порядок имеет две возможности:
- Одним из них находится там, где рассматриваемый узел – левый ребенок его родителя. В этом случае преемник в порядок будет самим родителем. Так что для нашего данного случая преемник в порядок будет 10.
- Второй случай – когда текущий узел является правильным ребенком его родителя. И у него нет правильного ребенка. Так что это самый правый узел в BST, и он не имеет преемника в порядок.
Обработка первого дела довольно просто для двоичного поиска. Для второго случая, где данный узел не имеет правильного ребенка (или любых родительских указателей), нам придется полагаться на нашу хорошую рекурсионный механизм и выполнять обход в заказа, пока мы не выясним родителя нашего данного узел.
Таким образом, худшая сложность Case может быть O (n), если происходит приведенное выше случай.
Используя этот алгоритм, мы можем быстро узнать студента, который будет сидеть прямо перед нами на экзамене.
Предшественник в заказах в BST
Это точная задняя часть предыдущего случая.
Опять же, нам нужно обрабатывать два разных случая при обнаружении предшественника узла в заказах в BST. Посмотрите на следующие диаграммы и постарайтесь связать два случая, упомянутые здесь.
Это тот случай, когда узел имеет левый ребенок. Нам нужно найти самый правый ребенок дерева, укоренившись на этом левом ребенке – самый правый узел в дереве, укорененный в 2.
Нет левого ребенка. Поэтому нам нужно найти родитель.
Если вы посмотрите внимательно, я только что изменил порядок прохождения здесь, а остальная часть кода такой же, как и раньше. (Примечание. Этот код используется, когда нет левого ребенка узла, для которого мы хотим найти предшественник в заказа).
Предшественник в целях заказа становится обратной преемником по порядку.
Ну, теперь, когда вы знаете, как вы должны организовать список посадочных классов, идут несколько твердых знаков ???. Просто шучу!! Обман плохой – никогда не делай этого!
Надеюсь, вы получили основную идею разных использований для структур данных и как найти преемника в порядке и предшественника в BST.
РЕДАКТИРОВАНИЕ: КУДОС ДИВИЯ ГОДАЯЛ, за то, что указывая на множество основных ошибок в первоначальном проекте, а также для обеспечения того, чтобы стать статьи вытекало:):)