用 Java 深入研究树,你了解多少?("Java 树结构深度解析:你掌握的程度有多深?")

原创
ithorizon 7个月前 (10-19) 阅读数 19 #后端开发

Java 树结构深度解析:你掌握的程度有多深?

一、引言

在计算机科学中,树是一种非常常见的数据结构,它模拟了一种层级关系,类似于自然界中的树。树结构在许多领域都有广泛应用,如数据库索引、优先队列、排序等。Java 提供了多种树结构的实现,如二叉树、平衡树(AVL树)、红黑树等。本文将深入探讨 Java 中的树结构,帮助读者更好地领会和掌握这一重要数据结构。

二、Java 树结构概述

Java 中的树结构核心分为两类:二叉树和非二叉树。二叉树是最常见的树结构,每个节点最多有两个子节点,分别为左子节点和右子节点。而非二叉树中的节点可以有多个子节点。下面分别介绍这两种树结构。

三、二叉树

二叉树是一种非常基础的树结构,下面将详细介绍二叉树的基本概念、存储结构以及常见操作。

3.1 基本概念

二叉树是一种节点间具有父子关系的树结构,每个节点最多有两个子节点。二叉树具有以下特点:

  • 每个节点最多有两个子节点,分别为左子节点和右子节点;
  • 每个子节点都有唯一的父节点;
  • 根节点没有父节点。

3.2 存储结构

二叉树通常使用数组或链表进行存储。下面分别介绍这两种存储结构。

3.2.1 数组存储结构

使用数组存储二叉树时,通常按照层次遍历的顺序存储节点。对于任意节点 i,其左子节点的索引为 2i+1,右子节点的索引为 2i+2。下面是一个使用数组存储二叉树的示例代码:

public class BinaryTreeArray {

private int[] data;

public BinaryTreeArray(int[] data) {

this.data = data;

}

public void printTree() {

for (int i = 0; i < data.length; i++) {

System.out.print(data[i] + " ");

}

System.out.println();

}

}

3.2.2 链表存储结构

使用链表存储二叉树时,每个节点包含三个部分:值、左子节点指针和右子节点指针。下面是一个使用链表存储二叉树的示例代码:

class TreeNode {

int value;

TreeNode left;

TreeNode right;

public TreeNode(int value) {

this.value = value;

this.left = null;

this.right = null;

}

}

public class BinaryTree {

private TreeNode root;

public BinaryTree(int value) {

root = new TreeNode(value);

}

public void insert(int value) {

TreeNode newNode = new TreeNode(value);

if (root == null) {

root = newNode;

} else {

TreeNode current = root;

while (true) {

if (value < current.value) {

if (current.left == null) {

current.left = newNode;

break;

} else {

current = current.left;

}

} else {

if (current.right == null) {

current.right = newNode;

break;

} else {

current = current.right;

}

}

}

}

}

}

3.3 常见操作

二叉树的常见操作包括遍历、查找、插入和删除等。下面分别介绍这些操作。

3.3.1 遍历

二叉树的遍历分为三种:前序遍历、中序遍历和后序遍历。下面是这三种遍历的示例代码:

public void preOrder(TreeNode node) {

if (node != null) {

System.out.print(node.value + " ");

preOrder(node.left);

preOrder(node.right);

}

}

public void inOrder(TreeNode node) {

if (node != null) {

inOrder(node.left);

System.out.print(node.value + " ");

inOrder(node.right);

}

}

public void postOrder(TreeNode node) {

if (node != null) {

postOrder(node.left);

postOrder(node.right);

System.out.print(node.value + " ");

}

}

3.3.2 查找

二叉树的查找操作通常使用递归实现。下面是一个查找二叉树中值为指定值的节点的示例代码:

public TreeNode search(TreeNode node, int value) {

if (node == null) {

return null;

}

if (node.value == value) {

return node;

} else if (value < node.value) {

return search(node.left, value);

} else {

return search(node.right, value);

}

}

3.3.3 插入

二叉树的插入操作通常遵循二叉搜索树的规则。下面是一个插入节点的示例代码:

public void insert(int value) {

TreeNode newNode = new TreeNode(value);

if (root == null) {

root = newNode;

} else {

TreeNode current = root;

while (true) {

if (value < current.value) {

if (current.left == null) {

current.left = newNode;

break;

} else {

current = current.left;

}

} else {

if (current.right == null) {

current.right = newNode;

break;

} else {

current = current.right;

}

}

}

}

}

3.3.4 删除

二叉树的删除操作相对繁复,需要考虑多种情况。下面是一个删除节点的示例代码:

public void delete(int value) {

root = deleteRecursive(root, value);

}

private TreeNode deleteRecursive(TreeNode node, int value) {

if (node == null) {

return null;

}

if (value == node.value) {

if (node.left == null && node.right == null) {

return null;

}

if (node.left == null) {

return node.right;

}

if (node.right == null) {

return node.left;

}

int smallestValue = findSmallestValue(node.right);

node.value = smallestValue;

node.right = deleteRecursive(node.right, smallestValue);

return node;

}

if (value < node.value) {

node.left = deleteRecursive(node.left, value);

} else {

node.right = deleteRecursive(node.right, value);

}

return node;

}

private int findSmallestValue(TreeNode node) {

return node.left == null ? node.value : findSmallestValue(node.left);

}

四、平衡树(AVL树)

平衡树是一种特殊的二叉搜索树,它确保任何节点的左右子树高度差不超过1。下面将介绍平衡树的基本概念、插入和删除操作。

4.1 基本概念

平衡树(AVL树)是一种自平衡的二叉搜索树,它通过旋转操作来保持树的平衡。平衡树的每个节点包含一个额外的信息:平衡因子(balance factor),描述该节点的左子树高度与右子树高度的差。

4.2 插入操作

平衡树的插入操作与二叉搜索树类似,但在插入后需要检查平衡因子,并选择平衡因子进行旋转操作。下面是一个插入节点的示例代码:

public void insert(int value) {

root = insertRecursive(root, value);

}

private TreeNode insertRecursive(TreeNode node, int value) {

if (node == null) {

return new TreeNode(value);

}

if (value < node.value) {

node.left = insertRecursive(node.left, value);

} else if (value > node.value) {

node.right = insertRecursive(node.right, value);

} else {

return node;

}

updateHeight(node);

int balanceFactor = getBalanceFactor(node);

if (balanceFactor > 1 && value < node.left.value) {

return rightRotate(node);

}

if (balanceFactor < -1 && value > node.right.value) {

return leftRotate(node);

}

if (balanceFactor > 1 && value > node.left.value) {

node.left = leftRotate(node.left);

return rightRotate(node);

}

if (balanceFactor < -1 && value < node.right.value) {

node.right = rightRotate(node.right);

return leftRotate(node);

}

return node;

}

private void updateHeight(TreeNode node) {

if (node != null) {

node.height = Math.max(height(node.left), height(node.right)) + 1;

}

}

private int height(TreeNode node) {

if (node == null) {

return 0;

}

return node.height;

}

private int getBalanceFactor(TreeNode node) {

if (node == null) {

return 0;

}

return height(node.left) - height(node.right);

}

private TreeNode rightRotate(TreeNode y) {

TreeNode x = y.left;

TreeNode T2 = x.right;

x.right = y;

y.left = T2;

updateHeight(y);

updateHeight(x);

return x;

}

private TreeNode leftRotate(TreeNode x) {

TreeNode y = x.right;

TreeNode T2 = y.left;

y.left = x;

x.right = T2;

updateHeight(x);

updateHeight(y);

return y;

}

4.3 删除操作

平衡树的删除操作与插入操作类似,也需要在删除后检查平衡因子,并进行相应的旋转操作。下面是一个删除节点的示例代码:

public void delete(int value) {

root = deleteRecursive(root, value);

}

