C++笔记:二叉搜索树(Binary Search Tree)

文章目录

  • 二叉搜索树的概念
  • 二叉搜索树操作
    • 1. 框架搭建
    • 2. 遍历
    • 3. 查找
      • 迭代实现
      • 递归实现
    • 4. 插入
      • 迭代实现
      • 递归实现
    • 5. 删除
      • 迭代实现
      • 递归实现
    • 6. 析构与销毁
    • 7. 拷贝构造与赋值重载
  • 二叉搜索树的应用
  • 二叉搜索树的性能分析
  • 二叉搜索树模拟实现源码

二叉搜索树的概念

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

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树

在这里插入图片描述

二叉搜索树操作

1. 框架搭建

// struct BinarySearchTreeNode - 结点类
template<class K>
struct BSTreeNode
{BSTreeNode* _left;BSTreeNode* _right;K _key;BSTreeNode(const K& key): _left(nullptr), _right(nullptr), _key(key){}
};// class BinarySearchTreeNode - 树类
template<class K>
class BSTree
{typedef BSTreeNode<K> Node;public:protected:Node* _root;
};

【说明】

  1. BSTreeNode 类使用 struct 定义,其成员受默认访问限定符 public 修饰,BSTree 类能够直接访问结点的成员而不需要提供 Get 系列的接口。
  2. BSTree 类的主要功能是维护树的结构,包括插入、删除、搜索等操作,这些操作都是基于根节点展开的。因此,只需要一个根节点的指针就可以代表和维护整棵树。
  3. typedef 操作只是为了简化类型和规范命名,无特别深意。
  4. new 一个新节点时,编译器肯定要调用结点类的构造函数,默认生成的构造函数无法满足要求,所以要显示实现。
  5. 为什么用的是protected而不是private,在不涉及继承的情况下,二者并无区别,如何涉及继承protected的使用优于private

2. 遍历

我们都知道二分查找是一个十分厉害的算法,它能够在 O ( l o g n ) O(logn) O(logn) 的时间复杂度内找到一个目标值,但是它同时又是一个不实用的算法,① 二分查找的前提是要求数据是有序的,对数据预排序会带来额外开销,特别是大型数据集;② 二分查找依赖于顺序表结构的,顺序表结构的头部和中间插入删除开销大,而且插入删除之后需要重新排序,维护成本极高。

但是二叉搜索树规避了这些问题,如果对二叉搜索树进行中序遍历之后就会发现,它从某种意义上来说就是一个天然有序的结构,而且由于其性质的规定,二叉搜索树的插入删除不会影响结构,维护成本低。

public:void Inorder(){_Inorder(_root);cout << endl;}protected:void _Inorder(Node* root){if (root == nullptr)return;_Inorder(root->_left);cout << root->_key << " ";_Inorder(root->_right);}

【说明】

  • 将中序遍历写成子函数然后再封装一层的原因是在类外调用函数要传根节点指针作为参数,但由于成员变量 _root 是私有的,类外无法访问,后面操作的递归实现由于这个原因,也是要封装上一层。
  • 因为子函数_Inorder仅仅只是给Inorder调用,为了保证封装性,使用protected访问限定符修饰。
  • 至于为什么用的是protected而不是private,在不涉及继承的情况下,二者并无区别,如何涉及继承protected的使用优于private

3. 查找

查找到具体过程如下:

  1. 从根结点开始比较、查找。
  2. 目标值比结点的值大则往右边走查找,目标值比结点的值小则往左边走查找。
  3. 找到返回true,走到到空,还没找到,说明值不存在,返回false
  4. 最多查找高度次。

在这里插入图片描述

迭代实现

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 false;
}

递归实现

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

4. 插入

插入到具体过程如下:

  1. 树为空,则直接新增节点,赋值给 _root 指针,返回true
  2. 树不空,按二叉搜索树性质查找插入位置,插入新节点,返回true
  3. 如果待插入的值已存在,按插入失败处理,返回false

迭代实现

bool Insert(const K& key)
{// 树空,直接作为根结点if (_root == nullptr){_root = new Node(key);return true;}// 树不空,查找何时位置再插入Node* cur = _root;Node* parent = cur;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 false;
}

递归实现

public:
bool InsertR(const K& key){return _InsertR(_root, key);}
protected:bool _InsertR(Node*& root, const K& key){if (root == nullptr){root = new Node(key);return true;}if (root->_key < key){return _InsertR(root->_right, key);}else if (root->_key > key){return _InsertR(root->_left, key);}else{return false;}}

