二叉树

树的简介

「树」的特点是:除了根结点以外,其它结点都有唯一的父亲结点。「树」还可以递归定义:

  • 树是有限结点的集合;
  • 这个集合或者是空集,或者其中有一个称为根结点的特殊结点,其余结点分别属于 不同不相交 的树,这些树分别是原始树(或称作原始树的根结点)的子树。

很多树的问题都可以使用「递归」方法解决。树形结构还有一个重要的特征区别于其它复杂的数据结构的特点:在树形结构里,能看到非常明显的 层次结构,用于表示各种常见的 层次 关系。

二叉树

二叉树是最简单的树形结构:如果一棵树中每个结点 至多 有两个孩子结点,这样的树就称为二叉树。 孩子结点与父亲结点之间的连接像极了树的分叉,因此叫做二叉树。 二叉树的结点如果有孩子结点的话,使用 左结点 和 右结点 来区分。类似可以定义多叉树:如果一棵树任意结点最多含有的结点数为 N,这棵树就是 N 叉树。

  • 一个结点、空结点、单链表也是二叉树,因为它们都符合二叉树的定义;
  • 一般而言,二叉树的两个孩子结点会规定次序,如果两个都有的话,分为左孩子和右孩子。如果只有一个孩子结点,可以只有左孩子结点,也可以只有右孩子结点。

完全二叉树与满二叉树

完全二叉树 :从形态上看完全二叉树是个 只缺了最后一行右边 的的三角形。即:完全二叉树所有的结点按照从上到下、从左到右紧凑摆放,中间不会有缺失结点。 就是完全二叉树的样子。

满二叉树:满二叉树首先是完全二叉树。其次,从形态上看,满二叉树是一个没有缺角的三角形,即每一层结点的个数都达到了这一层能达到的最大结点数。

二叉树的遍历

通过 一定的顺序 访问二叉树的 所有 结点。树的遍历有 深度优先遍历 (Depth First Search)广度优先遍历(Breadth First Search) ,其中深度优先遍历又分为前序遍历中序遍历后序遍历

练习

二叉树的层序遍历 - 中等

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[][]}
 */
var levelOrder = function (root) {
	const res = [];
	if (root === null) return res;
	const queue = [root];
	while (queue.length) {
		const curLevel = [];
		const curSize = queue.length;
		for (let i = 0; i < curSize; i++) {
			const cur = queue.shift();
			curLevel.push(cur.val);
			if (cur.left != null) {
				queue.push(cur.left);
			}
			if (cur.right != null) {
				queue.push(cur.right);
			}
		}
		res.push(curLevel);
	}
	return res;
};

二叉树的层次遍历 II - 中等

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[][]}
 */
var levelOrderBottom = function (root) {
	const res = [];
	if (root === null) return res;
	const queue = [root];
	while (queue.length) {
		const curLevel = [];
		const n = queue.length;
		for (let i = 0; i < n; i++) {
			const cur = queue.shift();
			curLevel.push(cur.val);
			if (cur.left) {
				queue.push(cur.left);
			}
			if (cur.right) {
				queue.push(cur.right);
			}
		}
		res.push(curLevel);
	}
	return res.reverse();
};

二叉树的锯齿形层次遍历 - 中等

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[][]}
 */
var zigzagLevelOrder = function (root) {
	const res = [];
	if (root === null) return res;
	const queue = [root];
	while (queue.length) {
		const curLevel = [];
		const n = queue.length;
		for (let i = 0; i < n; i++) {
			const cur = queue.shift();
			curLevel.push(cur.val);

			if (cur.left) queue.push(cur.left);
			if (cur.right) queue.push(cur.right);
		}
		res.push(res.length % 2 ? curLevel.reverse() : curLevel);
	}
	return res;
};

二叉树的前序遍历 - 简单

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var preorderTraversal = function (root) {
	const res = [];
	if (root === null) return res;

	const stack = [root];

	while (stack.length) {
		const cur = stack.pop();
		res.push(cur.val);

		if (cur.right) stack.push(cur.right);
		if (cur.left) stack.push(cur.left);
	}

	return res;
};

