在这里插入图片描述

Java中的贪心算法应用:决策树(ID3/C4.5)详解

决策树是一种常用的机器学习算法,它通过递归地将数据集分割成更小的子集来构建树形结构。ID3和C4.5是两种经典的决策树算法,它们都使用了贪心算法来选择最优的特征进行分割。下面我们将从原理到实现,全面详细地讲解这两种算法在Java中的应用。

一、决策树基础概念

1. 什么是决策树

决策树是一种树形结构,其中:

  • 内部节点表示一个特征或属性
  • 分支代表该特征的可能取值
  • 叶节点代表最终的决策结果(分类或回归值)

2. 决策树构建的核心问题

构建决策树时需要解决两个关键问题:

  1. 如何选择最优的特征进行分割(这正是贪心算法的应用点)
  2. 何时停止树的生长(预剪枝或后剪枝)

二、贪心算法在决策树中的应用

贪心算法在决策树构建中体现在每次选择当前最优的特征进行分割,而不考虑全局最优。这种局部最优的选择策略使得算法高效,但可能无法得到全局最优的决策树。

贪心选择策略:

  1. 从根节点开始,计算所有特征的信息增益(ID3)或信息增益比(C4.5)
  2. 选择信息增益(比)最大的特征作为当前节点的分割特征
  3. 对每个特征值创建分支,并递归地重复上述过程

三、ID3算法详解

1. ID3算法核心思想

ID3(Iterative Dichotomiser 3)算法使用信息增益作为特征选择标准,倾向于选择取值较多的特征。

2. 关键概念与公式

信息熵(Entropy):

度量样本集合纯度的指标,熵越小纯度越高。

公式:

H(D) = -Σ(p_k * log₂p_k)

其中p_k是第k类样本在数据集D中的比例

条件熵:

已知特征A的条件下,数据集D的熵。

公式:

H(D|A) = Σ(|D_v|/|D| * H(D_v))

其中D_v是特征A取值为v的子集

信息增益:

特征A对数据集D的信息增益是D的熵与条件熵之差。

公式:

Gain(D,A) = H(D) - H(D|A)

3. ID3算法步骤

  1. 计算数据集D的熵H(D)
  2. 对每个特征A:
    • 计算条件熵H(D|A)
    • 计算信息增益Gain(D,A)
  3. 选择信息增益最大的特征作为当前节点的分割特征
  4. 对每个特征值创建分支,递归构建子树
  5. 终止条件:
    • 所有样本属于同一类别
    • 没有剩余特征可用于分割
    • 分支下没有样本

4. ID3算法的局限性

  1. 倾向于选择取值较多的特征(可能过拟合)
  2. 不能处理连续值特征
  3. 不能处理缺失值
  4. 没有剪枝策略,容易过拟合

四、C4.5算法详解

C4.5是对ID3的改进算法,使用信息增益比作为特征选择标准,并增加了对连续值和缺失值的处理。

1. C4.5的改进点

  1. 使用信息增益比代替信息增益
  2. 可以处理连续值特征
  3. 可以处理缺失值
  4. 增加了剪枝策略

2. 关键概念与公式

固有值(Intrinsic Value):

特征A的固有值衡量特征取值的分散程度。

公式:

IV(A) = -Σ(|D_v|/|D| * log₂(|D_v|/|D|))
信息增益比:

信息增益与固有值的比值。

公式:

GainRatio(D,A) = Gain(D,A) / IV(A)

3. 连续值处理

对于连续值特征A:

  1. 将特征A的取值排序
  2. 考虑每两个相邻值的中间点作为候选分割点
  3. 对每个候选分割点t,将数据集分为A≤t和A>t两部分
  4. 计算每个分割点的信息增益,选择最优分割点

4. 缺失值处理

  1. 计算信息增益时,只使用非缺失样本
  2. 将缺失值样本按比例分配到各分支

