Рубрики
Без рубрики

Окончательное руководство по основным структурам данных древовидной древесины пошаговая в Python и JavaScript

Структура данных деревьев является одной из наиболее распространенной и эффективной формы хранения, чтобы легко сохранить данные … Теги от структур данных, информатики, JavaScript, Python.

Структура данных деревьев является одной из наиболее распространенной и эффективной формы хранения, чтобы сохранить данные легко доступными в нисходящей структуре, которая выглядит как пирамида. Он используется в базах данных и всевозможных приложениях, поэтому вам нужно освоить его, если вы хотите стать лучшим программистом. Кроме того, это одна из самых заданных структур данных в интервью программирования.

Кроме того, это скорее всего Рождество, и мы все знаем, что деревья чрезвычайно важно, чтобы мы дышили, что свежий чистый воздух, почему бы не узнать, как они работают в компьютерном мире?

Если вам интересно, это некоторые из лучших случаев использования для структур данных дерева:

  • Для иерархических систем, таких как HTML-страница или компьютер, в котором структура папки выполнена из папок и файлов, хранящихся внутри нескольких слоев, как это:
          C:/ Drive
        /   |    |  \
    Apps Games Music Desktop
  • Для реализации индексов внутри баз данных для доступа к контенту чрезвычайно быстро.
  • Чтобы предсказать то, что вы печатаете на своем телефоне, учитывая, что каждая буква доступна в дереве и дает вам предложения по улучшению вашей скорости.

Определения

Чтобы начать понимание деревьев, вы должны ознакомиться со следующими понятиями:

  • Узел Каждый элемент дерева, содержащий значение. Подумайте о них как капсулы, связанные друг с другом.
  • Root это первый узел дерева.
  • Край Является ли линия, которая соединяет 2 узла.
  • Ребенок это узел, который имеет родительский узел.
  • Родительский узел Это тот, который содержит детей, нижние узлы.
  • Листья У узлов, у которых нет ребенка, последние узлы в дереве.
  • Высота это длина (начиная с 1) самого длинного пути к листу.
  • Глубина это длина пути к корню. По сути, меньшая высота для конкретного элемента, чтобы вы могли сказать «Пункт 5 на глубине 7». Мы будем использовать его для создания алгоритмов для наших деревьев.

Бинарные деревья

Теперь, когда вы понимаете, как сделаны деревья, давайте посмотрим на определенный тип дерева. Существует много вариаций, но бинарное дерево является одним из самых используемых из-за его простоты и скорости, и они выглядят так:

             Root
            /     \
        Left       Right
       /    \     /     \
   Left   Right Left     Right

Это деревья, где каждый узел может содержать максимум 2 детей, называемых левым и правым узлами. В коде дерево – это куча вложенных объектов:

root => {
   value: 1,
   left: {
      value: 3,
      left: {
      value: 2,
      left: null,
      right: null,
      },
      right: {
      value: 8,
      left: null,
      right: null,
      }
   },
   right: {
      value: 3,
      left: null,
      right: null,
   }
}

Это конкретное дерево выглядит так:

                                   1
                                 /   \
                                3     3
                              /   \
                             2     8

Каждый узел представляет собой объект JavaScript из значения узла и левых и правых подвалок. Субъективы могут быть нулевыми, но ценность не может быть пустым.

В этом руководстве вы увидите фрагменты кода Python и JavaScript, чтобы понять реализацию на 2 различных языках со своими собственными различиями. Итак, вот код для создания этого конкретного дерева выше в Python:

class Node:
    value = None
    left = None
    right = None
    def __init__(value, self):
        self.value = value
    def add_left(value, self):
        self.left = Node(value)
    def add_right(value, self):
        self.right = Node(value)

tree = Node(11)
tree.add_left(8)
tree.add_right(16)
print(tree.value) # 11
print(tree.left.value) # 8
print(tree.right.value) # 16

# Let's add the next items
tree.left.add_left(5)
tree.left.add_right(10)
tree.right.add_right(18)

print(tree.value) # 11
print(tree.left.value) # 8
print(tree.right.value) # 16

print(tree.left.left.value) # 5
print(tree.left.right.value) # 10
print(tree.right.right.value) # 18