二叉树的中序遍历 - 简单

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var inorderTraversal = function (root) {
	const res = [];
	const stack = [];
	let cur = root;

	while (cur !== null || stack.length > 0) {
		// 将所有的左子节点压入栈中
		while (cur !== null) {
			stack.push(cur);
			cur = cur.left;
		}
		// 当前节点为空,表明已经到达左子树的最左边
		cur = stack.pop();
		res.push(cur.val); // 访问当前节点
		// 转向右子节点
		cur = cur.right;
	}
	return res;
};

二叉树的后序遍历 - 简单

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var postorderTraversal = function (root) {
	const res = [];
	if (root === null) return res;

	const find = (tree) => {
		if (tree.left) find(tree.left);
		if (tree.right) find(tree.right);
		res.push(tree.val);
	};

	find(root);

	return res;
};

二叉树的最小深度 - 简单

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
var minDepth = function (root) {
	let min = Number.MAX_SAFE_INTEGER;

	if (root === null) return 0;

	const findDeep = (tree, deep) => {
		// 到达叶子节点
		if (!tree.left && !tree.right) {
			min = Math.min(min, deep);
		}

		if (tree.left) {
			findDeep(tree.left, deep + 1);
		}
		if (tree.right) {
			findDeep(tree.right, deep + 1);
		}
	};

	findDeep(root, 1);

	return min;
};

路径总和 - 简单

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} targetSum
 * @return {boolean}
 */
var hasPathSum = function (root, targetSum) {
	if (root === null) return false;
	let result = false;
	const findTotal = (tree, total) => {
		if (result) return;
		total += tree.val;

		// 叶子节点
		if (!tree.left && !tree.right) {
			result = result || total === targetSum;
		}

		if (tree.left) {
			findTotal(tree.left, total);
		}

		if (tree.right) {
			findTotal(tree.right, total);
		}
	};

	findTotal(root, 0);

	return result;
};

求根到叶子节点数字之和 - 中等

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
var sumNumbers = function (root) {
	const res = [];

	if (root === null) return 0;

	const find = (tree, arr) => {
		arr.push(tree.val);
		if (!tree.left && !tree.right) {
			res.push(arr);
		}

		if (tree.left) find(tree.left, [...arr]);
		if (tree.right) find(tree.right, [...arr]);
	};

	find(root, []);

	return res.reduce((total, el) => {
		return total + Number(el.join(""));
	}, 0);
};

相同的树 - 简单

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} p
 * @param {TreeNode} q
 * @return {boolean}
 */
var isSameTree = function (p, q) {
	let same = true;
	const isSame = (p, q) => {
		if (!same) return;
		if (!p && !q) return;
		if ((p && !q) || (!p && q)) {
			same = false;
			return;
		}
		if (p.val !== q.val) {
			same = false;
			return;
		}
		isSame(p.left, q.left);
		isSame(p.right, q.right);
	};
	isSame(p, q);

	return same;
};

完全二叉树的节点个数 - 简单

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
var countNodes = function (root) {
	let count = 0;

	if (root === null) return 0;

	const find = (tree) => {
		count += 1;
		if (tree.left) find(tree.left);
		if (tree.right) find(tree.right);
	};

	find(root);

	return count;
};

对称二叉树 - 简单

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {boolean}
 */
var isSymmetric = function (root) {
	let same = true;

	const isSame = (left, right) => {
		if ((!left && !right) || !same) return;
		if ((!left && right) || (left && !right)) {
			same = false;
			return;
		}
		if (left.val !== right.val) {
			same = false;
			return;
		}
		isSame(left.left, right.right);
		isSame(left.right, right.left);
	};

	isSame(root.left, root.right);

	return same;
};

二叉树中的最大路径和 - 困难

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
var maxPathSum = function (root) {
	let max = Number.MIN_SAFE_INTEGER;

	if (root === null) return 0;

	const find = (tree) => {
		if (tree === null) return 0;

		const left = find(tree.left);
		const right = find(tree.right);

		max = Math.max(max, tree.val + left + right);

		return Math.max(tree.val + Math.max(left, right), 0);
	};

	find(root);

	return max;
};