5. C4.5算法步骤

  1. 计算数据集D的熵H(D)
  2. 对每个特征A:
    • 如果是离散特征:计算信息增益比
    • 如果是连续特征:找到最佳分割点并计算信息增益比
  3. 选择信息增益比最大的特征作为当前节点的分割特征
  4. 对每个特征值创建分支,递归构建子树
  5. 使用预剪枝或后剪枝策略防止过拟合

五、Java实现决策树

下面我们给出一个完整的Java实现,包括ID3和C4.5算法。

1. 数据结构定义

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;// 决策树节点类
class TreeNode {String featureName; // 分裂特征名称(内部节点)String decision; // 决策结果(叶节点)boolean isLeaf;Map<String, TreeNode> children; // 子节点映射public TreeNode() {children = new HashMap<>();}
}// 样本数据类
class DataSample {Map<String, String> features; // 特征名->特征值String label; // 类别标签public DataSample() {features = new HashMap<>();}
}// 数据集类
class DataSet {List<DataSample> samples;List<String> featureNames;public DataSet(List<String> featureNames) {this.featureNames = new ArrayList<>(featureNames);this.samples = new ArrayList<>();}public void addSample(DataSample sample) {samples.add(sample);}// 获取指定特征的取值集合public List<String> getFeatureValues(String featureName) {List<String> values = new ArrayList<>();for (DataSample sample : samples) {String value = sample.features.get(featureName);if (!values.contains(value)) {values.add(value);}}return values;}// 根据特征和取值分割数据集public DataSet split(String featureName, String value) {DataSet subset = new DataSet(featureNames);for (DataSample sample : samples) {if (sample.features.get(featureName).equals(value)) {subset.addSample(sample);}}return subset;}// 判断是否所有样本属于同一类别public boolean isPure() {if (samples.isEmpty()) return true;String firstLabel = samples.get(0).label;for (DataSample sample : samples) {if (!sample.label.equals(firstLabel)) {return false;}}return true;}// 获取多数类别public String getMajorityLabel() {Map<String, Integer> labelCounts = new HashMap<>();for (DataSample sample : samples) {labelCounts.put(sample.label, labelCounts.getOrDefault(sample.label, 0) + 1);}String majorityLabel = null;int maxCount = -1;for (Map.Entry<String, Integer> entry : labelCounts.entrySet()) {if (entry.getValue() > maxCount) {maxCount = entry.getValue();majorityLabel = entry.getKey();}}return majorityLabel;}
}