Узел класса содержит значение «Государственные переменные», слева и справа. Конструктор инициализирует значение корневого узла, когда методы ADD_LEFT и ADD_Right используются для настройки левых и правых узлов.

Обратите внимание, что левые и правильные значения являются еще одним экземпляром узла класса. Они не являются простой ценностями, они являются узлами со значением, слева и справа.

Алгоритмы дерева

Вот и забавная часть. Для навигации по деревьям и доступа к определенным значениям, мы используем что-то, называемое алгоритмами Tree Traversal, которые позволяют вам перемещаться в определенном направлении, чтобы найти значения быстрее. Давайте посмотрим на некоторые из самых популярных …

Есть 2 основных метода обхода:

  1. Метод глубины – сначала поиска (DFS)
  2. Метод по ширине-поиску (BFS)

Затем внутри DFS есть 3 подкатегории в зависимости от используемого порядка:

  1. До порядок
  2. Чтобы
  3. Пост порядка

Давайте посмотрим, чтобы понять каждый метод обхода в <1 минуту каждый:

До порядок

Шаги для выполнения этого следующие:

  • Сначала распечатайте значение текущего узла.
  • Затем перейдите на левый узел.
  • Затем перейдите к правильному узлу.

Перед проверкой алгоритма вот внедрение простой структуры данных дерева в JavaScript, которую мы будем использовать для примеров:

class Tree {
  value = null
  left = null
  right = null
  constructor(value) {
    this.value = value
  }
  addLeft(value) {
    this.left = new Tree(value)
  }
  addRight(value) {
    this.right = new Tree(value)
  }
}
const myTree = new Tree(1)

Это сделано рекурсивно, поэтому вам не нужно беспокоиться о чем-либо еще. Вот код в JavaScript:

preOrder() {
  console.log(this.value)
  if (this.left) this.left.preOrder()
  if (this.right) this.right.preOrder()
}

И вот полная реализация с использованием класса дерева в современном JavaScript:

class Tree {
  value = null
  left = null
  right = null
  constructor(value) {
    this.value = value
  }
  addLeft(value) {
    this.left = new Tree(value)
  }
  addRight(value) {
    this.right = new Tree(value)
  }
  preOrder() {
    console.log(this.value)
    if (this.left) this.left.preOrder()
    if (this.right) this.right.preOrder()
  }
}
const myTree = new Tree(1)

Чтобы

Шаги для выполнения этого следующие:

  • Перейти к левому узлу.
  • Затем распечатайте значение текущего узла.
  • Затем перейдите к правильному узлу.

Вот код в JS:

inOrder() {
  if (this.left) this.left.inOrder()
  console.log(this.value)
  if (this.right) this.right.inOrder()
}

И полная реализация:

class Tree {
  value = null
  left = null
  right = null
  constructor(value) {
    this.value = value
  }
  addLeft(value) {
    this.left = new Tree(value)
  }
  addRight(value) {
    this.right = new Tree(value)
  }
  inOrder() {
    if (this.left) this.left.inOrder()
    console.log(this.value)
    if (this.right) this.right.inOrder()
  }
}
const myTree = new Tree(1)

Пост порядка

Шаги для выполнения этого следующие:

  • Перейти к левому узлу.
  • Затем перейдите к правильному узлу.
  • Наконец распечатайте значение текущего узла.

Вот код в JS:

postOrder() {
  if (this.left) this.left.postOrder()
  if (this.right) this.right.postOrder()
  console.log(this.value)
}

Широк-первый поиск (BFS)

Последние методы прохождения наших деревьев используют уровень уровней от поперечного порта в ширину, который состоит в том, чтобы получить значения слоями. Вы проходите дерево горизонтально, пока не получите доступ к всем значениям.

Этот немного сложнее, потому что нам нужна дополнительная структура данных: очередь. Думайте о очередях как массивы, где вы можете получить только первый элемент. Вы можете добавлять элементы в конце очереди и удалить их обратно с самого начала. Как и кинотеатр, где люди покупают свои билеты в порядке, следуя очереди. Это называется FIFO, где первым вначале сначала. Например:

