简体   繁体   中英

Display Binary Search Tree Traversal on Javascript (Recursive way)

I am trying to console every data in a binary tree. My main problem is that I want to implementing in a recursive way. Basically I have this code so far:

this.levelOrder = function (root) {
    if (root.data != null) {
        console.log(root.data);

        if (root.left != null) {
            this.levelOrder(root.left);
        }

        if (root.right != null) {
            this.levelOrder(root.right)
        }
    } else {
        return;
    }
};

The output is 3 2 1 5 4 7

But It should be 3 2 5 1 4 7 . So Basically I am accessing the first child of the node instead of printing all the children first.

Assuming a tree like this,

      4
  2       6
1   3   5   7

and an object literal

tree = {
    data: 4,
    left: {
        data: 2,
        left: {
            data: 1,
            left: null,
            right: null
        },
        right: {
            data: 3,
            left: null,
            right: null
        }
    },
    right: {
        data: 6,
        left: {
            data: 5,
            left: null,
            right: null
        },
        right: {
            data: 7,
            left: null,
            right: null
        }
    }
};

you could call the function recursively and get first the left part and then the right part of the tree. The algorithm is called depth-first search .

This function uses a single check, because this is sufficient to check first and then to move on.

 var depthFirst = function (node) { if (node) { console.log(node.data); depthFirst(node.left); depthFirst(node.right) } }, tree = { data: 4, left: { data: 2, left: { data: 1, left: null, right: null }, right: { data: 3, left: null, right: null } }, right: { data: 6, left: { data: 5, left: null, right: null }, right: { data: 7, left: null, right: null } } }; depthFirst(tree); // 4 2 1 3 6 5 7

For a breadth-first search , an algorithm which is iterating every level of the tree first, you could use this code with the same tree data as above.

 var breadthFirst = function (node) { function bf(queue) { var newQueue = []; queue.forEach(function (node) { console.log(node.data); node.left && newQueue.push(node.left); node.right && newQueue.push(node.right); }); newQueue.length && bf(newQueue); } bf([node]); }, tree = { data: 4, left: { data: 2, left: { data: 1, left: null, right: null }, right: { data: 3, left: null, right: null } }, right: { data: 6, left: { data: 5, left: null, right: null }, right: { data: 7, left: null, right: null } } }; breadthFirst(tree); // 4 2 6 1 3 5 7

// Node Class
class Node {
  constructor (val, left=null, right=null) {
    this.val = val;
    this.left = left;
    this.right = right;
  }
}

// Binary Tree
const head = new Node(20, 
  new Node(10, 
  new Node(5, 
  new Node(3, 
  new Node(2, 
  new Node(1)), 
  new Node(4)), 
  new Node(8, 
  new Node(7, 
  new Node(6)), 
  new Node(9))), 
  new Node(15, 
  new Node(13, 
  new Node(11, 
  null, 
  new Node(12)), 
  new Node(14)), 
  new Node(18, 
  new Node(16, 
  null, 
  new Node(17)), 
  new Node(19)))), 
  new Node(30, new Node(25, 
  new Node(23, 
  new Node(22, 
  new Node(21)), 
  new Node(24)), 
  new Node(28, 
  new Node(27, 
  new Node(26)), 
  new Node(29))), 
  new Node(35, 
  new Node(33, 
  new Node(31, 
  null, 
  new Node(32)), 
  new Node(34)), 
  new Node(38, 
  new Node(36, 
  null, 
  new Node(37)), 
  new Node(39, 
  null, 
  new Node(40))))), 
  );

// VIEW
//                                                      20
//                                                     10 30
//                        5 15                           |                          25 35
//          3 8            |           13 18            ||           23 28            |            33 38
//   2 4     |    7 9     ||    11 14    |    16 19    |||    22 24    |    27 29    ||     31 34    |    36 39
//1 n | n n || 6 n | n n ||| n 12 | n n || 17 n | n n |||| 21 n | n n || 26 n | n n |||| n 32 | n n || n 37 | n 40

// In Order Tree Traversal
const inOrder = (node) => {
  if(node.left !== null) {
    inOrder(node.left);
  }
  console.log(node.val);
  if(node.right !== null) {
    inOrder(node.right);
  }
}

