js实现树的遍历

1,771 阅读3分钟

一、树是什么?

一种分层数据的抽象模型

1.前端工作中常见的树包括: DOM树、级联选择、树形控件...
2.JS中没有树,但是可以用Object和Array构建树
{
  value: "",
  label: "",
	children: [
    {
      value: "",
      label: "",
      children: []
    }
  ]
}
3.树的常用操作:深度/广度优先遍历、先中后序遍历

二、什么是深度/广度优先遍历?

深度优先遍历:尽可能深的搜索树的分支

image.png

深度优先遍历流程

  1. 访问根节点
  2. 对根节点的children挨个进行深度优先遍历 (递归)

代码实现

const tree = {
  val: "a",
  children: [
    {
      val: "b",
      children: [
        {
          val: "d",
          children: [],
        },
        {
          val: "e",
          children: [],
        },
      ],
    },
    {
      val: "c",
      children: [
        {
          val: "f",
          children: [],
        },
        {
          val: "g",
          children: [],
        },
      ],
    },
  ],
};
//深度优先遍历
const dfs = (root) => {
    console.log(root.val);
    root.children.forEach((child) => {dfs(child)});
}

dfs(tree);

广度优先遍历:先访问离根节点最近的节点

image.png

广度优先遍历流程

  1. 新建一个队列,把根节点入队
  2. 把队头出队并访问
  3. 把队头的children挨个入队
  4. 重复第二、三步,直到队列为空

代码实现

const tree = {
  val: "a",
  children: [
    {
      val: "b",
      children: [
        {
          val: "d",
          children: [],
        },
        {
          val: "e",
          children: [],
        },
      ],
    },
    {
      val: "c",
      children: [
        {
          val: "f",
          children: [],
        },
        {
          val: "g",
          children: [],
        },
      ],
    },
  ],
};
//广度优先遍历
const bfs = (root) => {
    const q = [root];
    while(q.length > 0) {
      const n = q.shift();
      console.log(n.val);
      n.children.forEach(child => {
        q.push(child);
      })
    }
    
}

bfs(tree);

三、二叉树的先中后序遍历

二叉树定义

树中每个节点最多只能有两个子节点

在js中通常用Object来模拟二叉树

const binaryTree = {
  val: 1,
  left: {
    val : 2,
    left: null,
    right: null
  },
  right: {
    val: 3,
    left: null,
    right: null
  }
}

先序遍历: 根左右

  1. 访问根节点
  2. 对根节点的左子树进行先序遍历
  3. 对根节点的右子树进行先序遍历

image.png

递归版代码实现

const bt = {
    val: 50,
    left: {
      val: 10,
      left: {
        val: 5,
        left: null,
        right: null,
      },
      right: {
        val: 15,
        left: null,
        right: null,
      },
    },
    right: {
      val: 70,
      left: {
        val: 60,
        left: null,
        right: null,
      },
      right: {
        val: 80,
        left: null,
        right: null,
      },
    },
  };

先序遍历
const preorder = (root) => {
    if (!root) { return };
    console.log(root.val);
    preorder(root.left);
    preorder(root.right);
}
// 50 10 5 15 70 60 80 

preorder(bt);

非递归版代码实现

const bt = {
    val: 50,
    left: {
      val: 10,
      left: {
        val: 5,
        left: null,
        right: null,
      },
      right: {
        val: 15,
        left: null,
        right: null,
      },
    },
    right: {
      val: 70,
      left: {
        val: 60,
        left: null,
        right: null,
      },
      right: {
        val: 80,
        left: null,
        right: null,
      },
    },
  };

const preorder = (root) => {
    if (!root) { return; }
    const stack = [root];
    while (stack.length) {
        const n = stack.pop();
        console.log(n.val);
        if (n.right) stack.push(n.right);
        if (n.left) stack.push(n.left);
    }
};

// 50 10 5 15 70 60 80 

preorder(bt);

中序遍历: 左根右

  1. 对根节点的左子树进行中序遍历
  2. 访问根节点
  3. 对根节点的右子树进行中序遍历

image.png

递归版代码实现

const bt = {
    val: 56,
    left: {
      val: 22,
      left: {
        val: 10,
        left: null,
        right: null,
      },
      right: {
        val: 30,
        left: null,
        right: null,
      },
    },
    right: {
      val: 81,
      left: {
        val: 77,
        left: null,
        right: null,
      },
      right: {
        val: 92,
        left: null,
        right: null,
      },
    },
  };
中序遍历
const inorder = (root) => {
    if (!root) { return; }
    inorder(root.left);
    console.log(root.val);
    inorder(root.right);
};

// 10  22 30 56  77 81 92

inorder(bt);

非递归版代码实现

const bt = {
    val: 56,
    left: {
      val: 22,
      left: {
        val: 10,
        left: null,
        right: null,
      },
      right: {
        val: 30,
        left: null,
        right: null,
      },
    },
    right: {
      val: 81,
      left: {
        val: 77,
        left: null,
        right: null,
      },
      right: {
        val: 92,
        left: null,
        right: null,
      },
    },
  };

const inorder = (root) => {
    if (!root) { return; }
    const stack = [];
    let p = root;
    while (stack.length || p) {
        while (p) {
            stack.push(p);
            p = p.left;
        }
        const n = stack.pop();
        console.log(n.val);
        p = n.right;
    }
};

// 10  22 30 56  77 81 92

inorder(bt);

后序遍历: 左右根

  1. 对根节点的左子树进行后序遍历
  2. 对根节点的右子树进行后序遍历
  3. 访问根节点

image.png

递归版代码实现

const bt = {
    val: 23,
    left: {
      val: 16,
      left: {
        val: 3,
        left: null,
        right: null,
      },
      right: {
        val: 22,
        left: null,
        right: null,
      },
    },
    right: {
      val: 45,
      left: {
        val: 37,
        left: null,
        right: null,
      },
      right: {
        val: 99,
        left: null,
        right: null,
      },
    },
  };

//后序遍历
const postorder = (root) => {
    if (!root) { return; }
    postorder(root.left);
    postorder(root.right);
    console.log(root.val);
};

// 3 22 16 37 99 45 23

postorder(bt);

非递归版代码实现

const bt = {
    val: 23,
    left: {
      val: 16,
      left: {
        val: 3,
        left: null,
        right: null,
      },
      right: {
        val: 22,
        left: null,
        right: null,
      },
    },
    right: {
      val: 45,
      left: {
        val: 37,
        left: null,
        right: null,
      },
      right: {
        val: 99,
        left: null,
        right: null,
      },
    },
  };

const postorder = (root) => {
    if (!root) { return; }
    const outputStack = [];
    const stack = [root];
    while (stack.length) {
        const n = stack.pop();
        outputStack.push(n);
        if (n.left) stack.push(n.left);
        if (n.right) stack.push(n.right);
    }
    while(outputStack.length){
        const n = outputStack.pop();
        console.log(n.val);
    }
};

postorder(bt);