2. 决策树工具类(实现ID3和C4.5)

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class DecisionTree {private boolean useID3; // true使用ID3,false使用C4.5public DecisionTree(boolean useID3) {this.useID3 = useID3;}// 计算信息熵private double calculateEntropy(DataSet dataSet) {Map<String, Integer> labelCounts = new HashMap<>();int total = dataSet.samples.size();for (DataSample sample : dataSet.samples) {labelCounts.put(sample.label, labelCounts.getOrDefault(sample.label, 0) + 1);}double entropy = 0.0;for (int count : labelCounts.values()) {double probability = (double) count / total;entropy -= probability * (Math.log(probability) / Math.log(2));}return entropy;}// 计算条件熵private double calculateConditionalEntropy(DataSet dataSet, String featureName) {double conditionalEntropy = 0.0;int total = dataSet.samples.size();List<String> featureValues = dataSet.getFeatureValues(featureName);for (String value : featureValues) {DataSet subset = dataSet.split(featureName, value);double subsetEntropy = calculateEntropy(subset);conditionalEntropy += ((double) subset.samples.size() / total) * subsetEntropy;}return conditionalEntropy;}// 计算信息增益private double calculateInformationGain(DataSet dataSet, String featureName) {double entropy = calculateEntropy(dataSet);double conditionalEntropy = calculateConditionalEntropy(dataSet, featureName);return entropy - conditionalEntropy;}// 计算固有值private double calculateIntrinsicValue(DataSet dataSet, String featureName) {double intrinsicValue = 0.0;int total = dataSet.samples.size();List<String> featureValues = dataSet.getFeatureValues(featureName);for (String value : featureValues) {DataSet subset = dataSet.split(featureName, value);double ratio = (double) subset.samples.size() / total;intrinsicValue -= ratio * (Math.log(ratio) / Math.log(2));}return intrinsicValue;}// 计算信息增益比private double calculateGainRatio(DataSet dataSet, String featureName) {double informationGain = calculateInformationGain(dataSet, featureName);double intrinsicValue = calculateIntrinsicValue(dataSet, featureName);// 避免除以0if (intrinsicValue == 0) {return 0;}return informationGain / intrinsicValue;}// 选择最佳分裂特征private String chooseBestFeature(DataSet dataSet, List<String> remainingFeatures) {String bestFeature = null;double bestScore = -Double.MAX_VALUE;for (String feature : remainingFeatures) {double score;if (useID3) {score = calculateInformationGain(dataSet, feature);} else {score = calculateGainRatio(dataSet, feature);}if (score > bestScore) {bestScore = score;bestFeature = feature;}}return bestFeature;}// 构建决策树public TreeNode buildTree(DataSet dataSet, List<String> remainingFeatures) {TreeNode node = new TreeNode();// 终止条件1:所有样本属于同一类别if (dataSet.isPure()) {node.isLeaf = true;node.decision = dataSet.samples.get(0).label;return node;}// 终止条件2:没有剩余特征可用于分割if (remainingFeatures.isEmpty()) {node.isLeaf = true;node.decision = dataSet.getMajorityLabel();return node;}// 选择最佳分裂特征String bestFeature = chooseBestFeature(dataSet, remainingFeatures);node.featureName = bestFeature;node.isLeaf = false;// 从剩余特征中移除已选特征List<String> newRemainingFeatures = new ArrayList<>(remainingFeatures);newRemainingFeatures.remove(bestFeature);// 递归构建子树List<String> featureValues = dataSet.getFeatureValues(bestFeature);for (String value : featureValues) {DataSet subset = dataSet.split(bestFeature, value);if (subset.samples.isEmpty()) {// 如果子集为空,创建叶节点,使用父节点的多数类别TreeNode leafNode = new TreeNode();leafNode.isLeaf = true;leafNode.decision = dataSet.getMajorityLabel();node.children.put(value, leafNode);} else {// 递归构建子树node.children.put(value, buildTree(subset, newRemainingFeatures));}}return node;}// 预测样本类别public String predict(TreeNode root, DataSample sample) {if (root.isLeaf) {return root.decision;}String featureValue = sample.features.get(root.featureName);TreeNode child = root.children.get(featureValue);if (child == null) {// 如果特征值在训练时未出现,返回null或采取其他策略return null;}return predict(child, sample);}// 打印决策树(用于调试)public void printTree(TreeNode node, String indent) {if (node.isLeaf) {System.out.println(indent + "Predict: " + node.decision);return;}System.out.println(indent + "Feature: " + node.featureName);for (Map.Entry<String, TreeNode> entry : node.children.entrySet()) {System.out.println(indent + "  " + entry.getKey() + ":");printTree(entry.getValue(), indent + "    ");}}
}

