Java版-速通数据结构-树基础知识
现在面试问mysql,红黑树好像都是必备问题了。动不动就让手写红黑树或者简单介绍下红黑树。然而,我们如果直接去看红黑树,可能会一下子蒙了。在看红黑树之前,需要先了解下树的基础知识,从简单到复杂,看看红黑树是在什么场景下出现的,是哪种东西。
本文主要是介绍二叉树,二叉搜索树,然后到高度平衡二叉树,根据树的基本操作和特点,帮助理解那些特殊结构的树,是怎样演化而来的。
二叉树(Binary tree)基本概念
二叉树(Binary tree)是树形结构的一个重要类型。看它这名字,就是最多有俩叉的一种特殊的树形结构。通常,它的俩叉分别叫做左子树
和右子树
。
对二叉树的结构定义如下:
public class TreeNode {public int val;public TreeNode left;public TreeNode right;public TreeNode(int x) {val = x;}
}
二叉树的遍历
前序遍历
前序遍历首先访问根节点,然后遍历左子树,最后遍历右子树。
例如,对于如上图二叉树,访问的先后顺序依次是:FBADCEGIH
。
下面使用简单递归来写个:
//前序遍历首先访问根节点,然后遍历左子树,最后遍历右子树public List<Integer> preorderTraversal(TreeNode root) {List<Integer> result = new ArrayList<>();generate(root, result);return result;}private void generate(TreeNode root, List<Integer> result) {if (root == null) {return;}result.add(root.val);if (root.left == null && root.right == null) {return;}if (root.left != null) {generate(root.left, result);}if (root.right != null) {generate(root.right, result);}}
中序遍历
中序遍历是先遍历左子树,然后访问根节点,然后遍历右子树。
例如,对于如上图二叉树,访问的先后顺序依次是:ABCDEFGHI
。
public List<Integer> inorderTraversal(TreeNode root) {List<Integer> result = new ArrayList<>();inorderTraversal(root, result);return result;}/*** 递归方式求解** @param root* @param result*/void inorderTraversal(TreeNode root, List<Integer> result) {if (root.left != null) {inorderTraversal(root.left, result);}result.add(root.val);if (root.right != null) {inorderTraversal(root.right, result);}}
后序遍历
后序遍历是先遍历左子树,然后遍历右子树,最后访问树的根节点。
例如,对于如上图二叉树,访问的先后顺序依次是:ACEDBHIGF
。
public List<Integer> postorderTraversal(TreeNode root) {List<Integer> result = new ArrayList<>();generate(root, result);return result;}/*** 递归方法** @param root* @param result*/private void generate(TreeNode root, List<Integer> result) {//后序遍历是先遍历左子树,然后遍历右子树,最后访问树的根节点if (root == null) {return;}if (root.left != null) {generate(root.left, result);}if (root.right != null) {generate(root.right, result);}result.add(root.val);}
层次遍历
层序遍历就是逐层遍历树结构。
例如,对于如上图二叉树,访问的先后顺序依次是:FBGADICEH
。
public List<List<Integer>> levelOrder(TreeNode root) {List<List<Integer>> result = new ArrayList<>();if (root == null) {return result;}Queue<TreeNode> queue = new LinkedList<>();queue.add(root);while (!queue.isEmpty()) {int size = queue.size();List<Integer> item = new ArrayList<>();for (int i = 0; i < size; i++) {TreeNode n = queue.poll();item.add(n.val);if (n.left != null) {queue.add(n.left);}if (n.right != null) {queue.add(n.right);}}result.add(item);}return result;}
二叉树的深度
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
是不是通过上面对于二叉树的遍历,发现,二叉树用递归方法,简直是太好写了,下面我们对这个深度的求解也使用递归:
int answer = 0;public int maxDepth(TreeNode root) {if (root == null) {return 0;}int depth = 1;depth(root, depth);return answer;}private void depth(TreeNode root, int depth) {if (root.left == null && root.right == null) {answer = Math.max(answer, depth);}if (root.left != null) {depth(root.left, depth + 1);}if (root.right != null) {depth(root.right, depth + 1);}}
到这里我们会发现,对于二叉树的遍历操作,几乎都可以用递归来解决,超简单呀。
二叉搜索树(BinarySearchTree)
BST 定义
BST
是二叉树的一种特殊表示形式,它满足如下特性:
- 1,每个节点中的值必须大于(或等于)存储在其左侧子树中的任何值
- 2,每个节点中的值必须小于(或等于)存储在其右子树中的任何值
我们可以把BST
看成是进化了的二叉树。而且观察BST
的这个特点,是不是让你想起来我们之前说过的数组的二分法,利用二分法对有序数组进行查找,可以提高搜索效率。如果对BST
进行搜索,我们也可以充分利用BST
的特征。
验证BST
对于二叉树,我们可以进行中序遍历(左中右),观察遍历得到的值是不是从小到大排列,可以使用此点验证二叉搜索树;
LinkedList<Integer> data = new LinkedList<>();public boolean isValidBST(TreeNode root) {if (root == null) {return true;}if (root.left != null && !isValidBST(root.left)) {return false;}//左中右遍历,让值依次增大即可if (!data.isEmpty()) {Integer n = data.peekLast();if (n >= root.val) {return false;}}data.add(root.val);if (root.right != null && !isValidBST(root.right)) {return false;}return true;}
BST基本操作
搜索
搜索的具体思路跟二分法也是蜜汁类似,不懂二分法的请翻看我以前写的关于数组的基本操作。
对于BST
来说,如果当前比较数值过小,往右搜索,过大,往左搜索。
public TreeNode searchBST(TreeNode root, int val) {if (root == null) {return null;}if (root.val == val) {return root;}if (root.val > val && root.left != null) {return searchBST(root.left, val);}if (root.val < val && root.right != null) {return searchBST(root.right, val);}return null;}
插入
对于插入操作也是一样的,在比较的基础上,找到合适的位置,哈哈。
public TreeNode insertIntoBST(TreeNode root, int val) {if (root == null) {return root;}if (root.val > val && root.left == null) {root.left = new TreeNode(val);return root;}if (root.val < val && root.right == null) {root.right = new TreeNode(val);return root;}if (root.left != null && root.val > val) {insertIntoBST(root.left, val);}if (root.right != null && root.val < val) {insertIntoBST(root.right, val);}return root;}
删除
对于删除操作,可能比上面两种操作相对复杂一点。
-
- 如果目标节点没有子节点,我们可以直接移除该目标节点。
-
- 如果目标节只有一个子节点,我们可以用其子节点作为替换。
-
- 如果目标节点有两个子节点,我们需要用其中序后继节点或者前驱节点来替换,再删除该目标节点。
对于 1 和 2,很好理解。对于3,我们先来看一个结点,值的大小顺序为:左<中<右
,如果我们要删除中间结点并且还要保持这个顺序不变,则我们有两个方法:1,使用左侧树的最大值去掉中间结点;2,使用右侧最小值取代中间结点。这样才能使得转变之后的树还满足BST
的特征。
代码如下:
/*** @param root* @param key* @return*/public TreeNode deleteNode(TreeNode root, int key) {if (root == null) {return root;}if (root.val == key) {if (root.left == null && root.right == null) {return null;}if (root.left == null) {root = root.right;return root;}if (root.right == null) {root = root.left;return root;}root = getLeftChildMaxNode(root);return root;}if (root.val > key) {root.left = deleteNode(root.left, key);return root;}if (root.val < key) {root.right = deleteNode(root.right, key);return root;}return root;}/*** 转变右子树最小结点** @param root* @return*/private TreeNode getRightChildMinNode(TreeNode root) {if (root == null) {return root;}TreeNode left = root.left;TreeNode right = root.right;if (right.left == null) {right.left = left;return right;}//root的left直接挪到root.left的最小结点上TreeNode temp = right.left;while (temp.left != null) {temp = temp.left;}temp.left = left;return right;}/*** 转变左子树最大结点** @return*/private TreeNode getLeftChildMaxNode(TreeNode root) {if (root == null) {return root;}TreeNode left = root.left;TreeNode right = root.right;if (left.right == null) {left.right = right;return left;}TreeNode temp = left.right;while (temp.right != null) {temp = temp.right;}temp.right = right;return left;}
高度平衡二叉树
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。
如果二叉搜索树的高度为 h ,则时间复杂度为 O(h) 。所以,二叉搜索树的高度的确很重要。对于一个有N个结点,高度为h的二叉树,h>= l o g 2 n {log_2{n}} log2n。对于具有 N 个节点的二叉搜索树的高度在 logN 到 N 区间变化。也就是说,搜索操作的时间复杂度可以从 logN 变化到 N 。这是一个巨大的性能差异。所以,我们应该尽量把二叉搜索树,往高度平衡的二叉搜索树上靠,来提高搜索效率。
高度平衡二叉树验证
emm,还是递归,超简单,按照定义写即可:
Boolean res = true;public boolean isBalanced(TreeNode root) {singleBalanced(root);return res;}int singleBalanced(TreeNode root) {if (root == null) {return 0;}int left = singleBalanced(root.left) + 1;int right = singleBalanced(root.right) + 1;if (Math.abs(left - right) > 1) {res = false;}return Math.max(left, right);}
有序数组转换成高度平衡二叉搜索树
我们取数组的中间元素作为根结点, 将数组分成左右两部分,对数组的两部分用递归的方法分别构建左右子树。
感觉其实是二分法的反作用。
这个可以用于对于普通二叉搜索树,先用中序遍历,生成有序数组,之后,将有序数组构建成高度平衡二叉树。
这是采用拆解重构建的方式构造高度平衡二叉树的一种方法。之后,我们还会介绍通过调整普通二叉搜索树,构建高度平衡二叉树或者近似于高度平衡二叉树的方法。
public TreeNode sortedArrayToBST(int[] nums) {if (nums == null || nums.length == 0) {return null;}return build(nums, 0, nums.length - 1);}TreeNode build(int[] nums, int left, int right) {if (left == right) {return new TreeNode(nums[left]);}int mid = (left + right) / 2;TreeNode root = new TreeNode(nums[mid]);if (left + 1 == right) {root.right = new TreeNode(nums[right]);return root;}if (left + 2 == right) {root.left = new TreeNode(nums[left]);root.right = new TreeNode(nums[right]);return root;}root.left = build(nums, left, mid - 1);root.right = build(nums, mid + 1, right);return root;}
N叉树
N叉树:一个结点有N个叉,哈哈。
二叉树属于N叉树的一个特例。
结点定义:
class Node {public int val;public List<Node> children;public Node() {}public Node(int _val, List<Node> _children) {val = _val;children = _children;}}
N叉树的遍历
前序遍历
public List<Integer> preorder(Node root) {List<Integer> res = new ArrayList<>();if (root == null) {return res;}res.add(root.val);if (root.children == null || root.children.size() < 1) {return res;}for (Node n : root.children) {List<Integer> items = preorder(n);res.addAll(items);}return res;}
后序遍历
/*** 递归方法** @param root* @return*/public List<Integer> postorder(Node root) {List<Integer> res = new ArrayList<>();if (root == null) {return res;}postorder(root, res);return res;}void postorder(Node root, List<Integer> res) {if (root.children == null) {res.add(root.val);return;}for (Node n : root.children) {postorder(n, res);}res.add(root.val);}
层序遍历
public List<List<Integer>> levelOrder(Node root) {List<List<Integer>> res = new ArrayList<>();if (root == null) {return res;}Queue<Node> queue = new LinkedList<>();queue.add(root);while (!queue.isEmpty()) {int size = queue.size();List<Integer> data = new ArrayList<>();for (int i = 0; i < size; i++) {Node node = queue.poll();data.add(node.val);if (node.children != null && node.children.size() > 0) {queue.addAll(node.children);}}res.add(data);}return res;}
前缀树
前缀树定义
前缀树特点:
- 根节点不包含字符,除根节点外每一个节点都只包含一个字符;
- 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串;
- 每个节点的所有子节点包含的字符都不相同。
下面为两种常用前缀树的结构:
1,使用数组来存储后缀结点:
class TrieNode {public static final int N = 26;public TrieNode[] children = new TrieNode[N];// ......}
2,使用map来存储后缀结点:
class TrieNode {public Map<Character, TrieNode> children = new HashMap<>();// ......
};
实现前缀树的插入和搜索
class Trie {private Map<Character, TrieNode> data = new HashMap<>();public Trie() {}public void insert(String word) {if (word == null || word.isEmpty()) {return;}if (search(word)) {return;}char[] words = word.toCharArray();char head = words[0];TrieNode currt = data.get(head);if (currt == null) {currt = new TrieNode(head);data.put(head, currt);}for (int i = 1; i < words.length; i++) {char c = words[i];if (currt.next == null) {currt.next = new ArrayList<>();}TrieNode target = currt.next.stream().filter(n -> n.c == c).findFirst().orElse(null);if (target == null) {target = new TrieNode(c);currt.next.add(target);}currt = target;}currt.isWord = true;}public boolean search(String word) {if (word == null || word.isEmpty()) {return false;}char[] words = word.toCharArray();char head = words[0];TrieNode currt = data.get(head);if (currt == null) {return false;}for (int i = 1; i < words.length; i++) {char c = words[i];if (currt.next == null || currt.next.size() == 0) {return false;}List<TrieNode> next = currt.next;TrieNode t = next.stream().filter(n -> n.c == c).findFirst().orElse(null);if (t == null) {return false;}currt = t;}return currt.isWord;}public boolean startsWith(String prefix) {if (prefix == null || prefix.isEmpty()) {return false;}char[] words = prefix.toCharArray();char head = words[0];TrieNode currt = data.get(head);if (currt == null) {return false;}for (int i = 1; i < words.length; i++) {char c = words[i];if (currt.next == null || currt.next.size() == 0) {return false;}List<TrieNode> next = currt.next;TrieNode t = next.stream().filter(n -> n.c == c).findFirst().orElse(null);if (t == null) {return false;}currt = t;}return true;}}
小结
本文从最简单的二叉树开始讲起,介绍了简单二叉树的遍历,
之后延伸到对于搜索友好的二叉搜索树,对比二叉搜索树和我之前chat里面讲过的二分法,你会发现,提高搜索效率的秘诀,在于构建有序的结构,之后尽量利用二分法的原理,使得搜索的时间复杂度靠近 l o g 2 n log_2n log2n。
但是在实际操作中,我们会发现,将树维护在高度平衡内,实在是要耗费的力气太大了,于是不得不在高度平衡和构建树上做一个妥协,由此衍生出了很多工业级别的树,但是限于本文篇幅,这里没有涉及,或许我会在后续的chat安排上。
除了二叉树,本文还简单介绍了下N叉树和前缀树,简单了解下树的其他应用方式。
相关文章:

Java版-速通数据结构-树基础知识
现在面试问mysql,红黑树好像都是必备问题了。动不动就让手写红黑树或者简单介绍下红黑树。然而,我们如果直接去看红黑树,可能会一下子蒙了。在看红黑树之前,需要先了解下树的基础知识,从简单到复杂,看看红黑树是在什么…...

详尽的oracle sql函数
1,CHR 输入整数,返回对应字符。 用法:select chr(65),chr(78) from dual; 2,ASCII 输入字符,返回对应ASCII码。 用法:select ascii(A),ascii(B) from dual; 3,CONCAT 输入两个字符串,…...

SAP IDOC Error VG205
今天在做IDOC 入栈处理销售订单的时候,一直报错VG205 There is no article description for item 000030 这个问题在通过WE19 前台显示的时候就不会遇见, 只有在接口传输的时候才会遇到 搜索发现,可以通过配置忽略此消息号 配置路径如下…...
DSP 的 CV 算子调用
01 前言 DSP 是 征程 5 上的数字信号处理器,专用于处理视觉、图像等信息。在 OE 包的 ddk/samples/vdsp_rpc_sample 路径下,提供了 DSP 使用示例,包括 nn 和 CV 两部分。 nn 示例涵盖了深度学习模型的相关算子,包括量化、反量化、…...

WMI攻击-基础篇(一)
#WMI攻击-基础篇(一) 这篇文章是关于WMI攻击系列文章的第一部分,面向新手。如果对Powershell有一定了解会对阅读本文有所帮助,但这并不是必需的,我们直接上干货。 #1、概述 为什么是WMI? WMI 是 Microso…...
使用Pygame创建一个简单的消消乐游戏
消消乐游戏是一种经典的益智游戏,玩家通过交换相邻的方块来形成三个或更多相同颜色的连续方块,从而消除它们。本文将介绍如何使用Python的Pygame库来创建一个简单的消消乐游戏。 准备工作 在开始之前,请确保已安装Pygame库。可以通过以下命…...
证明直纹面是可展曲面沿着直母线,曲面的切平面不变
目录 证明直纹面是可展曲面的当且仅当沿着直母线,曲面的切平面不变 证明直纹面是可展曲面的当且仅当沿着直母线,曲面的切平面不变 直纹面是可展曲面当且仅当沿着直母线,曲面的切平面不变. 证明:设直纹面 S S S的参数式为 r ( u …...

Chrome控制台 网站性能优化指标一览
打开chrome-》f12/右键查看元素-》NetWrok/网络 ctrlF5 刷新网页,可以看到从输入url到页面资源请求并加载网页,用于查看资源加载,接口请求,评估网页、网站性能等,如下图: request、stransferred、resour…...
Typora创建markdwon文件的基础语法
标题的创建 使用#空格xxx 可使xxx为标题,同时第一标题为#空格标题;第二标题为##空格标题2。以此类推最多可创建六个标题。 同时按住Ctrl1可创建第一标题,同时按住Ctrl2可创建第二标题,以此类推,最多可创建六个标题。也…...
《嵌入式硬件设计》
一、引言 嵌入式系统在现代科技中占据着至关重要的地位,广泛应用于消费电子、工业控制、汽车电子、医疗设备等众多领域。嵌入式硬件设计作为嵌入式系统开发的基础,直接决定了系统的性能、可靠性和成本。本文将深入探讨嵌入式硬件设计的各个方面ÿ…...
【AIGC】大模型面试高频考点-位置编码篇
【AIGC】大模型面试高频考点-位置编码篇 (一)手撕 绝对位置编码 算法(二)手撕 可学习位置编码 算法(三)手撕 相对位置编码 算法(四)手撕 Rope 算法(旋转位置编码…...
如何使用 SQL 语句创建一个 MySQL 数据库的表,以及对应的 XML 文件和 Mapper 文件
文章目录 1、SQL 脚本语句2、XML 文件3、Mapper 文件4、启动 ServiceInit 文件5、DataService 文件6、ComplianceDBConfig 配置文件 这个方式通常是放在项目代码中,使用配置在项目的启动时创建表格,SQL 语句放到一个 XML 文件中。在Spring 项目启动时&am…...

Unity性能优化---动态网格组合(二)
在上一篇中,组合的是同一个材质球的网格,如果其中有不一样的材质球会发生什么?如下图: 将场景中的一个物体替换为不同的材质球 运行之后,就变成了相同的材质。 要实现组合不同材质的网格步骤如下: 在父物体…...

JVM学习《垃圾回收算法和垃圾回收器》
目录 1.垃圾回收算法 1.1 标记-清除算法 1.2 复制算法 1.3 标记-整理算法 1.4 分代收集算法 2.垃圾回收器 2.1 熟悉一下垃圾回收的一些名词 2.2 垃圾回收器有哪些? 2.3 Serial收集器 2.4 Parallel Scavenge收集器 2.5 ParNew收集器 2.6 CMS收集器 1.垃圾…...

GPS模块/SATES-ST91Z8LR:电路搭建;直接用电脑的USB转串口进行通讯;模组上报定位数据转换地图识别的坐标手动查询地图位置
从事嵌入式单片机的工作算是符合我个人兴趣爱好的,当面对一个新的芯片我即想把芯片尽快搞懂完成项目赚钱,也想着能够把自己遇到的坑和注意事项记录下来,即方便自己后面查阅也可以分享给大家,这是一种冲动,但是这个或许并不是原厂希望的,尽管这样有可能会牺牲一些时间也有哪天原…...
什么是TCP的三次握手
TCP(传输控制协议)的三次握手是一个用于在两个网络通信的计算机之间建立连接的过程。这个过程确保了双方都有能力接收和发送数据,并且初始化双方的序列号。以下是三次握手的详细步骤: 第一次握手(SYN)&…...

《Clustering Propagation for Universal Medical Image Segmentation》CVPR2024
摘要 这篇论文介绍了S2VNet,这是一个用于医学图像分割的通用框架,它通过切片到体积的传播(Slice-to-Volume propagation)来统一自动(AMIS)和交互式(IMIS)医学图像分割任务。S2VNet利…...
Linux ifconfig ip 命令详解
简介 ifconfig 和 ip 命令用于配置和显示 Linux 上的网络接口。虽然 ifconfig 是传统工具,但现在已被弃用并被提供更多功能的 ip 命令取代。 ifconfig 安装 sudo apt install net-toolssudo yum install net-tools查看所有活动的网络接口 ifconfig启动/激活网络…...

Vue3 对于echarts使用 v-show,导致显示不全,宽度仅100px,无法重新渲染的问题
参考链接:解决Echarts图表使用v-show,显示不全,宽度仅100px的问题_echarts v-show图表不全-CSDN博客 Vue3 echarts v-show无法重新渲染的问题_v-show echarts不渲染-CSDN博客 原因不多赘述了,大概就是v-show 本身是结构已经存在,当数据发生…...

C++实现俄罗斯方块
俄罗斯方块 还记得俄罗斯方块吗?相信这是小时候我们每个人都喜欢玩的一个小游戏。顾名思义,俄罗斯方块自然是俄罗斯人发明的。这人叫阿列克谢帕基特诺夫。他设置这个游戏的规则是:由小方块组成的不同形状的板块陆续从屏幕上方落下来…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...

前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...

k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...
AGain DB和倍数增益的关系
我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...