【C++入门到精通】C++入门 ——搜索二叉树(二叉树进阶)

在这里插入图片描述

阅读导航

  • 前言
  • 一、搜索二叉树简介
    • 1. 概念
    • 2. 基本操作
      • ⭕搜索操作
        • 🍪搜索操作基本代码(非递归)
      • ⭕插入操作
        • 🍪插入操作基本代码(非递归)
      • ⭕删除操作
        • 🍪删除操作基本代码(非递归)
  • 二、搜索二叉树的实现
    • 1. 非递归实现
    • 2. 递归实现
  • 三、搜索二叉树的应用
    • 1. K模型
    • 2. KV模型
  • 四、搜索二叉树的性能分析
  • 总结
  • 温馨提示

前言

前面我们讲了C语言的基础知识,也了解了一些初阶数据结构,并且讲了有关C++的命名空间的一些知识点以及关于C++的缺省参数、函数重载,引用 和 内联函数也认识了什么是类和对象以及怎么去new一个 ‘对象’ ,也了解了C++中的模版,以及学习了几个STL的结构也相信大家都掌握的不错,接下来博主将会带领大家继续学习有关C++比较重要的知识点——搜索二叉树(二叉树进阶) 。下面话不多说坐稳扶好咱们要开车了😍

一、搜索二叉树简介

1. 概念

搜索二叉树,也称为二叉搜索树(Binary Search Tree,BST),是一种二叉树的特殊形式。它满足以下条件:

  1. 有序性:对于任意节点,其左子树中的所有节点的值都小于该节点的值,右子树中的所有节点的值都大于该节点的值。
  2. 唯一性:每个节点的值唯一,不存在相同值的节点。
  3. 递归结构:整个树的结构由左子树、右子树和根节点组成,其中左子树和右子树也是搜索二叉树。

在这里插入图片描述

2. 基本操作

⭕搜索操作

  • 从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。
  • 最多查找高度次,走到到空,还没找到,这个值不存在。

在搜索二叉树中查找一个特定值的操作很简单。从根节点开始,如果目标值等于当前节点的值,则找到了目标节点;如果目标值小于当前节点的值,则在左子树中继续搜索;如果目标值大于当前节点的值,则在右子树中继续搜索。通过递归或循环,可以在树中进行有效的搜索。

🍪搜索操作基本代码(非递归)

bool Find(const K& key)
{Node* ret = _root;// 循环查找节点,直到找到与 key 匹配的节点或遍历完整个树while (ret){// 如果当前节点的键值大于 key,继续在左子树中查找if (ret->_key > key){ret = ret->_left;}// 如果当前节点的键值小于 key,继续在右子树中查找else if (ret->_key < key){ret = ret->_right;}// 如果当前节点的键值等于 key,找到匹配节点,返回 trueelse{return true;}}// 遍历完整个树仍未找到匹配节点,返回 falsereturn false;
}

该函数接收一个键值 key,表示需要查找的值。函数首先将根节点指针 _root 赋值给临时指针变量 ret。然后通过循环遍历树进行查找,直到找到与 key 匹配的节点或遍历完整个树。

在循环中,根据当前节点的键值与 key 的比较结果决定下一步的查找方向:

  • 如果当前节点的键值大于 key,继续在左子树中查找;
  • 如果当前节点的键值小于 key,继续在右子树中查找;
  • 如果当前节点的键值等于 key,找到匹配节点,返回 true。

当遍历完整个树仍未找到匹配节点时,返回 false 表示未找到。

⭕插入操作

  • 树为空,则直接新增节点,赋值给root指针
  • 树不空,按二叉搜索树性质查找插入位置,插入新节点

要向搜索二叉树中插入新节点,需要找到合适的位置。从根节点开始,与当前节点的值比较,根据值的大小决定是在左子树还是右子树中进行插入。重复这个过程,直到找到一个空的位置,然后将新节点插入其中。

🍪插入操作基本代码(非递归)

bool Insert(const K& key)
{// 如果根节点为空,直接将 key 作为根节点创建if (_root == nullptr){_root = new Node(key);return true;}Node* ret = _root;Node* parent = nullptr;// 找到 key 应该插入的位置while (ret){// 如果 key 小于当前节点的键值,继续在左子树查找if (key < ret->_key){parent = ret;ret = ret->_left;}// 如果 key 大于当前节点的键值,继续在右子树查找else if (key > ret->_key){parent = ret;ret = ret->_right;}// 如果 key 已经存在于树中,返回 falseelse{return false;}}// 创建新节点,将其插入正确的位置Node* cur = new Node(key);if (parent->_key > key){parent->_left = cur;}else{parent->_right = cur;}return true;
}

