Java 二叉树从入门到精通-遍历与递归详解

Java 二叉树从入门到精通-遍历与递归详解

Java 二叉树从入门到精通-遍历与递归详解

二叉树不难,就是"递归+队列"

一、什么是二叉树?

1.1 生活中的二叉树

想象一个家族族谱:

 爷爷 / \ 爸爸 叔叔 / \ / \ 我 弟弟 堂哥 堂妹 

这就是一棵二叉树:

  • 爷爷是根节点(最顶层)
  • 爸爸和叔叔是爷爷的子节点
  • 我和弟弟是爸爸的子节点
  • 每个节点最多有2个子节点(左孩子、右孩子)

1.2 二叉树的定义

二叉树: 每个节点最多有两个子节点的树形结构

LeetCode标准节点结构:

/** * Definition for a binary tree node. * 这是LeetCode所有二叉树题目使用的标准结构 */publicclassTreeNode{int val;// 节点存储的值TreeNode left;// 指向左子节点的引用TreeNode right;// 指向右子节点的引用// 构造函数1:只传入值,左右子节点默认为nullTreeNode(){}// 构造函数2:传入值并初始化TreeNode(int val){this.val = val;}// 构造函数3:传入值和左右子节点TreeNode(int val,TreeNode left,TreeNode right){this.val = val;this.left = left;this.right = right;}}

举例:构建一棵树

 1 / \ 2 3 / \ 4 5 

方式1:逐个创建节点

TreeNode root =newTreeNode(1); root.left =newTreeNode(2); root.right =newTreeNode(3); root.left.left =newTreeNode(4); root.left.right =newTreeNode(5);

方式2:使用完整构造函数

TreeNode root =newTreeNode(1,newTreeNode(2,newTreeNode(4),newTreeNode(5)),newTreeNode(3));

1.3 二叉树的基本概念

节点: 树中的每个元素
根节点: 最顶层的节点(如上图的1)
叶子节点: 没有子节点的节点(如上图的3、4、5)
深度: 从根节点到当前节点的层数(根节点深度为0)
高度: 从当前节点到叶子节点的最大层数

示例:

 1 ← 根节点,深度0,高度2 / \ 2 3 ← 深度1,高度1(节点2)和0(节点3) / \ 4 5 ← 叶子节点,深度2,高度0 

1.4 特殊的二叉树

满二叉树: 每层节点都是满的,所有叶子节点在同一层

 1 / \ 2 3 / \ / \ 4 5 6 7 

特点:节点数 = 2^h - 1(h是高度)

完全二叉树: 除了最后一层,其他层都是满的,且最后一层从左到右连续

 1 / \ 2 3 / \ 4 5 

特点:适合用数组存储,堆就是完全二叉树

二叉搜索树(BST): 左子树所有节点 < 根节点 < 右子树所有节点

 5 / \ 3 7 / \ / \ 1 4 6 9 

特点:中序遍历是升序,查找效率O(log n)

平衡二叉树(AVL树): 任意节点的左右子树高度差不超过1

 5 / \ 3 7 / / \ 1 6 9 

特点:保证树的高度平衡,查找/插入/删除都是O(log n)

平衡二叉搜索树: 同时满足BST和平衡树的性质

 5 / \ 3 7 / \ / \ 2 4 6 8 

特点:结合了BST的有序性和平衡树的高效性,常见实现有AVL树、红黑树

对比总结:

类型特点应用场景
满二叉树每层都满理论模型
完全二叉树最后一层从左到右连续堆、优先队列
二叉搜索树左<根<右查找、排序
平衡二叉树高度差≤1保证性能
平衡二叉搜索树BST+平衡数据库索引、TreeMap

二、二叉树的遍历方式

遍历就是"按某种顺序访问所有节点"。

2.1 两大类遍历方式

深度优先遍历(DFS): 先往深处走,走到底再回头

  • 前序遍历(根-左-右)
  • 中序遍历(左-根-右)
  • 后序遍历(左-右-根)

广度优先遍历(BFS): 一层一层地访问

  • 层序遍历(从上到下,从左到右)

2.2 遍历顺序对比

以这棵树为例:

 1 / \ 2 3 / \ 4 5 

前序遍历: 1 → 2 → 4 → 5 → 3(根-左-右)
中序遍历: 4 → 2 → 5 → 1 → 3(左-根-右)
后序遍历: 4 → 5 → 2 → 3 → 1(左-右-根)
层序遍历: 1 → 2 → 3 → 4 → 5(一层一层)


三、深度优先遍历(DFS)

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

顺序: 先访问根节点,再访问左子树,最后访问右子树

递归实现(详细注释版):

/** * 前序遍历:根-左-右 * @param root 当前节点 */publicvoidpreorder(TreeNode root){// 递归终止条件:节点为空,直接返回if(root ==null)return;// 1. 访问根节点(前序的"前"就是指这里)System.out.print(root.val +" ");// 2. 递归遍历左子树preorder(root.left);// 3. 递归遍历右子树preorder(root.right);}// 调用示例publicstaticvoidmain(String[] args){TreeNode root =newTreeNode(1,newTreeNode(2,newTreeNode(4),newTreeNode(5)),newTreeNode(3));System.out.print("前序遍历结果:");preorder(root);// 输出:1 2 4 5 3}

执行过程详解:

树: 1 / \ 2 3 / \ 4 5 执行流程(带调用栈): preorder(1) ├─ 输出1 ├─ preorder(2) │ ├─ 输出2 │ ├─ preorder(4) │ │ ├─ 输出4 │ │ ├─ preorder(null) → 返回 │ │ └─ preorder(null) → 返回 │ └─ preorder(5) │ ├─ 输出5 │ ├─ preorder(null) → 返回 │ └─ preorder(null) → 返回 └─ preorder(3) ├─ 输出3 ├─ preorder(null) → 返回 └─ preorder(null) → 返回 最终输出:1 2 4 5 3 

迭代实现(用栈):

publicList<Integer>preorderTraversal(TreeNode root){List<Integer> result =newArrayList<>();if(root ==null)return result;// 用栈模拟递归Stack<TreeNode> stack =newStack<>(); stack.push(root);while(!stack.isEmpty()){// 弹出栈顶节点并访问TreeNode node = stack.pop(); result.add(node.val);// 先压右子树,再压左子树(栈是后进先出,所以先压右边)if(node.right !=null) stack.push(node.right);if(node.left !=null) stack.push(node.left);}return result;}

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

顺序: 先访问左子树,再访问根节点,最后访问右子树

递归实现(详细注释版):

/** * 中序遍历:左-根-右 * @param root 当前节点 */publicvoidinorder(TreeNode root){// 递归终止条件if(root ==null)return;// 1. 先递归遍历左子树inorder(root.left);// 2. 访问根节点(中序的"中"就是指这里,在中间访问)System.out.print(root.val +" ");// 3. 最后递归遍历右子树inorder(root.right);}// 调用示例publicstaticvoidmain(String[] args){TreeNode root =newTreeNode(1,newTreeNode(2,newTreeNode(4),newTreeNode(5)),newTreeNode(3));System.out.print("中序遍历结果:");inorder(root);// 输出:4 2 5 1 3}

执行过程详解:

树: 1 / \ 2 3 / \ 4 5 执行流程(带调用栈): inorder(1) ├─ inorder(2) │ ├─ inorder(4) │ │ ├─ inorder(null) → 返回 │ │ ├─ 输出4 │ │ └─ inorder(null) → 返回 │ ├─ 输出2 │ └─ inorder(5) │ ├─ inorder(null) → 返回 │ ├─ 输出5 │ └─ inorder(null) → 返回 ├─ 输出1 └─ inorder(3) ├─ inorder(null) → 返回 ├─ 输出3 └─ inorder(null) → 返回 最终输出:4 2 5 1 3 

迭代实现(用栈):

publicList<Integer>inorderTraversal(TreeNode root){List<Integer> result =newArrayList<>();Stack<TreeNode> stack =newStack<>();TreeNode curr = root;while(curr !=null||!stack.isEmpty()){// 一直往左走,把所有左节点压栈while(curr !=null){ stack.push(curr); curr = curr.left;}// 弹出栈顶节点,访问 curr = stack.pop(); result.add(curr.val);// 转向右子树 curr = curr.right;}return result;}

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

顺序: 先访问左子树,再访问右子树,最后访问根节点

递归实现(详细注释版):

/** * 后序遍历:左-右-根 * @param root 当前节点 */publicvoidpostorder(TreeNode root){// 递归终止条件if(root ==null)return;// 1. 先递归遍历左子树postorder(root.left);// 2. 再递归遍历右子树postorder(root.right);// 3. 最后访问根节点(后序的"后"就是指这里,最后访问)System.out.print(root.val +" ");}// 调用示例publicstaticvoidmain(String[] args){TreeNode root =newTreeNode(1,newTreeNode(2,newTreeNode(4),newTreeNode(5)),newTreeNode(3));System.out.print("后序遍历结果:");postorder(root);// 输出:4 5 2 3 1}

执行过程详解:

树: 1 / \ 2 3 / \ 4 5 执行流程(带调用栈): postorder(1) ├─ postorder(2) │ ├─ postorder(4) │ │ ├─ postorder(null) → 返回 │ │ ├─ postorder(null) → 返回 │ │ └─ 输出4 │ ├─ postorder(5) │ │ ├─ postorder(null) → 返回 │ │ ├─ postorder(null) → 返回 │ │ └─ 输出5 │ └─ 输出2 ├─ postorder(3) │ ├─ postorder(null) → 返回 │ ├─ postorder(null) → 返回 │ └─ 输出3 └─ 输出1 最终输出:4 5 2 3 1 

迭代实现(用栈):

publicList<Integer>postorderTraversal(TreeNode root){List<Integer> result =newArrayList<>();if(root ==null)return result;Stack<TreeNode> stack =newStack<>(); stack.push(root);while(!stack.isEmpty()){TreeNode node = stack.pop(); result.add(0, node.val);// 插入到结果开头// 先压左子树,再压右子树if(node.left !=null) stack.push(node.left);if(node.right !=null) stack.push(node.right);}return result;}

3.4 三种遍历的对比

遍历方式顺序输出位置应用场景
前序遍历根-左-右进入节点时输出复制树、序列化
中序遍历左-根-右左子树遍历完输出BST排序输出
后序遍历左-右-根左右子树都遍历完输出删除树、计算高度

记忆技巧:

  • 前序:根节点在前面(先输出根)
  • 中序:根节点在中间(左子树输出完再输出根)
  • 后序:根节点在后面(左右子树都输出完再输出根)

四、广度优先遍历(BFS)

4.1 层序遍历

顺序: 从上到下,从左到右,一层一层访问

示例:

 1 / \ 2 3 / \ 4 5 层序遍历:1 → 2 → 3 → 4 → 5 

实现(用队列):

publicList<Integer>levelOrder(TreeNode root){List<Integer> result =newArrayList<>();if(root ==null)return result;// 用队列实现层序遍历Queue<TreeNode> queue =newLinkedList<>(); queue.offer(root);while(!queue.isEmpty()){// 弹出队首节点并访问TreeNode node = queue.poll(); result.add(node.val);// 先加左子节点,再加右子节点(队列是先进先出)if(node.left !=null) queue.offer(node.left);if(node.right !=null) queue.offer(node.right);}return result;}

执行过程:

初始:queue = [1] 第1轮: 弹出1,输出1 加入2和3 queue = [2, 3] 第2轮: 弹出2,输出2 加入4和5 queue = [3, 4, 5] 第3轮: 弹出3,输出3 queue = [4, 5] 第4轮: 弹出4,输出4 queue = [5] 第5轮: 弹出5,输出5 queue = [] 结果:1 2 3 4 5 

4.2 分层输出(重要)

需求: 输出每一层的节点

示例:

 1 / \ 2 3 / \ 4 5 输出: [[1], [2,3], [4,5]] 

实现:

publicList<List<Integer>>levelOrder(TreeNode root){List<List<Integer>> result =newArrayList<>();if(root ==null)return result;Queue<TreeNode> queue =newLinkedList<>(); queue.offer(root);while(!queue.isEmpty()){int size = queue.size();// 当前层的节点数(关键!)List<Integer> level =newArrayList<>();// 遍历当前层的所有节点for(int i =0; i < size; i++){TreeNode node = queue.poll(); level.add(node.val);if(node.left !=null) queue.offer(node.left);if(node.right !=null) queue.offer(node.right);} result.add(level);}return result;}

关键:int size = queue.size() 记录当前层的节点数


4.3 DFS vs BFS

对比项DFS(深度优先)BFS(广度优先)
数据结构栈(或递归)队列
遍历方式先往深处走一层一层走
空间复杂度O(h),h是树高O(w),w是最大宽度
应用场景路径问题、树的高度最短路径、层级问题

记忆技巧:

  • DFS = 深(Deep)= 栈(Stack)= 递归
  • BFS = 宽(Broad)= 队列(Queue)= 层序

五、二叉树的经典问题

5.1 求二叉树的最大深度(LeetCode 104)

题目: 给定一个二叉树,找出其最大深度。

示例:

 3 / \ 9 20 / \ 15 7 最大深度:3 

思路: 树的深度 = max(左子树深度, 右子树深度) + 1

递归实现:

publicintmaxDepth(TreeNode root){// 空节点深度为0if(root ==null)return0;// 递归计算左右子树深度int leftDepth =maxDepth(root.left);int rightDepth =maxDepth(root.right);// 当前节点深度 = 左右子树最大深度 + 1returnMath.max(leftDepth, rightDepth)+1;}

5.2 翻转二叉树(LeetCode 226)

题目: 翻转一棵二叉树。

递归实现:

publicTreeNodeinvertTree(TreeNode root){if(root ==null)returnnull;// 交换左右子树TreeNode temp = root.left; root.left = root.right; root.right = temp;// 递归翻转左右子树invertTree(root.left);invertTree(root.right);return root;}

5.3 对称二叉树(LeetCode 101)

题目: 判断一棵二叉树是否对称。

递归实现:

publicbooleanisSymmetric(TreeNode root){if(root ==null)returntrue;returnisMirror(root.left, root.right);}privatebooleanisMirror(TreeNode left,TreeNode right){if(left ==null&& right ==null)returntrue;if(left ==null|| right ==null)returnfalse;if(left.val != right.val)returnfalse;returnisMirror(left.left, right.right)&&isMirror(left.right, right.left);}

5.4 二叉树的最近公共祖先(LeetCode 236)

递归实现:

publicTreeNodelowestCommonAncestor(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;}

5.5 二叉树的右视图(LeetCode 199)

BFS实现:

publicList<Integer>rightSideView(TreeNode root){List<Integer> result =newArrayList<>();if(root ==null)return result;Queue<TreeNode> queue =newLinkedList<>(); queue.offer(root);while(!queue.isEmpty()){int size = queue.size();for(int i =0; i < size; i++){TreeNode node = queue.poll();// 每层的最后一个节点if(i == size -1){ result.add(node.val);}if(node.left !=null) queue.offer(node.left);if(node.right !=null) queue.offer(node.right);}}return result;}

5.6 路径总和(LeetCode 112)

递归实现:

publicbooleanhasPathSum(TreeNode root,int targetSum){if(root ==null)returnfalse;// 叶子节点,判断是否等于目标和if(root.left ==null&& root.right ==null){return root.val == targetSum;}// 递归左右子树,目标和减去当前节点值returnhasPathSum(root.left, targetSum - root.val)||hasPathSum(root.right, targetSum - root.val);}

六、二叉搜索树(BST)

6.1 什么是二叉搜索树?

定义: 左子树所有节点 < 根节点 < 右子树所有节点

示例:

 5 / \ 3 7 / \ / \ 1 4 6 9 

特点: 中序遍历BST,得到的是升序序列

中序遍历: 1 → 3 → 4 → 5 → 6 → 7 → 9(升序)


6.2 验证二叉搜索树(LeetCode 98)

递归实现:

publicbooleanisValidBST(TreeNode root){returnisValid(root,null,null);}privatebooleanisValid(TreeNode root,Integer min,Integer max){if(root ==null)returntrue;// 当前节点值必须在(min, max)范围内if(min !=null&& root.val <= min)returnfalse;if(max !=null&& root.val >= max)returnfalse;// 递归左子树:最大值是当前节点值// 递归右子树:最小值是当前节点值returnisValid(root.left, min, root.val)&&isValid(root.right, root.val, max);}

6.3 BST中的搜索(LeetCode 700)

递归实现:

publicTreeNodesearchBST(TreeNode root,int val){if(root ==null|| root.val == val){return root;}if(val < root.val){returnsearchBST(root.left, val);}else{returnsearchBST(root.right, val);}}

6.4 BST中的插入(LeetCode 701)

递归实现:

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

七、二叉树的构建

7.1 从前序和中序构建二叉树(LeetCode 105)

递归实现:

publicTreeNodebuildTree(int[] preorder,int[] inorder){returnbuild(preorder,0, preorder.length -1, inorder,0, inorder.length -1);}privateTreeNodebuild(int[] preorder,int preStart,int preEnd,int[] inorder,int inStart,int inEnd){if(preStart > preEnd)returnnull;// 前序遍历的第一个元素是根节点int rootVal = preorder[preStart];TreeNode root =newTreeNode(rootVal);// 在中序遍历中找到根节点的位置int index = inStart;for(int i = inStart; i <= inEnd; i++){if(inorder[i]== rootVal){ index = i;break;}}// 左子树的节点数int leftSize = index - inStart;// 递归构建左右子树 root.left =build(preorder, preStart +1, preStart + leftSize, inorder, inStart, index -1); root.right =build(preorder, preStart + leftSize +1, preEnd, inorder, index +1, inEnd);return root;}

优化:用HashMap加速查找

// 优化版本:用HashMap存储中序遍历的值和索引,避免每次都遍历查找privateMap<Integer,Integer> inorderMap;publicTreeNodebuildTree(int[] preorder,int[] inorder){// 预处理:把中序遍历的值和索引存入HashMap inorderMap =newHashMap<>();for(int i =0; i < inorder.length; i++){ inorderMap.put(inorder[i], i);}returnbuild(preorder,0, preorder.length -1, inorder,0, inorder.length -1);}privateTreeNodebuild(int[] preorder,int preStart,int preEnd,int[] inorder,int inStart,int inEnd){if(preStart > preEnd)returnnull;int rootVal = preorder[preStart];TreeNode root =newTreeNode(rootVal);// O(1)时间查找根节点在中序遍历中的位置int index = inorderMap.get(rootVal);int leftSize = index - inStart; root.left =build(preorder, preStart +1, preStart + leftSize, inorder, inStart, index -1); root.right =build(preorder, preStart + leftSize +1, preEnd, inorder, index +1, inEnd);return root;}

时间复杂度: 从O(n²)优化到O(n)


7.2 从中序和后序构建二叉树(LeetCode 106)

递归实现:

publicTreeNodebuildTree(int[] inorder,int[] postorder){returnbuild(inorder,0, inorder.length -1, postorder,0, postorder.length -1);}privateTreeNodebuild(int[] inorder,int inStart,int inEnd,int[] postorder,int postStart,int postEnd){if(inStart > inEnd)returnnull;// 后序遍历的最后一个元素是根节点int rootVal = postorder[postEnd];TreeNode root =newTreeNode(rootVal);// 在中序遍历中找到根节点的位置int index = inStart;for(int i = inStart; i <= inEnd; i++){if(inorder[i]== rootVal){ index = i;break;}}int leftSize = index - inStart; root.left =build(inorder, inStart, index -1, postorder, postStart, postStart + leftSize -1); root.right =build(inorder, index +1, inEnd, postorder, postStart + leftSize, postEnd -1);return root;}

八、常见错误和避坑指南

错误1:递归没有终止条件

// 错误:没有判断root == nullpublicvoidpreorder(TreeNode root){System.out.print(root.val +" ");// 空指针异常!preorder(root.left);preorder(root.right);}// 正确:先判断publicvoidpreorder(TreeNode root){if(root ==null)return;System.out.print(root.val +" ");preorder(root.left);preorder(root.right);}

错误2:混淆DFS和BFS的数据结构

// 错误:BFS用栈Stack<TreeNode> stack =newStack<>();// 应该用队列!// 正确:BFS用队列Queue<TreeNode> queue =newLinkedList<>();

错误3:层序遍历没有记录层数

// 错误:无法区分每一层while(!queue.isEmpty()){TreeNode node = queue.poll();// 无法知道当前是第几层}// 正确:记录每层的节点数while(!queue.isEmpty()){int size = queue.size();// 当前层的节点数for(int i =0; i < size; i++){TreeNode node = queue.poll();// ...}}

错误4:判断叶子节点条件错误

// 错误:只判断左子节点if(root.left ==null){// 这不是叶子节点,可能还有右子节点}// 正确:左右子节点都为空if(root.left ==null&& root.right ==null){// 这才是叶子节点}

错误5:BST验证只判断左右子节点

// 错误:只判断直接子节点if(root.left.val < root.val && root.right.val > root.val){// 不够,要判断整个左右子树}// 正确:判断整个子树的范围isValid(root.left, min, root.val);isValid(root.right, root.val, max);

九、同类题推荐

掌握本文内容后,可以刷这些题:

题号题目难度核心思路
144二叉树的前序遍历简单DFS递归/迭代
94二叉树的中序遍历简单DFS递归/迭代
145二叉树的后序遍历简单DFS递归/迭代
102二叉树的层序遍历中等BFS队列
104二叉树的最大深度简单递归/BFS
226翻转二叉树简单递归交换
101对称二叉树简单递归判断
236二叉树的最近公共祖先中等递归查找
199二叉树的右视图中等BFS/DFS
112路径总和简单递归
98验证二叉搜索树中等递归/中序遍历
700二叉搜索树中的搜索简单递归/迭代
701二叉搜索树中的插入中等递归
105从前序与中序构建二叉树中等递归构建
106从中序与后序构建二叉树中等递归构建

十、总结

二叉树的核心就是递归。 理解了递归,二叉树就不难了。

两大遍历方式:

  • DFS(深度优先):前序、中序、后序,用递归或栈
  • BFS(广度优先):层序遍历,用队列

三种DFS遍历:

  • 前序:根-左-右(先输出根)
  • 中序:左-根-右(左子树输出完再输出根)
  • 后序:左-右-根(左右子树都输出完再输出根)

BST特性:

  • 左子树 < 根节点 < 右子树
  • 中序遍历是升序

学习建议:

  1. 先理解递归,画图模拟执行过程
  2. 手写三种DFS遍历的递归代码
  3. 理解BFS的队列实现
  4. 刷简单题巩固(前中后序、层序、最大深度)
  5. 再刷中等题(翻转、对称、公共祖先、构建)

作者:[识君啊]

不要做API的搬运工,要做原理的探索者!

Read more

爬虫技术分享

网络爬虫技术分享 作者:技术分享 日期:2026年3月 适用语言:Python / Java 一、什么是网络爬虫? 网络爬虫(Web Crawler),又称网络蜘蛛或网络机器人,是一种按照一定规则自动抓取网页信息的自动化程序。其本质是模拟人类浏览器访问网页的行为,通过发送 HTTP 请求获取页面内容,再从中提取有价值的结构化数据并加以存储,最终服务于数据分析、业务监控、信息聚合等场景。 爬虫的工作流程可以简单概括为四个核心步骤: 获取网页 → 提取信息 → 保存数据 → 自动化调度 这四个步骤形成了一个完整的数据采集闭环。理解这个闭环,是学习爬虫技术的基础。 为什么要学爬虫? 在数据驱动的时代,数据就是生产力。无论是金融分析、市场调研还是风控建模,高质量、及时准确的数据都是前提。爬虫技术提供了一种低成本、高效率的数据获取手段,在银行、证券、保险等金融机构中有着广泛的实际应用价值。 二、爬虫在银行相关技术中的应用 爬虫技术在银行及金融领域的应用远比想象中广泛,以下是几个典型场景: 1. 舆情监控与品牌风控 银行需要实时关注网络上与自身品牌、

By Ne0inhk
MySQL的下载与安装步骤

MySQL的下载与安装步骤

一、写在前面         MySQL是最流行的关系型数据库管理系统之一,属于Oracle旗下产品。MySQL软件采用了双授权政策,分为社区版和商业版,由于其体积小、速度快、总体拥有成本低,并且开放源码,一般中小型和大型网站的开发都选择 MySQL作为网站数据库。         本篇博客将向大家介绍MySQL数据库的安装步骤,包括下载、安装和配置等,希望能对大家有所帮助(文章内容较多,但步骤非常详细,需要可酌情跳过)。 二、MySQL的下载         MySQL官方提供了两种不同的MySQL版本:社区版本(MySQL Community Server)和商业版本(MySQL Enterprise Edition),我们一般选择社区版本进行下载和使用。 1.官网下载         打开MySQL官网下载页面,下滑找到社区版下载入口:         点击进入Windows版本下载:         我们可以选择需要的MySQL版本以及所需的操作系统,这里选择离线安装: 注意:MySQL 8.0 是带有 MySQL Installer 的最后一个系列。从

By Ne0inhk
Flutter 组件 sw 的适配 鸿蒙Harmony 实战 - 驾驭高性能微服务路由架构、实现鸿蒙端 HTTP 流量语义分发与逻辑守卫方案

Flutter 组件 sw 的适配 鸿蒙Harmony 实战 - 驾驭高性能微服务路由架构、实现鸿蒙端 HTTP 流量语义分发与逻辑守卫方案

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 sw 的适配 鸿蒙Harmony 实战 - 驾驭高性能微服务路由架构、实现鸿蒙端 HTTP 流量语义分发与逻辑守卫方案 前言 在鸿蒙(OpenHarmony)生态的分布式业务网关、多端协同数据中转站以及需要实现极端细粒度接口管控的各种后端闭环应用开发中,“请求路由的执行效率与逻辑灵活性”是决定系统能否支撑起高并发访问请求的命门所在。面对包含上百个动态参数的 RESTful API 契约、需要针对鸿蒙手机、自研设备等不同终端执行差异化鉴权的复杂路由逻辑。如果仅仅依靠原始的 if-else 显式判定或性能低下的线性字符串匹配。不仅会导致路由分发的延迟随着接口数量增加而呈指数级上升,更会因为缺乏一套工业级的“语义化(Semantic)”路由映射规范。引发严重的服务逻辑归属混乱与权限越界风险。 我们需要一种“语义分发、匹配自洽”的路由艺术。 sw(在 Shelf 生态中常指高效的 Switch/Router 增强件)是一套专注于实现极致性能与

By Ne0inhk
一卡通核心交易平台的国产数据库实践解析:架构、迁移与高可用落地

一卡通核心交易平台的国产数据库实践解析:架构、迁移与高可用落地

文章目录 * 摘要 * 1. 业务与技术挑战拆解 * 2. 总体架构(从数据库边界看) * 3. 数据模型:以“不可变流水”为中心 * 3.1 流水表(交易事实表)建议 * 3.2 账户与余额:把“强一致”收敛到最小 * 4. 高可用与容灾:把“不可用窗口”工程化 * 4.1 同城高可用:主备切换与防脑裂 * 4.2 异地灾备:以“可恢复”为目标设计链路 * 5. 性能与稳定性:把瓶颈消灭在“写路径” * 5.1 连接治理:让资源可控 * 5.2 SQL治理:少做无谓计算

By Ne0inhk