// Pre Order Tree Traversal
const preOrder = (node) => {
  console.log(node.val);
  if(node.left !== null) {
    preOrder(node.left);
  }
  if(node.right !== null) {
    preOrder(node.right);
  }
}

// Post Order Tree Traversal
const postOrder = (node) => {
  if(node.left !== null) {
    postOrder(node.left);
  }
  if(node.right !== null) {
    postOrder(node.right);
  }
  console.log(node.val);
}

// Node Count Recursively
const nodeCount = (node) => {
  if(node.left !== null) {
    nodeCount(node.left);
  }
  if(node.right !== null) {
    nodeCount(node.right);
  }
  count++;
}

// Sum of all Nodes Recursively
const totalValue = (node) => {
  if(node.left !== null) {
    totalValue(node.left);
  }
  if(node.right !== null) {
    totalValue(node.right);
  }
  total += node.val;
}

// inOrder(head);
// preOrder(head);
// postOrder(head);

let count = 0;
nodeCount(head)
console.log(count);

let total = 0;
totalValue(head)
console.log(total);
// NOTE
// if the values are continuous between 1/0 and n then the total is simply (n*(n+!))/2
// if the values are continuous between m and n then the total is simply ((n*(n+!))/2) - ((m*(m+!))/2)

Assuming the Node definition is like this:

export default class Node {
    constructor(data) {
        this.data = data;
        this.left = null;
        this.right = null;
    }
}

And the Tree definition is something like this:

import Node from './Node.js';

export default class BinarySearchTree {
    constructor() {
        this.root = null;
    }
    //methods such as search, traversal, insert, etc...
}

You can perform the traversals like this (preferably as methods of the BinarySearchTree) :

inorder(node) {
    if (node !== null) {
        this.inorder(node.left);
        console.log(node.data);
        this.inorder(node.right);
    }
}

postorder(node) {
    if (node !== null) {
        this.postorder(node.left);
        this.postorder(node.right);
        console.log(node.data);
    }
}

preorder(node) {
    if (node !== null) {
        console.log(node.data);
        this.preorder(node.left);
        this.preorder(node.right);
    }
}

If you want to store the result of the traversal, you could pass down some object the methods would append the values to.

--

BONUS

If you want to display the whole tree in a somewhat pleasing manner, I'd suggest using some additional information stored in the nodes and in the state of your app. First I'd add some additional info to each Node definition (x,y):

export default class Node {
    constructor(data) {
        this.data = data;
        this.left = null;
        this.right = null;
        this.x = 0;
        this.y = 0;
    }
}

Then create an empty grid (matrix) based on the current tree data,

createMatrix = (minX, maxX, depth) => {
        let displayMatrix = [];
        for (let i = 0; i <= depth; i++) {
            let line = [];
            for (let j = 0; j <= maxX - minX; j++) {
                line.push(' ');
            }
            displayMatrix.push(line);
        }
        return displayMatrix;
}

Where minX is the smallest X value among all nodes, and maxX is the largest (getting these values could happen while inserting the nodes into the tree, or by traversing the tree and comparing node properties) , and then fill it with the node data and the "\\", "/" characters between nodes:

    fillMatrix(node, matrix, minX, side = "root") {
      if(node !== null){
        matrix[node.y][node.x + Math.abs(minX)] = node.data;
        if(side === "left"){
            matrix[node.y-1][node.x + Math.abs(minX)+1] = '/';
        }
        else if (side === "right"){
            matrix[node.y - 1][node.x + Math.abs(minX) - 1] = '\\';
        }
        this.fillMatrix(node.left, matrix, minX, "left");
        this.fillMatrix(node.right, matrix, minX, "right");
      }
      return matrix;
    }

Then this result matrix would hold a " " where there are no 'lines' and no values, hold the numbers where the node.data are, and hold "/" or "\\" as the lines between the nodes. Then you can use this to display it on a canvas or in the console.

For example, here I created a table dynamically, and in every element there is a div, which is formatted differently based on what kind of character it holds:

在此处输入图片说明