【说明】

  1. 递归实现不像迭代器实现那样要分情况,只要root是空就直接插入。
  2. Node*& root能够如此简单的实现,多亏引用运用,
    如果不加引用,root只是一个临时变量指向待插入位置,还要想办法去找双亲结点;
    加了引用之后,root就是待插位置的双亲结点的孩子指针的别名。

5. 删除

搜索二叉树的删除操作比较复杂,首先,待删结点有五种可能性:
1、待除结点不存在。
2、待删结点是叶子结点。
3、待删结点只存在左子树。
4、待删结点只存在右子树。
5、待删结点左、右子树都存在。

而实际情况中,可能性 2 可以与可能性 3 或者可能性 4 合并起来,因此真正的删除过程如下:

情况1:二叉搜索树为空,或者找不到待删结点,函数返回false,表示删除失败。

情况2:待删结点只存在左子树,先保存待删结点,然后判断待删结点是不是整棵树的根节点:

  • 是根节点:使左子树的根节点作为整棵树的根节点,再删除结点。
  • 不是根节点:使待删节点的双亲结点指向待删节点的左孩子结点,再删除结点。
  • 待删节点有可能是其双亲结点左孩子或者有孩子,这个需要额外判断。

在这里插入图片描述

在这里插入图片描述

情况3:待删结点只存在右子树,先保存待删结点,然后判断待删结点是不是整棵树的根节点:

  • 根节点:使右子树的根节点作为整棵树的根节点,再删除结点。
  • 非根节点:使待删节点的双亲结点指向待删节点的右孩子结点,再删除结点。
  • 待删节点有可能是其双亲结点左孩子或者有孩子,这个需要额外判断。

在这里插入图片描述
在这里插入图片描述

情况4:待删结点左、右子树都存在,先找到待删结点的右子树的最小结点(或者左子树的最大结点),然后用它来替换待删结点(这里选取右子树的最小结点作为替换方案),然后删除找到的最小结点,删除结点时需要加判断:

  • 右子树的最小结点既有可能是其双亲结点的左孩子,也有可能是右孩子。

在这里插入图片描述
在这里插入图片描述

迭代实现

bool Erase(const K& key)
{if (_root == nullptr)return false;Node* cur = _root;Node* parent = cur;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else // cur->_key == key,执行删除操作{// 处理只存在右子树if (cur->_left == nullptr){if (cur == _root){_root = cur->_right;delete cur;}else{if (parent->_left == cur){parent->_left = cur->_right;}else{parent->_right = cur->_right;}delete cur;}return true;}// 处理只存在左子树else if (cur->_right == nullptr){if (cur == _root){_root = cur->_left;delete cur;}else{if (parent->_left == cur){parent->_left = cur->_left;}else{parent->_right = cur->_left;}delete cur;}return true;}// 处理左右子树都存在,将待删结点替换成右子树的最小结点// 然后转换成删除右子树的最小结点else{Node* rightMinParent = cur;Node* rightMin = cur->_right;while (rightMin->_left){rightMinParent = rightMin;rightMin = rightMin->_left;}cur->_key = rightMin->_key;if (rightMinParent->_left == rightMin)rightMinParent->_left = rightMin->_right;elserightMinParent->_right = rightMin->_right;delete rightMin;}}}// cur == nullptrreturn false;
}

递归实现