该函数接收一个键值 key,表示需要插入的值。函数首先判断根节点是否为空,如果为空,则直接将 key 作为根节点插入并返回 true。否则,使用循环找到 key 需要插入的位置:

  • 如果 key 小于当前节点的键值,继续在左子树查找;
  • 如果 key 大于当前节点的键值,继续在右子树查找;
  • 如果 key 等于当前节点的键值,则说明值已经存在于树中,返回 false。

当找到正确的位置时,创建一个新的节点 cur,并将其插入到树中。具体实现如下:

  • 如果 key 小于父亲节点的键值,将 cur 插入父节点的左子树中;
  • 如果 key 大于父亲节点的键值,将 cur 插入父节点的右子树中。

插入完成后,返回 true 表示插入成功。

⭕删除操作

删除节点的过程相对复杂,需要考虑不同的情况,主要分为以下三种情况处理:

  1. 删除节点没有子节点(叶子节点):直接将该节点删除即可,将其父节点指向它的指针置为 NULL。

  2. 删除节点有一个子节点:将该节点的子节点替代该节点的位置,将该节点的父节点指向它的指针直接指向子节点。

  3. 删除节点有两个子节点:这是最复杂的情况。需要找到该节点的后继节点或者前驱节点来替代该节点的位置。后继节点是指比当前节点大的最小节点,前驱节点是指比当前节点小的最大节点。

    • 找到后继节点的方法是:在当前节点的右子树中找到值最小的节点,即右子树中最左边的节点,然后将其值复制到待删除节点的位置,再删除后继节点。

    • 找到前驱节点的方法是:在当前节点的左子树中找到值最大的节点,即左子树中最右边的节点,然后将其值复制到待删除节点的位置,再删除前驱节点。

🍪删除操作基本代码(非递归)

bool Erase(const K& key)
{Node* ret = _root;Node* parent = nullptr;// 循环查找节点,直到找到匹配的节点或遍历完整个树while (ret){// 如果 key 小于当前节点的键值,继续在左子树查找if (key < ret->_key){parent = ret;ret = ret->_left;}// 如果 key 大于当前节点的键值,继续在右子树查找else if (key > ret->_key){parent = ret;ret = ret->_right;}// 如果找到匹配的节点else{// 删除节点// 1. 如果当前节点的左子树为空if (ret->_left == nullptr){// 如果是根节点if (_root->_key == key){Node* tmp = _root;_root = _root->_right;delete tmp;return true;}else{// 如果是父节点的左子节点if (parent->_left == ret){parent->_left = ret->_right;}// 如果是父节点的右子节点else{parent->_right = ret->_right;}delete ret;}}// 2. 如果当前节点的右子树为空else if (ret->_right == nullptr){// 如果是根节点if (_root->_key == key){Node* tmp = _root;_root = _root->_left;delete tmp;return true;}else{// 如果是父节点的左子节点if (parent->_left == ret){parent->_left = ret->_left;}// 如果是父节点的右子节点else{parent->_right = ret->_left;}delete ret;}}// 3. 如果当前节点的左右子树都不为空else{// 找到右子树中最小的节点,用来替代当前节点Node* pminRight = ret;Node* minRight = ret->_right;while (minRight->_left){pminRight = minRight;minRight = minRight->_left;}// 将右子树中最小节点的值赋给当前节点,并删除最小节点ret->_key = minRight->_key;if (pminRight->_left == minRight){pminRight->_left = minRight->_right;}else{pminRight->_right = minRight->_right;}delete minRight;}return true;}}// 遍历完整个树仍未找到匹配节点,返回 falsereturn false;
}

该函数接收一个键值 key,表示需要删除的值。函数首先将根节点指针 _root 赋值给临时指针变量 ret,并初始化父节点指针 parentnullptr。然后通过循环遍历树进行查找,直到找到与 key 匹配的节点或遍历完整个树。

在循环中,根据待删除节点的键值与 key 的比较结果决定下一步的查找方向:

  • 如果 key 小于当前节点的键值,继续在左子树中查找;
  • 如果 key 大于当前节点的键值,继续在右子树中查找;
  • 如果当前节点的键值等于 key,找到匹配节点,并执行删除操作。