翻转二叉树 - 简单

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {TreeNode}
 */
var invertTree = function (root) {
	const reverse = (tree) => {
		if (tree === null) return tree;

		const temp = tree.left;
		tree.left = tree.right;
		tree.right = temp;

		reverse(tree.left);
		reverse(tree.right);

		return tree;
	};

	return reverse(root);
};

平衡二叉树 - 简单

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {boolean}
 */
var isBalanced = function (root) {
	let res = true;

	if (root === null) return res;

	const find = (tree) => {
		if (!res) return 0;
		if (tree === null) return 0;

		const leftDeep = find(tree.left);
		const rightDeep = find(tree.right);

		if (Math.abs(leftDeep - rightDeep) > 1) {
			res = false;
		}

		return Math.max(leftDeep, rightDeep) + 1;
	};

	find(root);

	return res;
};

经典问题:从前、中序遍历序列构造二叉树

从前序与中序遍历序列构造二叉树 - 中等

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {number[]} preorder
 * @param {number[]} inorder
 * @return {TreeNode}
 */
function buildTree(preorder, inorder) {
	if (!preorder.length || !inorder.length) {
		return null;
	}

	// 前序遍历的第一个元素是根节点
	const rootVal = preorder[0];
	const root = new TreeNode(rootVal);

	// 在中序遍历中找到根节点的位置
	const rootIndex = inorder.indexOf(rootVal);

	// 递归构造左子树和右子树
	root.left = buildTree(
		preorder.slice(1, rootIndex + 1),
		inorder.slice(0, rootIndex)
	);
	root.right = buildTree(
		preorder.slice(rootIndex + 1),
		inorder.slice(rootIndex + 1)
	);

	return root;
}

从中序与后序遍历序列构造二叉树 - 中等

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {number[]} inorder
 * @param {number[]} postorder
 * @return {TreeNode}
 */
var buildTree = function (inorder, postorder) {
	if (!inorder.length || !postorder.length) return null;

	const head = postorder.at(-1);

	const root = new TreeNode(head);

	const index = inorder.indexOf(head);

	root.left = buildTree(
		inorder.slice(0, index),
		postorder.slice(0, inorder.slice(0, index).length)
	);

	root.right = buildTree(
		inorder.slice(index + 1),
		postorder.slice(inorder.slice(0, index).length, -1)
	);

	return root;
};

典型问题:二叉树的最近公共祖先

二叉树的最近公共祖先 - 中等

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @param {TreeNode} p
 * @param {TreeNode} q
 * @return {TreeNode}
 */
var lowestCommonAncestor = function (root, p, q) {
	if (root === null || root === p || root === q) return root;
	const left = lowestCommonAncestor(root.left, p, q);
	const right = lowestCommonAncestor(root.right, p, q);

	if (left != null && right != null) {
		return root;
	}

	if (left === null) {
		return right;
	}

	return left;
};

二叉树的序列化与反序列化 - 困难

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */

/**
 * Encodes a tree to a single string.
 *
 * @param {TreeNode} root
 * @return {string}
 */
var serialize = function (root) {
	if (root === null) return "";

	const nextOrder = [];
	const midOrder = [];

	let id = 0;
	const map = new Map();

	const find = (tree, type) => {
		if (tree === null) return null;

		if (!map.has(tree)) {
			map.set(tree, id++);
		}

		if (type === 1) {
			find(tree.left, 1);
			find(tree.right, 1);
			nextOrder.push(`${map.get(tree)}:${tree.val}`);
		} else {
			find(tree.left, 2);
			midOrder.push(`${map.get(tree)}:${tree.val}`);
			find(tree.right, 2);
		}
	};

	find(root, 1);
	find(root, 2);

	return nextOrder.join(".") + "|" + midOrder.join(".");
};

