当前位置: 首页 > news >正文

树的常见算法及Java实现

一、常见树的类型及应用        

树类型 (Tree Type)主要特点 (Key Characteristics)经典应用场景 (Typical Use Cases)涉及的重要算法 (Related Algorithms)
二叉树 (Binary Tree)每个节点最多有两个子节点(左子树、右子树)。结构自由,没有严格平衡规则。作为其他复杂树结构的基础;用于表达数学表达式(表达式树)。树的遍历(前序、中序、后序、层序)。
二叉搜索树 (BST - Binary Search Tree)在二叉树基础上,满足:左子树上所有节点的值 < 根节点的值 < 右子树上所有节点的值。用于快速查找、插入、删除数据,理想情况下时间复杂度为 O(log n)。查找、插入、删除。
平衡二叉搜索树 (Balanced BST)1. AVL 树
通过旋转操作严格保持平衡(任意节点的左右子树高度差绝对值不超过1)。查找效率极高。

2. 红黑树 (Red-Black Tree)
通过着色和旋转规则保持近似平衡(从根到叶子的最长路径不超过最短路径的2倍)。插入删除效率更高,统计性能更优。
AVL 树:适用于大量查询操作而插入删除相对较少的场景,如数据库索引。

红黑树:广泛应用于需要高效插入删除的场景,如:
Java中的 HashMap(冲突链表超过阈值转红黑树)
C++ STL 中的 mapset
Linux 进程调度
旋转 (Rotations):左旋、右旋。
AVL:计算平衡因子并进行调整。
红黑树:节点变色和旋转以满足五大性质。
B 树 (B-Tree)多路平衡搜索树,一个节点可以有多个key和多个子节点。节点大小通常等于一个磁盘页,大大减少磁盘I/O次数数据库索引文件系统(如NTFS, ReiserFS)的核心数据结构。专门为处理海量数据和外存(磁盘)存储而设计。分裂(Split)、合并(Merge)、查找、插入、删除。
B+ 树 (B+ Tree)B树的变体,所有数据记录都存储在叶子节点,且叶子节点之间通过指针相连形成有序链表。非叶子节点仅存放键(索引)。比B树更广泛应用于数据库索引(如MySQL InnoDB)和操作系统的文件索引。原因:
1. 查询效率更稳定(任何查找都要到叶子节点)。
2. 范围查询和全表扫描效率极高(通过叶子节点链表)。
分裂、合并、查找、插入、删除。
字典树 (Trie)又名前缀树,专门用于处理字符串。节点的位置决定了其代表的字符串(从根到该节点的路径)。
搜索引擎的自动补全/提示
输入法联想
IP路由表的最长前缀匹配
- 统计和排序大量字符串
插入字符串、搜索前缀/完整字符串、深度优先遍历收集所有单词。
堆 (Heap)一种特殊的完全二叉树,满足堆属性:父节点的值总是 >=(最大堆)或 <=(最小堆)所有子节点的值。不是搜索树,无法快速查找任意值。
优先队列(Priority Queue)的实现
堆排序(Heap Sort)
求Top K问题
- 图算法中的Dijkstra算法
上浮 (Sift Up / Percolate Up)
下沉 (Sift Down / Heapify)
建堆(Heapify)
线段树 (Segment Tree)一种用于处理区间/段查询的二叉树结构。每个节点代表一个区间,并存储该区间的聚合信息(如 sum, min, max)。
区间求和、求最值
区间更新(如给区间内所有值加上一个数)
- 统计类、范围类问题
建树 (Build)
区间查询 (Query / Range Query)
区间更新 (Update / Range Update) (常用懒惰传播 Lazy Propagation 优化)
哈夫曼树 (Huffman Tree)一种带权路径长度最短的最优二叉树自底向上构建,频率越高的字符离根越近,编码越短。数据压缩(如GZIP, JPEG, MP3等编码的基础算法),用于构建前缀编码,即哈夫曼编码。构建哈夫曼树生成哈夫曼编码
  • 需要快速查找、插入、删除(通用):首选红黑树,它在实践中综合性能最好。

  • 海量数据,涉及磁盘存储(如数据库):选择B+树,它最大限度地减少了磁盘访问次数。

  • 处理字符串和前缀相关的问题:选择字典树 (Trie)

  • 需要管理优先级的任务调度:选择来实现优先队列。

  • 频繁进行区间统计和更新:选择线段树

  • 目标是为了数据压缩:使用哈夫曼树构建编码。

