Java数据结构第十五期:走进二叉树的奇妙世界(四)



专栏:Java数据结构秘籍
个人主页:手握风云
目录
一、二叉树OJ练习题(续)
1.1. 二叉树的层序遍历
1.2. 二叉树的最近公共祖先
1.3. 从前序与中序遍历序列构造二叉树
1.4. 从中序与后序遍历序列构造二叉树
1.5. 根据二叉树创建字符串
一、二叉树OJ练习题(续)
1.1. 二叉树的层序遍历
层序遍历,就是从左到右依次访问每个节点。这里我们要借助队列来非递归方式的实现。我们先将根结点root放入队列,再用cur引用来接收弹出的根结点最后再打印。当左右子树不为空时,再一次将左子树和右子树放入队列中。然后先弹出左子树,如果左子树的左右结点不为空,再次放入。当队列为空时,遍历过程结束。所以下面这棵二叉树的打印结果应为“4271369”。



import java.util.LinkedList;
import java.util.Queue;class TreeNode{public int val;public TreeNode left;public TreeNode right;public TreeNode() {}public TreeNode(int val) {this.val = val;}public TreeNode(int val, TreeNode left, TreeNode right) {this.val = val;this.left = left;this.right import java.util.LinkedList;
import java.util.Queue;class TreeNode{public int val;public TreeNode left;public TreeNode right;public TreeNode() {}public TreeNode(int val) {this.val = val;}public TreeNode(int val, TreeNode left, TreeNode right) {this.val = val;this.left = left;this.right = right;}
}public class Solution {public void levelOrder(TreeNode root){Queue<TreeNode> queue = new LinkedList<TreeNode>();queue.offer(root);while(! queue.isEmpty()){TreeNode cur = queue.poll();System.out.print(cur.val+" ");if(cur.left != null){queue.offer(cur.left);}if(cur.right != null){queue.offer(cur.right);}}}
}= right;}
}public class Solution {public void levelOrder(TreeNode root){Queue<TreeNode> queue = new LinkedList<TreeNode>();queue.offer(root);while(! queue.isEmpty()){TreeNode cur = queue.poll();System.out.print(cur.val+" ");if(cur.left != null){queue.offer(cur.left);}if(cur.right != null){queue.offer(cur.right);}}}
}
public class Test {public static void main(String[] args) {TreeNode root = new TreeNode(4,new TreeNode(2),new TreeNode(7));root.left.left = new TreeNode(1);root.left.right = new TreeNode(3);root.right.left = new TreeNode(6);root.right.right = new TreeNode(9);Solution solution = new Solution();solution.levelOrder(root);}
}
但题目当中给出的类型是嵌套List<List<Integer>>,同时根据输出的格式来,创建一个二维数组,第一层放入第一列中。我们依然可以借助上面的队列来实现。与上面的方法类似,我们还需要再定义一个整型size变量来接受队列的长度,根据队列的长度来选择弹出与进入的操作。

public List<List<Integer>> levelOrder1(TreeNode root){List<List<Integer>> ret = new ArrayList<>();if(root == null) return ret;Queue<TreeNode> queue1 = new LinkedList<TreeNode>();queue1.offer(root);while(! queue1.isEmpty()){List<Integer> curList = new ArrayList<>();int size = queue1.size();while(size != 0){TreeNode cur = queue1.poll();curList.add(cur.val);if(cur.left != null){queue1.offer(cur.left);}if(cur.right != null){queue1.offer(cur.right);}size--;}ret.add(curList);}return ret;}
import java.util.List;public class Test {public static void main(String[] args) {Solution solution = new Solution();TreeNode root = new TreeNode(4,new TreeNode(2),new TreeNode(7));root.left.left = new TreeNode(1);root.left.right = new TreeNode(3);root.right.left = new TreeNode(6);root.right.right = new TreeNode(9);solution.levelOrder(root);List<List<Integer>> result = solution.levelOrder1(root);System.out.println(result);}
}
1.2. 二叉树的最近公共祖先