/**
 * Decodes your encoded data to tree.
 *
 * @param {string} data
 * @return {TreeNode}
 */
var deserialize = function (data) {
	if (data === "") return null;

	const arr = data.split("|");

	const nextOrder = arr[0].split(".").map((el) => {
		const [id, val] = el.split(":").map(Number);
		return { id, val };
	});

	const midOrder = arr[1].split(".").map((el) => {
		const [id, val] = el.split(":").map(Number);
		return { id, val };
	});

	const build = (mid, next) => {
		if (!mid.length || !next.length) return null;
		const { id, val } = next.pop();
		const tree = new TreeNode(val);
		const index = mid.findIndex((node) => node.id === id);

		tree.left = build(mid.slice(0, index), next.slice(0, index));
		tree.right = build(mid.slice(index + 1), next.slice(index));

		return tree;
	};

	const root = build(midOrder, nextOrder);

	return root;
};

二叉树的直径 - 简单

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
var diameterOfBinaryTree = function (root) {
	if (root === null) return 0;

	let max = 0;

	const find = (tree) => {
		if (tree === null) return 0;

		const left = find(tree.left);
		const right = find(tree.right);

		// 更新最大直径
		max = Math.max(left + right, max);

		// 返回树的高度
		return Math.max(left, right) + 1;
	};

	find(root);

	return max;
};

收集树上所有苹果的最少时间 - 中等

js 复制代码
/**
 * @param {number} n
 * @param {number[][]} edges
 * @param {boolean[]} hasApple
 * @return {number}
 */
var minTime = function (n, edges, hasApple) {
	// 构建图的邻接表表示
	const graph = new Array(n).fill(0).map(() => []);
	for (const [from, to] of edges) {
		graph[from].push(to);
		graph[to].push(from);
	}

	// DFS函数,返回从当前节点出发收集所有苹果所需的时间
	const dfs = (node, parent) => {
		let time = 0;

		for (const neighbor of graph[node]) {
			if (neighbor === parent) continue; // 避免回到父节点

			const childTime = dfs(neighbor, node);
			if (childTime > 0 || hasApple[neighbor]) {
				time += childTime + 2; // 往返邻居节点的时间
			}
		}

		return time;
	};

	return dfs(0, -1);
};

二叉搜索树

二叉搜索树还可以通过 递归 的方式定义:

  • 二叉搜索树可以是一棵空树;
  • 二叉搜索树由根结点,左子树和右子树组成,其中左子树和右子树都是二叉搜索树,并且左子树上所有结点的键值 小于 根结点的键值,并且右子树上所有结点的键值 大于 根结点的键值。

根据二叉搜索树的定义得到的性质 由二叉搜索树的定义和中序遍历的定义得到:二叉搜索树中序遍历得到的序列是有序的。

通过具体例子理解二叉搜索树是如何组织数据

在有序数组里查找元素可以使用二分查找算法,看到了一个元素的值,和目标元素比较:

  • 如果找到了这个元素,进行相关操作;
  • 如果目标元素的值 小于 当前看到的元素值,继续向左边查找;
  • 如果目标元素的值 大于 当前看到的元素值,继续向右边查找。

二叉搜索树的定义保证了键的有序,向二叉搜索树中插入元素,首先需要查找到插入元素的位置。查找的时候使用类似二分查找的思路:从根结点开始,通过比较 决定向左走还是向右走,直到来到叶子结点,即:每次选择子树中的一半,跳过另一半,这是减治思想的应用。

二叉搜索树查找元素与插入元素是类似的逻辑:在插入元素的时候,意识地维护了有序性,进而在查找元素的时候,可以根据 有序性 查找元素,时间复杂度为 O(logN),这里N 是树结点的个数,logN 是树的高度的近似值。

删除二叉搜索树中的结点 - 中等

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} key
 * @return {TreeNode}
 */