let queue = []
queue.push(1)
queue.push(2)
queue.push(3) // Now our queue is [1, 2, 3]
queue.get() // Returns "1"
queue.shift() // Removes "1"
console.log(queue) // Returns [2, 3]

Шаги следующие:

  • Распечатайте значение первого элемента очереди.
  • Добавьте весь левый узел в очередь.
  • Добавьте весь правый узел в очередь.

Вы начинаете с добавления всего корневого узла в очередь. Вот как это выглядит в JavaScript:

bfs() {
  let queue = []
  queue.push(this)
  while (queue.length > 0) {
    const active = queue[0]
    console.log(active.value)
    if (active.left) queue.push(active.left)
    if (active.right) queue.push(active.right)
    queue.shift()
  }
}

Вот полная реализация:

class Tree {
  value = null
  left = null
  right = null
  constructor(value) {
    this.value = value
  }
  addLeft(value) {
    this.left = new Tree(value)
  }
  addRight(value) {
    this.right = new Tree(value)
  }
  bfs() {
    let queue = []
    queue.push(this)
    let counter = 0
    while (queue.length > 0 && counter < 100) {
      const active = queue[0]
      console.log(active.value)
      if (active.left) queue.push(active.left)
      if (active.right) queue.push(active.right)
      queue.shift()
      counter++
    }
  }
}
const myTree = new Tree(1)

Сейчас это полная реализация первого поиска и сначала поиска по глубину и широко распространена, что вы только что видели для вашего будущего ссылки:

class Tree {
  value = null
  left = null
  right = null
  constructor(value) {
    this.value = value
  }
  addLeft(value) {
    this.left = new Tree(value)
  }
  addRight(value) {
    this.right = new Tree(value)
  }
  preOrder() {
    console.log(this.value)
    if (this.left) this.left.preOrder()
    if (this.right) this.right.preOrder()
  }
  inOrder() {
    if (this.left) this.left.inOrder()
    console.log(this.value)
    if (this.right) this.right.inOrder()
  }
  postOrder() {
    if (this.left) this.left.postOrder()
    if (this.right) this.right.postOrder()
    console.log(this.value)
  }
  bfs() {
    let queue = []
    queue.push(this)
    let counter = 0
    while (queue.length > 0 && counter < 100) {
      const active = queue[0]
      console.log(active.value)
      if (active.left) queue.push(active.left)
      if (active.right) queue.push(active.right)
      queue.shift()
      counter++
    }
  }
}
const myTree = new Tree(1)

А в Python:

from Queue import Queue

class Node:
    value = None
    left = None
    right = None
    def __init__(self, value):
        self.value = value
    def add_left(self, value):
        self.left = Node(value)
    def add_right(self, value):
        self.right = Node(value)
    def pre_order(self):
        print(self.value)
        if self.left:
            self.left.pre_order()
        if self.right:
            self.right.pre_order()
    def in_order(self):
        if self.left:
            self.left.in_order()
        print(self.value)
        if self.right:
            self.right.in_order()
    def post_order(self):
        if self.left:
            self.left.post_order()
        if self.right:
            self.right.post_order()
        print(self.value)
    def breadth_first_search(self):
        queue = Queue()
        queue.put(self)
        while not queue.empty():
            active = queue.get()
            print(active.value)
            if active.left:
                queue.put(active.left)
            if active.right:
                queue.put(active.right)
# Some sample data
tree = Node('html')
tree.add_right('body')
tree.right.add_left('h1')
tree.right.add_right('h2')
# A few examples here
tree.pre_order()
print('\n')
tree.in_order()
print('\n')
tree.post_order()
print('\n')
tree.breadth_first_search()

Двоичная структура данных деревьев поиска

Теперь, когда вы видели, как создавать и использовать деревья, давайте посмотрим на определенные реализации структуры данных дерева, называемую двоичным валком поиска.

Эти типы деревьев известны заказываемыми, для того, чтобы все элементы сортируются вертикально. Как работает, это следующее:

  • Начните с корневого элемента, например, номер 20.
  • Если следующий узел в дереве меньше корня, поместите его влево.
  • Если он больше, поместите его вправо.