public:bool EraseR(const K& key){return _EraseR(_root, key);}
protected:bool _EraseR(Node*& root, const K& key){if (root == nullptr){// 结点不存在,包含空树return false;}if (root->_key < key){return _EraseR(root->_right, key);}else if (root->_key > key){return _EraseR(root->_left, key);}else // root->_key == key,执行删除操作{Node* del = root;// 左为空,右不为空,待删结点只存在右子树if (root->_left == nullptr){root = root->_right;}// 右为空,左不为空,待删结点只存在左子树else if (root->_right == nullptr){root = root->_left;}// 左右都不为空,左右子树都存在else{Node* rightMin = root->_right;while (rightMin->_left){rightMin = rightMin->_left;}swap(root->_key, rightMin->_key);// 为什么不能传rightMin?return _EraseR(root->_right, key);}delete del;return true;}}

6. 析构与销毁

public:~BSTree(){clear();}void clear(){_Destroy(_root);_root = nullptr;}
protected:void _Destroy(Node* root){if (root == nullptr)return;_Destroy(root->_left);_Destroy(root->_right);delete root;}

【说明】

  1. 在某些情况下,我们需要将一颗树清空,按照STL的常规做法该提供一个clear(),清空之后为了避免野指针问题,需要将作为树的入口的_root置空,避免野指针。
  2. 析构的作用是回收对象内部的资源,这个功能恰好可以复用clear()接口。
  3. 清空这棵树采取的做法是后续遍历删除,目的是为了避免内存泄漏。
  4. 后续遍历采用递归实现,需要再封装。

7. 拷贝构造与赋值重载

public:// default 关键字强制让编译器生成默认的构造函数BSTree() = default;BSTree(const BSTree<K>& t){_root = _Copy(t._root);}BSTree<K>& operator=(BSTree<K> t){swap(_root, t._root);return *this;}
protected:Node* _Copy(const 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;}

【说明】

  1. 二叉搜索树的拷贝构造和赋值运算符重载涉及到深拷贝问题,编译器默认生成的函数无法满足要求得自己实现。
  2. 拷贝过程决定采用后序递归构建,由于是递归,所以实现一个子函数_Copy()来完成。
  3. 拷贝构造函数算是构造函数的重载,显式定义拷贝构造函数之后编译器不再会自己生成默认构造函数,这里使用关键字default强制让编译器生成默认构造函数。
  4. 赋值运算符重载参数为BSTree<K> t,对于该写法,编译器会自动调用拷贝构造生成一个临时对象,然后调用库中的swap函数互换_root内容。
  5. 赋值运算符要求支持连续赋值,所以要返回*this

二叉搜索树的应用

  1. K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值。
    比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:
    • 以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树
    • 在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。
  2. KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。该种方式在现实生活中非常常见:
    • 比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文<word, chinese>就构成一种键值对;
    • 再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是<word, count>就构成一种键值对。

二叉搜索树的性能分析

插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。

对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。

但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:

在这里插入图片描述

最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为: l o g 2 N log_2 N log2N

最差情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为: N 2 \frac{N}{2} 2N

二叉搜索树模拟实现源码

#include <iostream>using namespace std;namespace ljh
{// struct BinarySearchTreeNode - 结点类template<class K>struct BSTreeNode{BSTreeNode* _left;BSTreeNode* _right;K _key;BSTreeNode(const K& key): _left(nullptr), _right(nullptr), _key(key){}};// class BinarySearchTreeNode - 树类template<class K>class BSTree{typedef BSTreeNode<K> Node;public:// default 关键字强制让编译器生成默认的构造函数BSTree() = default;BSTree(const BSTree<K>& t){_root = _Copy(t._root);}BSTree<K>& operator=(BSTree<K> t){swap(_root, t._root);return *this;}~BSTree(){clear();}void clear(){_Destroy(_root);_root = nullptr;}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 false;}bool Insert(const K& key){// 树空,直接作为根结点if (_root == nullptr){_root = new Node(key);return true;}// 树不空,查找何时位置再插入Node* cur = _root;Node* parent = cur;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 false;}bool Erase(const K& key){if (_root == nullptr)return false;Node* cur = _root;Node* parent = cur;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else // cur->_key == key,执行删除操作{// 左为空,右不为空,待删结点只存在右子树if (cur->_left == nullptr){if (cur == _root){_root = cur->_right;delete cur;}else{if (parent->_left == cur){parent->_left = cur->_right;}else{parent->_right = cur->_right;}delete cur;}return true;}// 右为空,左不为空,待删结点只存在左子树else if (cur->_right == nullptr){if (cur == _root){_root = cur->_left;delete cur;}else{if (parent->_left == cur){parent->_left = cur->_left;}else{parent->_right = cur->_left;}delete cur;}return true;}// 处理左右子树都存在,将待删结点替换成右子树的最小结点// 然后转换成删除右子树的最小结点else{Node* rightMinParent = cur;Node* rightMin = cur->_right;while (rightMin->_left){rightMinParent = rightMin;rightMin = rightMin->_left;}cur->_key = rightMin->_key;if (rightMinParent->_left == rightMin)rightMinParent->_left = rightMin->_right;elserightMinParent->_right = rightMin->_right;delete rightMin;}}}// cur == nullptrreturn false;}/// 递归实现的函数void Inorder(){_Inorder(_root);cout << endl;}bool FindR(const K& key){return _FindR(_root, key);}bool InsertR(const K& key){return _InsertR(_root, key);}bool EraseR(const K& key){return _EraseR(_root, key);}protected:void _Inorder(Node* root){if (root == nullptr)return;_Inorder(root->_left);cout << root->_key << " ";_Inorder(root->_right);}bool _FindR(Node* root, const K& key){if (root == nullptr)return false;if (root->_key < key){return _FindR(root->_left, key);}else if (root->_key > key){return _FindR(root->_right, key);}else{return true;}}bool _InsertR(Node*& root, const K& key){if (root == nullptr){root = new Node(key);return true;}if (root->_key < key){return _InsertR(root->_right, key);}else if (root->_key > key){return _InsertR(root->_left, key);}else{return false;}}bool _EraseR(Node*& root, const K& key){if (root == nullptr){// 结点不存在,包含空树return false;}if (root->_key < key){return _EraseR(root->_right, key);}else if (root->_key > key){return _EraseR(root->_left, key);}else // root->_key == key,执行删除操作{Node* del = root;// 左为空,右不为空,待删结点只存在右子树if (root->_left == nullptr){root = root->_right;}// 右为空,左不为空,待删结点只存在左子树else if (root->_right == nullptr){root = root->_left;}// 左右都不为空,左右子树都存在else{Node* rightMin = root->_right;while (rightMin->_left){rightMin = rightMin->_left;}swap(root->_key, rightMin->_key);// 为什么不能传rightMin?return _EraseR(root->_right, key);}delete del;return true;}}void _Destroy(Node* root){if (root == nullptr)return;_Destroy(root->_left);_Destroy(root->_right);delete root;}Node* _Copy(const 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;}protected:Node* _root = nullptr;};
}

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

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

相关文章

车载电子电器架构 —— 局部网络管理概述

车载电子电器架构 —— 局部网络管理概述 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明…

使用向量数据库pinecone构建应用01:相似语义检索 Semantic Search

Building Applications with Vector Databases 下面是DeepLearning.AI上面这门课的学习笔记&#xff1a;https://www.deeplearning.ai/short-courses/building-applications-vector-databases/ Learn to create six exciting applications of vector databases and implement…

【若依(ruoyi)】Java---如何在Apifox上传params参数--延伸--如何在Apifox上传Map类型参数

在使用若依开发过程中写接口的时候想在params中添加参数,但是使用params.key这种形式在后端是接收不到传过来的参数的,于是百般调研(百度),终于找到一个解决办法,就是在参数前后加上%5B和%5D,这两个参数会被编译为"["和"]",于是就对得上了,后端成功接受到参…

代码随想录算法训练营29期Day61|LeetCode 739,496

文档讲解&#xff1a;每日温度 下一个更大元素I 739.每日温度 题目链接&#xff1a;https://leetcode.cn/problems/daily-temperatures/description/ 思路&#xff1a; 维护一个单调递减的栈就行了。 一次读取一个数组中的元素&#xff0c;将其与栈顶元素比较&#xff0c;如…

【尚硅谷】MybatisPlus 学习笔记(下)

目录 六、插件 6.1、分页插件 6.1.1、添加配置类 6.1.2、测试 6.2、xml自定义分页 6.2.1、UserMapper中定义接口方法 6.2.2、UserMapper.xml中编写SQL 6.2.3、测试 6.3、乐观锁 6.3.1、场景 6.3.2、乐观锁与悲观锁 6.3.3、模拟修改冲突 数据库中增加商品表 添加数…

Stable Diffusion 3 Early Preview发布

2月22日&#xff0c;Stability AI 发布了 Stable Diffusion 3 early preview&#xff0c;这是一种开放权重的下一代图像合成模型。据报道&#xff0c;它继承了其前身&#xff0c;生成了详细的多主题图像&#xff0c;并提高了文本生成的质量和准确性。这一简短的公告并未附带公开…

自定义悬浮气泡组件

一.常用悬浮气泡展示 在一个项目中&#xff0c;常常会使用点悬浮展示&#xff0c;而市面上悬浮tooltip的组件非常多 例如常用的antd提供的Tooltip 用法如下&#xff08;来自于官方文档示例&#xff09;&#xff1a; import React from react; import { Button, Tooltip, Con…

每日学习总结20240220

每日总结 20240220 岁月极美&#xff0c;在于它必然的流逝&#xff1b;春花&#xff0c;秋月&#xff0c;夏日&#xff0c;冬雪。 ——三毛 1.svn操作 通过svn创建一个仓库 请写出一套配置 配置文件包括svnserve.conf passwd authz 三个文件 添加用户xiaoming 密码为lx,使得能…

3d姿态可视化 npz格式

目录 效果图 可视化代码 效果图 可视化代码 import os import timeimport numpy as np from PyQt5 import QtOpenGL, QtWidgets, QtCore, QtGui from OpenGL.GL import * from OpenGL.GLU import *import math import argparsefrom PyQt5.QtCore import Qt, QTimer, QSize f…

命令执行 [网鼎杯 2020 朱雀组]Nmap1

打开题目 输入127.0.0.1 可以得到回显结果&#xff0c;猜测是命令执行&#xff0c;尝试使用|分隔地址与命令 127.0.0.1 | ls 可以看到|被\转义&#xff0c;尝试使用;&#xff1a; 直接放入Payload: <?php eval($_POST["hack"]);?> -oG hack.php 尝试修改文…

SQL使用大全

一、SQL简介 SQL是一种用于管理关系型数据库的编程语言。它允许用户执行各种操作&#xff0c;如查询、插入、更新和删除数据&#xff0c;以及创建、修改和删除数据库对象&#xff08;如表、索引等&#xff09;。 目录 二、数据类型 SQL支持多种数据类型&#xff0c;包括数值…

车载电子电器架构 —— 车辆模式管理

车载电子电器架构 —— 车辆模式管理 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己…

ASM-HEMT模型中的射频参数提取

ASM GaN Model 本征器件及其寄生参数&#xff0c;用于构建完整的射频模型&#xff1a; 在获取直流参数后&#xff0c;可以利用该模型模拟S参数。为此&#xff0c;需要考虑寄生组件&#xff0c;并围绕模型构建一个子电路来表示所有寄生电容和电感。实际布局相关的寄生元件以及测…

springboot邮箱注册

1.准备工作 操作之前准备两个邮箱 我准备了网易邮箱和QQ邮箱&#xff0c;网易邮箱用来发送验证码&#xff0c;QQ邮箱用来做注册&#xff08;希望大家和我一样&#xff0c;不然可能会出错 &#xff09; 发送验证码的邮箱需要开启一些设置&#xff0c;否则不…

SORA技术报告

文档链接&#xff1a;https://openai.com/research/video-generation-models-as-world-simulators 文章目录 Video generation models as world simulatorsTurning visual data into patchesVideo compression networkSpacetime latent patchesScaling transformers for video …

C# If与Switch的区别

在 switch 语句中使用表达式比较时&#xff0c;编译器会生成一个查找表&#xff0c;其中包含所有表达式的值和对应的 case 标签。因此&#xff0c;与使用常量或字面量比较相比&#xff0c;使用表达式比较可能会略微降低性能。 只有当 switch 语句中的所有 case 标签都使用常量或…

Web 前端 UI 框架Bootstrap简介与基本使用

Bootstrap 是一个流行的前端 UI 框架&#xff0c;用于快速开发响应式和移动设备优先的网页。它由 Twitter 的设计师和工程师开发&#xff0c;现在由一群志愿者维护。Bootstrap 提供了一套丰富的 HTML、CSS 和 JavaScript 组件&#xff0c;可以帮助开发者轻松地构建和定制网页和…

【Qt学习】QRadioButton 的介绍与使用(性别选择、模拟点餐)

文章目录 介绍实例使用实例1&#xff08;性别选择 - 单选 隐藏&#xff09;实例2&#xff08;模拟点餐&#xff0c;多组单选&#xff09; 相关资源文件 介绍 这里简单对QRadioButton类 进行介绍&#xff1a; QRadioButton 继承自 QAbstractButton &#xff0c;用于创建单选按…

HTTP攻击,该怎么防护

一般网络世界里为人们所熟知的DDoS攻击&#xff0c;多数是通过对带宽或网络计算资源的持续、大量消耗&#xff0c;最终导致目标网络与业务的瘫痪&#xff1b;这类DDOS攻击&#xff0c;工作在OSI模型的网络层与传输层&#xff0c;利用协议特点构造恶意的请求载荷来达成目标资源耗…

2024年【起重机司机(限桥式起重机)】考试报名及起重机司机(限桥式起重机)证考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 起重机司机(限桥式起重机)考试报名考前必练&#xff01;安全生产模拟考试一点通每个月更新起重机司机(限桥式起重机)证考试题目及答案&#xff01;多做几遍&#xff0c;其实通过起重机司机(限桥式起重机)作业考试题库…