var deleteNode = function (root, key) {
	if (root === null) return null;
	if (root.val > key) {
		root.left = deleteNode(root.left, key);
		return root;
	} else if (root.val < key) {
		root.right = deleteNode(root.right, key);
		return root;

		// 当前节点 = key
	} else {
		// 左子树为空,则右子树成为当前节点
		if (root.left === null) {
			return root.right;
		}

		// 右子树为空,则左子树成为当前节点
		if (root.right === null) {
			return root.left;
		}

		// 当前节点存在左右子树时,使用前驱节点代替当前节点
		const successor = findMax(root.left); // 前驱节点

		successor.left = removeMax(root.left);

		successor.right = root.right;

		return successor;
	}
};

const removeMax = (root) => {
	if (root.right === null) {
		return root.left;
	}
	root.right = removeMax(root.right);
	return root;
};

const findMax = (root) => {
	if (root.right === null) {
		return root;
	}
	return findMax(root.right);
};

二叉搜索树中的搜索 - 简单

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} val
 * @return {TreeNode}
 */
var searchBST = function (root, val) {
	if (root === null) return null;
	if (root.val > val) {
		return searchBST(root.left, val);
	} else if (root.val < val) {
		return searchBST(root.right, val);
	} else {
		return root;
	}
};

二叉搜索树中的插入操作 - 中等

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} val
 * @return {TreeNode}
 */
var insertIntoBST = function (root, val) {
	if (root === null) return new TreeNode(val);
	if (root.val > val) {
		root.left = insertIntoBST(root.left, val);
	} else {
		root.right = insertIntoBST(root.right, val);
	}
	return root;
};

二叉搜索树迭代器 - 中等

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 */
var BSTIterator = function (root) {
	this.root = root;
	this.list = [];
	this.index = 0;
	this.find(root);
	console.log(this.list);
	return null;
};

/**
 * @return {number}
 */
BSTIterator.prototype.next = function () {
	if (this.index >= this.list.length) return null;
	const result = this.list[this.index];
	this.index++;
	return result;
};

/**
 * @return {boolean}
 */
BSTIterator.prototype.hasNext = function () {
	return this.index <= this.list.length - 1;
};

/**
 * @return {number}
 */
BSTIterator.prototype.find = function (root) {
	if (root === null) return;
	if (root.left) this.find(root.left);
	this.list.push(root.val);
	if (root.right) this.find(root.right);
};

/**
 * Your BSTIterator object will be instantiated and called as such:
 * var obj = new BSTIterator(root)
 * var param_1 = obj.next()
 * var param_2 = obj.hasNext()
 */

序列化和反序列化二叉搜索树 - 困难

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */

/**
 * Encodes a tree to a single string.
 *
 * @param {TreeNode} root
 * @return {string}
 */
var serialize = function (root) {
	if (root === null) return "";

	const data = [];

	// 前序遍历
	function pre(tree) {
		if (tree === null) return null;
		data.push(tree.val);
		if (tree.left) pre(tree.left);
		if (tree.right) pre(tree.right);
	}
	pre(root);

	return data.join("|");
};

/**
 * Decodes your encoded data to tree.
 *
 * @param {string} data
 * @return {TreeNode}
 */
var deserialize = function (data) {
	if (data === "") return null;

	const preL = data.split("|");
	const midL = [...preL].sort((a, b) => a - b);

	function build(pre, mid) {
		if (!pre.length && !mid.length) return null;
		const head = pre.shift(); // 头节点
		const idx = mid.indexOf(head);

		const tree = new TreeNode(head);

		if (pre.length) {
			tree.left = build(pre.slice(0, idx), mid.slice(0, idx));
		}
		if (mid.length) {
			tree.right = build(pre.slice(idx), mid.slice(idx + 1));
		}

		return tree;
	}

	const root = build(preL, midL);

	return root;
};

将有序数组转换为二叉搜索树 - 简单

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {number[]} nums
 * @return {TreeNode}
 */
var sortedArrayToBST = function (nums) {
	const len = nums.length;
	if (!len) return null;

	const buildBST = (left, right) => {
		if (left > right) return null;
		const mid = left + Math.floor((right - left) / 2); // 当前头节点
		const root = new TreeNode(nums[mid]);

		root.left = buildBST(left, mid - 1);
		root.right = buildBST(mid + 1, right);

		return root;
	};

	return buildBST(0, len - 1);
};