private TreeNode deleteRecursive(TreeNode node, int value) {

if (node == null) {

return null;

}

if (value < node.value) {

node.left = deleteRecursive(node.left, value);

} else if (value > node.value) {

node.right = deleteRecursive(node.right, value);

} else {

if (node.left == null || node.right == null) {

TreeNode temp = node.left != null ? node.left : node.right;

if (temp == null) {

temp = node;

node = null;

} else {

node = temp;

}

} else {

int smallestValue = findSmallestValue(node.right);

node.value = smallestValue;

node.right = deleteRecursive(node.right, smallestValue);

}

}

if (node == null) {

return null;

}

updateHeight(node);

int balanceFactor = getBalanceFactor(node);

if (balanceFactor > 1 && getBalanceFactor(node.left) >= 0) {

return rightRotate(node);

}

if (balanceFactor > 1 && getBalanceFactor(node.left) < 0) {

node.left = leftRotate(node.left);

return rightRotate(node);

}

if (balanceFactor < -1 && getBalanceFactor(node.right) <= 0) {

return leftRotate(node);

}

if (balanceFactor < -1 && getBalanceFactor(node.right) > 0) {

node.right = rightRotate(node.right);

return leftRotate(node);

}

return node;

}

五、红黑树

红黑树是一种自平衡的二叉搜索树,它通过确保节点的颜色和子树的黑高相等来保持树的平衡。下面将介绍红黑树的基本概念和插入操作。

5.1 基本概念

红黑树是一种特殊的二叉搜索树,它具有以下特性:

  • 每个节点或者是红的,或者是黑的;
  • 根节点是黑的;
  • 所有叶子节点(NIL节点)是黑的;
  • 如果一个节点是红的,那么它的两个子节点都是黑的;
  • 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

5.2 插入操作

红黑树的插入操作相对繁复,需要考虑多种情况并进行相应的旋转和颜色调整。下面是一个插入节点的示例代码:

public void insert(int value) {

TreeNode newNode = new TreeNode(value);

newNode.color = RED;

root = insertRecursive(root, newNode);

fixInsertion(root, newNode);

}

private TreeNode insertRecursive(TreeNode node, TreeNode newNode) {

if (node == null) {

return newNode;

}

if (newNode.value < node.value) {

node.left = insertRecursive(node.left, newNode);

} else if (newNode.value > node.value) {

node.right = insertRecursive(node.right, newNode);

}

return node;

}

private void fixInsertion(TreeNode root, TreeNode node) {

TreeNode parent = null;

TreeNode grandParent = null;

while (node != root && node.color == RED && node.parent.color == RED) {

parent = node.parent;

grandParent = parent.parent;

if (parent == grandParent.left) {

TreeNode uncle = grandParent.right;

if (uncle != null && uncle.color == RED) {

uncle.color = BLACK;

parent.color = BLACK;

grandParent.color = RED;

node = grandParent;

} else {

if (node == parent.right) {

parent = leftRotate(parent);

node = node.left;

}

parent.color = BLACK;

grandParent.color = RED;

rightRotate(grandParent);

}

} else {

TreeNode uncle = grandParent.left;

if (uncle != null && uncle.color == RED) {

uncle.color = BLACK;

parent.color = BLACK;

grandParent.color = RED;

node = grandParent;

} else {

if (node == parent.left) {

parent = rightRotate(parent);

node = node.right;

}

parent.color = BLACK;

grandParent.color = RED;

leftRotate(grandParent);

}

}

}

root.color = BLACK;

}

private TreeNode leftRotate(TreeNode node) {

TreeNode rightChild = node.right;

node.right = rightChild.left;

if (node.right != null) {

node.right.parent = node;

}

rightChild.parent = node.parent;

if (node.parent == null) {

root = rightChild;

} else if (node == node.parent.left) {

node.parent.left = rightChild;

} else {

node.parent.right = rightChild;

}

rightChild.left = node;

node.parent = rightChild;

return rightChild;

}

private TreeNode rightRotate(TreeNode node) {

TreeNode leftChild = node.left;

node.left = leftChild.right;

if (node.left != null) {

node.left.parent = node;

}

leftChild.parent = node.parent;

if (node.parent == null) {

root = leftChild;

} else if (node == node.parent.right) {

node.parent.right = leftChild;

} else {

node.parent.left = leftChild;

}

leftChild.right = node;

node.parent = leftChild;

return leftChild;

}

六、总结

本文详细介绍了 Java 中的树结构,包括二叉树、平衡树(AVL树)和红黑树。通过对这些树结构的深入解析,我们可以更好地领会树的基本概念、存储结构以及常见操作。掌握这些树结构对于领会 Java 数据结构和算法具有重要意义,有助于我们在实际开发中更加高效地解决问题。


本文由IT视界版权所有,禁止未经同意的情况下转发

文章标签: 后端开发


热门