3. 使用示例

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;public class Main {public static void main(String[] args) {// 定义特征名称List<String> featureNames = Arrays.asList("Outlook", "Temperature", "Humidity", "Wind");// 创建数据集DataSet dataSet = new DataSet(featureNames);// 添加样本数据(天气、温度、湿度、风况、是否打球)addSample(dataSet, "Sunny", "Hot", "High", "Weak", "No");addSample(dataSet, "Sunny", "Hot", "High", "Strong", "No");addSample(dataSet, "Overcast", "Hot", "High", "Weak", "Yes");addSample(dataSet, "Rain", "Mild", "High", "Weak", "Yes");addSample(dataSet, "Rain", "Cool", "Normal", "Weak", "Yes");addSample(dataSet, "Rain", "Cool", "Normal", "Strong", "No");addSample(dataSet, "Overcast", "Cool", "Normal", "Strong", "Yes");addSample(dataSet, "Sunny", "Mild", "High", "Weak", "No");addSample(dataSet, "Sunny", "Cool", "Normal", "Weak", "Yes");addSample(dataSet, "Rain", "Mild", "Normal", "Weak", "Yes");addSample(dataSet, "Sunny", "Mild", "Normal", "Strong", "Yes");addSample(dataSet, "Overcast", "Mild", "High", "Strong", "Yes");addSample(dataSet, "Overcast", "Hot", "Normal", "Weak", "Yes");addSample(dataSet, "Rain", "Mild", "High", "Strong", "No");// 使用ID3算法构建决策树System.out.println("ID3 Decision Tree:");DecisionTree id3Tree = new DecisionTree(true);TreeNode id3Root = id3Tree.buildTree(dataSet, new ArrayList<>(featureNames));id3Tree.printTree(id3Root, "");// 使用C4.5算法构建决策树System.out.println("\nC4.5 Decision Tree:");DecisionTree c45Tree = new DecisionTree(false);TreeNode c45Root = c45Tree.buildTree(dataSet, new ArrayList<>(featureNames));c45Tree.printTree(c45Root, "");// 预测新样本DataSample newSample = new DataSample();newSample.features.put("Outlook", "Sunny");newSample.features.put("Temperature", "Cool");newSample.features.put("Humidity", "High");newSample.features.put("Wind", "Strong");System.out.println("\nPrediction for new sample (Sunny, Cool, High, Strong):");System.out.println("ID3: " + id3Tree.predict(id3Root, newSample));System.out.println("C4.5: " + c45Tree.predict(c45Root, newSample));}private static void addSample(DataSet dataSet, String outlook, String temperature, String humidity, String wind, String label) {DataSample sample = new DataSample();sample.features.put("Outlook", outlook);sample.features.put("Temperature", temperature);sample.features.put("Humidity", humidity);sample.features.put("Wind", wind);sample.label = label;dataSet.addSample(sample);}
}

六、算法优化与扩展

1. 预剪枝策略

为了防止过拟合,可以在决策树构建过程中加入预剪枝策略:

// 在buildTree方法中添加预剪枝判断
public TreeNode buildTree(DataSet dataSet, List<String> remainingFeatures, int maxDepth, int currentDepth) {// 终止条件3:达到最大深度if (currentDepth >= maxDepth) {TreeNode leafNode = new TreeNode();leafNode.isLeaf = true;leafNode.decision = dataSet.getMajorityLabel();return leafNode;}// ...原有代码...
}

2. 连续值特征处理

扩展C4.5算法处理连续值特征:

// 在DecisionTree类中添加连续值处理方法
private String chooseBestFeatureForContinuous(DataSet dataSet, List<String> remainingFeatures) {String bestFeature = null;double bestScore = -Double.MAX_VALUE;double bestSplitPoint = 0;for (String feature : remainingFeatures) {// 检查是否是连续值特征(假设连续值特征以"Cont_"前缀标识)if (feature.startsWith("Cont_")) {// 获取所有样本的该特征值并排序List<Double> values = new ArrayList<>();for (DataSample sample : dataSet.samples) {values.add(Double.parseDouble(sample.features.get(feature)));}Collections.sort(values);// 检查相邻值之间的候选分割点for (int i = 0; i < values.size() - 1; i++) {double splitPoint = (values.get(i) + values.get(i + 1)) / 2;// 临时修改特征值为离散值(≤splitPoint和>splitPoint)DataSet tempDataSet = new DataSet(dataSet.featureNames);for (DataSample sample : dataSet.samples) {DataSample tempSample = new DataSample();for (String f : dataSet.featureNames) {if (f.equals(feature)) {double val = Double.parseDouble(sample.features.get(f));tempSample.features.put(f, val <= splitPoint ? "≤" + splitPoint : ">" + splitPoint);} else {tempSample.features.put(f, sample.features.get(f));}}tempSample.label = sample.label;tempDataSet.addSample(tempSample);}double score = calculateGainRatio(tempDataSet, feature);if (score > bestScore) {bestScore = score;bestFeature = feature;bestSplitPoint = splitPoint;}}} else {// 离散特征处理(原有逻辑)double score = calculateGainRatio(dataSet, feature);if (score > bestScore) {bestScore = score;bestFeature = feature;}}}// 对于连续值特征,保存分割点信息if (bestFeature != null && bestFeature.startsWith("Cont_")) {return bestFeature + ":" + bestSplitPoint;}return bestFeature;
}

3. 缺失值处理

扩展C4.5算法处理缺失值:

// 在DecisionTree类中添加缺失值处理方法
private double calculateInformationGainWithMissing(DataSet dataSet, String featureName) {// 计算非缺失样本的比例int total = dataSet.samples.size();int missingCount = 0;for (DataSample sample : dataSet.samples) {if (sample.features.get(featureName) == null) {missingCount++;}}if (missingCount == total) {return 0; // 所有样本该特征都缺失}double nonMissingRatio = (double) (total - missingCount) / total;// 创建非缺失样本的子集DataSet nonMissingSubset = new DataSet(dataSet.featureNames);for (DataSample sample : dataSet.samples) {if (sample.features.get(featureName) != null) {nonMissingSubset.addSample(sample);}}// 计算信息增益double entropy = calculateEntropy(dataSet);double conditionalEntropy = calculateConditionalEntropy(nonMissingSubset, featureName);return nonMissingRatio * (entropy - conditionalEntropy);
}// 在构建树时处理缺失值
private TreeNode buildTreeWithMissing(DataSet dataSet, List<String> remainingFeatures) {// ...类似原有buildTree方法,但在分割时处理缺失值...// 对于有缺失值的样本,将其按比例分配到各分支for (String value : featureValues) {DataSet subset = dataSet.split(bestFeature, value);// 计算该特征值的比例double ratio = (double) subset.samples.size() / (dataSet.samples.size() - missingCount);// 添加缺失值样本到该分支,但权重按比例// 实际实现可能需要修改数据结构以支持加权样本// ...}// ...
}

七、决策树的优缺点

优点:

  1. 易于理解和解释(可视化)
  2. 不需要太多数据预处理(如归一化)
  3. 可以处理数值和类别数据
  4. 能够处理多输出问题
  5. 使用白盒模型,结果可解释

缺点:

  1. 容易过拟合(需要剪枝)
  2. 可能不稳定(数据微小变化导致完全不同树)
  3. 贪心算法不能保证全局最优
  4. 对某些类型的关系(如XOR)难以学习
  5. 类别不平衡时可能偏向多数类

八、实际应用中的考虑

  1. 特征选择:决策树对特征选择敏感,应选择有区分力的特征
  2. 剪枝策略:合理设置预剪枝参数或使用后剪枝
  3. 类别不平衡:可以使用类权重或采样方法
  4. 多棵树集成:随机森林等集成方法可以提升性能
  5. 并行化:决策树构建可以并行化加速

九、总结

决策树作为一种经典的机器学习算法,其贪心的分割策略使其高效且易于理解,而Java的实现展示了算法的具体细节。理解这些基础算法对于掌握更复杂的集成方法(如随机森林、GBDT等)至关重要。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/923616.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/923616.shtml
英文地址,请注明出处:http://en.pswp.cn/news/923616.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华为任旭东:开源协作,激发创新,共创智能世界 | GOSIM HANGZHOU 2025

GOSIM HANGZHOU 2025峰会盛大开幕&#xff0c;华为首席开源联络官、CNCF基金会董事任旭东以《开源协作&#xff0c;激发创新&#xff0c;共创智能世界》为题发表Keynote演讲。颠覆性技术到工业应用的转换时间越来越短&#xff0c;AI技术正在推动传统软件产业的演进&#xff0c;…

本地部署 GPS 跟踪系统 Traccar 并实现外部访问

Traccar 是一款集成了强大的 java 后端服务的 GPS 跟踪系统 。它支持在多种设备使用&#xff0c;在物流运输、资产管理和个人安全等领域应用。本文将详细的介绍如何利用 Docker 在本地部署 Traccar 并结合路由侠实现外网访问本地部署的 Traccar 。 第一步&#xff0c;本地部署…

【开题答辩全过程】以 “川趣玩”旅行团预定微信小程序为例,包含答辩的问题和答案

个人简介一名14年经验的资深毕设内行人&#xff0c;语言擅长Java、php、微信小程序、Python、Golang、安卓Android等开发项目包括大数据、深度学习、网站、小程序、安卓、算法。平常会做一些项目定制化开发、代码讲解、答辩教学、文档编写、也懂一些降重方面的技巧。感谢大家的…

Android Doze低电耗休眠模式 与 WorkManager

1. Doze模式下&#xff0c;WorkManager setInitialDelay设置小于15分钟&#xff0c;被系统强制到15分钟执行&#xff0c;怎么办 ? Android 拥有两项省电功能&#xff0c;通过管理设备未连接电源时应用的行为来延长用户电池续航时间&#xff1a;低电耗模式 (Doze) 和应用待机模…

iOS 能耗监控与电池优化实战:如何查看App耗电量、分析CPU、GPU内存使用、(uni-app iOS开发性能调试指南)

在 iOS 应用开发中&#xff0c;能耗与电池消耗是用户最直观的体验指标。 即便功能完善&#xff0c;如果 App 存在以下问题&#xff1a; 电池掉电快、设备发热严重&#xff1b;后台任务执行过多&#xff1b;页面渲染与文件操作引发 CPU/GPU 过载&#xff1b;日志或缓存导致频繁 …

Git 本地分支推送多个远程分支

方法一&#xff1a;一次性推送命令 命令格式&#xff1a; git push <远程仓库名> <本地分支引用>:<远程分支名1> <本地分支引用>:<远程分支名2> ...具体步骤&#xff1a; 确保你的代码修改已经提交到了本地分支 git add . git commit -m "你…

抖音私信评论互动消息通知监听自动获取,通过qq机器人转发到qq来通知

抖音私信评论互动消息通知监听自动获取&#xff0c;通过qq机器人转发到qq来通知 如果不是抖音平台&#xff0c;其他平台也类似的&#xff0c;也可以实现&#xff0c;只是目前懒得写了 本期视频点赞过10个就开源代码 有需要的人可以在视频底下留言 需求反馈多的我可以实现

UVM验证工具--gvim

目录 gvim语法高亮 gvim支持git Linux环境自带gvim工具&#xff0c;我们需要做如下设置&#xff1a; 支持UVM、SystemVerilog、verilog语法高亮支持git&#xff08;实时显示对文件的修改&#xff09; gvim语法高亮 gvim支持git

MyBatis 从入门到精通(第二篇)—— 核心架构、配置解析与 Mapper 代理开发

在第一篇博客中&#xff0c;我们掌握了 MyBatis 的基础概念与环境搭建&#xff0c;成功通过简单查询实现了数据持久化。但要真正用好 MyBatis&#xff0c;还需深入理解其 “内部工作原理” 与 “企业级开发规范”。本篇将聚焦三大核心&#xff1a;MyBatis 架构与核心类、全局配…

uniapp+<script setup lang=“ts“>单个时间格式转换(format)

有问题的时间&#xff08;只示例&#xff0c;不是真实数据&#xff09;修改后的时间展示&#xff08;只示例&#xff0c;不是真实数据&#xff09;原代码<view v-else-if"item?.payTime" class"order-info-item">支付时间&#xff1a;item?.payTim…

运维安全05,iptables规则保存与恢复

一&#xff1a;网络安全1.1、昨日功能优化配置后引发的问题&#xff1a;配置iptables后防火墙起到了防护作用&#xff0c;但使用127.0.0.1访问不了数据库了[rootlocalhost /]# mysql -u admin -p -h 127.0.0.1 Enter password:思考&#xff1a;如果使用localhost可以访问吗&…

线性代数 · 矩阵 | 秩 / 行秩 / 列秩 / 计算方法

注&#xff1a;本文为 “线性代数 矩阵 | 秩” 相关合辑。 图片清晰度受引文原图所限。 略作重排&#xff0c;未全校去重。 如有内容异常&#xff0c;请看原文。 矩阵的秩及其应用 一、矩阵秩的基本概念 &#xff08;一&#xff09;k 阶子式 设矩阵 A(aij)mnA (a_{ij})_{m…

Ajax-day2(图书管理)-弹框显示和隐藏

Bootstrap 弹框图书管理-Bootsrap 弹框&#xff08;一&#xff09;属性控制一、模板代码二、弹框模板三、bootsrap 的显示弹框属性完整代码&#xff08;二&#xff09;JS 控制一、模板代码二、步骤图书管理-Bootsrap 弹框 Bootstrap 框架渲染列表&#xff08;查&#xff09;新…

【Linux网络】认识https

认识https一&#xff0c;概念铺垫1.1 什么是加密&#xff1f;1.2 为什么要加密&#xff1f;1.3 加密的方式1.4 数据摘要&数据指纹二&#xff0c;认识https2.1 方案1-只使用对称加密2.2 方案2-只使用非对称加密2.3 方案3-双方都使用非对称加密2.4 方案4-非对称加密对称加密2…

OC-AFNetworking

文章目录AFNetworking简介问题&#x1f914;优化策略解决AFNetworking局限性使用单例进行网络请求的优势使用单例进行网络请求的风险最优使用使用参数讲解POST请求AFNetworking 简介 这篇文章旨在实现使用AFNetworking设置一个集中的单通道网络对象&#xff0c;该对象与MVC组建…

【数据结构】跳表

目录 1.什么是跳表-skiplist 2.skiplist的效率如何保证&#xff1f; 3.skiplist的实现 3.1节点和成员设计 3.2查找实现 3.3前置节点查找 3.4插入实现 3.5删除实现 3.6随机层数 3.7完整代码 4.skiplist跟平衡搜索树和哈希表的对比 1.什么是跳表-skiplist skiplist是由…

html实现右上角有个图标,鼠标移动到该位置出现手型,点击会弹出登录窗口。

写了一段html代码实现的效果&#xff1a;实现右上角有个图标&#xff0c;鼠标移动到该位置出现手型&#xff0c;点击会弹出登录窗口。功能实现前端&#xff0c;没有实现后端。<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF…

STM32G4 电流环闭环(二) 霍尔有感运行

目录一、STM32G4 电流环闭环(二) 霍尔有感运行2. 霍尔有感运行附学习参考网址欢迎大家有问题评论交流 (* ^ ω ^)一、STM32G4 电流环闭环(二) 霍尔有感运行 2. 霍尔有感运行 文章使用的BLDC在定子侧以互差120电角度的位置安装三个霍尔元件Ha&#xff0c;Hb&#xff0c;Hc。当…

展示框选择

好的&#xff0c;非常感谢您提供更详细的项目情况。这是一个非常典型的父子组件通信场景。 根据您的新需求&#xff0c;我将对代码进行重构&#xff1a; FaultSelect.vue (子组件): 这个组件现在将变得更加“纯粹”。它只负责自身的下拉框逻辑&#xff0c;不关心外部按钮&#…

第5课:上下文管理与状态持久化

第5课:上下文管理与状态持久化 课程目标 掌握上下文存储和检索策略 学习会话状态管理 了解数据持久化方案 实践实现上下文管理系统 课程内容 5.1 上下文管理基础 什么是上下文管理? 上下文管理是Agent系统中维护和利用历史信息的能力,包括: 对话历史:用户与Agent的交互…