有序链表转换二叉搜索树 - 中等

js 复制代码
/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {ListNode} head
 * @return {TreeNode}
 */
var sortedListToBST = function (head) {
	if (head === null) return null;
	const len = getLen(head); // 链表长度
	this.head = head;
	return buildBST.apply(this, [0, len - 1]);
};

function buildBST(left, right) {
	if (left > right) return null;
	const mid = left + Math.floor((right - left) / 2);
	const tree = new TreeNode();
	tree.left = buildBST.apply(this, [left, mid - 1]);
	tree.val = this.head.val;
	this.head = this.head.next;
	tree.right = buildBST.apply(this, [mid + 1, right]);
	return tree;
}

const getLen = (head) => {
	let count = 0;
	while (head) {
		count++;
		head = head.next;
	}
	return count;
};

二叉搜索树的最近公共祖先 - 中等

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */

/**
 * @param {TreeNode} root
 * @param {TreeNode} p
 * @param {TreeNode} q
 * @return {TreeNode}
 */
var lowestCommonAncestor = function (root, p, q) {
	if (root === null) return null;

	const find = (tree) => {
		if (tree === null) return null;
		tree.left = find(tree.left);
		tree.right = find(tree.right);

		if (tree.val === p.val || tree.val === q.val) return tree;

		if (tree.left && tree.right) {
		} else if (tree.left) {
			tree = tree.left;
		} else {
			tree = tree.right;
		}

		return tree;
	};

	console.log(find(root));
	return find(root);
};

把二叉搜索树转换为累加树 - 中等

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {TreeNode}
 */
var convertBST = function (root) {
	let sum = 0;
	const add = (tree) => {
		if (tree != null) {
			add(tree.right);
			sum += tree.val;
			tree.val = sum;
			add(tree.left);
		}
		return tree;
	};
	add(root);
	return root;
};

两数之和 IV - 输入 BST - 简单

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} k
 * @return {boolean}
 */
var findTarget = function (root, k) {
	if (root === null) return false;
	const list = [];
	const find = (tree) => {
		if (tree.left) find(tree.left);
		list.push(tree.val);
		if (tree.right) find(tree.right);
	};

	find(root);

	if (list.length <= 1) return false;

	for (let i = 0; i < list.length - 1; i++) {
		for (let j = i + 1; j < list.length; j++) {
			if (list[i] + list[j] === k) return true;
		}
	}

	return false;
};

典型问题:二叉搜索树中第 K 小的元素

二叉搜索树中第 K 小的元素 - 中等

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} k
 * @return {number}
 */
var kthSmallest = function (root, k) {
	let count = k;
	let res;
	// 中序遍历二叉搜索树,得到的是升序的数组
	const find = (tree) => {
		if (tree === null) return null;
		find(tree.left);
		count--;
		if (count === 0) {
			res = tree.val;
			return;
		}
		find(tree.right);
	};

	find(root);
	return res;
};

修剪二叉搜索树 - 中等

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} low
 * @param {number} high
 * @return {TreeNode}
 */
var trimBST = function (root, low, high) {
	// 中序遍历
	const find = (tree) => {
		if (tree === null) return null;
		if (tree?.left) tree.left = find(tree.left);
		if (tree.val < low) {
			tree = find(tree.right);
		} else if (tree.val > high) {
			tree = find(tree.left);
		}
		if (tree?.right) tree.right = find(tree.right);

		return tree;
	};
	return find(root);
};

二叉搜索树节点最小距离 - 中等

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} low
 * @param {number} high
 * @return {TreeNode}
 */
var trimBST = function (root, low, high) {
	// 中序遍历
	const find = (tree) => {
		if (tree === null) return null;
		if (tree?.left) tree.left = find(tree.left);
		if (tree.val < low) {
			tree = find(tree.right);
		} else if (tree.val > high) {
			tree = find(tree.left);
		}
		if (tree?.right) tree.right = find(tree.right);

		return tree;
	};
	return find(root);
};

