对于程序猿(攻城狮)来说,初期可能很少有机会涉及数据结构和算法的相关工作,后期对相关业务也已经了解得相对熟悉了,各种各样的技术或多或少都已经用过或者了解过或者听说过了,那么这个时候我们是否应该去突破下自己的瓶颈呢?对于这一阶段的程序猿来说,应该要去了解下数据结构、设计模式、算法的相关知识。
在这里,我总结下我自己接触过的二叉树相关知识。
二叉树介绍
二叉树(Binary tree)是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重要。二叉树特点是每个结点最多只能有两棵子树,且有左右之分。
我们先来看一棵二叉树
二叉树树的遍历
在二叉树中,树的遍历是一个非常有趣的事情,因为它有很多种遍历的方式:前序遍历、中序遍历、后序遍历、层次遍历。那接下来,我会一一介绍给大家。
// 这里用 js 对象的形式写一个简单的二叉树
var treenode = {
value: 1,
left: {
value: 2,
left: {
value: 4,
},
right: {
value: 5,
left: {
value: 7,
},
right: {
value: 8,
},
},
},
right: {
value: 3,
right: {
value: 6,
},
},
}
- 前序遍历
前序遍历(DLR),是二叉树遍历的一种,也叫做先根遍历、先序遍历、前序周游,可记做根左右。前序遍历首先访问根结点然后遍历左子树,最后遍历右子树。
// 递归实现
function preOrderTraversal(root) {
var result = []
function preOrderTraversalNode(node) {
if (node) {
result.push(node.value)
if (node.left) {
preOrderTraversalNode(node.left)
}
if (node.right) {
preOrderTraversalNode(node.right)
}
}
}
preOrderTraversalNode(root)
return result
}
// 迭代实现
function preOrderTraversal(root) {
var list = [],
stack = []
if (root) {
stack.push(root)
}
while (stack.length > 0) {
var curNode = stack.pop()
list.push(curNode.value)
if (curNode.right) {
stack.push(curNode.right)
}
if (curNode.left) {
stack.push(curNode.left)
}
}
return list
}
preOrderTraversal(treenode) // [1, 2, 4, 5, 7, 8, 3, 6]
- 中序遍历
中序遍历(LDR)是二叉树遍历的一种,也叫做中根遍历、中序周游。在二叉树中,中序遍历首先遍历左子树,然后访问根结点,最后遍历右子树。
// 递归实现
function inorderTraversal(root) {
var result = []
function inOrderTraversalNode(node) {
if (node) {
if (node.left) {
inOrderTraversalNode(node.left)
}
result.push(node.value)
if (node.right) {
inOrderTraversalNode(node.right)
}
}
}
inOrderTraversalNode(root)
return result
}
// 迭代实现
function inorderTraversal(root) {
var list = [],
stack = []
while (root || stack.length > 0) {
while (root) {
stack.push(root);
root = root.left;
}
root = stack.pop();
list.push(root.value);
root = root.right;
}
return list;
}
inorderTraversal(treenode) // [4, 2, 7, 5, 8, 1, 3, 6]
- 后序遍历
后序遍历(LRD)是二叉树遍历的一种,也叫做后根遍历、后序周游,可记做左右根。后序遍历有递归算法和非递归算法两种。在二叉树中,先左后右再根,即首先遍历左子树,然后遍历右子树,最后访问根结点。
// 递归实现
function postorderTraversal(root) {
var result = []
function postorderTraversalNode(node) {
if (node) {
if (node.left) {
postorderTraversalNode(node.left)
}
if (node.right) {
postorderTraversalNode(node.right)
}
result.push(node.value)
}
}
postorderTraversalNode(root)
return result
}
// 迭代实现
function postorderTraversal(root) {
var list = [],
stack = []
if (root) {
stack.push(root)
}
while (stack.length > 0) {
var curNode = stack.pop()
list.unshift(curNode.value)
if (curNode.left) {
stack.push(curNode.left)
}
if (curNode.right) {
stack.push(curNode.right)
}
}
return list
}
postorderTraversal(treenode) // [4, 7, 8, 5, 2, 6, 3, 1]
- 层次遍历
在二叉树中,从上往下遍历。
function levelorderTraversal(root) {
const list = [];
let stack = [];
if (root) {
stack.push(root);
}
while (stack.length > 0) {
const newStack = [];
for (let i = 0; i < stack.length; i++) {
const curNode = stack[i];
list.push(curNode.value);
if (curNode.left) {
newStack.push(curNode.left);
}
if (curNode.right) {
newStack.push(curNode.right);
}
}
stack = newStack;
}
return list;
}
levelorderTraversal(treenode) // [1, 2, 3, 4, 5, 6, 7, 8]
- 对称二叉树判断
判断两颗二叉树是否对称
const isSymmetrical = (treenodeA, treenodeB) => {
if (!treenodeA && !treenodeB) {
return true
}
if (!treenodeA || !treenodeB) {
return false
}
if (treenodeA.value !== treenodeB.value) {
return false
}
return isSymmetrical(treenodeA.left, treenodeB.right) && isSymmetrical(treenodeA.right, treenodeB.left)
}
const treenodeB = {
value: 1,
left: {
value: 3,
left: {
value: 6,
},
},
right: {
value: 2,
left: {
value: 5,
left: {
value: 8,
},
right: {
value: 7,
},
},
right: {
value: 4,
},
},
}
isSymmetrical(treenode, treenodeB) // true
今天就介绍到这里了。之后,我也会介绍实现二叉树、二叉树搜索、移除节点等等方法...