二、树的遍历算法与构建

二叉树的基本节点结构:

class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int val) {this.val = val;this.left = null;this.right = null;}
}

2.1 树的深度优先遍历(DFS)

2.1.1 前序遍历 (根-左-右)

// 递归实现
public void preorderTraversal(TreeNode root) {if (root == null) return;System.out.print(root.val + " ");preorderTraversal(root.left);preorderTraversal(root.right);
}// 迭代实现
public void preorderTraversalIterative(TreeNode root) {if (root == null) return;Stack<TreeNode> stack = new Stack<>();stack.push(root);while (!stack.isEmpty()) {TreeNode node = stack.pop();System.out.print(node.val + " ");if (node.right != null) stack.push(node.right);if (node.left != null) stack.push(node.left);}
}

2.1.2 中序遍历 (左-根-右)

// 递归实现
public void inorderTraversal(TreeNode root) {if (root == null) return;inorderTraversal(root.left);System.out.print(root.val + " ");inorderTraversal(root.right);
}// 迭代实现
public void inorderTraversalIterative(TreeNode root) {Stack<TreeNode> stack = new Stack<>();TreeNode curr = root;while (curr != null || !stack.isEmpty()) {while (curr != null) {stack.push(curr);curr = curr.left;}curr = stack.pop();System.out.print(curr.val + " ");curr = curr.right;}
}

2.1.3 后序遍历 (左-右-根)

// 递归实现
public void postorderTraversal(TreeNode root) {if (root == null) return;postorderTraversal(root.left);postorderTraversal(root.right);System.out.print(root.val + " ");
}// 迭代实现
public void postorderTraversalIterative(TreeNode root) {if (root == null) return;Stack<TreeNode> stack = new Stack<>();Stack<TreeNode> output = new Stack<>();stack.push(root);while (!stack.isEmpty()) {TreeNode node = stack.pop();output.push(node);if (node.left != null) stack.push(node.left);if (node.right != null) stack.push(node.right);}while (!output.isEmpty()) {System.out.print(output.pop().val + " ");}
}

2.2 广度优先遍历 (BFS/层次遍历)

public void levelOrderTraversal(TreeNode root) {if (root == null) return;Queue<TreeNode> queue = new LinkedList<>();queue.offer(root);while (!queue.isEmpty()) {TreeNode node = queue.poll();System.out.print(node.val + " ");if (node.left != null) queue.offer(node.left);if (node.right != null) queue.offer(node.right);}
}

2.3 树的构建

2.3.1 从前序和中序遍历构建二叉树