两棵二叉搜索树中的所有元素 - 中等

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root1
 * @param {TreeNode} root2
 * @return {number[]}
 */
var getAllElements = function (root1, root2) {
	const r1 = [];
	function find(tree) {
		if (tree === null) return;
		if (tree?.left) find(tree.left);
		r1.push(tree.val);
		if (tree?.right) find(tree.right);
	}
	find(root1);
	const r2 = [];
	function find2(tree) {
		if (tree === null) return;
		if (tree?.left) find2(tree.left);
		r2.push(tree.val);
		if (tree?.right) find2(tree.right);
	}
	find2(root2);
	return r1.concat(r2).sort((a, b) => a - b);
};

典型问题:验证二叉搜索树

验证二叉搜索树 - 中等

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {boolean}
 */
var isValidBST = function (root) {
	let prev;
	let result = true;
	const find = (tree) => {
		if (tree === null) return;

		if (tree.left) find(tree.left);
		if (prev && result) {
			result = tree.val > prev.val ? true : false;
		}

		prev = tree;

		if (tree.right) find(tree.right);
	};
	find(root);

	return result;
};

二叉搜索子树的最大键值和 - 困难

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
var maxSumBST = function (root) {
	if (root === null) return 0;
	let res = Number.MIN_SAFE_INTEGER;
	// 采用后序遍历
	// 先判断左子树是否为二叉搜索树,判断右子树是否为二叉搜索树
	// 再判断左子树.val是否小于当前val,判断右子树.val是否大于.val
	// 符合条件则累加起来
	const dfs = (tree) => {
		if (tree === null) return null;
		tree.left = dfs(tree.left);

		tree.right = dfs(tree.right);

		if (tree.left && tree.right) {
			if (tree.val > tree.left.max && tree.val < tree.right.min) {
				tree.total = tree.val + tree.left.total + tree.right.total;
				tree.isBST = true;
				tree.min = tree.left.min;
				tree.max = tree.right.max;
			}
		} else if (tree.left && !tree.right) {
			if (tree.left.isBST && tree.left.max < tree.val) {
				tree.total = tree.val + tree.left.total;
				tree.isBST = true;
				tree.min = tree.left.min;
				tree.max = tree.val;
			}
		} else if (!tree.left && tree.right) {
			if (tree.right.isBST && tree.val < tree.right.min) {
				tree.total = tree.val + tree.right.total;
				tree.isBST = true;
				tree.min = tree.val;
				tree.max = tree.right.max;
			}
		} else {
			tree.total = tree.val;
			tree.isBST = true;

			tree.min = tree.val;
			tree.max = tree.val;
		}

		if (tree.total) {
			res = Math.max(tree.total, res);
		}

		return tree;
	};
	dfs(root);

	return res < 0 ? 0 : res;
};
相关推荐
真的很上进33 分钟前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
噢,我明白了4 小时前
同源策略:为什么XMLHttpRequest不能跨域请求资源?
javascript·跨域
sanguine__4 小时前
APIs-day2
javascript·css·css3
关你西红柿子4 小时前
小程序app封装公用顶部筛选区uv-drop-down
前端·javascript·vue.js·小程序·uv
济南小草根5 小时前
把一个Vue项目的页面打包后再另一个项目中使用
前端·javascript·vue.js
小木_.5 小时前
【python 逆向分析某有道翻译】分析有道翻译公开的密文内容,webpack类型,全程扣代码,最后实现接口调用翻译,仅供学习参考
javascript·python·学习·webpack·分享·逆向分析
Aphasia3115 小时前
一次搞懂 JS 对象转换,从此告别类型错误!
javascript·面试
m0_748256566 小时前
Vue - axios的使用
前端·javascript·vue.js
m0_748256346 小时前
QWebChannel实现与JS的交互
java·javascript·交互
胡西风_foxww6 小时前
【es6复习笔记】函数参数的默认值(6)
javascript·笔记·es6·参数·函数·默认值