当前位置: 首页 > news >正文

[数据结构进阶 C++] 二叉搜索树(BinarySearchTree)的模拟实现

在这里插入图片描述

文章目录

  • 1、二叉搜索树
    • 1.1 二叉搜索数的概念
    • 1.2 二叉搜索树的操作
      • 1.2.1 二叉搜索树的查找
      • 1.2.2 二叉搜索树的插入
      • 1.2.3 二叉搜索树的删除
  • 2、二叉搜索树的应用
    • 2.1 K模型
    • 2.2 KV模型
  • 3、二叉搜索树的性能分析
  • 4、K模型与KV模型完整代码
    • 4.1 二叉搜索树的模拟实现(K模型)
    • 4.2 KV模型的模拟实现

1、二叉搜索树

1.1 二叉搜索数的概念

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树
    我们先给出两个示例:
    在这里插入图片描述
    此二叉树就不是搜索二叉树,根据性质来看,每颗子树也是二叉搜索树,红圈圈起来的地方显然是违背了这个性质。
    在这里插入图片描述
    此搜索二叉树按性质来看是完全满足的,因此此二叉树就是二叉搜索树。

1.2 二叉搜索树的操作

二叉搜索树的操作包含,增删查,下面我们就来分别模拟实现这些接口。

1.2.1 二叉搜索树的查找

思路:
a、从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。
b、最多查找高度次,走到到空,还没找到,这个值不存在。
查找比较好理解,这里就不画图了,直接开始写代码。
1.查找的非递归方法:

bool Find(const K& key)
{Node* root = _root;while (root){if (key > root->_key)root = root->_right;else if (key < root->_key)root = root->_left;elsereturn true;}return false;
}

2.查找的递归方法:

bool FindR(const K& key)
{return _FindR(_root, key);
}bool _FindR(Node* root, const K& key)
{if (nullptr == root)return false;if (key > root->_key)return _FindR(root->_right, key);else if (key < root->_key)return _FindR(root->_left, key);elsereturn true;
}

在递归查找中,我们用户在使用的时候只需要填入要查找的数字,但是我们的查找接口需要两个参数,开始节点与查找数值,因此我们在 FindR 下再封装一层 _FindR 就可以实现了。

1.2.2 二叉搜索树的插入

二叉搜索树的插入思路:
a. 树为空,则直接新增节点,赋值给root指针
b. 树不空,按二叉搜索树性质查找插入位置,插入新节点
我们先来以图例演示一遍过程:
对下面这棵树插入一个值为11的节点
在这里插入图片描述

这里是成功插入的情况,具体过程是上面的图例,我们在梳理一遍:
1、首先,我们用两个结点指针 parent和cur ,cur不断寻找正确插入位置,parent不断记下来cur的父节点指针;
2、当 cur 找到正确位置之后,先 new 一个节点对象给 cur,此时 new 出来的对象只是给了 cur 这个指针变量,并没有链接起来;
3、判断要插入的位置是 parent 的左孩子还是右孩子,再将其链接到正确位置插入就算完成了。
失败情况:
因为是二叉搜索树,节点左子树的值全小于节点的值,右子树的值全大于节点的值,不存在相等的情况,因此当找到一个节点的值与要插入的值相等时,就是插入失败,此时返回false。
根据上面的图示以及过程的梳理我们来写非递归版本的代码:

bool Insert(const K& key)
{if (nullptr == _root){_root = new Node(key);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (key > cur->_key){parnet = cur;cur = cur->_right;}else if (key < cur->_key){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(key);if (key > parent->_key){parent->_right = cur;}else{parent->_left = cur;}return true;
}

insert接口我们只提供非递归版本的。

1.2.3 二叉搜索树的删除

首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情
况:
a. 要删除的结点无孩子结点
b. 要删除的结点只有左孩子结点
c. 要删除的结点只有右孩子结点
d. 要删除的结点有左、右孩子结点

看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程
如下:
情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点–直接删除
情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点–直接删除
情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点
中,再来处理该结点的删除问题–替换法删除
1、删除节点没有左孩子

在这里插入图片描述

// 1、左为空
if (cur->_left == nullptr)
{if (cur == _root){_root = cur->_right;}if (parent->_left == cur){parent->_left = cur->_right;}else{parent->_right = cur->_right;}
}

2、删除节点没有右孩子
在这里插入图片描述

// 2、右为空
else if (cur->_right == nullptr)
{if (cur == _root){_root = cur->_left;}		if (parent->_left == cur){parent->_left = cur->_left}else{parent->_right = cur->_left;}
}

3、删除节点左右孩子都存在
在这里插入图片描述

// 3、左右都不为空
else
{Node* parent = cur;Node* subLeft = cur->_right;while (subLeft->_left){parent = subLeft;subLeft = subLeft->_left;}swap(cur->_key, subLeft->_key);if (subLeft == parent->_left)parent->_left = subLeft->_right;elseparent->_right = subLeft->_right;delete(subLeft);
}

整个删除接口合在一起的代码:

bool Erase(const K& key)
{Node* parent = nullptr;Node* cur = _root;while (cur){if (key > cur->_key){parnet = cur;cur = cur->_right}else if (key < cur->_key){parent = cur;cur = cur->_left;}else{// 1、左为空if (cur->_left == nullptr){if (cur == _root){_root = cur->_right;}if (parent->_left == cur){parent->_left = cur->_right;}else{parent->_right = cur->_right;}delete(cur);}// 2、右为空else if (cur->_right == nullptr){if (cur == _root){_root = cur->_left;}		if (parent->_left == cur){parent->_left = cur->_left}else{parent->_right = cur->_left;}delete(cur);}// 3、左右都不为空else{Node* parent = cur;Node* subLeft = cur->_right;while (subLeft->_left){parent = subLeft;subLeft = subLeft->_left;}swap(cur->_key, subLeft->_key);if (subLeft == parent->_left)parent->_left = subLeft->_right;elseparent->_right = subLeft->_right;delete(subLeft);}}}	
}

递归版本:
递归版本与非递归版本类似, 非递归版本中使用while循环来找key位置,递归就是不断调用自身来找key位置当找到后依然分三种情况来分析:左为空、右为空、左右都不为空。
递归版本中用引用来接受传来的root,因此不用考虑链接问题也就不存在判断删除位置是父节点的左还是右,直接修改root即可。
1、左为空/左右都为空:先记下要删除的节点,再修改root,最后释放删除的节点。 Node* del = root; root = root->_right; delete(del);
2、右为空:先记下要删除的节点,再修改root,最后释放删除的节点。Node* del = root; root = root->_left; delete(del);
3、左右都不为空:先找到右子树的最左节点(最小值),交换 root->_key与subLeft->_key, 右子树的最左节点一定是叶子节点,因此删除subLeft直接再去调用函数自身即可,即再来一次左右都为空(左为空)的删除。

bool EraseR(const K& key)
{return _EraseR(_root, key);
}
bool _EraseR(Node*& root, const K& key)
{if (root == nullptr)return false;if (root->_key < key){_EraseR(root->_right, key);}else if (root->_key > key){_EraseR(root->_left, key);}else{if (root->_left == nullptr){Node* del = root;root = root->_right;delete(del);// 必须释放,不释放会导致内存泄漏return true;}else if (root->_right == nullptr){Node* del = root;root = root->_left;delete(del);return true;}else{Node* subLeft = root->_right;while (subLeft->_left){subLeft = subLeft->_left;}swap(root->_key, subLeft->_key);return _EraseR(root->_right, key);}}
}

2、二叉搜索树的应用

2.1 K模型

K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值。
比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:

  • 以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树
  • 在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。

2.2 KV模型

KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。 该种方式在现实生活中非常常见:
● 比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文<word, chinese>就构成一种键值对;
● 再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是<word, count>就构成一种键值对。

3、二叉搜索树的性能分析

插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。
对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。
但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:
在这里插入图片描述
最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为:log_2 N
最差情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为:N

最差情况是退化为单支树,性能就变得很差了,因为后续我们将改进搜索二叉树的插入与删除,来实现平衡二叉搜索树,即AVL树与红黑树(RB树)。

4、K模型与KV模型完整代码

4.1 二叉搜索树的模拟实现(K模型)

namespace key
{template <class K>struct BSTreeNode{BSTreeNode* _left;BSTreeNode* _right;K _key;BSTreeNode(const K& key):_left(nullptr), _right(nullptr), _key(key){}};template <class K>class BSTree{typedef BSTreeNode<K> Node;public:bool Insert(const K& key){if (_root == nullptr){_root = new Node(key);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(key);if (parent->_key < key){parent->_right = cur;}else{parent->_left = cur;}return true;}bool Find(const K& key){Node* cur = _root;while (cur){if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}else{return true;}}return true;}bool Erase(const K& key){Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{// 删除分情况讨论:// 1. 左为空(左右都为空);2. 右为空;3.左右都不为空if (cur->_left == nullptr)// 左为空{if (cur == _root)// 根节点是删除的节点情况_root = cur->_right;else{if (cur == parent->_left){parent->_left = cur->_right;}else{parent->_right = cur->_right;}}delete(cur);}else if (cur->_right == nullptr)// 右为空{if (cur == _root)// 根节点是删除的节点情况_root = cur->_left;else{if (cur == parent->_left){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}delete(cur);}else// 左右都不为空{// 替换法,找左子树最大 / 右子树最小来替换curNode* parent = cur;Node* subLeft = cur->_right;while (subLeft->_left){parent = subLeft;subLeft = subLeft->_left;}swap(cur->_key, subLeft->_key);if (subLeft == parent->_left)parent->_left = subLeft->_right;else// 处理删除根节点的情况parent->_right = subLeft->_right;delete(subLeft);}return true;}}return false;}void InOrder(){_InOrder(_root);cout << endl;}//递归bool InsertR(const K& key){return _InsertR(_root, key);}bool FindR(const K& key){return _FindR(_root, key);}bool EraseR(const K& key){return _EraseR(_root, key);}// C++11BSTree() = default; // 强制编译器生成默认构造BSTree(const BSTree<K>& t){_root = Copy(t._root);}// 赋值重载,现代写法BSTree<K>& operator=(BSTree<K> t){swap(_root, t._root);return *this;}~BSTree(){Destroy(_root);}private:Node* Copy(Node* root){if (root == nullptr)return nullptr;Node* newRoot = new Node(root->_key);newRoot->_left = Copy(root->_left);newRoot->_right = Copy(root->_right);return newRoot;}void Destroy(Node*& root){if (root == nullptr)return;Destroy(root->_left);Destroy(root->_right);delete root;root = nullptr;}bool _InsertR(Node*& root, const K& key){if (root == nullptr){root = new Node(key);return true;}if (root->_key < key){_InsertR(root->_right, key);}else if (root->_key > key){_InsertR(root->_left, key);}else{return false;}}bool _FindR(Node* root, const K& key){if (root == nullptr){return false;}if (root->_key < key){_FindR(root->_right, key);}else if (root->_key > key){_FindR(root->_left, key);}else{return true;}}bool _EraseR(Node*& root, const K& key){if (root == nullptr)return false;if (root->_key < key){_EraseR(root->_right, key);}else if (root->_key > key){_EraseR(root->_left, key);}else{if (root->_left == nullptr){Node* del = root;root = root->_right;delete(del);// 必须释放,不释放会导致内存泄漏return true;}else if (root->_right == nullptr){Node* del = root;root = root->_left;delete(del);return true;}else{Node* subLeft = root->_right;while (subLeft->_left){subLeft = subLeft->_left;}swap(root->_key, subLeft->_key);return _EraseR(root->_right, key);}}}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}private:Node* _root = nullptr;};
};

4.2 KV模型的模拟实现

namespace kv
{template <class K, class V>struct BSTreeNode{BSTreeNode<K, V>* _left;BSTreeNode<K, V>* _right;K _key;V _val;BSTreeNode(const K& key, const V& val):_left(nullptr), _right(nullptr), _key(key), _val(val){}};template <class K, class V>class BSTree{typedef BSTreeNode<K, V> Node;public:bool Insert(const K& key, const V& val){if (_root == nullptr){_root = new Node(key, val);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(key, val);if (parent->_key < key){parent->_right = cur;}else{parent->_left = cur;}return true;}Node* Find(const K& key){Node* cur = _root;while (cur){if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}else{return cur;}}return nullptr;}bool Erase(const K& key){Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{// 删除分情况讨论:// 1. 左为空(左右都为空);2. 右为空;3.左右都不为空if (cur->_left == nullptr)// 左为空{if (cur == _root)// 根节点是删除的节点情况_root = cur->_right;else{if (cur == parent->_left){parent->_left = cur->_right;}else{parent->_right = cur->_right;}}delete(cur);}else if (cur->_right == nullptr)// 右为空{if (cur == _root)// 根节点是删除的节点情况_root = cur->_left;else{if (cur == parent->_left){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}delete(cur);}else// 左右都不为空{// 替换法,找左子树最大 / 右子树最小来替换curNode* parent = cur;Node* subLeft = cur->_right;while (subLeft->_left){parent = subLeft;subLeft = subLeft->_left;}swap(cur->_key, subLeft->_key);if (subLeft == parent->_left)parent->_left = subLeft->_right;else// 处理删除根节点的情况parent->_right = subLeft->_right;delete(subLeft);}return true;}}return false;}void InOrder(){_InOrder(_root);cout << endl;}private:void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << " : " << root->_val << endl;_InOrder(root->_right);}private:Node* _root = nullptr;};
};

相关文章:

[数据结构进阶 C++] 二叉搜索树(BinarySearchTree)的模拟实现

文章目录 1、二叉搜索树1.1 二叉搜索数的概念1.2 二叉搜索树的操作1.2.1 二叉搜索树的查找1.2.2 二叉搜索树的插入1.2.3 二叉搜索树的删除 2、二叉搜索树的应用2.1 K模型2.2 KV模型 3、二叉搜索树的性能分析4、K模型与KV模型完整代码4.1 二叉搜索树的模拟实现&#xff08;K模型…...

PostGIS学习教程十四:更多的空间连接

PostGIS学习教程十四&#xff1a;更多的空间连接 在上一节中&#xff0c;我们看到了ST_Centroid(geometry)和ST_Union([geometry])函数&#xff0c;以及一些简单的示例。在本节中&#xff0c;我们将用它们做一些更详细的事情。 提示&#xff1a;写完文章后&#xff0c;目录可以…...

【爬虫软件】孔夫子二手书采集

项目演示 孔网爬取图书信息 目录结构 [ |-- api-ms-win-core-synch-l1-2-0.dll, |-- api-ms-win-core-sysinfo-l1-1-0.dll, |-- api-ms-win-core-timezone-l1-1-0.dll, |-- api-ms-win-core-util-l1-1-0.dll, |-- api-ms-win-crt-conio-l1-1-0.dll, |-- api…...

P8736 [蓝桥杯 2020 国 B] 游园安排

题目描述 L \mathrm{L} L 星球游乐园非常有趣&#xff0c;吸引着各个星球的游客前来游玩。小蓝是 L \mathrm{L} L 星球 游乐园的管理员。 为了更好的管理游乐园&#xff0c;游乐园要求所有的游客提前预约&#xff0c;小蓝能看到系统上所有预约游客的名字。每个游客的名字由一…...

初识Docker-什么是docker

Docker是一个快速交付应用、运行应用的技术 目录 一、Docker 二、运用场景 一、什么是Docker&#xff1f;它的作用是什么&#xff1f; Docker如何解决大型项目依赖关系复杂&#xff0c;不同组件依赖的兼容性问题? Docker允许开发中将应用、依赖、函数库、配置一起打包&…...

maven的pom.xml设置本地仓库

配置 在Maven项目中&#xff0c;您可以在pom.xml文件中配置本地仓库的路径。在pom.xml文件中&#xff0c;您可以添加以下配置来指定本地仓库的路径&#xff1a; <project>...<repositories><repository><id>local-repo</id><url>file://…...

Qt获取屏幕DPI缩放比

获取屏幕缩放比 网上很多代码是用 logicalDotsPerInch 除以 96 来获取屏幕缩放比&#xff1a; // Windows 除以 96&#xff0c;macOS 除以 72 qreal factor window->screen()->logicalDotsPerInch() / 96.0; 当使能了缩放适配后&#xff0c;logicalDotsPerInch 值就不…...

Spring MVC控制层框架

三、Spring MVC控制层框架 目录 一、SpringMVC简介和体验 1. 介绍2. 主要作用3. 核心组件和调用流程理解4. 快速体验 二、SpringMVC接收数据 1. 访问路径设置2. 接收参数&#xff08;重点&#xff09; 2.1 param 和 json参数比较2.2 param参数接收2.3 路径 参数接收2.4 json参…...

vmware安装银河麒麟V10高级服务器操作系统

vmware安装银河麒麟V10高级服务器操作系统 1、下载银河麒麟V10镜像2、VMware安装银河麒麟V10高级服务器操作系统2.1、新建虚拟机2.2、安装虚拟机 3、配置银河麒麟V10高级服务器操作系统3.1、安装vmware tools3.2、配置静态IP地址 和 dns3.3、查看磁盘分区3.4、查看系统版本 1、…...

掌握Jenknis基础概念

目录 任务&#xff08;Jobs&#xff09; 构建&#xff08;Builds&#xff09; 触发器&#xff08;Triggers&#xff09; 构建环境&#xff08;Build Environment&#xff09;&#xff1a; 插件&#xff08;Plugins&#xff09;&#xff1a; 参数化构建&#xff08;Paramet…...

AWS 知识二:AWS同一个VPC下的ubuntu实例通过ldapsearch命令查询目录用户信息

前言&#xff1a; 前提&#xff1a;需要完成我的AWS 知识一创建一个成功运行的目录。 主要两个重要&#xff1a;1.本地windows如何通过SSH的方式连接到Ubuntu实例 2.ldapsearch命令的构成 一 &#xff0c;启动一个新的Ubuntu实例 1.创建一个ubuntu实例 具体创建实例步骤我就不…...

Ubuntu 常用命令之 fdisk 命令用法介绍

&#x1f4d1;Linux/Ubuntu 常用命令归类整理 fdisk 是一个用于处理磁盘分区的命令行工具&#xff0c;它在 Linux 系统中广泛使用。fdisk 命令可以创建、删除、更改、复制和显示硬盘分区&#xff0c;以及更改硬盘的分区 ID。 fdisk 命令的常用参数如下 -l&#xff1a;列出所…...

论文中公式怎么降重 papergpt

大家好&#xff0c;今天来聊聊论文中公式怎么降重&#xff0c;希望能给大家提供一点参考。 以下是针对论文重复率高的情况&#xff0c;提供一些修改建议和技巧&#xff0c;可以借助此类工具&#xff1a; 论文中公式怎么降重 一、引言 在论文撰写过程中&#xff0c;公式是表达学…...

27. 过滤器

Filter(过滤器)简介 Filter 的基本功能是对 Servlet 容器调用 Servlet 的过程进行拦截&#xff0c;从而在 Servlet 进行响应处理的前后实现一些特殊的功能。在 Servlet API 中定义了三个接口类来开供开发人员编写 Filter 程序&#xff1a;Filter, FilterChain, FilterConfigFi…...

做一个wiki页面是体验HTML语义的好方法

HTML语义&#xff1a;如何运用语义类标签来呈现Wiki网页 在上一篇文章中&#xff0c;我花了大量的篇幅和你解释了正确使用语义类标签的好处和一些场景。那么&#xff0c;哪些场景适合用到语义类标签呢&#xff0c;又如何运用语义类标签呢&#xff1f; 不知道你还记不记得在大…...

金融CRM有用吗?金融行业CRM有哪些功能

市场形式波诡云谲&#xff0c;金融行业也面临着资源体系分散、竞争力后继不足、未知风险无法规避等问题。金融企业该如何解决这些问题&#xff0c;或许可以了解一下CRM管理系统&#xff0c;和其提供的金融行业CRM解决方案。 金融行业是银行业、保险业、信托业、证券业和租赁业…...

@XmlAccessorType+@XmlElement完美解决Java类到XML映射问题

前言&#xff1a; 最近项目在做静态代码扫描的时候&#xff0c;出现Java类中成员变量命名的问题&#xff0c;开头字母必须小写&#xff0c;但是这个类成员是对接其他公司的字段&#xff0c;对方提供的请求格式是XML&#xff0c;必须将Java类转化为XML的格式&#xff0c;而且这…...

软件渗透测试有哪些测试流程?权威安全测试报告的重要性

软件渗透测试也是安全测试的一种&#xff0c;是通过模拟恶意黑客的攻击方法&#xff0c;来评估计算机网络系统安全的一种评估方法。作为网络安全防范的一种新技术&#xff0c;对于网络安全组织具有实际应用价值。 一、软件渗透测试的过程   软件渗透测试的过程通常包括四个主…...

安防视频融合云平台/智慧监控平台EasyCVR如何添加验证码调用接口?

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…...

浏览器输入一个url,它的解析过程

URL解析&#xff1a; 浏览器首先解析URL&#xff0c;提取其中的协议&#xff08;例如&#xff0c;HTTP、HTTPS&#xff09;、域名和路径等信息。这个过程被称为URL解析。 DNS解析&#xff1a; 浏览器会检查域名的IP地址是否已经缓存。如果没有缓存或者缓存已经过期&#xff0c;…...

线程同步:确保多线程程序的安全与高效!

全文目录&#xff1a; 开篇语前序前言第一部分&#xff1a;线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分&#xff1a;synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分&#xff…...

如何在最短时间内提升打ctf(web)的水平?

刚刚刷完2遍 bugku 的 web 题&#xff0c;前来答题。 每个人对刷题理解是不同&#xff0c;有的人是看了writeup就等于刷了&#xff0c;有的人是收藏了writeup就等于刷了&#xff0c;有的人是跟着writeup做了一遍就等于刷了&#xff0c;还有的人是独立思考做了一遍就等于刷了。…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf

FTP 客服管理系统 实现kefu123登录&#xff0c;不允许匿名访问&#xff0c;kefu只能访问/data/kefu目录&#xff0c;不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...

STM32HAL库USART源代码解析及应用

STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...

解读《网络安全法》最新修订,把握网络安全新趋势

《网络安全法》自2017年施行以来&#xff0c;在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂&#xff0c;网络攻击、数据泄露等事件频发&#xff0c;现行法律已难以完全适应新的风险挑战。 2025年3月28日&#xff0c;国家网信办会同相关部门起草了《网络安全…...

Caliper 配置文件解析:fisco-bcos.json

config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...

苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会

在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...

加密通信 + 行为分析:运营商行业安全防御体系重构

在数字经济蓬勃发展的时代&#xff0c;运营商作为信息通信网络的核心枢纽&#xff0c;承载着海量用户数据与关键业务传输&#xff0c;其安全防御体系的可靠性直接关乎国家安全、社会稳定与企业发展。随着网络攻击手段的不断升级&#xff0c;传统安全防护体系逐渐暴露出局限性&a…...

DAY 45 超大力王爱学Python

来自超大力王的友情提示&#xff1a;在用tensordoard的时候一定一定要用绝对位置&#xff0c;例如&#xff1a;tensorboard --logdir"D:\代码\archive (1)\runs\cifar10_mlp_experiment_2" 不然读取不了数据 知识点回顾&#xff1a; tensorboard的发展历史和原理tens…...

麒麟系统使用-进行.NET开发

文章目录 前言一、搭建dotnet环境1.获取相关资源2.配置dotnet 二、使用dotnet三、其他说明总结 前言 麒麟系统的内核是基于linux的&#xff0c;如果需要进行.NET开发&#xff0c;则需要安装特定的应用。由于NET Framework 是仅适用于 Windows 版本的 .NET&#xff0c;所以要进…...