/*                    |
                      h
          /                     \
         d                       l
    /         \             /         \
   b           f           j           n
 /   \       /   \       /   \       /   \
a     c     e     g     i     k     m     o

bt.insert("d");
bt.insert("l");
bt.insert("b");
bt.insert("f");
bt.insert("j");
bt.insert("n");
bt.insert("a");
bt.insert("c");
bt.insert("e");
bt.insert("g");
bt.insert("i");
bt.insert("k");
bt.insert("m");
bt.insert("o");
      

*/
function printTree(inorder, bredthOrder, size) {
   size = size || 2;
   const total = inorder.length;
   const numberOfLevels = Math.ceil(Math.log2(total));
   let pointerBOrder = 0;
   let blank = " ";
   for (let level = 0; level < numberOfLevels; level++) {
      let itemPerLevel = Math.pow(2, level);
      let out = [];
      let branches = [];
      let slantDirection = true;//up
      for (let itemNumber = 0; itemNumber < itemPerLevel; itemNumber++) {
         let item = bredthOrder[pointerBOrder++];
         for (let x = 1; x <= inorder.length; x++) {
            const ino = inorder[x - 1];
            let nInd = size * (x - 1);
            if (item == ino) {
               out[nInd] = item;
               if (item) {
                  if (bredthOrder[0] == item) {
                     branches[nInd] = "|";
                  } else if (slantDirection) {
                     branches[nInd + 1] = "/";
                  } else {
                     if (nInd - 1 >= 0) {
                        branches[nInd - 1] = "\\";
                     }
                  }
                  slantDirection = !slantDirection;
               }
            } else {
               out[nInd] = out[nInd] || blank;
            }
            branches[nInd] = branches[nInd] || blank;
            for (let fill = 1; fill <= size - 1; fill++) {
               out[nInd + fill] = blank;
               branches[nInd + fill] = branches[nInd + fill] || blank;
            }
         }
      }
      console.log(branches.join(''));
      console.log(out.join(''));
      out = [];
   }
}
class Node {
   constructor(value) {
      this.left = null;
      this.right = null;
      this.value = value;
   }
}
class BinaryTree {
   constructor(value) {
      this.root = value == null ? null : new Node(value);
   }

   insert(value, node) {
      if (node == null) {
         node = this.root;
      }
      if (node == null) {
         node = new Node(value);
         return;
      }
      if (node.value >= value) {
         if (node.left == null) {
            node.left = new Node(value);
            return;
         } else {
            this.insert(value, node.left);
            return;
         }
      } else {
         if (node.right == null) {
            node.right = new Node(value);
            return;
         } else {
            this.insert(value, node.right);
            return;
         }
      }
   }
   print(node, acc) {
      if (node == null) {
         return acc;
      } else {
         if (node.left != null) {
            acc = this.print(node.left, acc);
         }
         acc.push(node.value);
         if (node.right != null) {
            acc = this.print(node.right, acc);
         }
         return acc;
      }
   }
   printInOrder() {
      return this.print(this.root, []);
   }
   getSiblings(node) {
      if (node == null) {
         return [];
      }
      let acc = [];
      if (node.left != null) {
         acc.push(node.left);
      }
      if (node.right != null) {
         acc.push(node.right);
      }
      return acc;
   }
   printBredthOrder() {
      let result = [];
      if (this.root == null) {

      } else {
         let acc = [this.root];
         let workingAcc = [this.root];
         let tmpAcc = [];
         do {
            tmpAcc = [];
            for (let i = 0; i < workingAcc.length; i++) {
               acc = [...acc, workingAcc[i].left];
               acc = [...acc, workingAcc[i].right];
               let left = this.getSiblings(workingAcc[i].left);
               let right = this.getSiblings(workingAcc[i].right);
               tmpAcc = [...tmpAcc, ...left];
               tmpAcc = [...tmpAcc, ...right];
            }
            acc = [...acc, ...tmpAcc];
            workingAcc = tmpAcc;
         } while (tmpAcc.length != 0);
         for (let i = 0; i < acc.length; i++) {
            result.push(acc[i].value);
         }
      }

      return result;
   }
}
let bt = new BinaryTree("h");
bt.insert("d");
bt.insert("l");
bt.insert("b");
bt.insert("f");
bt.insert("j");
bt.insert("n");
bt.insert("a");
bt.insert("c");
bt.insert("e");
bt.insert("g");
bt.insert("i");
bt.insert("k");
bt.insert("m");
bt.insert("o");
let io = bt.printInOrder();
let bo = bt.printBredthOrder();
printTree(io, bo);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM