每个节点最多两个孩子,即左右子树,子树也可以有孩子
二叉树类型定义
class TreeNode{
value: number
left?: TreeNode
right?: TreeNode
constructor(value: number, left?: TreeNode, right?: TreeNode){
this.value = value;
this.left = left;
this.right = right
}
}
const node4 = new TreeNode(4);
const node2 = new TreeNode(2, node4);
const node5 = new TreeNode(5);
const node6 = new TreeNode(6);
const node3 = new TreeNode(3, node5, node6);
const node1 = new TreeNode(1, node2, node3);
多叉树定义
class MultiTreeNode{
value: number
children?: Array<MultiTreeNode>
constructor(value: number, children?: MultiTreeNode){
this.value = value;
this.children = children;
}
}
广度优先遍历(层序遍历)
从二叉树的第一层开始逐层遍历,同一层中,从左至右遍历节点
借助队列实现
function bfs(root: TreeNode){
const queue = new Array<TreeNode>();
let level = 1;
queue.push(root);
while(queue.length){
const size = queue.length; //当前层级的节点数;
level ++;
for(let i = 0; i < size; i++){
const cur = queue.shift()!; // 非空断言,否则下面cur.value可能会认为undefined
console.log(cur.value);
// 加入下一层级的节点
if(cur.left){
queue.push(cur.left)
}
if(cur.right){
queue.push(cur.right)
}
}
}
}
- 时间复杂度 O(n) -- 每个节点只访问一次
- for循环可以去掉,输出结果不变,只不过不能确定哪几个节点属于一个层级
深度优先遍历(递归遍历)
D:访问根节点, L:遍历根节点左子树, R:遍历根节点右子树
- 先序遍历:DLR 1 2 3 4 5 6 7
function preOrder(root?: TreeNode){
if(!root) return;
console.log(root.value);
preOrder(root.left);
preOrder(root.right);
}
- 中序遍历:LDR 3 2 4 7 1 6 5
preOrder(root.left);
console.log(root.value);
preOrder(root.right);
}
- 后序遍历:LRD 3 4 2 7 6 5 1
preOrder(root.left);
preOrder(root.right);
console.log(root.value);
}
算法框架
function traverse(root?: TreeNode){
if(!root) return;
// 前序位置
traverse(root.left);
// 中序位置
traverse(root.right);
// 后序位置
}
- 递归 - 定义函数,假设它实现某种功能
104. 二叉树最大深度
对比左子树 右子树的高度,取最大值+1
function maxDepth(root: TreeNode){
if(!root) return 0;
const lh = maxDepth(root.left);
const rh = maxDepth(root.right);
// 后序位置
return Math.max(lh, rh) + 1;
}
144. 二叉树前序遍历
let res = [];
function help(root){
if(!root) return;
// 前序位置
res.push(root.val);
help(root.left);
help(root.right);
}
/**
* @param {TreeNode} root
* @return {number[]}
*/
var preorderTraversal = function(root) {
res = [];
help(root);
return res;
};
543. 二叉树直径
任意两节点最长路径长度 - 左右子树之和最大值
let len = 0;
function help(root){ //help函数 - 返回树的高度
if(!root) return 0; //节点空,高度为0
const lh = help(root.left);
const rh = help(root.right);
// 后序位置
const h = Math.max(lh, rh) + 1;
len = Math.max(len, lh+rh); //取左右子树和最高值
return h;
}
var diameterOfBinaryTree = function(root) {
len = 0;
help(root);
return len;
};
help() 预先定义该函数的功能,就是返回节点的高度; 假设 help 成立
递归:先 假设,假设成立情况下,再推导
后序位置的代码,是自底向上的,可以获取左右子树的结果,可以获取更多信息
105. 根据前序和中序遍历,构造二叉树
前序确定根节点,中序确定左右子树
需要子数组标识, 前序遍历,中序遍历数组
function build(preOrder, preStart:开始下标, preEnd:结束下标,
inOrder, inStart, inEnd){
// 递归终止条件
if(preStart > preEnd) return null
// 前序 计算位置
const rootVal = preOrder[preStart]; //根节点
const index = inOrder.indexOf(rootVal); //根节点在中序遍历中的位置
const leftSize = index - inStart; //左子树长度
const leftNode = build(preOrder, preStart + 1, preStart + leftSize,
inOrder, inStart, index - 1);
const rightNode = build(preOrder, preStart + leftSize +1, preEnd,
inOrder, index+1, inEnd);
// 后序 生成树
const root = new TreeNode(rootVal, leftNode, rightNode)
}
function buildTree(preOrder, inOrder){
return build(preOrder,0,preOrder.length-1,
inOrder,0,inOrder.length-1)
}