Допустим, вам нужно создать двоичное дерево поиска из этих входов: 20, 10, 49, 28, 59, 29 Начните с корневого узла, в этом случае 20. Следующий узел, 10, будет помещен влево, потому что 10 меньше 20 Итак, у нас есть:

   20
  /
10

Затем следующий узел 49, идет вправо на 20, потому что он больше:

   20
  /  \
10    49

Далее узел 28 идет слева от узла 49. Почему? Поскольку мы не можем изменить существующие узлы, все, что мы можем сделать, это добавлять элементы на текущее дерево, чтобы он идет слева от 49, как это:

   20
  /  \
10    49
     /
   28

После этого мы добавляем узел 59 вправо 49, потому что он больше:

    20
   /  \
 10    49
      /  \
    28    59

Наконец мы добавляем элемент 29 справа от 28 вроде так:

    20
   /  \
 10    49
      /  \
    28    59
      \
       29

Теперь вы можете спросить, почему бы не добавить 29 слева от узла 59? В конце концов, 29 меньше 59, так что он мог пойти туда. Хороший вопрос. Причина, по которой мы добавляем его справа от 28, а не слева от 59, потому что двоичное дерево поиска должно быть заказано вертикально, что означает, что если вы идете слева направо, вы увидите, что элементы растут в цене. Посмотрите на текущую структуру. Сначала у нас есть слева, затем 20 слегка вправо и сверху его, затем 28 ниже 20, то 49, 29 ниже 49 и 59. Это не имеет смысла идти ниже после того, как до 59 года.

Просто чтобы быть понятным, что мы следуем следующим образом: это этот алгоритм:

  • Начните в корне.
  • Это узел, который мы хотим вставить больше корня? Идите направо, еще слева.
  • Продолжайте идти вниз по уровню, проверка, если узел больше или меньше, пока вы не найдете правильное место.

Вот разбивка последнего элемента, который мы добавили, узел 29:

  1. 29 больше или меньше 20? Больше, иди направо.
  2. 29 больше или меньше 49? Меньший, иди налево.
  3. 29 больше или меньше, чем 28? Больше, иди направо.
  4. Потому что нет больше узлов, сохраняют эту позицию.

Вот почему мы не разместили узел 29 слева от 59, даже если он может показаться логичным сначала.

Вот давно ожидаемый код в Python и JavaScript для реализации двоичного поиска дерева:

В JavaScript:

class BinarySearchTree {
  value = null
  left = null
  right = null
  constructor (value) {
    this.value = value
  }
  addNode (value) {
    if (value >= this.value && !this.right) {
      this.right = new BinarySearchTree(value)
    } else if (value < this.value && !this.left) {
      this.left = new BinarySearchTree(value)
    } else if (value >= this.value && this.right) {
      this.right.addNode(value)
    } else if (value < this.value && this.left) {
      this.left.addNode(value)
    }
  }
  bfs() {
    let queue = []
    queue.push(this)
    let counter = 0
    while (queue.length > 0 && counter < 100) {
      const active = queue[0]
      console.log(active.value)
      if (active.left) queue.push(active.left)
      if (active.right) queue.push(active.right)
      queue.shift()
      counter++
    }
  }
}
// BST with 20, 10, 49, 28, 59, 29
let b = new BinarySearchTree(20)
b.addNode(10)
b.addNode(49)
b.addNode(28)
b.addNode(59)
b.addNode(29)
b.bfs()

В Python:

import Queue from Queue
class BinarySearchTree:
    value = None
    left = None
    right = None
    def __init__(self, value):
        self.value = value
    def add_node(self, value):
        if value >= self.value and self.right is None:
            self.right = BinarySearchTree(value)
        elif value < self.value and self.left is None:
            self.left = BinarySearchTree(value)
        elif value >= self.value and self.right is not None:
            self.right.add_node(value)
        elif value < self.value and self.left is not None:
            self.left.add_node(value)
    def breadth_first_search(self):
        queue = Queue()
        queue.put(self)
        while not queue.empty():
            active = queue.get()
            print(active.value)
            if active.left:
                queue.put(active.left)
            if active.right:
                queue.put(active.right)
# BST with 20, 10, 49, 28, 59, 29
binarySearchTree = BinarySearchTree(20)
binarySearchTree.add_node(10)
binarySearchTree.add_node(49)
binarySearchTree.add_node(28)
binarySearchTree.add_node(59)
binarySearchTree.add_node(29)
binarySearchTree.breadth_first_search()

Как видите, я добавил широкий поиск первого уровня, чтобы проверить, что значения добавляются правильно из узлов, которые мы ранее увидели, чтобы убедиться, что они добавляются в правильном порядке.

add_node Метод использует рекурсию для добавления узлов, вызывая ту же функцию снова и снова, пока элемент не будет размещен. Это означает, что все рекурсивные звонки будут храниться в памяти, поэтому, если вы работаете на небольшом компьютере памяти, вы можете столкнуться с проблемами, если вы создаете массивное двоичное дерево поиска. Если вам интересно по теме лимитов памяти в рекурсивных функциях, проверьте эту страницу https://rosettacode.org/wiki/find_limit_of_recursion. Где вы увидите фактические пределы каждого языка.

Например, в JavaScript максимальная глубина рекурсии в Chrome составляет 10473, в то время как в Firefox это 3000. Хотя это может варьироваться от версии к версии. В моем случае я получаю 11402 уровней глубоким до того, как он достигнет предела памяти. Вы можете попробовать сами на своем компьютере, запустив эту функцию в инструментах разработчиков Chrome (по праву нажав на щелчок и выбираю «Осмотрите» на любой странице, чтобы открыть вкладку консоли, где вы можете вставить этот код):

function recurse(depth)
{
 try
 {
  return recurse(depth + 1);
 }
 catch(ex)
 {
  return depth;
 }
}

let maxRecursion = recurse(1);
console.log("Recursion depth on this system is " + maxRecursion)

Возвращаясь к дереву BST, обратите внимание, как add_node Метод помещает идентичные значения вправо и меньшим значениям влево. В случае, если вы добавляете тот же узел с повторным значением. Вы можете настроить свои собственные правила для исключительных случаев, таких как те, где мы повторили значения или неверные.

Единственное отличие от бинарного дерева и двоичного поиска – это способ размещения элементов. В бинарном дереве вы добавляете узлы влево или вправо в зависимости от ваших предпочтений, в то время как в двоичном валке поиска вы позволяете алгоритму решить правильное место в упорядоченной моде с add_node метод. Это позволяет существовать очень эффективные методы поиска, поскольку вам не нужно проверять все дерево, просто ветвь в правильное значение.

Найти значения в двоичных валках поиска

Теперь давайте посмотрим, как мы находим значения в нашем двоичном дереве поиска. Процесс найти, если данное значение существует где-то идет так:

  1. Учитывая значение x, проверьте, если X больше или меньше корневого узла.
  2. Если больше продолжайте проверять на следующем уровне справа или налево, в противном случае.
  3. Если больше нет узлов, чтобы проверить или если значение одинаково, см., Если текущее значение узла является тем, кого мы ищем.
  4. Верните True или False в зависимости от того, если значение существует в двоичном дереве поиска.

Вот реализация в JavaScript:

existsNode (value) {
  if (value > this.value && this.right) {
    return this.right.existsNode(value)
  } else if (value < this.value && this.left) {
    return this.left.existsNode(value)
  }
  return value == this.value
}

А в Python:

def exists_node(self, value):
  if value > self.value and self.right:
    return self.right.exists_node(value)
  elif value < self.value and self.left:
    return self.left.exists_node(value)
  return value is self.value

Удаление значений в двоичных валках поиска

Когда дело доходит до удаления узлов двоичного дерева поиска, нам нужно иметь в виду возможные различия, которые могут возникнуть. Давайте посмотрим на 3 сценария, чтобы проиллюстрировать, что происходит в разных деревьях с переменными узлами:

Удаление узла без детей

Вот как это выглядит:

      10                         10
     /  \                          \
    8    20    –Delete 8–>          20

По сути, мы оставляем пустое пространство. Так что в коде это должно сделать следующее:

  1. Найдите узел для удаления
  2. Проверьте, если узел мы удалении не имеют детей
  3. Если это так, просто опустойте его значение, заменяя его NULL или NOOT, в зависимости от вашего языка