如果是上图中第三种情况,那么我们就直接返回root。如果是第一种情况,当root向下遍历时,遇到p、q结点时,直接返回到root(如下图所示)。

如果是第二种情况,当root遍历到p节点时,5结点返回p的地址,同时我们也可以找到q结点并返回。但还是有一种极端情况,q是孩子节点p的一个子结点。按照上面的思路,直接就返回这个结点。所以这种极端情况可以总结为,只要root遍历到p或者q中一个,直接返回对应的结点。因为p、q都在同一棵子树上,当root去遍历另一棵子树时,会返回null,所以最终结果是p,与p或q在根节点上是类似的。


完整代码实现:
class TreeNode{public int val;public TreeNode left;public TreeNode right;public TreeNode() {}public TreeNode(int val) {this.val = val;}public TreeNode(int val, TreeNode left, TreeNode right) {this.val = val;this.left = left;this.right = right;}
}public class Solution {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q){if(root == null){return root;}if(root == p || root == q){return root;}TreeNode leftT = lowestCommonAncestor(root.left,p,q);TreeNode rightT = lowestCommonAncestor(root.right,p,q);if(leftT != null && rightT != null){//p、q分别在左右子树上return root;}else if(leftT != null){return leftT;} else if (rightT != null) {return rightT;}return null;}
}
public class Test {public static void main(String[] args) {TreeNode root = new TreeNode(3,new TreeNode(5),new TreeNode(1));root.left.left = new TreeNode(6);root.left.right = new TreeNode(2,new TreeNode(7),new TreeNode(4));root.right.left = new TreeNode(0);root.right.right = new TreeNode(8);TreeNode p = root.left;TreeNode q = root.right;Solution solution = new Solution();TreeNode cur = solution.lowestCommonAncestor(root,p,q);System.out.println(cur.val);}
}
这道题还有另一种做法:如果我们把二叉树里的每一个结点新增加一个前驱域,用来存储父节点的地址,那么这道题的思路就变成了一链表交点的形式来求最近的公共祖先结点。可是定义的TreeNode类里面的成员变量里面没有这个变量。此时可以利用两个栈来存储从root到p、q结点路径上的结点。
基本思路:只要root不为空,就把结点扔进栈当中。让长度较大的栈先弹出一定的元素,使得两个栈长度相等。两个栈再同时弹出元素,判断两个值是否相等,相等则是最近的公共祖先结点。

而下面问题又来了,我们该如何求路径上的结点?只要root不等于p或者q,就将该节点放进栈中并继续递归;当root等于p或者q时,就停止。如果在遍历过程中某一个结点既不等于p、q,且左右都为空,那么这个元素就会被弹出。
完整代码实现:
import java.util.Stack;class TreeNode{public int val;public TreeNode left;public TreeNode right;public TreeNode() {}public TreeNode(int val) {this.val = val;}public TreeNode(int val, TreeNode left, TreeNode right) {this.val = val;this.left = left;this.right = right;}
}public class Solution {public boolean getPath(TreeNode root, TreeNode node, Stack<TreeNode> stack){if(root == null){return false;}stack.push(root);if(root == node){return true;}boolean flag = getPath(root.left,node,stack);if(flag){return true;}flag = getPath(root.right,node,stack);if(flag){return true;}stack.pop();return false;}public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q){if(root == null){return null;}Stack<TreeNode> stack1 = new Stack<>();Stack<TreeNode> stack2 = new Stack<>();getPath(root,p,stack1);getPath(root,q,stack2);int len1 = stack1.size();int len2 = stack2.size();int len = len1 - len2;if(len < 0){len = Math.abs(len);while(len != 0){stack2.pop();len--;}}else{while(len != 0){stack1.pop();len--;}}//保证两个栈的长度一样while (!stack1.isEmpty() && !stack2.isEmpty()){if(stack1.peek() == stack2.peek()){return stack1.pop();}else{stack1.pop();stack2.pop();}}return null;}
}
public class Test {public static void main(String[] args) {TreeNode root = new TreeNode(3,new TreeNode(5),new TreeNode(1));root.left.left = new TreeNode(6);root.left.right = new TreeNode(2,new TreeNode(7),new TreeNode(4));root.right.left = new TreeNode(0);root.right.right = new TreeNode(8);TreeNode p = root.left;TreeNode q = root.right;Solution solution = new Solution();TreeNode cur = solution.lowestCommonAncestor(root,p,q);System.out.println(cur.val);}
}
1.3. 从前序与中序遍历序列构造二叉树
基本思路:1.遍历前序遍历的数组,遇到元素之后,在中序遍历数组当中找到该数字;2.该数字的左边就是左树,右边就是右树。上述两步构成一个递归来构造子树。

我们以中序遍历的数组的第一个元素ibegin,最后一个元素iend之间找到二叉树的根,因为是前序遍历,先有的左树再有的右树,那么左边的区间就会是(9,x) = (ibegin,iend),iend = iroot-1;相反我们去递归右树,ibegin=iroot+1。也就是说,递归根结点创建左树和右树时,还需要知道ibegin和iend的范围。
我们还需要额外创建一个方法来接受ibegin和iend的参数。创建root,利用buildTreeChild方法递归来创建根结点的左树和右树。可我们不知道中序遍历的数组中根结点的下标,还需要一个方法来查找根结点的下标。
public class Solution {public TreeNode buildTree(int[] preorder, int[] inorder){return buildTreeChild(preorder,0,inorder,0, inorder.length-1);}public TreeNode buildTreeChild(int[] preorder, int prevIndex,int[] inorder, int inbegin, int inend){TreeNode root = new TreeNode(preorder[prevIndex]);int rootIndex = findIndex(inorder,inbegin,inend,preorder[prevIndex]);prevIndex++;root.left = buildTreeChild(preorder,prevIndex,inorder,inbegin,rootIndex-1);root.right = buildTreeChild(preorder,prevIndex,inorder,rootIndex-1,inend);return root;}private int findIndex(int[] inorder,int inbegin,int inend,int key) {for(int i = inbegin; i <= inend; i++) {if(inorder[i] == key) {return i;}}return -1;}
}
但此时的代码还是有一点逻辑上的问题,就是递归结束的条件是什么?一棵二叉树,总有一棵子树的左右子树都为空。但我们上面的代码没有null。所以要处理一下边界情况:
if(inbegin > inend){return null;
}
还存在另一个问题,就是局部变量的定义。因为二叉树遍历完左树的时候,最后给根返回0,从0再去遍历右子树。所以我们把prevIndex定义为成员变量。
完整代码实现:
class TreeNode{int val;TreeNode left;TreeNode right;public TreeNode() {}public TreeNode(int val) {this.val = val;}public TreeNode(int val, TreeNode left, TreeNode right) {this.val = val;this.left = left;this.right = right;}
}public class Solution {public int prevIndex;public TreeNode buildTree(int[] preorder, int[] inorder){return buildTreeChild(preorder,inorder,0, inorder.length-1);}public TreeNode buildTreeChild(int[] preorder, int[] inorder, int inbegin, int inend){if(inbegin > inend){return null;}TreeNode root = new TreeNode(preorder[prevIndex]);int rootIndex = findIndex(inorder,inbegin,inend,preorder[prevIndex]);prevIndex++;root.left = buildTreeChild(preorder,inorder,inbegin,rootIndex-1);root.right = buildTreeChild(preorder,inorder,rootIndex+1,inend);return root;}private int findIndex(int[] inorder,int inbegin,int inend,int key) {for(int i = inbegin; i <= inend; i++) {if(inorder[i] == key) {return i;}}return -1;}
}
public class Test {public static void PrintTreeNode(TreeNode root){if(root == null){return;}System.out.print(root.val+" ");PrintTreeNode(root.left);PrintTreeNode(root.right);}public static void main(String[] args) {Solution soluion = new Solution();int[] preOrder = new int[]{3,9,20,15,7};int[] inOrder = new int[]{9,3,15,20,7};TreeNode root = soluion.buildTree(preOrder,inOrder);PrintTreeNode(root);}
}
1.4. 从中序与后序遍历序列构造二叉树
与上面一题的思路一样,但后序遍历的顺序是“左子树、右子树、根”,那根结点从后面开始找。并且在创建树的过程中,要先创建右树再创建左树。
class TreeNode{int val;TreeNode left;TreeNode right;public TreeNode() {}public TreeNode(int val) {this.val = val;}public TreeNode(int val, TreeNode left, TreeNode right) {this.val = val;this.left = left;this.right = right;}
}public class Solution {public int postIndex = 0;public TreeNode buildTree(int[] inorder, int[] postorder){postIndex = postorder.length-1;return buildTreeChild(postorder,inorder,0, inorder.length-1);}public TreeNode buildTreeChild(int[] preorder, int[] inorder, int inbegin, int inend){if(inbegin > inend){return null;}TreeNode root = new TreeNode(preorder[postIndex]);int rootIndex = findIndex(inorder,inbegin,inend,preorder[postIndex]);postIndex--;root.right = buildTreeChild(preorder,inorder,rootIndex+1,inend);root.left = buildTreeChild(preorder,inorder,inbegin,rootIndex-1);return root;}private int findIndex(int[] inorder,int inbegin,int inend,int key) {for(int i = inbegin; i <= inend; i++) {if(inorder[i] == key) {return i;}}return -1;}
}
public class Test {public static void PrintTreeNode(TreeNode root){if(root == null){return;}PrintTreeNode(root.left);PrintTreeNode(root.right);System.out.print(root.val+" ");}public static void main(String[] args) {Solution solution = new Solution();int[] Inorder = new int[]{9,3,15,20,7};int[] Postorder = new int[]{9,15,7,20,3};TreeNode root = solution.buildTree(Inorder,Postorder);PrintTreeNode(root);}
}
1.5. 根据二叉树创建字符串

通过上图分析:当1的左子树不为空,就用一个(,2的左子树也不为空,也使用一个(,4再往下递归返回null,直接)闭合;2的右子树为null,返回);1的右子树不为空,返回(,3递归返回null,直接)闭合。
所以我们可以总结下来规律:当子树不为空时,直接加左括号;当root的左树为空,且右树也为空,直接加右括号闭合;当root的左树不为空,右树为空,也加右括号闭合。
public void tree2strChild(TreeNode root,StringBuilder stringBuilder){if(root == null){return;}stringBuilder.append(root.val);//判断根的左子树if(root.left != null){stringBuilder.append("(");tree2strChild(root.left,stringBuilder);//递归左树stringBuilder.append(")");//左树走完,右括号闭合}else {if(root.right == null){return;//因为4结点走完,返回2结点,这里本身就会加一个")"}else {}}//判断根的右子树if(root.right != null){stringBuilder.append("(");tree2strChild(root.right,stringBuilder);stringBuilder.append(")");}else {}}
但也存在另一种情况:如果子树的左边为空,右边不为空,就直接加一对小括号,再去递归右树,把4再加进去。再继续往下走,如果root.right为空,正好符合上面2结点的情况:2的左边走完,右边为空,直接return加右括号。所以只要左树为空,右树不为空,就不做任何处理。

完整代码实现:
class TreeNode{public int val;public TreeNode left;public TreeNode right;public TreeNode() {}public TreeNode(int val) {this.val = val;}public TreeNode(int val, TreeNode left, TreeNode right) {this.val = val;this.left = left;this.right = right;}
}public class Solution {public String tree2str(TreeNode root){StringBuilder stringBuilder = new StringBuilder();tree2strChild(root,stringBuilder);return stringBuilder.toString();}public void tree2strChild(TreeNode root,StringBuilder stringBuilder){if(root == null){return;}stringBuilder.append(root.val);//判断根的左子树if(root.left != null){stringBuilder.append("(");tree2strChild(root.left,stringBuilder);//递归左树stringBuilder.append(")");//左树走完,右括号闭合}else {if(root.right == null){return;//因为4结点走完,返回2结点,这里本身就会加一个")"}else {stringBuilder.append("()");}}//判断根的右子树if(root.right != null){stringBuilder.append("(");tree2strChild(root.right,stringBuilder);stringBuilder.append(")");}else {return;}}
}
public class Test {public static void main(String[] args) {Solution solution = new Solution();TreeNode root1 = new TreeNode(1,new TreeNode(2),new TreeNode(3));root1.left.left = new TreeNode(4);TreeNode root2 = new TreeNode(1,new TreeNode(2),new TreeNode(3));root2.left.right = new TreeNode(4);System.out.println(solution.tree2str(root1));System.out.println(solution.tree2str(root2));}
}相关文章:
Java数据结构第十五期:走进二叉树的奇妙世界(四)
专栏:Java数据结构秘籍 个人主页:手握风云 目录 一、二叉树OJ练习题(续) 1.1. 二叉树的层序遍历 1.2. 二叉树的最近公共祖先 1.3. 从前序与中序遍历序列构造二叉树 1.4. 从中序与后序遍历序列构造二叉树 1.5. 根据二叉树创建…...
【MySQL】CAST()在MySQL中的用法以及其他常用的数据类型转换函数
1. cast() CAST() 在 MySQL 中用于将一个表达式的类型转换为另一个类型。这在处理不同类型的数据时非常有用,比如将字符串转换为数字,或者将浮点数转换为整数等。 1.1 CAST() 函数的基本语法 CAST() 函数的基本语法如下: CAST(expression…...
使用Truffle、Ganache、MetaMask、Vue+Web3完成的一个简单区块链项目
文章目录 概要初始化Truffle项目创建编写合约编译合约配置Ganache修改truffle-config.js文件编写迁移文件部署合约使用Truffle 控制台使用MetaMask和VueWeb3与链交互 概要 使用Truffle、Ganache、MetaMask、VueWeb3完成的一个简单区块链项目。 初始化Truffle项目 安装好truf…...
初出茅庐的小李博客之按键驱动库使用
驱动库介绍 源码地址:https://github.com/jiejieTop/ButtonDrive 使用只需3步,创建按键,按键事件与回调处理函数链接映射,周期检查按键,支持单双击、连按、长按;采用回调处理按键事件(自定义消…...
如何调试Linux内核?
通过创建一个最小的根文件系统,并使用QEMU和GDB进行调试。 1.准备工作环境 确保系统上安装了所有必要的工具和依赖项。 sudo apt-get update //更新一下软件包 sudo apt-get install build-essential git libncurses-dev bison flex libssl-dev qemu-system-x…...
ECharts组件封装教程:Vue3中的实践与探索
在日常的前端开发中,ECharts 作为一款强大且易用的图表库,被广泛应用于数据可视化场景。为了更好地在 Vue3 项目中复用 ECharts 功能,我们可以将其封装成一个组件。本文将带大家一步步实现 ECharts 的 Vue3 组件封装,并演示如何在父组件中调用和使用。 一、封装 ECharts 组…...
NAT 代理服务 内网穿透
🌈 个人主页:Zfox_ 🔥 系列专栏:Linux 目录 一:🔥 NAT 技术背景二:🔥 NAT IP 转换过程三:🔥 NAPT四:🔥 代理服务器🦋 正向…...
CAN硬件协议详解
一、基本理论: 1、CAN的总线结构: CAN总线 网络结构 有 闭环和开环 两种形式;无论实际的网络多复杂,都离不开这两种基本结构。 闭环结构的CAN总线网络,总线的两端各并联一个120Ω的电阻,两…...
网络安全等级保护:网络安全等级保护基本技术
下面我们概括性探讨一下等级保护用到的一些技术,有关这些技术的每一个方面的每一个部分都可以是一部大块头,甚至一部大块头都无法介绍清楚,需要系列性的书籍去展开,所以这里也只能做到抛砖而已。期望起到抛砖引玉的作用࿰…...
信刻光盘安全隔离与信息交换系统让“数据摆渡”安全高效
随着数据传输、存储及信息技术的飞速发展,信息安全保护已成为重中之重。各安全领域对跨网数据交互的需求日益迫切,数据传输的安全可靠性成为不可忽视的关键。为满足业务需求并遵守保密规范,针对于涉及重要秘密信息,需做到安全的物…...
数据结构课程设计(java实现)---九宫格游戏,也称幻方
【问题描述】 九宫格,一款数字游戏,起源于河图洛书,与洛书是中国古代流传下来的两幅神秘图案,历来被认为是河洛文化的滥觞,中华文明的源头,被誉为"宇宙魔方"。九宫格游戏对人们的思维锻炼有着极大…...
[思考记录]AI时代下,悄然的改变
尝试用 xAI-Grok 去了解DS开源周的信息,有那么点被Grok的输出惊艳到。“请你以技术编辑的角色,重点参考官方文档,介绍DeepSeek开源周的内容,写一篇技术分享文章。”,得到的文字看起来很是舒服,内容靠谱、结…...
JAVA笔记【一】
现实 (抽象) 类 (创建) 对象 特点: 1.面向对象 2.跨平台 3.安全性 4.多线程 java程序基本结构 1. java源代码文件实际是普通的文本文件,源代码文件必须是.java扩展名,且必须小写 2. …...
[Java基础] 常用注解
文章目录 1. 元注解2. 非元注解2.1 常用JDK自带注解2.2 常用Spring相关注解2.2.1 在Spring框架中,注解用于简化配置和增强代码的可读性。以下是常用的Spring注解的一部分2.2.2 针对controller的相关注解2.2.3 AOP相关注解2.2.4 Enable系列注解 2.3 常用Lombok注解 1…...
uvm中的run_test作用
在SystemVerilog和UVM验证环境中,run_test() 是启动UVM仿真流程的核心函数。它负责初始化UVM框架、创建测试用例实例,并触发UVM的Phase机制来执行验证环境的构建和运行 1. run_test() 的作用 run_test() 是UVM提供的内置函数,定义在UVM库中…...
brew search报错,xcrun:error:invalid active developer path CommandLineTools
问题出现的原因 出现“xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun”错误,通常是因为Xcode命令行工具未正确安装或其路径已损坏。以下是几种常见的…...
C#内置委托(Action)(Func)
概述 在 C# 中,委托是一种类型,它表示对具有特定参数列表和返回类型的方法的引用。C# 提供了一些内置委托,使得开发者可以更方便地使用委托功能,无需手动定义委托类型。本文将详细介绍 Action 和 Func 这两个常用的内置委托。 A…...
kubernetes 部署项目
随着容器化技术的发展,使用Kubernetes(简称K8s)来部署和管理应用已经成为现代软件开发的标准实践之一。Kubernetes提供了一套强大的工具集,使得部署、扩展和管理应用程序变得更为简便高效。本文将带你走过从准备环境到部署一个实际…...
《几何原本》命题I.2
《几何原本》命题I.2 从一个给定的点可以引一条线段等于已知的线段。 设 A A A 为给定点, B C BC BC 为给定线段 连接 A B AB AB,作等边 △ A B D \triangle ABD △ABD 以 B B B 为圆心, B C BC BC 为半径作小圆 延长 D B DB DB 交小圆…...
【我的 PWN 学习手札】House of Kiwi
House of Kiwi 之前我们利用IO_FILE一般是通过劫持vtable来实现的, House of Kiwi虽然不是通过劫持vtable来实现,但实质上是劫持vtable指向的全局的_IO_file_jumps_表来实现的。注意:对于某些版本的glibc,_IO_file_jumps_并不可写…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...
自然语言处理——Transformer
自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN,但是…...
自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
PAN/FPN
import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...
