繁体   English   中英

在 Javascript 上显示二叉搜索树遍历(递归方式)

[英]Display Binary Search Tree Traversal on Javascript (Recursive way)

我正在尝试控制二叉树中的每个数据。 我的主要问题是我想以递归方式实现。 到目前为止,基本上我有这个代码:

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;
    }
};

输出为3 2 1 5 4 7

但它应该是3 2 5 1 4 7 所以基本上我是访问节点的第一个孩子,而不是先打印所有孩子。

假设一棵树是这样的,

      4
  2       6
1   3   5   7

和一个对象字面量

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
        }
    }
};

您可以递归调用该函数并首先获取树的左侧部分,然后获取右侧部分。 该算法称为深度优先搜索

此函数使用单个检查,因为这足以先检查然后继续。

 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

对于广度优先搜索(一种首先迭代树的每个级别的算法),您可以将此代码与上述相同的树数据一起使用。

 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)

假设 Node 定义是这样的:

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

而 Tree 的定义是这样的:

import Node from './Node.js';

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

您可以像这样执行遍历(最好作为 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);
    }
}

如果你想存储遍历的结果,你可以传递一些方法会将值附加到的对象。

——

奖金

如果您想以一种令人愉悦的方式显示整个树,我建议使用存储在节点和应用程序状态中的一些附加信息。 首先,我会为每个节点定义 (x,y) 添加一些附加信息:

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

然后根据当前的树数据创建一个空的网格(矩阵),

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;
}

其中 minX 是所有节点中最小的 X 值,maxX 是最大的(在将节点插入树时或通过遍历树并比较节点属性时可能会获得这些值) ,然后用节点数据填充它并节点之间的“\\”、“/”字符:

    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;
    }

然后这个结果矩阵将在没有“线”和值的地方保存一个“”,保存 node.data 所在的数字,并保存“/”或“\\”作为节点之间的线。 然后您可以使用它在画布或控制台中显示它。

例如,这里我动态创建了一个表格,在每个元素中都有一个 div,它的格式根据它所包含的字符类型而不同:

在此处输入图片说明

/*                    |
                      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);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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