Для этого метода нам нужно пройти значение, которое мы хотим удалить, и родитель, поскольку это важно для завершения удаления. В качестве альтернативы вы можете реализовать метод, который возвращает родитель узла, который вы хотите удалить, но в этом случае мы будем держать его простым и просто пройти родитель.

Кроме того, метод вернет истинный или ложный в зависимости от того, находит ли он узел для удаления или нет. Мы будем использовать рекурсию. Обратите внимание, что вы не можете удалить корневой узел, поскольку не имеет смысла делать это, когда вы можете просто заменить все дерево, чтобы мы не позволили этой возможности.

Так что в JavaScript:

deleteNode (value, parent) {
  // Find the node to delete using recursion
  if (this.value == value) {
    if (parent.left && parent.left.value == value) {
      parent.left = null
      return true
    } else {
      parent.right = null // One of the branches must be the exact value so we remove the right if the left is not the right value
      return true
    }
  } else if (value > this.value && this.right) {
    return this.right.deleteNode(value, this)
  } else if (value < this.value && this.left) {
    return this.left.deleteNode(value, this)
  } else {
    return false // We didn't find the element
  }
}

Вы можете посмотреть, как он сравнивает значение текущего узла, чтобы увидеть, равно ли значение равно, больше или меньше, чтобы вызовите функцию рекурсивно, пока не найдут узел для удаления, либо у нас нет узлов. Это возможно только потому, что наше двоичное дерево поиска заказывается, чтобы мы могли перемещаться по дереву логичным способом.

Удаление узла только одним детьми

Когда вы хотите удалить узел, и этот узел имеет ровно один ребенок, то, что мы делаем в этом случае, заменяют текущий узел с ребенком вместо того, чтобы оставить его пустым. Вот визуальный:

    10                          10
   /  \                        /  \
  8    20    –Delete 20–>     8    11
      /
     11

И код в JavaScript. У вас будет последний код в Python в конце этого руководства:

deleteNode (value, parent) {
  // Find the node to delete using recursion
  if (this.value == value) {
    if (parent.left && parent.left.value == value) {
      // If the element we are trying to delete has a child and only one we replace it with that child
      if (this.left && !this.right) {
        parent.left = this.left
      } else if (!this.left && this.right) {
        parent.left = this.right
      } else if (this.left && this.right) {
        // The node we want to delete has 2 children
      } else {
        // The node we want to delete has no children
        parent.left = null
      }
    } else {
      // One of the branches must be the exact value so we remove the right if the left is not the right value
      if (this.left && !this.right) {
        parent.right = this.left
      } else if (!this.left && this.right) {
        parent.right = this.right
      } else if (this.left && this.right) {
        // The node we want to delete has 2 children
      } else {
        // The node we want to delete has no children
        parent.right = null
      }
    }
    return true
  } else if (value > this.value && this.right) {
    return this.right.deleteNode(value, this)
  } else if (value < this.value && this.left) {
    return this.left.deleteNode(value, this)
  } else {
    return false // We didn't find the element
  }
}

Там мы просто обновляем узел, который будет удален с соответствующим ребенком. Если узел удалить 2 детей, мы еще ничего не делаем. Остальная логика остается то же самое, учитывая, что процесс обхода не изменился.

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

Удаление узла 2 детьми

Теперь, когда дело доходит до удаления узла с 2 детьми, нам нужно настроить некоторые правила. Вы можете заменить узел левым ребенком или с правым ребенком, зная, что вам придется разместить другую сторону в крайний крайний или крайний край этого «обновленного» узла. Рассмотрим эту ситуацию:

     10                          10
    /  \                        /  \
   8    20    –Delete 20–>     8    14
       /  \                        /  \
     14    23                    11    17
    /  \                                \
  11    17                               23

Мы сняли узел 20, то мы решили заменить это пустое пространство левым узлом, подняв его, зная, что правая сторона удаленного узла, 23, будет помещена в самом правом помещении повышенного узла. Таким образом, узел 23 будет размещен на самом большом узле филиала 14, последний элемент на самом правом месте.

