算法记录——树
二叉树
3.1二叉树的最大深度
思路:二叉树的最大深度 = 根节点的最大高度。因此本题可以转换为求二叉树的最大高度。
而求高度的时候应该采用后序遍历。遍历顺序为:左右中。每次遍历的节点按后序遍历顺序,先收集左右孩子的最大高度,再最后处理当前节点的最大高度!因此用后序遍历。
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
class Solution {public int maxDepth(TreeNode root) {//注意:这题虽然求的是最大的深度,但我们可以转换思路。求树的最大深度 = 根节点的最大高度!if(root == null) return 0;//当前节点左孩子的高度int leftHeight = maxDepth(root.left);//当前节点右孩子的高度int rightHeight = maxDepth(root.right);return Math.max(leftHeight,rightHeight) + 1;//当前节点的最大高度就是左右孩子中更高的那个+1}
}
3.2 搜索二叉树的判断
思路:
首先我们知道二叉搜索树的性质:任何一个节点的左子树的所有节点的值都小于该节点的值,右子树的所有节点的值都大于该节点的值。
由这个性质我们可以知道,对于一个二叉搜索树,中序遍历这个树,得到的结构一定是升序的!
方法一:利用额外的集合,先中序遍历整个树,把每个值取到。再判断集合中是否为升序排序。
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
class Solution {public static boolean isValidBST(TreeNode root) {if(root.left == null && root.right == null) return true;ArrayList<Integer> arr = new ArrayList();//用于存放每个节点值的集合f(root,arr);for (int i = 0; i < arr.size() - 1; i++) {if (arr.get(i + 1) <= arr.get(i)) {return false;}}return true;}public static void f(TreeNode root, ArrayList arr) {//中序遍历if (root == null) return;f(root.left,arr);arr.add(root.val);f(root.right,arr);}
}
方法二:定义一个变量,用于保存每次要比较值的上一个值的大小。
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
class Solution {public static boolean isValidBST(TreeNode root) {if (root.left == null && root.right == null) return true;Stack<TreeNode> stack = new Stack<>();int preValue = Integer.MIN_VALUE;while (!stack.isEmpty() || root != null) {if (root != null) {//先一路把当前节点的左孩子全部遍历进栈stack.push(root);root = root.left;} else {//总有一个时刻root跑到null,说明当前点没有左孩子root = stack.pop();//root赋值为最后一个进栈的没有左孩子的节点//这里刚遍历完当前节点的左孩子,如果在这里打印就是中序遍历//System.out.print(root.val);//所以我们在这里每次比较当前节点,和前一个要比较节点的大小,就相当于中序遍历if (root.val > preValue || root.val == Integer.MIN_VALUE) {//说明当前节点满足搜索二叉树的性质preValue = root.val;} else {//否则不满足搜索二叉树,直接返回falsereturn false;}root = root.right;//遍历当前节点的右孩子}}return true;}
}
3.3 判断完全二叉树
先说下性质:
满二叉树:在一颗二叉树中,如果每个结点都存在左子树和右子树,并且所有叶节点都在同一层上,这样的树为满二叉树。
完全二叉树:相同深度的满二叉树的所有结点(不包含叶子)在该树上都有相应的节点(包含叶子)与之对应且所有左子树先存在,才会存在右子树,然后才会存在下层子树的情况,这样的树为完全二叉树 。
可根据下图区分:
思路:层序遍历,根据完全二叉树的性质。
1.当有节点存在有右孩子没左孩子的时候,直接返回false
2.当遍历到第一个叶子节点时,要确保接下来每一个节点都是叶子节点!
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
class Solution {public boolean isCompleteTree(TreeNode root) {if (root == null) return true;//创建一个队列用来做层序遍历LinkedList<TreeNode> queue = new LinkedList<>();queue.add(root);TreeNode l = null;//代表当前节点的左孩子TreeNode r = null;//代表当前节点的右孩子Boolean leaf = false;//一个开关,代表当前有没有遍历到叶子节点while (!queue.isEmpty()) {root = queue.poll();l = root.left;r = root.right;if ((leaf && (l != null || r != null))//前面已经存在叶子节点了,但当前节点不是叶子节点||(l == null && r != null)//有右无左直接返回false) return false;if (l == null || r == null) leaf = true;//如果当前节点是叶子节点if (l != null) queue.add(l);if (r != null) queue.add(r);}return true;}
}
3.4判断平衡二叉树
思路:
根据平衡二叉树的性质,判断当前节点下的树是不是平衡二叉树,只要做到一下几点判断:
1.左孩子要是平衡二叉树
2.右孩子要是平衡二叉树
3.左右孩子的高度差小于等于1
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
class Solution {public boolean isBalanced(TreeNode root) {if (root == null) return true;boolean leftBalanced = isBalanced(root.left);//判断当前节点左子树是不是平衡二叉树boolean rightBalanced = isBalanced(root.right);//判断当前节点右子树是不是平衡二叉树int leftHeight = getHeight(root.left);//获取左子树高度int rightHeight = getHeight(root.right);//获取右子树高度//只有当左右子树都为平衡二叉树且左右子树高度差<=1时,当前点才是平衡二叉树return leftBalanced && rightBalanced && (Math.abs(leftHeight - rightHeight) <= 1);}public static int getHeight(TreeNode root) {//获取当前节点的高度if (root == null) return 0;int leftHeight = getHeight(root.left);//获取当前节点左孩子的高度int rightHeight = getHeight(root.right);//获取当前节点右孩子的高度return Math.max(leftHeight, rightHeight) + 1;//当前点的高度 = 左右孩子中更高的高度+1}
}
3.5找二叉树中两个节点的最近公共祖先
方法一:比较麻烦,空间复杂度较高,但比较好理解。
思路:1.创建一个map集合,先遍历所有节点,把每个节点的父节点存放在当前集合中。
map<当前节点,当前节点的父节点>
2.创建一个set集合,遍历当前节点1的所有祖先节点,并全部放入set集合中。
3.遍历节点2的所有祖先节点。每次遍历判断set集合中有没有当前节点,如果有,当前节点就是二者的共同祖先。由于都是从下网上遍历,所以第一个共同祖先就是最近共同祖先!
注意:这里方法一只提供一种思路,但空间复杂度和时间复杂度都较高,不推荐。
方法一代码:
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode(int x) { val = x; }* }*/
class Solution {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {HashMap<TreeNode, TreeNode> map = new HashMap<>();map.put(root, root);//根节点的父节点就是自己f(root, map);HashSet<TreeNode> set = new HashSet<>();set.add(p);TreeNode cur = p;while (cur != root) {//从p网上遍历其所有的祖先,把p每一个祖先都存放在set集合中set.add(map.get(cur));cur = map.get(cur);//当前节点赋值为其父节点}set.add(root);//根节点单独放入set集合cur = q;while (cur != root) {//遍历q的所有祖先,把q每个祖先都和p的祖先比较,当出现第一个相同节点,就是二者最近共同的祖先if (set.contains(cur)) {return cur;}cur = map.get(cur);//当前节点赋值为其父节点}return root;}/*** 遍历树,把每个节点的父节点放入map集合中** @param root 当前节点* @param map 存放节点关系的集合*/public void f(TreeNode root, Map map) {if (root == null) {return;}map.put(root.left, root);map.put(root.right, root);f(root.left, map);f(root.right, map);}
}
方法二:
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode(int x) { val = x; }* }*/
class Solution {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {//对于这个方法。如果某个子树下p、q都没有,它一定返回的就是空!//遇到空就返回空,遇到p或q就返回p或qif(root == null || root == p || root == q) return root;TreeNode l = lowestCommonAncestor(root.left,p,q);//当前节点左子树的公共祖先TreeNode r = lowestCommonAncestor(root.right,p,q);//当前节点右子树的公共祖先if(l != null && r != null) return root;//如果当前节点的左右子树都有p或q。当前节点就是公共祖先return l == null ? r : l;//如果左孩子为空就返回右孩子,如果右孩子也是空,那就也返回空!}
}
两个节点的分布无非就两种情况:
1.o1、o2中某一个是另一个的祖先。
2.o1、o2两个点分布在某一个公共祖先的两边。
情况一的图:
return l == null ? r : l;
对于这种情况,A往左遍历,遍历到o1直接,就返回o1了。往右遍历,返回null。整体返回如果左不为空,就返回左,反之返回右。如果左右都为空,这返回右也就是返回空!
情况二的图:
if(l != null && r != null) return root;
对于节点B。就是这种情况,左右两边返回值都不为空,返回的就是当前节点B。而对于B上面的节点,另外一边没有o1或o2,返回的一定是空。因此对于B和null,上面节点往上返回的还是B!
3.6前缀树
class Trie {TrieNode root;public Trie() {root = new TrieNode();}class TrieNode{int pass;int end;HashMap<Character,TrieNode> nexts;public TrieNode(){pass = 0;end = 0;nexts = new HashMap<Character,TrieNode>();}}public void insert(String word) {if(word == null) return;char[] chars = word.toCharArray();root.pass++;TrieNode node = root;for(int i = 0; i < chars.length; i++){if(node.nexts.get(chars[i]) == null){//当前节点第一次被加入node.nexts.put(chars[i],new TrieNode());}node = node.nexts.get(chars[i]);node.pass++;}node.end++;}public boolean search(String word) {if(word == null) return true;char[] chars = word.toCharArray();TrieNode node = root;for(int i = 0; i < chars.length; i++){if(node.nexts.get(chars[i]) == null){//当前节点第一次被加入return false;}node = node.nexts.get(chars[i]);}if(node.end > 0) return true;return false;}public boolean startsWith(String prefix) {if(prefix == null) return true;char[] chars = prefix.toCharArray();TrieNode node = root;for(int i = 0; i < chars.length; i++){if(node.nexts.get(chars[i]) == null){//当前节点第一次被加入return false;}node = node.nexts.get(chars[i]);}return true;}
}/*** Your Trie object will be instantiated and called as such:* Trie obj = new Trie();* obj.insert(word);* boolean param_2 = obj.search(word);* boolean param_3 = obj.startsWith(prefix);*/
3.7.树中任意两个点之间的最大距离
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
class Solution {public int diameterOfBinaryTree(TreeNode root) {ResInfo process = process(root);return process.max - 1;}public ResInfo process(TreeNode root) {if (root == null) return new ResInfo(0, 0);//左右递归获取结果ResInfo left = process(root.left);ResInfo right = process(root.right);int t = left.height + right.height + 1;//当前点的最大距离为://如果当前根节点不参与:1.左孩子中最大距离 2.右孩子中最大距离//当前点参与:3.左孩子最大高度 + 右孩子最大高度 + 1//从以上三种情况中求最大值,就是以当前点为根的任意两点之间的最大距离maxint max = Math.max(Math.max(left.max, right.max), t);//求当前点的最大高度:左右高度更高的 + 1int height = Math.max(left.height, right.height) + 1;return new ResInfo(max, height);}class ResInfo {int max;//以当前点为根的任意两点之间的最大距离int height;//当前点的最大高度public ResInfo() {}public ResInfo(int max, int height) {this.max = max;this.height = height;}}}
3.8.节点与其子树之间的最大差值
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
class Solution {public int maxAncestorDiff(TreeNode root) {resType process = process(root);return process.res;}public class resType {int max;//其子树中的最小值int min;//其子树中的最大值int res;//最大差值public resType() {}public resType(int max, int min, int res) {this.max = max;this.min = min;this.res = res;}}public resType process(TreeNode root) {if (root == null) return new resType(Integer.MIN_VALUE, Integer.MAX_VALUE, 0);if (root.left == null && root.right == null) {//如果遍历到叶子节点return new resType(root.val, root.val, 0);}//向左右子树要信息resType leftRes = process(root.left);resType rightRes = process(root.right);int max = Math.max(leftRes.max, Math.max(rightRes.max, root.val));//找到左右子树中最大的值int min = Math.min(leftRes.min, Math.min(rightRes.min, root.val));//找到左右子树中最小的值//最大差值由可能由三部分组成//左子树中的最大差值、右子树的最大差值//以及,当前点与左右子树最大最小值绝对值之差int res = Math.max(Math.abs(root.val - max), Math.abs(root.val - min));res = Math.max(res, Math.max(leftRes.res, rightRes.res));return new resType(max, min, res);}
}
3.9 二叉树的层平均值
这里注意的一点就是:每次循环队列长度就是该层的元素个数,这点需要注意一下。
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
class Solution {public List<Double> averageOfLevels(TreeNode root) {LinkedList<TreeNode> queue = new LinkedList<>();ArrayList<Double> res = new ArrayList<>();queue.add(root);while (!queue.isEmpty()) {//代表这一层元素的个数int size = queue.size();long sum = 0;//遍历这一层for (int i = 0; i < size; i++) {TreeNode node = queue.poll();sum += node.val;if (node.left != null) queue.add(node.left);if (node.right != null) queue.add(node.right);}res.add( ((double)sum / size));}return res;}
}
3.10.二叉树展开为链表
思路:
- 将左子树插入到右子树的地方
- 将原来的右子树接到左子树的最右边节点
- 考虑新的右子树的根节点,一直重复上边的过程,直到新的右子树为 null
1/ \2 5/ \ \
3 4 6//将 1 的左子树插入到右子树的地方1\2 5/ \ \3 4 6
//将原来的右子树接到左子树的最右边节点1\2 / \ 3 4 \5\6//将 2 的左子树插入到右子树的地方1\2 \ 3 4 \5\6 //将原来的右子树接到左子树的最右边节点1\2 \ 3 \4 \5\6 ......
public void flatten(TreeNode root) {while (root != null) { //左子树为 null,直接考虑下一个节点if (root.left == null) {root = root.right;} else {// 找左子树最右边的节点TreeNode pre = root.left;while (pre.right != null) {pre = pre.right;} //将原来的右子树接到左子树的最右边节点pre.right = root.right;// 将左子树插入到右子树的地方root.right = root.left;root.left = null;// 考虑下一个节点root = root.right;}}
}
相关文章:

算法记录——树
二叉树 3.1二叉树的最大深度 思路:二叉树的最大深度 根节点的最大高度。因此本题可以转换为求二叉树的最大高度。 而求高度的时候应该采用后序遍历。遍历顺序为:左右中。每次遍历的节点按后序遍历顺序,先收集左右孩子的最大高度,…...
单片机在控制和自动化任务中的应用场景广泛
单片机在控制和自动化任务中的应用场景广泛,以下是一些具体示例: 1. 家电控制 洗衣机:单片机用于控制洗衣周期、温度和水位。微波炉:控制加热时间、功率和用户界面。 2. 工业自动化 生产线监控:单片机用于控制传送…...

UEFI EDK2框架学习(三)——protocol
一、Protocol协议 搜索支持特定Protocol的设备,获取其Handle gBS->LocateHandleBuffer 将内存中的Driver绑定到给定的ControllerHandle gBS->OpenProtocol 二、代码实现 Protocol.c #include <Uefi.h> #include <Library/UefiLib.h> #includ…...

PostgreSQL的字段存储类型了解
PostgreSQL的字段存储类型了解 在 PostgreSQL 中,每个字段(列)都有其存储类型,这些存储类型决定了数据库如何存储和处理该字段的数据。了解和适当地利用这些存储类型,可以提高数据库的性能和存储效率。 主要的存储类…...

CTFshow 命令执行 web29~web36(正则匹配绕过)
目录 web29 方法一:include伪协议包含文件读取 方法二:写入文件 方法三:通识符 web30 方法一:filter伪协议文件包含读取 方法二:命令执行函数绕过 方法三:写入文件 web31 方法一:filter伪…...

【顺序表使用练习】发牌游戏
【顺序表使用练习】发牌游戏 1. 介绍游戏2. 实现52张牌3. 实现洗牌4. 实现发牌5. 效果展示 1. 介绍游戏 首先先为大家介绍一下设计要求 实现52张牌(这里排除大小王)洗牌——打乱牌的顺序发牌——3个人,1人5张牌 2. 实现52张牌 创建Code对象创…...

1.7 编码与调制
欢迎大家订阅【计算机网络】学习专栏,开启你的计算机网络学习之旅! 文章目录 前言前言1 基本术语2 常用的编码方法2.1 不归零编码2.2 归零编码2.3 反向归零编码2.4 曼彻斯特编码2.5 差分曼彻斯特编码 3 常用的调制方法3.1 调幅(AM)…...

004集—— txt格式坐标写入cad(CAD—C#二次开发入门)
如图所示原始坐标格式,xy按空格分开,将坐标按顺序在cad中画成多段线: 坐标xy分开并按行重新输入txt,效果如下: 代码如下 : using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Runtime; us…...
CSS中的font-variation-settings:探索字体的可变性
随着Web字体的发展,设计师们不再局限于传统的字体样式。现代Web字体支持可变字体(Variable Fonts),这种字体允许开发者在单一的字体文件中包含多种字形样式。通过使用CSS中的font-variation-settings属性,我们可以控制…...

组合优化与凸优化 学习笔记5 对偶拉格朗日函数
有的时候约束条件有点难搞,我们可以把它放到目标函数里面。 记得之前凸函数的时候的结论吗?一大堆函数,每一段都取最大的,最后会得到一个凸函数。同理,每一段都取最小的,得到的是一个凹函数。就这样&#x…...

监控易监测对象及指标之:Exchange邮件服务器监测
在现代企业运营中,邮件服务器的作用至关重要,它不仅承载着企业内外的信息传递,还是协同工作的重要工具。为了确保邮件服务器的稳定运行,以及邮件的顺畅收发,采用高效的监控系统是不可或缺的。监控易作为一款专业的监控…...

【机器学习基础】Transformer学习
Transformer学习 梯度消失FeedForward层激活函数的主要作用是在网络中加入非线性变换 梯度消失 梯度爆炸 FeedForward层 Transformer结构: Transformer结构主要分为两大部分: 一是Encoder层结构:Encoder 的输入由 Input Embedding 和 Positional Embedding 求和输入Multi…...
mysql如何不使用窗口函数,去统计出入库情况
mysql如何不使用窗口函数,去统计出入库情况 你把这个表看做 进出库表,每个物料把时间正序后 依次累加数量 ,看这个物料的时间线上 是否会出现负数,1号进货5个 2号出库3个 3号你不能出库3个 最多俩个 不然就是负库存,…...

uni-app canvas文本自动换行
封装 支持单行文本超出换行。多行文本顺位排版 // 填充自动换行的文本function fillFeedText({ctx, text, x, y, maxWidth, lineHeight, color, size}) {// 文本配置ctx.setFontSize(size);ctx.setFillStyle(color);// 计算文本换行宽高,换行逻辑const words text…...

【设计模式-职责链】
定义 职责链模式是一种行为设计模式,**它通过将请求发送给链上的多个处理者来避免请求发送者与处理者之间的紧密耦合。每个处理者可以选择处理请求或将其传递给链中的下一个处理者。**这样,可以将处理请求的责任链式组织,从而实现更灵活的请…...
Prompt:在AI时代,提问比答案更有价值
你好,我是三桥君 随着AI技术的飞速发展,我们进入了一个信息爆炸的时代。在这个时代,只要你会提问,AI就能为你提供满意的答案。这种现象让很多人开始思考:在这个答案触手可及的时代,答案的价值是否还像以前…...
whatis命令:关于命令的简短描述
一、命令简介 whatis 命令用于查询命令、函数、文件等的基本用途,查询结果只是一句简短的描述。 例如 $ whatis ls ls (1) - list directory contents返回关于 ls 命令的简短描述。这个结果实质是来自于man手册的一个章节,在较新的L…...
ICM20948 DMP代码详解(54)
接前一篇文章:ICM20948 DMP代码详解(53) 上一回解析了inv_icm20948_compass_dmp_cal函数的大部分代码,本回继续讲解inv_icm20948_compass_dmp_cal函数的余下内容。为了便于理解和回顾,再次贴出inv_icm20948_compass_dmp_cal函数代码,在EMD-Core\sources\Invn\Devices\Dri…...

RabbitMQ的应用问题
一、幂等性保障 幂等性是数学和计算机科学中某些运算的性质, 它们可以被多次应⽤, ⽽不会改变初始应⽤的结果 数学上的幂等性: f(x)f(f(x)) |x| 数据库操作幂等性: 数据库的 select 操作. 不同时间两次查询的结果可能不同, 但是这个操作是符合幂等性…...
C++14:通过make_index_sequence实现将tuple转换为array
如何将vector转换为array呢 #include <iostream> #include <tuple> #include <array> using namespace std;template <typename V, typename... Types, size_t... I> constexpr auto do_tuple_to_array(tuple<V, Types...>&& tuple, in…...

Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...
mongodb源码分析session执行handleRequest命令find过程
mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程,并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令,把数据流转换成Message,状态转变流程是:State::Created 》 St…...

高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...
动态 Web 开发技术入门篇
一、HTTP 协议核心 1.1 HTTP 基础 协议全称 :HyperText Transfer Protocol(超文本传输协议) 默认端口 :HTTP 使用 80 端口,HTTPS 使用 443 端口。 请求方法 : GET :用于获取资源,…...