public class PreInOrderBuilder {private Map<Integer, Integer> inOrderMap;private int preIndex;public TreeNode buildTree(int[] preorder, int[] inorder) {if (preorder == null || inorder == null || preorder.length != inorder.length) {return null;}// 创建中序遍历值到索引的映射inOrderMap = new HashMap<>();for (int i = 0; i < inorder.length; i++) {inOrderMap.put(inorder[i], i);}preIndex = 0;return buildTreeHelper(preorder, 0, inorder.length - 1);}private TreeNode buildTreeHelper(int[] preorder, int inStart, int inEnd) {if (inStart > inEnd) {return null;}// 前序遍历的第一个元素是根节点int rootVal = preorder[preIndex++];TreeNode root = new TreeNode(rootVal);// 在中序遍历中找到根节点的位置int inIndex = inOrderMap.get(rootVal);// 递归构建左子树和右子树root.left = buildTreeHelper(preorder, inStart, inIndex - 1);root.right = buildTreeHelper(preorder, inIndex + 1, inEnd);return root;}
}

2.3.2 从中序和后序遍历构建二叉树

public class InPostOrderBuilder {private Map<Integer, Integer> inOrderMap;private int postIndex;public TreeNode buildTree(int[] inorder, int[] postorder) {if (inorder == null || postorder == null || inorder.length != postorder.length) {return null;}// 创建中序遍历值到索引的映射inOrderMap = new HashMap<>();for (int i = 0; i < inorder.length; i++) {inOrderMap.put(inorder[i], i);}postIndex = postorder.length - 1;return buildTreeHelper(postorder, 0, inorder.length - 1);}private TreeNode buildTreeHelper(int[] postorder, int inStart, int inEnd) {if (inStart > inEnd) {return null;}// 后序遍历的最后一个元素是根节点int rootVal = postorder[postIndex--];TreeNode root = new TreeNode(rootVal);// 在中序遍历中找到根节点的位置int inIndex = inOrderMap.get(rootVal);// 注意:需要先构建右子树,再构建左子树root.right = buildTreeHelper(postorder, inIndex + 1, inEnd);root.left = buildTreeHelper(postorder, inStart, inIndex - 1);return root;}
}

2.3.3 从前序和后序遍历构建二叉树(不唯一,需附加条件)

public class PrePostOrderBuilder {private Map<Integer, Integer> postOrderMap;private int preIndex;// 注意:前序+后序不能唯一确定二叉树,需要附加条件// 这里假设树是真二叉树(每个节点有0或2个子节点)public TreeNode buildTree(int[] preorder, int[] postorder) {if (preorder == null || postorder == null || preorder.length != postorder.length) {return null;}// 创建后序遍历值到索引的映射postOrderMap = new HashMap<>();for (int i = 0; i < postorder.length; i++) {postOrderMap.put(postorder[i], i);}preIndex = 0;return buildTreeHelper(preorder, 0, postorder.length - 1);}private TreeNode buildTreeHelper(int[] preorder, int postStart, int postEnd) {if (postStart > postEnd) {return null;}if (preIndex >= preorder.length) {return null;}// 前序遍历的第一个元素是根节点int rootVal = preorder[preIndex++];TreeNode root = new TreeNode(rootVal);if (postStart == postEnd) {return root;}// 找到左子树的根节点在后序遍历中的位置int leftRootVal = preorder[preIndex];int leftRootIndex = postOrderMap.get(leftRootVal);// 递归构建左子树和右子树if (leftRootIndex <= postEnd - 1) {root.left = buildTreeHelper(preorder, postStart, leftRootIndex);root.right = buildTreeHelper(preorder, leftRootIndex + 1, postEnd - 1);}return root;}
}

2.3.4 从层次和中序遍历构建二叉树

public class LevelInOrderBuilder {private Map<Integer, Integer> inOrderMap;public TreeNode buildTree(int[] levelOrder, int[] inOrder) {if (levelOrder == null || inOrder == null || levelOrder.length != inOrder.length) {return null;}// 创建中序遍历值到索引的映射inOrderMap = new HashMap<>();for (int i = 0; i < inOrder.length; i++) {inOrderMap.put(inOrder[i], i);}return buildTreeHelper(levelOrder, 0, inOrder.length - 1);}private TreeNode buildTreeHelper(int[] levelOrder, int inStart, int inEnd) {if (inStart > inEnd) {return null;}if (levelOrder.length == 0) {return null;}// 找到当前层次遍历中第一个出现在中序遍历区间[inStart, inEnd]中的元素TreeNode root = null;int rootIndexInLevel = -1;for (int i = 0; i < levelOrder.length; i++) {int val = levelOrder[i];if (inOrderMap.get(val) >= inStart && inOrderMap.get(val) <= inEnd) {root = new TreeNode(val);rootIndexInLevel = i;break;}}if (root == null) {return null;}int rootIndexInInorder = inOrderMap.get(root.val);// 提取左子树和右子树的层次遍历序列int[] leftLevel = extractLevelOrder(levelOrder, rootIndexInLevel, inStart, rootIndexInInorder - 1);int[] rightLevel = extractLevelOrder(levelOrder, rootIndexInLevel, rootIndexInInorder + 1, inEnd);// 递归构建左子树和右子树root.left = buildTreeHelper(leftLevel, inStart, rootIndexInInorder - 1);root.right = buildTreeHelper(rightLevel, rootIndexInInorder + 1, inEnd);return root;}private int[] extractLevelOrder(int[] levelOrder, int rootIndex, int inStart, int inEnd) {if (inStart > inEnd) {return new int[0];}List<Integer> result = new ArrayList<>();for (int i = 0; i < levelOrder.length; i++) {if (i == rootIndex) continue; // 跳过根节点int val = levelOrder[i];int indexInInorder = inOrderMap.get(val);if (indexInInorder >= inStart && indexInInorder <= inEnd) {result.add(val);}}// 转换为数组int[] arr = new int[result.size()];for (int i = 0; i < result.size(); i++) {arr[i] = result.get(i);}return arr;}
}

三、常见树算法

3.1 计算树的高度/深度

public int maxDepth(TreeNode root) {if (root == null) return 0;return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
}

3.2 判断对称二叉树

public boolean isSymmetric(TreeNode root) {if (root == null) return true;return isMirror(root.left, root.right);
}private boolean isMirror(TreeNode t1, TreeNode t2) {if (t1 == null && t2 == null) return true;if (t1 == null || t2 == null) return false;return (t1.val == t2.val) && isMirror(t1.left, t2.right) && isMirror(t1.right, t2.left);
}

3.2 判断平衡二叉树

public boolean isBalanced(TreeNode root) {return checkHeight(root) != -1;
}private int checkHeight(TreeNode root) {if (root == null) return 0;int leftHeight = checkHeight(root.left);if (leftHeight == -1) return -1;int rightHeight = checkHeight(root.right);if (rightHeight == -1) return -1;if (Math.abs(leftHeight - rightHeight) > 1) return -1;return 1 + Math.max(leftHeight, rightHeight);
}

3.3 二叉搜索树验证

public boolean isValidBST(TreeNode root) {return isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE);
}private boolean isValidBST(TreeNode node, long min, long max) {if (node == null) return true;if (node.val <= min || node.val >= max) return false;return isValidBST(node.left, min, node.val) && isValidBST(node.right, node.val, max);
}

3.4 最近公共祖先 (LCA)

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {if (root == null || root == p || root == q) return root;TreeNode left = lowestCommonAncestor(root.left, p, q);TreeNode right = lowestCommonAncestor(root.right, p, q);if (left != null && right != null) return root;return left != null ? left : right;
}

四、二叉搜索树操作

注意:二叉搜索树的中序遍历为升序

4.1 查找节点

public TreeNode searchBST(TreeNode root, int val) {if (root == null || root.val == val) return root;return val < root.val ? searchBST(root.left, val) : searchBST(root.right, val);
}

4.2 插入节点

public TreeNode insertIntoBST(TreeNode root, int val) {if (root == null) return new TreeNode(val);if (val < root.val) {root.left = insertIntoBST(root.left, val);} else {root.right = insertIntoBST(root.right, val);}return root;
}

4.3 删除节点

public TreeNode deleteNode(TreeNode root, int key) {if (root == null) return null;if (key < root.val) {root.left = deleteNode(root.left, key);} else if (key > root.val) {root.right = deleteNode(root.right, key);} else {if (root.left == null) return root.right;if (root.right == null) return root.left;TreeNode minNode = findMin(root.right);root.val = minNode.val;root.right = deleteNode(root.right, root.val);}return root;
}private TreeNode findMin(TreeNode node) {while (node.left != null) {node = node.left;}return node;
}

五、堆

5.1 大根堆(Max Heap)

import java.util.Arrays;public class MaxHeap {private int[] heap;private int size;private int capacity;public MaxHeap(int capacity) {this.capacity = capacity;this.size = 0;this.heap = new int[capacity];}// 获取父节点索引private int parent(int i) {return (i - 1) / 2;}// 获取左子节点索引private int leftChild(int i) {return 2 * i + 1;}// 获取右子节点索引private int rightChild(int i) {return 2 * i + 2;}// 交换元素private void swap(int i, int j) {int temp = heap[i];heap[i] = heap[j];heap[j] = temp;}// 插入元素public void insert(int value) {if (size >= capacity) {System.out.println("Heap is full");return;}// 将新元素添加到堆的末尾heap[size] = value;int current = size;size++;// 向上调整堆while (current > 0 && heap[current] > heap[parent(current)]) {swap(current, parent(current));current = parent(current);}}// 删除并返回最大元素public int extractMax() {if (size <= 0) {System.out.println("Heap is empty");return Integer.MIN_VALUE;}if (size == 1) {size--;return heap[0];}// 保存最大值int max = heap[0];// 将最后一个元素移到根节点heap[0] = heap[size - 1];size--;// 向下调整堆maxHeapify(0);return max;}// 向下调整堆private void maxHeapify(int i) {int left = leftChild(i);int right = rightChild(i);int largest = i;if (left < size && heap[left] > heap[largest]) {largest = left;}if (right < size && heap[right] > heap[largest]) {largest = right;}if (largest != i) {swap(i, largest);maxHeapify(largest);}}// 构建最大堆public void buildMaxHeap(int[] arr) {if (arr.length > capacity) {System.out.println("Array size exceeds heap capacity");return;}System.arraycopy(arr, 0, heap, 0, arr.length);size = arr.length;// 从最后一个非叶子节点开始调整for (int i = (size / 2) - 1; i >= 0; i--) {maxHeapify(i);}}// 堆排序(升序)public void heapSort(int[] arr) {buildMaxHeap(arr);for (int i = size - 1; i > 0; i--) {swap(0, i);size--;maxHeapify(0);}// 恢复原始大小size = arr.length;}// 打印堆public void printHeap() {System.out.print("Heap: ");for (int i = 0; i < size; i++) {System.out.print(heap[i] + " ");}System.out.println();}// 测试代码public static void main(String[] args) {MaxHeap maxHeap = new MaxHeap(10);// 插入元素maxHeap.insert(10);maxHeap.insert(5);maxHeap.insert(20);maxHeap.insert(15);maxHeap.insert(30);maxHeap.printHeap();// 提取最大值System.out.println("Extracted max: " + maxHeap.extractMax());maxHeap.printHeap();// 堆排序int[] arr = {12, 11, 13, 5, 6, 7};maxHeap.heapSort(arr);System.out.println("Sorted array: " + Arrays.toString(arr));}
}

5.2 小根堆(Min Heap)

import java.util.Arrays;public class MinHeap {private int[] heap;private int size;private int capacity;public MinHeap(int capacity) {this.capacity = capacity;this.size = 0;this.heap = new int[capacity];}// 获取父节点索引private int parent(int i) {return (i - 1) / 2;}// 获取左子节点索引private int leftChild(int i) {return 2 * i + 1;}// 获取右子节点索引private int rightChild(int i) {return 2 * i + 2;}// 交换元素private void swap(int i, int j) {int temp = heap[i];heap[i] = heap[j];heap[j] = temp;}// 插入元素public void insert(int value) {if (size >= capacity) {System.out.println("Heap is full");return;}// 将新元素添加到堆的末尾heap[size] = value;int current = size;size++;// 向上调整堆while (current > 0 && heap[current] < heap[parent(current)]) {swap(current, parent(current));current = parent(current);}}// 删除并返回最小元素public int extractMin() {if (size <= 0) {System.out.println("Heap is empty");return Integer.MAX_VALUE;}if (size == 1) {size--;return heap[0];}// 保存最小值int min = heap[0];// 将最后一个元素移到根节点heap[0] = heap[size - 1];size--;// 向下调整堆minHeapify(0);return min;}// 向下调整堆private void minHeapify(int i) {int left = leftChild(i);int right = rightChild(i);int smallest = i;if (left < size && heap[left] < heap[smallest]) {smallest = left;}if (right < size && heap[right] < heap[smallest]) {smallest = right;}if (smallest != i) {swap(i, smallest);minHeapify(smallest);}}// 构建最小堆public void buildMinHeap(int[] arr) {if (arr.length > capacity) {System.out.println("Array size exceeds heap capacity");return;}System.arraycopy(arr, 0, heap, 0, arr.length);size = arr.length;// 从最后一个非叶子节点开始调整for (int i = (size / 2) - 1; i >= 0; i--) {minHeapify(i);}}// 堆排序(降序)public void heapSort(int[] arr) {buildMinHeap(arr);for (int i = size - 1; i > 0; i--) {swap(0, i);size--;minHeapify(0);}// 恢复原始大小size = arr.length;}// 打印堆public void printHeap() {System.out.print("Heap: ");for (int i = 0; i < size; i++) {System.out.print(heap[i] + " ");}System.out.println();}// 测试代码public static void main(String[] args) {MinHeap minHeap = new MinHeap(10);// 插入元素minHeap.insert(10);minHeap.insert(5);minHeap.insert(20);minHeap.insert(15);minHeap.insert(30);minHeap.printHeap();// 提取最小值System.out.println("Extracted min: " + minHeap.extractMin());minHeap.printHeap();// 堆排序int[] arr = {12, 11, 13, 5, 6, 7};minHeap.heapSort(arr);System.out.println("Sorted array: " + Arrays.toString(arr));}
}

5.3 Java内置的PriorityQueue可以用作堆

import java.util.PriorityQueue;
import java.util.Collections;public class HeapWithPriorityQueue {public static void main(String[] args) {// 小根堆(默认)PriorityQueue<Integer> minHeap = new PriorityQueue<>();minHeap.add(10);minHeap.add(5);minHeap.add(20);System.out.println("Min heap: " + minHeap);System.out.println("Min element: " + minHeap.peek());System.out.println("Extracted min: " + minHeap.poll());System.out.println("Min heap after extraction: " + minHeap);// 大根堆(使用Comparator.reverseOrder())PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder());maxHeap.add(10);maxHeap.add(5);maxHeap.add(20);System.out.println("\nMax heap: " + maxHeap);System.out.println("Max element: " + maxHeap.peek());System.out.println("Extracted max: " + maxHeap.poll());System.out.println("Max heap after extraction: " + maxHeap);}
}

http://www.xdnf.cn/news/1377127.html

相关文章:

  • 【yocto】Yocto Project 核心:深入了解.inc文件
  • Java循环结构全解析
  • android 嵌套webview 全屏展示 页面延伸到状态栏且不被底部导航栏遮挡
  • 高并发内存池(11)-PageCache获取Span(下)
  • 【C++标准库】<ios>详解基于流的 I/O
  • 腾讯云 CVM 上的 SpringBoot 应用避免非法访问
  • 寄存器的原理
  • YOLOv8-SMOT:一种高效鲁棒的实时小目标跟踪框架:基于切片辅助训练与自适应关联
  • 人工智能-python-深度学习-反向传播优化算法
  • ESP32使用场景及大规模物联网IoT
  • 流水线用到的Dockerfile和构建脚本build.sh
  • 如何安装 mysql-installer-community-8.0.21.0.tar.gz(Linux 详细教程附安装包下载)​
  • 神经网络学习笔记11——高效卷积神经网络架构SqueezeNet
  • 聊一聊 单体分布式 和 微服务分布式
  • 深度学习——优化函数
  • 自学嵌入式第二十九天:Linux系统编程-线程
  • flume监控文件写入 Kafka 实战:解耦应用与消息队列的最佳实践
  • 在语言模型监督式微调(SFT)中的 负对数似然(Negative Log-Likelihood, NLL)等价于最大化似然
  • 软考-系统架构设计师 管理信息系统(MIS)详细讲解
  • 为什么编码智能体可以重塑开发范式?
  • 【Mascaret】QGIS中Mascaret插件的使用
  • ESP8266:Arduino学习
  • 高并发内存池(12)-ThreadCache回收内存
  • 【HTML】隐藏滚动条但保留功能
  • 什么是AI+?什么是人工智能+?
  • redis---set详解
  • ICCV 2025 | 清华IEDA提出GUAVA,单图创建可驱动的上半身3D化身!实时、高效,还能捕捉细腻的面部表情和手势。
  • 《MongoDB 常用命令详解:从数据库操作到高级查询》
  • Windows/Linux 环境下 Jmeter 性能测试的安装与使用
  • 未成功:使用 Nginx 搭建代理服务器(正向代理 HTTPS 网站)