二叉树
概念
树
树是一种非线性的数据结构,以分层的方式存储数据。常见的树结构有家挺关系图谱,公司组织结构。
二叉树
二叉树是一种特殊的树,它的子节点不超过两个。
二叉查找树
二叉查找树是一种特殊的二叉树,它把相对较小的数据保存在左节点,把较大的数据保存在右节点(我们假设数据不重复)。
下面我们主要研究二叉查找树的相关代码
二叉树创建及实例方法
// 节点对象
class Node {
constructor(data) {
this.right = null;
this.left = null;
this.data = data;
}
}
// 二叉树类
class BST {
constructor() {
this.root = null;
}
insert(data) {
const node = new Node(data);
if (this.root === null) {
this.root = node;
} else {
let curNode = this.root;
while(1) {
if (data < curNode.data) {
if (curNode.left) {
curNode = curNode.left;
} else {
curNode.left = node;
break;
}
} else {
if (curNode.right) {
curNode = curNode.right;
} else {
curNode.right = node;
break;
}
}
}
}
}
// 利用查找二叉树的特点,递归左子树的左节点即是最小节点
getMin() {
let curNode = this.root;
while(curNode.left !== null) {
curNode = curNode.left;
}
return curNode.data;
}
// 利用查找二叉树的特点,递归右子树的右节点即是最大节点
getMax() {
let curNode = this.root;
while(curNode.right !== null) {
curNode = curNode.right;
}
return curNode.data;
}
find(data) {
let curNode = this.root;
while(curNode) {
if (curNode.data === data) {
return curNode;
}
if (curNode.data > data) {
curNode = curNode.left;
} else {
curNode = curNode.right;
}
}
return null;
}
}
我们为 BST 加上了4个实例方法,insert 用于给二叉树添加节点,getMin 用于查找最小节点数据,getMax 用于查找最大节点数据,find 用于查找对应数据的节点。
二叉树节点的删除
对于二叉树节点的删除,根据待删除节点的节点关系,我们分为几个不同的情况
-
待删除节点为叶子节点,直接删除节点即可
-
待删除节点只含有左(右)子树,将链接指向左(右)子树即可
-
待删除节点拥有左右子树,将节点数据替换为子子树中的最大值(或者右子树的最小值)(这样不会破坏二叉树结构),同时删除右子树对应的叶子节点。
// ...
remove(data) {
this.root = this.removeNode(this.root, data);
}
removeNode(node, data) {
if (node === null) {
return null;
}
if (data === node.data) {
// 对应1 叶子节点
if (node.left === null && node.right === null) {
return null;
}
// 对应2 只有左子树
if (node.right === null) {
return node.left;
}
// 对应2 只有右子树
if (node.left === null) {
return node.right;
}
// 对应3 同时拥有左右子树
// 查找最大左子树
let leftMaxNode = node.left;
while(leftMaxNode.right) {
leftMaxNode = leftMaxNode.right;
}
// 替换数据
const temp = node.data;
node.data = leftMaxNode.data;
leftMaxNode.data = temp;
// 删除对应最大值节点
node.right = this.removeNode(node.right, temp);
return node;
} else {
// 递归查找数据
if (data > node.data) {
node.right = this.removeNode(node.right, data);
}
if (data < node.data) {
node.left = this.removeNode(node.left, data);
}
return node;
}
}
搜索二叉树(遍历)
二叉树的遍历分为深度优先遍历和广度优先遍历
-
深度优先遍历:优先递归访问到叶子节点
-
广度优先遍历:层次遍历,优先访问同一层节点,一层一层访问
深度优先遍历
深度优先遍历也可细分为
-
前序遍历:访问顺序为
根节点 -> 左子树 -> 右子树 -
中序遍历:访问顺序为
左子树 -> 根节点 -> 右子树 -
后序遍历:访问顺序为
左子树 -> 右子树 -> 根节点
相关代码如下
// ···
// 前序
preOrder(node) {
if (node) {
console.log(node.data);
this.preOrder(node.left);
this.preOrder(node.right);
}
}
// 中序
inOrder(node) {
if (node) {
this.inOrder(node.left);
console.log(node.data);
this.inOrder(node.right);
}
}
// 后序
postOrder(node) {
if (node) {
this.postOrder(node.left);
this.postOrder(node.right);
console.log(node.data);
}
}
广度优先遍历
widthOrder(nodes) {
const nextNodes = [];
nodes.forEach(node => {
console.log(node.data);
if (node.left) nextNodes.push(node.left);
if (node.right) nextNodes.push(node.right);
});
if (nextNodes.length) {
this.widthOrder(nextNodes);
}
}
二叉树序列确定唯一树
我们可以根据其中两种遍历顺序确定二叉树
-
前序 + 中序->树 -
后序 + 中序->树 -
前序 + 后序-> 无法确定唯一树
// ···
// 前序 + 中序 -> 树
buildTreeBasePre(preOrder, inOrder) {
if (preOrder.length < 1) {
return null;
}
const idx = inOrder.indexOf(preOrder[0]);
const leftInOrder = inOrder.slice(0, idx);
const rightInOrder = inOrder.slice(idx + 1);
const leftPreOrder = preOrder.slice(1, leftInOrder.length + 1);
const rightPreOrder = preOrder.slice(leftInOrder.length + 1);
const node = new Node(preOrder[0]);
if (leftInOrder.length) {
node.left = this.buildTreeBasePre(leftPreOrder, leftInOrder);
}
if (rightInOrder.length) {
node.right = this.buildTreeBasePre(rightPreOrder, rightInOrder);
}
return node;
}
// 后序 + 中序 -> 树
buildTreeBasePost(inOrder, postOrder) {
const len = inOrder.length;
if (len.length < 1) {
return null;
}
const idx = inOrder.indexOf(postOrder[len - 1]);
const leftInOrder = inOrder.slice(0, idx);
const rightInOrder = inOrder.slice(idx + 1);
const leftPostOrder = postOrder.slice(0, leftInOrder.length);
const rightPostOrder = postOrder.slice(leftInOrder.length, -1);
const node = new Node(postOrder[len - 1]);
if (leftInOrder.length) {
node.left = this.buildTreeBasePost(leftInOrder, leftPostOrder);
}
if (rightInOrder.length) {
node.right = this.buildTreeBasePost(rightInOrder, rightPostOrder);
}
return node;
}
计数二叉树
有时候当数据有重复时,我们可以为二叉树加上计数功能来统计数据的重复次数
class Node {
constructor(data) {
this.right = null;
this.left = null;
this.data = data;
// 添加count
this.count = 1;
}
}
// ···
// 更新数据统计
update(data) {
const result = this.find(data);
result.count++;
}
完整代码
class Node {
constructor(data) {
this.right = null;
this.left = null;
this.data = data;
// 计数统计
this.count = 1;
}
}
class BST {
constructor() {
this.root = null;
}
// 插入节点
insert(data) {
const node = new Node(data);
if (this.root === null) {
this.root = node;
} else {
let curNode = this.root;
while(1) {
if (data <= curNode.data) {
if (curNode.left) {
curNode = curNode.left;
} else {
curNode.left = node;
break;
}
} else {
if (curNode.right) {
curNode = curNode.right;
} else {
curNode.right = node;
break;
}
}
}
}
}
// 前序遍历
preOrder(node) {
if (node) {
console.log(node.data);
this.preOrder(node.left);
this.preOrder(node.right);
}
}
// 中序遍历
inOrder(node) {
if (node) {
this.inOrder(node.left);
console.log(node.data);
this.inOrder(node.right);
}
}
// 后序遍历
postOrder(node) {
if (node) {
this.postOrder(node.left);
this.postOrder(node.right);
console.log(node.data);
}
}
// 广度优先遍历
widthOrder(nodes) {
const nextNodes = [];
nodes.forEach(node => {
console.log(node.data);
if (node.left) nextNodes.push(node.left);
if (node.right) nextNodes.push(node.right);
});
if (nextNodes.length) {
this.widthOrder(nextNodes);
}
}
// 前序 + 中序 -> 树
buildTreeBasePre(preOrder, inOrder) {
if (preOrder.length < 1) {
return null;
}
const idx = inOrder.indexOf(preOrder[0]);
const leftInOrder = inOrder.slice(0, idx);
const rightInOrder = inOrder.slice(idx + 1);
const leftPreOrder = preOrder.slice(1, leftInOrder.length + 1);
const rightPreOrder = preOrder.slice(leftInOrder.length + 1);
const node = new Node(preOrder[0]);
if (leftInOrder.length) {
node.left = this.buildTreeBasePre(leftPreOrder, leftInOrder);
}
if (rightInOrder.length) {
node.right = this.buildTreeBasePre(rightPreOrder, rightInOrder);
}
return node;
}
// 后序 + 中序 -> 树
buildTreeBasePost(inOrder, postOrder) {
const len = inOrder.length;
if (len.length < 1) {
return null;
}
const idx = inOrder.indexOf(postOrder[len - 1]);
const leftInOrder = inOrder.slice(0, idx);
const rightInOrder = inOrder.slice(idx + 1);
const leftPostOrder = postOrder.slice(0, leftInOrder.length);
const rightPostOrder = postOrder.slice(leftInOrder.length, -1);
const node = new Node(postOrder[len - 1]);
if (leftInOrder.length) {
node.left = this.buildTreeBasePost(leftInOrder, leftPostOrder);
}
if (rightInOrder.length) {
node.right = this.buildTreeBasePost(rightInOrder, rightPostOrder);
}
return node;
}
// 查找最小值
getMin() {
let curNode = this.root;
while(curNode.left !== null) {
curNode = curNode.left;
}
return curNode.data;
}
// 查找最大值
getMax() {
let curNode = this.root;
while(curNode.right !== null) {
curNode = curNode.right;
}
return curNode.data;
}
// 查找值
find(data) {
let curNode = this.root;
while(curNode) {
if (curNode.data === data) {
return curNode;
}
if (curNode.data > data) {
curNode = curNode.left;
} else {
curNode = curNode.right;
}
}
return null;
}
// 更新计数统计
update(data) {
const result = this.find(data);
result.count++;
}
// 删除节点
remove(data) {
this.root = this.removeNode(this.root, data);
}
// 删除节点函数
removeNode(node, data) {
if (node === null) {
return null;
}
if (data === node.data) {
// 对应1 叶子节点
if (node.left === null && node.right === null) {
return null;
}
// 对应2 只有左子树
if (node.right === null) {
return node.left;
}
// 对应2 只有右子树
if (node.left === null) {
return node.right;
}
// 对应3 同时拥有左右子树
// 查找最大左子树
let leftMaxNode = node.left;
while(leftMaxNode.right) {
leftMaxNode = leftMaxNode.right;
}
// 替换数据
const temp = node.data;
node.data = leftMaxNode.data;
leftMaxNode.data = temp;
// 删除对应最大值节点
node.right = this.removeNode(node.right, temp);
return node;
} else {
// 递归查找数据
if (data > node.data) {
node.right = this.removeNode(node.right, data);
}
if (data < node.data) {
node.left = this.removeNode(node.left, data);
}
return node;
}
}
}
欢迎到前端学习打卡群一起学习~516913974