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_并不可写…...

网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...

简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

黑马Mybatis
Mybatis 表现层:页面展示 业务层:逻辑处理 持久层:持久数据化保存 在这里插入图片描述 Mybatis快速入门 
STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...

React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...