Надеюсь, что имеет смысл. Если нет, помните, что все элементы слева всегда меньше, чем текущий узел и элементы справа всегда больше, чем текущий узел. Вот почему, когда мы подняем левый узел, мы хотим разместить висит правый узел на последнее правильное место нашего повышенного узла. Помните, что двоичные деревья могут иметь только 2 детей.

Вот как это выглядит в JavaScript:

deleteNode (value, parent) {
  // Find the node to delete using recursion
  if (this.value == value) {
    if (parent.left && parent.left.value == value) {
      // If the element we are trying to delete has a child and only one we replace it with that child
      if (this.left && !this.right) {
        parent.left = this.left
      } else if (!this.left && this.right) {
        parent.left = this.right
      } else if (this.left && this.right) {
        // The node we want to delete has 2 children
        // 1\. We will replace the deleted node with the left one so find the largest node on the left and place the hanging right node there
        // 2\. Then replace the node after the hanging one has been taken care of (we can only have 2 children in binary trees)
        parent.left = this.left
        let currentNode = this.left
        let isAdded = false
        while (!isAdded) {
          if (currentNode.right) {
            currentNode = currentNode.right
          } else {
            // Add the hanging right node to the end of the left branch from the right
            currentNode.right = this.right
            isAdded = true
          }
        }
      } else {
        // The node we want to delete has no children
        parent.left = null
      }
    } else {
      // One of the branches must be the exact value so we remove the right if the left is not the right value
      if (this.left && !this.right) {
        parent.right = this.left
      } else if (!this.left && this.right) {
        parent.right = this.right
      } else if (this.left && this.right) {
        // The node we want to delete has 2 children
        parent.right = this.left
        let currentNode = this.left
        let isAdded = false
        while (!isAdded) {
          if (currentNode.right) {
            currentNode = currentNode.right
          } else {
            // Add the hanging right node to the end of the left branch from the right
            currentNode.right = this.right
            isAdded = true
          }
        }
      } else {
        // The node we want to delete has no children
        parent.right = null
      }
    }
    return true
  } else if (value > this.value && this.right) {
    return this.right.deleteNode(value, this)
  } else if (value < this.value && this.left) {
    return this.left.deleteNode(value, this)
  } else {
    return false // We didn't find the element
  }
}

Вот объяснение:

  • Когда узел для удаления имеет 2 детей, мы запускаем этот код:
    // The node we want to delete has 2 children
    parent.right = this.left
    let currentNode = this.left
    let isAdded = false
    while (!isAdded) {
      if (currentNode.right) {
        currentNode = currentNode.right
      } else {
        // Add the hanging right node to the end of the left branch from the right
        currentNode.right = this.right
        isAdded = true
      }
    }
  • То, что он делает, сначала замените узел, чтобы удалить с левым ребенком, включая все суб-дети, поскольку они подключены автоматически ссылками.
  • После этого нам нужно расположить висит правый ребенок до конца правильной ветви левого ребенка. Мы бежим в то время как Цикл, который обращается к левому ребенку, тот, который мы вернули прямо сейчас, и мы находим самый правый узел. Это узел с наивысшим значением.
  • Мы знаем, что последний правый узел не имеет детей, поэтому мы используем Если Заявление и установите подвесной правый узел в качестве ребенка последнего правого узла от левого ребенка.
  • Теперь удаленный узел был заменен левым ребенком, а правый ребенок был помещен в нижней части левого ребенка, поскольку это наибольшее значение, потому что мы используем заказанные двоичные деревья.

Сначала он сбивает с толку, поэтому обязательно напишите свои собственные образцы для проверки функциональности и дайте мне знать, если вы обнаружите какие-либо ошибки для исправления или улучшений.

Обязательно присоединитесь к моим списке электронного письма, чтобы получить эксклюзивные руководства по электронной почте, который вы не найдете в другом месте, написанные мной от 5 лет опыта работы в программировании здесь: http://eepurl.com/ddq2yx.

Оригинал: “https://dev.to/merlox/the-ultimate-guide-to-master-tree-data-structures-step-by-step-in-python-and-javascript-5dno”