数据结构与算法进阶指南:左神算法课笔记(六)
2024-02-07 10:36:12
数据结构和算法是计算机科学的基础,掌握它们对于编写高效且可维护的代码至关重要。在左神的算法课中,我们深入探讨了各种数据结构和算法,包括二叉树。二叉树是一种非线性数据结构,可用于表示分层数据并执行快速搜索和插入操作。
在这一篇笔记中,我们将学习判断二叉树、判断完全二叉树、判断满二叉树、判断平衡二叉树、二叉树的公共祖先问题、二叉树的后继节点问题以及二叉树的序列化和反序列化。
判断二叉树
二叉树是一种数据结构,其中每个节点最多有两个子节点。判断一个给定的树是否为二叉树很简单:只需要检查每个节点的子节点数量是否小于或等于 2。如果满足此条件,则该树是二叉树,否则不是。
boolean isBinaryTree(Node root) {
if (root == null) {
return true;
}
if (root.left != null && root.left.parent != root) {
return false;
}
if (root.right != null && root.right.parent != root) {
return false;
}
return isBinaryTree(root.left) && isBinaryTree(root.right);
}
判断完全二叉树
完全二叉树是一种特殊的二叉树,其中除了最后一层之外,所有层都已完全填满。最后一层可能不完全填满,但所有节点都必须尽可能地靠左对齐。
判断一个给定的树是否为完全二叉树需要进行递归检查。对于每个节点,检查其左子树和右子树的高度是否相等。如果相等,则该节点是完全二叉树的根节点。否则,该树不是完全二叉树。
boolean isCompleteBinaryTree(Node root) {
if (root == null) {
return true;
}
int leftHeight = getHeight(root.left);
int rightHeight = getHeight(root.right);
return leftHeight == rightHeight && isCompleteBinaryTree(root.left) && isCompleteBinaryTree(root.right);
}
int getHeight(Node root) {
if (root == null) {
return 0;
}
return 1 + Math.max(getHeight(root.left), getHeight(root.right));
}
判断满二叉树
满二叉树是一种特殊的二叉树,其中每个节点都包含两个子节点。
判断一个给定的树是否为满二叉树很简单:只需要检查每个节点是否包含两个子节点。如果满足此条件,则该树是满二叉树,否则不是。
boolean isFullBinaryTree(Node root) {
if (root == null) {
return true;
}
if (root.left == null || root.right == null) {
return false;
}
return isFullBinaryTree(root.left) && isFullBinaryTree(root.right);
}
判断平衡二叉树
平衡二叉树是一种特殊的二叉树,其中每个节点的左右子树高度差绝对值不超过 1。
判断一个给定的树是否为平衡二叉树需要进行递归检查。对于每个节点,计算其左右子树的高度差。如果绝对值不超过 1,则该节点是平衡二叉树的根节点。否则,该树不是平衡二叉树。
boolean isBalancedBinaryTree(Node root) {
if (root == null) {
return true;
}
int leftHeight = getHeight(root.left);
int rightHeight = getHeight(root.right);
return Math.abs(leftHeight - rightHeight) <= 1 && isBalancedBinaryTree(root.left) && isBalancedBinaryTree(root.right);
}
int getHeight(Node root) {
if (root == null) {
return 0;
}
return 1 + Math.max(getHeight(root.left), getHeight(root.right));
}
二叉树的公共祖先问题
二叉树的公共祖先是给定两个节点的最低祖先节点。
查找二叉树的公共祖先可以使用递归算法。对于给定的两个节点,从根节点开始,沿着两条路径递归向下查找。如果在某一层的左右子树中分别找到了两个节点,则该层的根节点就是公共祖先。如果两个节点都在同一侧,则继续沿着该侧递归查找。
Node findCommonAncestor(Node root, Node node1, Node node2) {
if (root == null) {
return null;
}
if (root == node1 || root == node2) {
return root;
}
Node left = findCommonAncestor(root.left, node1, node2);
Node right = findCommonAncestor(root.right, node1, node2);
if (left != null && right != null) {
return root;
}
return left != null ? left : right;
}
二叉树的后继节点问题
二叉树的后继节点是给定节点在中序遍历中的下一个节点。
查找二叉树的后继节点可以使用递归算法。对于给定的节点,如果其右子树不为空,则其后继节点是右子树中最左边的节点。否则,后继节点是该节点的父节点。如果该节点是根节点,则没有后继节点。
Node findSuccessor(Node node) {
if (node == null) {
return null;
}
if (node.right != null) {
Node current = node.right;
while (current.left != null) {
current = current.left;
}
return current;
}
Node parent = node.parent;
while (parent != null && node == parent.right) {
node = parent;
parent = parent.parent;
}
return parent;
}
二叉树的序列化和反序列化
二叉树的序列化是将二叉树转换为一维数组的过程。反序列化是将一维数组还原为二叉树的过程。
二叉树的序列化可以使用先序遍历算法。对于每个节点,将节点的值存储在数组中。如果节点为空,则存储一个特殊值(例如 -1)来表示空节点。
void serialize(Node root, int[] array, int index) {
if (root == null) {
array[index] = -1;
return;
}
array[index] = root.val;
serialize(root.left, array, 2 * index + 1);
serialize(root.right, array, 2 * index + 2);
}
二叉树的反序列化可以使用相同的数组和索引。对于每个节点,从数组中读取节点的值。如果节点的值不是特殊值(例如 -1),则创建一个新节点并将其存储在数组中。
Node deserialize(int[] array, int index) {
if (array[index] == -1) {
return null;
}
Node root = new Node(array[index]);
root.left = deserialize(array, 2 * index + 1);
root.right = deserialize(array, 2 * index + 2);
return root;
}
以上是判断二叉树、判断完全二叉树、判断满二叉树、判断平衡二叉树、二叉树的公共祖先问题、二叉树的后继节点问题以及二叉树的序列化和反序列化的概念和算法。熟练掌握这些概念对于使用二叉树解决实际问题至关重要。