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

Методы обхода дерева в JavaScript

Дерево – интересная структура данных. Он имеет широкий спектр применений в всевозможных областях …. Tagged с JavaScript, Tree, Algorithms.

Дерево – интересная структура данных. Он имеет широкий спектр применений в всевозможных областях. Например:

  • DOM – это структура данных дерева
  • Каталог и файлы в нашей ОС могут быть представлены как деревья
  • Семейная иерархия может быть представлена как дерево.

Существует куча вариаций дерева (таких как кучи, BST и т. Д.), Которые можно использовать при решении проблем, связанных с планированием, обработкой изображений, базами данных и т. Д. Многие из сложных проблем могут показаться не связанными с деревом при быстром взгляде, но на самом деле могут быть представлены как единые. Мы также рассмотрим такие проблемы (в более поздних частях этой серии), чтобы увидеть, как деревья могут сделать, казалось бы, сложные проблемы намного легче понимать и решать.

Введение

Реализация Узел Для двоичного дерева довольно просто.

function Node(value){
  this.value = value
  this.left = null
  this.right = null
}
// usage
const root = new Node(2)
root.left = new Node(1)
root.right = new Node(3)

Таким образом, эти несколько строк кода создаст для нас двоичное дерево, которое выглядит так:

        2  
      /  \
     1    3
   /        \
null       null

Прохладный! Так что это было легко. Теперь, как мы можем использовать это?

Прохождение

Давайте начнем с попытки пройти через эти соединенные узлы деревьев (или дерево). Так же, как мы можем перевернуть через массив, было бы круто, если бы мы могли «перевернуть» через узлы деревьев. Тем не менее, деревья не являются линейными структурами данных, такими как массивы, поэтому нет одного способа пересечения их. Мы можем широко классифицировать подходы к обходу на следующие:

  • Ширина первая обход
  • Глубина первого обхода

Ширина Первый поиск/обход (BFS)

При таком подходе мы пересекаем уровень дерева по уровню. Мы начинали с корня, затем покрывали всех его детей, и мы охватываем всех детей 2 -го уровня, так далее и так далее. Например, для приведенного выше дерева, Traversal приведет к чему -то подобному:

2, 1, 3

Вот иллюстрация с немного сложным деревом, чтобы сделать это еще проще, чтобы понять:

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

  1. Инициировать очередь с корнем в нем
  2. Удалить первый элемент из очереди
  3. Протолкнуть влево и правое дети, выпавшие в очередь
  4. Повторите шаги 2 и 3, пока очередь не станет пустой

Вот как этот алгоритм будет выглядеть как пост -реализация:

function walkBFS(root){
  if(root === null) return

  const queue = [root]
  while(queue.length){
      const item = queue.shift()
      // do something
      console.log(item)

      if(item.left) queue.push(item.left)
      if(item.right) queue.push(item.right)
   }
}

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

function walkBFS(root){
  if(root === null) return

  const queue = [root], ans = []

  while(queue.length){
      const len = queue.length, level = []
      for(let i = 0; i < len; i++){
          const item = queue.shift()
          level.push(item)
          if(item.left) queue.push(item.left)
          if(item.right) queue.push(item.right)
       }
       ans.push(level)
   }
  return ans
}

Глубина первый поиск/обход (DFS)

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

 root node -> left node -> right node // pre-order traversal
 left node -> root node -> right node // in-order traversal
 left node -> right node -> root node // post-order traversal

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

Заказ о предварительном заказе

Вот как выглядит предварительный заказ для дерева:

 root node -> left node -> right node 

Обманывать:

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

Реализация:

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

function walkPreOrder(root){
  if(root === null) return

  // do something here
  console.log(root.val)

  // recurse through child nodes
  if(root.left) walkPreOrder(root.left)
  if(root.right) walkPreOrder(root.left)
}

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

function walkPreOrder(root){
  if(root === null) return

  const stack = [root]
  while(stack.length){
      const item = stack.pop()

      // do something
      console.log(item)

      if(item.right) stack.push(item.right)
      if(item.left) stack.push(item.left)
   }
}

Прохождение по порядку

Вот как выглядит порядок в дереве:

root node -> left node -> right node 

Обманывать:

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

Реализация:

Рекурсивный:

function walkInOrder(root){
  if(root === null) return

  if(root.left) walkInOrder(root.left)

 // do something here
  console.log(root.val)

  if(root.right) walkInOrder(root.right)
}

Итеративный:

function walkInOrder(root){
  if(root === null) return

  const stack = []
  let current = root

  while(stack.length || current){
      while(current){
         stack.push(current)
         current = current.left
      }
      const last = stack.pop()

      // do something
      console.log(last)

      current = last.right
   }
}

Пострядный обход

Вот как выглядит Posterord Traversal для дерева:

 left node -> right node -> root node 

Обманывать:

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

Реализация:

Давайте погрузимся в фактическую реализацию для такого обхода.

Рекурсивный:

function walkPostOrder(root){
  if(root === null) return

  if(root.left) walkPostOrder(root.left)
  if(root.right) walkPostOrder(root.right)

  // do something here
  console.log(root.val)

}

Итеративный:

function walkPostOrder(root){
  if(root === null) return []

  const tempStack = [root], mainStack = []

  while(tempStack.length){
      const last = tempStack.pop()

      mainStack.push(last)

      if(last.left) tempStack.push(last.left)
      if(last.right) tempStack.push(last.right)
    }

    return mainStack.reverse()
}

Бонус: JavaScript наконечник

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

 for(let node of walkPreOrder(tree) ){
   console.log(node)
 }

Выглядит действительно красиво и довольно просто для чтения, не так ли? Все, что нам нужно сделать, это использовать Прогулка функция, которая вернет итератор.

Вот как мы можем изменить наш Walkpreorder функция выше, чтобы вести себя в соответствии с примером, разделенным выше:

function* walkPreOrder(root){
   if(root === null) return

  const stack = [root]
  while(stack.length){
      const item = stack.pop()
      yield item
      if(item.right) stack.push(item.right)
      if(item.left) stack.push(item.left)
   }
}

Эта статья была первоначально опубликована в Stackfull.dev . Если вы хотите, чтобы вас уведомляли, когда я бросаю больше таких статей, рассмотрите возможность подписки на Информационный бюллетень Анкет

Оригинал: “https://dev.to/anishkumar/tree-data-structure-in-javascript-1o99”