删除操作分为以下三种情况:

  1. 如果当前节点的左子树为空:将当前节点的右子树链接到父节点的对应位置,并删除当前节点。
  2. 如果当前节点的右子树为空:将当前节点的左子树链接到父节点的对应位置,并删除当前节点。
  3. 如果当前节点的左右子树都不为空:需要找到当前节点右子树中最小的节点(即右子树的最左下角节点),将该节点的值赋给当前节点,然后删除最小节点。

在执行删除操作时,可能存在以下两种特殊情况:

  • 如果当前节点是根节点:需要更新 _root 指针,让其指向新的根节点。
  • 如果当前节点是某个节点的左子节点或右子节点:需要将该节点的父节点链接到删除节点的子节点,与该节点相邻的子树会被删除,然后释放删除节点的内存。

最后,如果遍历整个树仍未找到匹配节点,说明该节点不存在,函数返回 false。

二、搜索二叉树的实现

1. 非递归实现

namespace yws
{
template<class K>struct BSTreeNode{// 二叉搜索树节点的定义BSTreeNode(const K& key): _left(nullptr), _right(nullptr), _key(key){}BSTreeNode<K>* _left;   // 左子节点指针BSTreeNode<K>* _right;  // 右子节点指针K _key;                 // 节点存储的键值};template<class K>class BSTree{typedef BSTreeNode<K> Node;public://构造BSTree():_root(nullptr){}//拷贝构造BSTree(const BSTree<K>& t){_root = Copy(t._root);}//赋值构造BSTree<K>& operator=(BSTree<K> t){std::swap(_root, t._root);return *this;}//析构~BSTree(){Destory(_root);}bool Insert(const K& key){// 如果根节点为空,直接将 key 作为根节点创建if (_root == nullptr){_root = new Node(key);return true;}Node* ret = _root;Node* parent = nullptr;// 找到 key 应该插入的位置while (ret){// 如果 key 小于当前节点的键值,继续在左子树查找if (key < ret->_key){parent = ret;ret = ret->_left;}// 如果 key 大于当前节点的键值,继续在右子树查找else if (key > ret->_key){parent = ret;ret = ret->_right;}// 如果 key 已经存在于树中,返回 falseelse{return false;}}// 创建新节点,将其插入正确的位置Node* cur = new Node(key);if (parent->_key > key){parent->_left = cur;}else{parent->_right = cur;}return true;}bool Find(const K& key){Node* ret = _root;// 循环查找节点,直到找到与 key 匹配的节点或遍历完整个树while (ret){// 如果当前节点的键值大于 key,继续在左子树中查找if (ret->_key > key){ret = ret->_left;}// 如果当前节点的键值小于 key,继续在右子树中查找else if (ret->_key < key){ret = ret->_right;}// 如果当前节点的键值等于 key,找到匹配节点,返回 trueelse{return true;}}// 遍历完整个树仍未找到匹配节点,返回 falsereturn false;}bool Erase(const K& key){Node* ret = _root;Node* parent = nullptr;// 循环查找节点,直到找到匹配的节点或遍历完整个树while (ret){// 如果 key 小于当前节点的键值,继续在左子树查找if (key < ret->_key){parent = ret;ret = ret->_left;}// 如果 key 大于当前节点的键值,继续在右子树查找else if (key > ret->_key){parent = ret;ret = ret->_right;}// 如果找到匹配的节点else{// 删除节点// 1. 如果当前节点的左子树为空if (ret->_left == nullptr){// 如果是根节点if (_root->_key == key){Node* tmp = _root;_root = _root->_right;delete tmp;return true;}else{// 如果是父节点的左子节点if (parent->_left == ret){parent->_left = ret->_right;}// 如果是父节点的右子节点else{parent->_right = ret->_right;}delete ret;}}// 2. 如果当前节点的右子树为空else if (ret->_right == nullptr){// 如果是根节点if (_root->_key == key){Node* tmp = _root;_root = _root->_left;delete tmp;return true;}else{// 如果是父节点的左子节点if (parent->_left == ret){parent->_left = ret->_left;}// 如果是父节点的右子节点else{parent->_right = ret->_left;}delete ret;}}// 3. 如果当前节点的左右子树都不为空else{// 找到右子树中最小的节点,用来替代当前节点Node* pminRight = ret;Node* minRight = ret->_right;while (minRight->_left){pminRight = minRight;minRight = minRight->_left;}// 将右子树中最小节点的值赋给当前节点,并删除最小节点ret->_key = minRight->_key;if (pminRight->_left == minRight){pminRight->_left = minRight->_right;}else{pminRight->_right = minRight->_right;}delete minRight;}return true;}}// 遍历完整个树仍未找到匹配节点,返回 falsereturn false;}protected:Node* Copy(Node* root){if (root == nullptr){return nullptr;}Node newnode = new Node(root->_key);newnode->_left = Copy(root->_left);newnode->_right = Copy(root->_right);return newnode;}void Destory(Node*& root){if (root == nullptr)return;Destory(root->_right);Destory(root->_left);delete root;root = nullptr;}}private:Node* _root = nullptr;};
}

2. 递归实现

namespace yws
{template<class K>struct BSTreeNode{BSTreeNode(const K& key): _left(nullptr), _right(nullptr), _key(key){}BSTreeNode<K>* _left;BSTreeNode<K>* _right;K _key;};template<class K>class BSTree{typedef BSTreeNode<K> Node;public://构造BSTree():_root(nullptr){}//拷贝构造BSTree(const BSTree<K>& t){_root = Copy(t._root);}//赋值构造BSTree<K>& operator=(BSTree<K> t){std::swap(_root, t._root);return *this;}//析构~BSTree(){Destory(_root);}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:Node* Copy(Node* root){if (root == nullptr){return nullptr;}Node newnode = new Node(root->_key);newnode->_left = Copy(root->_left);newnode->_right = Copy(root->_right);return newnode;}void Destory(Node*& root){if (root == nullptr)return;Destory(root->_right);Destory(root->_left);delete root;root = nullptr;}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 (key < root->_key){_FindR(root->_left, key);}else if (key > root->_key){_FindR(root->_right, key);}else{return true;}}bool _InsertR(Node*& root, const K& key){if (root == nullptr){root = new Node(key);return true;}if (key < root->_key){_InsertR(root->_left, key);}else if (key > root->_key){_InsertR(root->_right, key);}else{return false;}}bool _EraseR(Node*& root, const K& key){if (root == nullptr){return false;}if (key > root->_key){return _EraseR(root->_right, key);}else if (key < root->_key){return _EraseR(root->_left, key);}else{//开始删除Node* del = root;//1.左节点为空if (root->_left == nullptr){root = root->_right;}//2.右节点为空else if (root->_right == nullptr){root = root->_left;}//3.左右节点都不为空else{//找右树的最小节点,代替Node* minRight = root->_right;while (minRight->_left){minRight = minRight->_left;}root->_key = minRight->_key;return _EraseR(root->_right, root->_key);}delete del;return true;}}private:Node* _root = nullptr;};
}

三、搜索二叉树的应用

1. K模型

K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值。因此,K模型对于不需要复杂数据处理功能的应用来说是一种轻量级的数据管理方案。

比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:

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

2. KV模型

KV模型:每一个关键码key,都有与之对应的值Value,即 <Key, Value> 的键值对。该种方式在现实生活中非常常见

假设有一个存储学生信息的系统,每个学生信息仅包括学生的唯一ID和学生的姓名。这种情况下,我们可以使用K模型进行存储,其中学生的ID作为key,学生姓名作为value

如下所示是几个学生的数据:

学生ID学生姓名
1001张三
1002李四
1003王五

使用KV模型存储这些数据时,只需要记录学生ID作为key,对应的学生姓名作为value。例如,使用搜索二叉树来存储学生信息,可以根据学生ID快速查找到对应的学生姓名。这个例子展示了如何将KV模型与搜索二叉树结合,实现数据存储和检索的需求。

当需要查询某个学生的姓名时,只需要在二叉树中查找对应的学生ID,然后返回该学生的姓名即可。

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

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

但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:
在这里插入图片描述
最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为: l o g 2 N log_2 N log2N
最差情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为: N 2 \frac{N}{2} 2N

如果退化成单支树,二叉搜索树的性能就失去了。那能否进行改进,不论按照什么次序插入关键码,二叉搜索树的性能都能达到最优?那么我们后面的AVL树和红黑树就可以完美解决上面的问题了。

总结

搜索二叉树是一种常用的数据结构,用于存储和检索数据。搜索二叉树的应用,包括K模型和KV模型。K模型指的是只存储键而不存储关联值的数据模型。KV模型则是使用键值对来存储和访问数据的一种模型,可以用搜索二叉树实现。

最后,搜索二叉树的性能取决于树的平衡程度,理想情况下的时间复杂度为O(log n),但如果树不平衡,性能会下降至O(n)。为了保持树的平衡,可以采用平衡二叉树的变种,如红黑树或AVL树。

综上所述,搜索二叉树是一种重要的数据结构,具有广泛的应用。了解搜索二叉树的基本操作、实现方法和性能分析,对于合理选择和使用数据结构,提高数据操作效率具有重要意义。

温馨提示

感谢您对博主文章的关注与支持!另外,我计划在未来的更新中持续探讨与本文相关的内容,会为您带来更多关于C++以及编程技术问题的深入解析、应用案例和趣味玩法等。请继续关注博主的更新,不要错过任何精彩内容!

再次感谢您的支持和关注。期待与您建立更紧密的互动,共同探索C++、算法和编程的奥秘。祝您生活愉快,排便顺畅!
在这里插入图片描述

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

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

相关文章

MySQL无法查看系统默认字符集以及校验规则

show variables like character_set_database; show variables like collation_database;这个错误信息表示MySQL在尝试访问performance_schema.session_variables表时&#xff0c;发现该表不存在。这个问题可能是由于MySQL的版本升级导致的。解决这个问题的一种方法是运行mysql…

论文浅尝 | 训练语言模型遵循人类反馈的指令

笔记整理&#xff1a;吴亦珂&#xff0c;东南大学硕士&#xff0c;研究方向为大语言模型、知识图谱 链接&#xff1a;https://arxiv.org/abs/2203.02155 1. 动机 大型语言模型&#xff08;large language model, LLM&#xff09;可以根据提示完成各种自然语言处理任务。然而&am…

Java JUC 并发编程(笔记)

文章目录 再谈多线程并发与并行顺序执行并发执行并行执行 再谈锁机制重量级锁轻量级锁偏向锁锁消除和锁粗化 JMM内存模型Java内存模型重排序volatile关键字happens-before原则 多线程编程核心锁框架Lock和Condition接口可重入锁公平锁与非公平锁 读写锁锁降级和锁升级 队列同步…

[构建 Vue 组件库] 小尾巴 UI 组件库 —— 横向商品卡片(仿淘宝)

文章归档于&#xff1a;https://www.yuque.com/u27599042/row3c6 组件库地址 npm&#xff1a;https://www.npmjs.com/package/xwb-ui?activeTabreadmegitee&#xff1a;https://gitee.com/tongchaowei/xwb-ui 下载 npm i xwb-ui配置 按需导入 import {组件名 } from xwb-…

【Unity】 2D 游戏 库存模块实现

库存模块主要参考了 youtube 上的视频 BMo 的 Flexible INVENTORY SYSTEM in Unity with Events and Scriptable Objects 和 Simple Inventory UI in Unity With Grid Layouts 这两个视频是一个系列 还是一个视频也是 BMo的 How To INTERACT with Game Objects using UNITY E…

Nginx详解 第五部分:Ngnix反向代理(负载均衡 动静分离 缓存 透传 )

Part 5 一、正向代理与反向代理1.1 正向代理简介1.2 反向代理简介 二、配置反向代理2.1 反向代理配置参数2.1.1 proxy_pass2.1.2 其余参数 2.2 配置实例:反向代理单台web服务器2.3 代理转发 三、反向代理实现动静分离四、缓存功能五、反向代理客户端的IP透传5.1 原理概述5.2 一…

谁在为网络安全制造标尺?

“我们想帮助企业往后退一步&#xff0c;去全局的看一下自己的安全能力建设水平如何&#xff0c;以及在当下的阶段最应该做的安全建设是什么&#xff1f; ” 度量&#xff0c;对应的是更清晰的认知。而对企业安全而言&#xff0c;这种认知&#xff0c;也更在成为一把新的标尺…

Redis带你深入学习数据类型set

目录 1、set 2、set相关命令 2.1、添加元素 sadd 2.2、获取元素 smembers 2.3、判断元素是否存在 sismember 2.4、获取set中元素数量 scard 2.5、删除元素spop、srem 2.6、移动元素smove 2.7、集合中相关命令&#xff1a;sinter、sinterstore、sunion、sunionstore、s…

CSS:屏幕正中间有个元素A,元素A中有文字A,随着屏幕宽度的增加

始终需要满足以下条件&#xff1a; A元素垂直居中于屏幕***&#xff1b;A元素距离屏幕左右边距各10px&#xff1b;A元素里面的文字”A”的font-size:20px&#xff1b;水平垂直居中;A元素的高度始终是A元素宽度的50%; (如果搞不定可以实现为A元素的高度固定为200px;)请用 html及…

【Unity基础】3.脚本控制物体运动天空盒

【Unity基础】3.脚本控制物体运动&天空盒 大家好&#xff0c;我是Lampard~~ 欢迎来到Unity基础系列博客&#xff0c;所学知识来自B站阿发老师~感谢 &#xff08;一&#xff09;搭建开发环境 &#xff08;1&#xff09;下载visual studio 在我们下载unity编译器的时候&…

Microsoft Edge网页视频播放绿屏解决方法(B站)

一&#xff1a;问题&#xff0c;在B站观看视频时有绿色条纹 二&#xff1a;查找原因&#xff0c;未知 三&#xff1a;解决方法 三.1网页设置关闭硬件加速 三.2 点击视频播放下的 “小齿轮”&#xff0c;然后点击“更多播放设置” 把播放策略 “默认” 改为“AVC” 四&…

Tomcat配置域名和端口

Tomcat配置域名和端口 1.进入tomcat文件夹2. cd 到你的tomcat下3. 修改server.xml文件中监听端口4. 重启tomcat 1.进入tomcat文件夹 2. cd 到你的tomcat下 3. 修改server.xml文件中监听端口 继续修改server.xml中Host 4. 重启tomcat 进入bin ./shutdown.sh ./startup.sh …

etcd分布式存储

etcd分布式存储 etcd简介etcd下载安装etcd常用命令etcd配置参数etcd集群golang操作etcd

rrweb入门

rrweb 背景 rrweb 是 record and replay the web&#xff0c;是当下很流行的一个录制屏幕的开源库。与我们传统认知的录屏方式&#xff08;如 WebRTC&#xff09;不同的是&#xff0c;rrweb 录制的不是真正的视频流&#xff0c;而是一个记录页面 DOM 变化的 JSON 数组&#x…

【鲁棒电力系统状态估计】基于投影统计的电力系统状态估计的鲁棒GM估计器(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

SpringCloud Alibaba 入门到精通 - Nacos

SpringCloud Alibaba 常用组件 一、基础结构搭建1.父工程创建2.子工程创建 二、Nacos&#xff1a;注册中心1.服务端搭建2.注册中心-客户端搭建3.注册中心-管理页面4.注册中心-常用配置5.注册中心-核心功能总结 三、Nacos注册中心集成Load Balancer 、OpenFeign1.Nacos客户端集成…

一键部署k8s集群

前置动作 关闭防火墙 systemctl disable firewalld && systemctl stop firewalld 关闭SELinux sed -i s#SELINUXenforcing#SELINUXdisabled#g /etc/selinux/config && grep SELINUXdisabled /etc/selinux/config setenforce 0 getenforce 关闭swap # 关闭…

前端面试题JS篇(4)

浏览器缓存 浏览器缓存分为强缓存和协商缓存&#xff0c;当客户端请求某个资源时&#xff0c;获取缓存的流程如下&#xff1a; 先根据这个资源的一些 http header 判断它是否命中强缓存&#xff0c;如果命中&#xff0c;则直接从本地获取缓存资源&#xff0c;不会发请求到服务…

c语言练习44:深入理解strstr

深入理解strstr strstr作用展示&#xff1a; #include <stdio.h> #include <string.h> int main() {char str[] "This is a simple string";char* pch;pch strstr(str, "simple");/*strncpy(pch, "sample", 6);*/printf("%s…

Android逆向学习(一)vscode进行android逆向修改并重新打包

Android逆向学习&#xff08;一&#xff09;vscode进行android逆向修改并重新打包 写在前面 其实我不知道这个文章能不能写下去&#xff0c;其实我已经开了很多坑但是都没填上&#xff0c;现在专利也发出去了&#xff0c;就开始填坑了&#xff0c;本坑的主要内容是关于androi…