27.C++进阶:红黑树实现|插入|查找|验证|与AVL比较

红⿊树的概念

红⿊树是⼀棵⼆叉搜索树,他的每个结点增加⼀个存储位来表⽰结点的颜⾊,可以是红⾊或者⿊⾊。通过对任何⼀条从根到叶⼦的路径上各个结点的颜⾊进⾏约束,红⿊树确保没有⼀条路径会⽐其他路径⻓出2倍,因⽽是接近平衡的。

红⿊树的规则
  1. 每个结点不是红⾊就是⿊⾊
  2. 根结点是⿊⾊的
  3. 如果⼀个结点是红⾊的,则它的两个孩⼦结点必须是⿊⾊的,也就是说任意⼀条路径不会有连续的红⾊结点。
  4. 对于任意⼀个结点,从该结点到其所有NULL结点的简单路径上,均包含相同数量的⿊⾊结点说明:《算法导论》等书籍上补充了⼀条每个叶⼦结点(NIL)都是⿊⾊的规则。他这⾥所指的叶⼦结点不是传统的意义上的叶⼦结点,⽽是我们说的空结点,有些书籍上也把NIL叫做外部结点。NIL是为了⽅便准确的标识出所有路径,《算法导论》在后续讲解实现的细节中也忽略了NIL结点,所以我们知道⼀下这个概念即可。

思考⼀下,红⿊树如何确保最⻓路径不超过最短路径的2倍的
  • 由规则4可知,从根到NULL结点的每条路径都有相同数量的⿊⾊结点,所以极端场景下,最短路径就就是全是⿊⾊结点的路径,假设最短路径⻓度为bh(black height)。
  • 由规则2和规则3可知,任意⼀条路径不会有连续的红⾊结点,所以极端场景下,最⻓的路径就是⼀⿊⼀红间隔组成,那么最⻓路径的⻓度为2*bh
  • 综合红⿊树的4点规则⽽⾔,理论上的全⿊最短路径和⼀⿊⼀红的最⻓路径并不是在每棵红⿊树都存在的。假设任意⼀条从根到NULL结点路径的⻓度为x,那么bh <= h <= 2*bh
红⿊树的效率

假设N是红⿊树树中结点数量,h最短路径的⻓度,那么2h−1≤N<22∗h−12^h-1\le N <2^{2*h}-12h1N<22h1,由此推出h≈log⁡Nh\approx \log NhlogN,也就是意味着红⿊树增删查改最坏也就是⾛最⻓路径2∗log⁡N2*\log N2logN,那么时间复杂度还是O(log⁡N)O(\log N)O(logN)
红⿊树的表达相对AVL树要抽象⼀些,AVL树通过⾼度差直观的控制了平衡。红⿊树通过4条规则的颜⾊约束,间接的实现了近似平衡,他们效率都是同⼀档次,但是相对⽽⾔,插⼊相同数量的结点,红⿊树的旋转次数是更少的,因为他对平衡的控制没那么严格。

红⿊树的实现

红⿊树的结构
// 枚举值表⽰颜⾊ enum Colour { RED, BLACK }; // 这⾥我们默认按key/value结构实现 template<class K, class V> struct RBTreeNode { // 这⾥更新控制平衡也要加⼊parent指针 pair<K, V> _kv; RBTreeNode<K, V>* _left; RBTreeNode<K, V>* _right; RBTreeNode<K, V>* _parent; Colour _col; RBTreeNode(const pair<K, V>& kv) :_kv(kv) , _left(nullptr) , _right(nullptr) , _parent(nullptr) ,_col(RED) {} }; template<class K, class V> class RBTree { typedef RBTreeNode<K, V> Node; public: private: Node* _root = nullptr; };

红⿊树的插⼊

红⿊树树插⼊⼀个值的⼤概过程
  1. 插⼊⼀个值按⼆叉搜索树规则进⾏插⼊,插⼊后我们只需要观察是否符合红⿊树的4条规则。
  2. 如果是空树插⼊,新增结点是⿊⾊结点。如果是⾮空树插⼊,新增结点必须红⾊结点,因为⾮空树插⼊,新增⿊⾊结点就破坏了规则4,规则4是很难维护的。
  3. ⾮空树插⼊后,新增结点必须红⾊结点,如果⽗亲结点是⿊⾊的,则没有违反任何规则,插⼊结束
  4. ⾮空树插⼊后,新增结点必须红⾊结点,如果⽗亲结点是红⾊的,则违反规则3。进⼀步分析,c是红⾊,p为红,g必为⿊,这三个颜⾊都固定了,关键的变化看u的情况,需要根据u分为以下⼏种情况分别处理。
    说明:下图中假设我们把新增结点标识为c(cur),c的⽗亲标识为p(parent),p的⽗亲标识为g(grandfather),p的兄弟标识为u(uncle)。
情况1:变⾊

c为红,p为红,g为⿊,u存在且为红,则将p和u变⿊,g变红。在把g当做新的c,继续往上更新。
分析:因为p和u都是红⾊,g是⿊⾊,把p和u变⿊,左边⼦树路径各增加⼀个⿊⾊结点,g再变红,相当于保持g所在⼦树的⿊⾊结点的数量不变,同时解决了c和p连续红⾊结点的问题,需要继续往上更新是因为,g是红⾊,如果g的⽗亲还是红⾊,那么就还需要继续处理;如果g的⽗亲是⿊⾊,则处理结束了;如果g就是整棵树的根,再把g变回⿊⾊。
情况1只变⾊,不旋转。所以⽆论c是p的左还是右,p是g的左还是右,都是上⾯的变⾊处理⽅式。

  • 跟AVL树类似,图0我们展⽰了⼀种具体情况,但是实际中需要这样处理的有很多种情况。
  • 图1将以上类似的处理进⾏了抽象表达,d/e/f代表每条路径拥有hb个⿊⾊结点的⼦树,a/b代表每条路径拥有hb-1个⿊⾊结点的根为红的⼦树,hb>=0。
  • 图2/图3/图4,分别展⽰了hb==0/hb==1/hb==2的具体情况组合分析,当hb等于2时,这⾥组合情况上百亿种,这些样例是帮助我们理解,不论情况多少种,多么复杂,处理⽅式⼀样的,变⾊再继续往上处理即可,所以我们只需要看抽象图即可。

情况2:单旋+变⾊

c为红,p为红,g为⿊,u不存在或者u存在且为⿊,u不存在,则c⼀定是新增结点,u存在且为⿊,则c⼀定不是新增,c之前是⿊⾊的,是在c的⼦树中插⼊,符合情况1,变⾊将c从⿊⾊变成红⾊,更新上来的。
分析:p必须变⿊,才能解决,连续红⾊结点的问题,u不存在或者是⿊⾊的,这⾥单纯的变⾊⽆法解决问题,需要旋转+变⾊。

如果p是g的左,c是p的左,那么以g为旋转点进⾏右单旋,再把p变⿊,g变红即可。p变成课这颗树新的根,这样⼦树⿊⾊结点的数量不变,没有连续的红⾊结点了,且不需要往上更新,因为p的⽗亲是⿊⾊还是红⾊或者空都不违反规则。

如果p是g的右,c是p的右,那么以g为旋转点进⾏左单旋,再把p变⿊,g变红即可。p变成课这颗树新的根,这样⼦树⿊⾊结点的数量不变,没有连续的红⾊结点了,且不需要往上更新,因为p的⽗亲是⿊⾊还是红⾊或者空都不违反规则。

情况2:双旋+变⾊

c为红,p为红,g为⿊,u不存在或者u存在且为⿊,u不存在,则c⼀定是新增结点,u存在且为⿊,则c⼀定不是新增,c之前是⿊⾊的,是在c的⼦树中插⼊,符合情况1,变⾊将c从⿊⾊变成红⾊,更新上来的。
分析:p必须变⿊,才能解决,连续红⾊结点的问题,u不存在或者是⿊⾊的,这⾥单纯的变⾊⽆法解决问题,需要旋转+变⾊。

如果p是g的左,c是p的右,那么先以p为旋转点进⾏左单旋,再以g为旋转点进⾏右单旋,再把c变⿊,g变红即可。c变成课这颗树新的根,这样⼦树⿊⾊结点的数量不变,没有连续的红⾊结点了,且不需要往上更新,因为c的⽗亲是⿊⾊还是红⾊或者空都不违反规则。

如果p是g的右,c是p的左,那么先以p为旋转点进⾏右单旋,再以g为旋转点进⾏左单旋,再把c变⿊,g变红即可。c变成课这颗树新的根,这样⼦树⿊⾊结点的数量不变,没有连续的红⾊结点了,且不需要往上更新,因为c的⽗亲是⿊⾊还是红⾊或者空都不违反规则。

红⿊树的插⼊代码实现

// 旋转代码的实现跟AVL树是⼀样的,只是不需要更新平衡因⼦ bool Insert(const pair<K, V>& kv) { if (_root == nullptr) { _root = new Node(kv); _root->_col = BLACK; return true; } Node* parent = nullptr; Node* cur = _root; while (cur) { if (cur->_kv.first < kv.first) { parent = cur; cur = cur->_right; } else if (cur->_kv.first > kv.first) { parent = cur; cur = cur->_left; } else { return false; } } cur = new Node(kv); // 新增结点。颜⾊红⾊给红⾊ cur->_col = RED; if (parent->_kv.first < kv.first) { parent->_right = cur; } else { parent->_left = cur; } cur->_parent = parent; while (parent && parent->_col == RED) { Node* grandfather = parent->_parent; // p //g u if (parent == grandfather->_left) { Node* uncle = grandfather->_right; if (uncle && uncle->_col == RED) { // u存在且为红 -》变⾊再继续往上处理 parent->_col = uncle->_col = BLACK; grandfather->_col = RED; cur = grandfather; parent = cur->_parent; } else { // u存在且为⿊或不存在 -》旋转+变⾊ if (cur == parent->_left) { // g // p u // c //单旋 RotateR(grandfather); parent->_col = BLACK; grandfather->_col = RED; } else { // g // p u // c //双旋 RotateL(parent); RotateR(grandfather); cur->_col = BLACK; grandfather->_col = RED; } break; } } else { // g // u p Node* uncle = grandfather->_left; // 叔叔存在且为红,-》变⾊即可 if (uncle && uncle->_col == RED) { parent->_col = uncle->_col = BLACK; grandfather->_col = RED; // 继续往上处理 cur = grandfather; parent = cur->_parent; } else // 叔叔不存在,或者存在且为⿊ { // 情况⼆:叔叔不存在或者存在且为⿊ // 旋转+变⾊ // g //u p // c if (cur == parent->_right) { RotateL(grandfather); parent->_col = BLACK; grandfather->_col = RED; } else { // g //u p // c RotateR(parent); RotateL(grandfather); cur->_col = BLACK; grandfather->_col = RED; } break; } } } _root->_col = BLACK; return true; }

红⿊树的查找

按⼆叉搜索树逻辑实现即可,搜索效率为O(logN)

Node* Find(const K& key) { Node* cur = _root; while (cur) { if (cur->_kv.first < key) { cur = cur->_right; } else if (cur->_kv.first > key) { cur = cur->_left; } else { return cur; } } return nullptr; }

红⿊树的验证

这⾥获取最⻓路径和最短路径,检查最⻓路径不超过最短路径的2倍是不可⾏的,因为就算满⾜这个条件,红⿊树也可能颜⾊不满⾜规则,当前暂时没出问题,后续继续插⼊还是会出问题的。所以我们还是去检查4点规则,满⾜这4点规则,⼀定能保证最⻓路径不超过最短路径的2倍。

  1. 规则1枚举颜⾊类型,天然实现保证了颜⾊不是⿊⾊就是红⾊。
  2. 规则2直接检查根即可
  3. 规则3前序遍历检查,遇到红⾊结点查孩⼦不太⽅便,因为孩⼦有两个,且不⼀定存在,反过来检查⽗亲的颜⾊就⽅便多了。
  4. 规则4前序遍历,遍历过程中⽤形参记录跟到当前结点的blackNum(⿊⾊结点数量),前序遍历遇到⿊⾊结点就++blackNum,⾛到空就计算出了⼀条路径的⿊⾊结点数量。再任意⼀条路径⿊⾊结点数量作为参考值,依次⽐较即可。
bool Check(Node* root, int blackNum, const int refNum) { if (root == nullptr) { // 前序遍历⾛到空时,意味着⼀条路径⾛完了 //cout << blackNum << endl; if (refNum != blackNum) { cout << "存在⿊⾊结点的数量不相等的路径" << endl; return false; } return true; } // 检查孩⼦不太⽅便,因为孩⼦有两个,且不⼀定存在,反过来检查⽗亲就⽅便多了 if (root->_col == RED && root->_parent->_col == RED) { cout << root->_kv.first << "存在连续的红⾊结点" << endl; return false; } if (root->_col == BLACK) { blackNum++; } return Check(root->_left, blackNum, refNum) && Check(root->_right, blackNum, refNum); } bool IsBalance() { if (_root == nullptr) return true; if (_root->_col == RED) return false; // 参考值 int refNum = 0; Node* cur = _root; while (cur) { if (cur->_col == BLACK) { ++refNum; } cur = cur->_left; } return Check(_root, 0, refNum); }

手撕红黑树

#pragma once enum Colour { RED, BLACK }; template<class K, class V> struct RBTreeNode { RBTreeNode<K, V>* _left; RBTreeNode<K, V>* _right; RBTreeNode<K, V>* _parent; pair<K, V> _kv; Colour _col; RBTreeNode(const pair<K, V>& kv) :_left(nullptr) , _right(nullptr) , _parent(nullptr) , _kv(kv) ,_col(RED) {} }; template<class K, class V> class RBTree { typedef RBTreeNode<K, V> Node; public: bool Insert(const pair<K, V>& kv) { if (_root == nullptr) { _root = new Node(kv); _root->_col = BLACK; return true; } Node* parent = nullptr; Node* cur = _root; while (cur) { if (cur->_kv.first < kv.first) { parent = cur; cur = cur->_right; } else if (cur->_kv.first > kv.first) { parent = cur; cur = cur->_left; } else { return false; } } cur = new Node(kv); // 红色的 if (parent->_kv.first < kv.first) { parent->_right = cur; } else { parent->_left = cur; } cur->_parent = parent; while (parent && parent->_col == RED) { Node* grandfather = parent->_parent; if (parent == grandfather->_left) { Node* uncle = grandfather->_right; // 情况一:叔叔存在且为红 if (uncle && uncle->_col == RED) { // 变色 parent->_col = uncle->_col = BLACK; grandfather->_col = RED; // 继续往上处理 cur = grandfather; parent = cur->_parent; } else { // 情况二:叔叔不存在或者存在且为黑 // 旋转+变色 if (cur == parent->_left) { // g // p u // c RotateR(grandfather); parent->_col = BLACK; grandfather->_col = RED; } else { // g // p u // c RotateL(parent); RotateR(grandfather); cur->_col = BLACK; grandfather->_col = RED; } break; } } else { Node* uncle = grandfather->_left; // 情况一:叔叔存在且为红 if (uncle && uncle->_col == RED) { // 变色 parent->_col = uncle->_col = BLACK; grandfather->_col = RED; // 继续往上处理 cur = grandfather; parent = cur->_parent; } else { // 情况二:叔叔不存在或者存在且为黑 // 旋转+变色 // g // u p // c if (cur == parent->_right) { RotateL(grandfather); parent->_col = BLACK; grandfather->_col = RED; } else { // g // u p // c RotateR(parent); RotateL(grandfather); cur->_col = BLACK; grandfather->_col = RED; } break; } } } _root->_col = BLACK; return true; } void RotateL(Node* parent) { Node* subR = parent->_right; Node* subRL = subR->_left; parent->_right = subRL; if (subRL) subRL->_parent = parent; subR->_left = parent; Node* ppnode = parent->_parent; parent->_parent = subR; if (parent == _root) { _root = subR; subR->_parent = nullptr; } else { if (ppnode->_left == parent) { ppnode->_left = subR; } else { ppnode->_right = subR; } subR->_parent = ppnode; } } void RotateR(Node* parent) { Node* subL = parent->_left; Node* subLR = subL->_right; parent->_left = subLR; if (subLR) subLR->_parent = parent; subL->_right = parent; Node* ppnode = parent->_parent; parent->_parent = subL; if (parent == _root) { _root = subL; subL->_parent = nullptr; } else { if (ppnode->_left == parent) { ppnode->_left = subL; } else { ppnode->_right = subL; } subL->_parent = ppnode; } } void _InOrder(Node* root) { if (root == nullptr) return; _InOrder(root->_left); cout << root->_kv.first << endl; _InOrder(root->_right); } void InOrder() { _InOrder(_root); } bool Check(Node* cur) { if (cur == nullptr) return true; if (cur->_col == RED && cur->_parent->_col == RED) { cout << cur->_kv.first << "存在连续的红色节点" << endl; return false; } return Check(cur->_left) && Check(cur->_right); } bool IsBalance() { if (_root && _root->_col == RED) return false; return Check(_root); } private: Node* _root = nullptr; }; void TestRBTree1() { //int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 }; int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 }; RBTree<int, int> t; for (auto e : a) { t.Insert(make_pair(e, e)); // 1、先看是插入谁导致出现的问题 // 2、打条件断点,画出插入前的树 // 3、单步跟踪,对比图一一分析细节原因 //cout << e << "->" << t.IsBalance() << endl; } t.InOrder(); cout << t.IsBalance() << endl; }

test.cpp

#include<iostream> using namespace std; #include<set> #include<map> #include"AVLTree.h" #include"RBTree.h" void TestRBTree_AVLTree() { const int N = 10000000; vector<int> v; v.reserve(N); srand(time(0)); for (size_t i = 0; i < N; i++) { v.push_back(rand() + i); //v.push_back(i); //cout << v.back() << endl; } RBTree<int, int> t1; AVLTree<int, int> t2; size_t begin1 = clock(); for (auto e : v) { t1.Insert(make_pair(e, e)); } size_t end1 = clock(); size_t begin2 = clock(); for (auto e : v) { t2.Insert(make_pair(e, e)); } size_t end2 = clock(); cout << "RBTree RoateSize:" << t1.GetRotateSize() << endl; cout << "AVLTree RoateSize:" << t2.GetRotateSize() << endl; cout << "RBTree Insert:" << end1 - begin1 << endl; cout << "AVLTree Insert:" << end2 - begin2 << endl; cout << "RBTree IsBalance:" << t1.IsBalance() << endl; cout << "AVLTree IsBalance:" << t2.IsBalance() << endl; cout << "RBTree Height:" << t1.Height() << endl; cout << "RBTree Size:" << t1.Size() << endl; cout << "AVLTree Height:" << t2.Height() << endl; cout << "AVLTree Size:" << t2.Size() << endl; size_t begin3 = clock(); // 确定在的值 for (auto e : v) { t1.Find(e); } // 随机值 /*for (size_t i = 0; i < N; i++) { t1.Find((rand() + i)); }*/ size_t end3 = clock(); size_t begin4 = clock(); // 确定在的值 for (auto e : v) { t2.Find(e); } // 随机值 /*for (size_t i = 0; i < N; i++) { t2.Find((rand() + i)); }*/ size_t end4 = clock(); cout << "RBTree Find:" << end3 - begin3 << endl; cout << "AVLTree Find:" << end4 - begin4 << endl; } int main() { TestRBTree_AVLTree(); return 0; }
红黑树与AVL树的比较

红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O(log2Nlog_2Nlog2N),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。

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

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

相关文章

基于Python大数据的就业网站可视化系统设计与实现 开题

目录研究背景与意义研究内容与方法创新点预期成果应用价值开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;研究背景与意义 随着互联网技术和大数据的快速发展&#xff0c;就业市场信息呈现爆…

【盘点】2026年最值得使用的6款降AI工具(附知网维普aigc率检测对比)

每到毕业季&#xff0c;知乎私信里最扎心的问题永远是&#xff1a;“学长&#xff0c;救命&#xff01;查重率过了&#xff0c;但导师甩给我一张红得刺眼的报告单&#xff0c;说我的论文ai率指标爆表了&#xff01;” 说实话&#xff0c;我也经历过那种“渡劫”的崩溃。明明是…

《一年顶十年》书摘|关于情商:如何既取悦自己又让别人舒服?

情商 别做讨好者 讨好者&#xff0c;会特别在意别人的感受&#xff0c;总是想办法去讨好别人。 其实&#xff0c;如果你是用“讨”的姿态去对别人好&#xff0c;别人并不会觉得你有多好&#xff0c;你也得不到想要的尊重和珍惜&#xff0c;更得不到真正的友谊。 关系&#xff0…

【飞腾平台实时Linux方案系列】第十七篇 - 飞腾平台实时Linux系统备份与灾难恢复。

一、简介&#xff1a;备份不是“可选项”&#xff0c;是工业现场“硬指标”飞腾芯片(FT-1500A/FT-2000/FT-D2000)已批量应用于能源、矿山、轨道交通等关键领域&#xff0c;系统一旦宕机&#xff1a;变电站控制中断 → 区域停电&#xff0c;损失千万&#xff1b;矿井提升机失控 …

基于Python爬虫的网络小说热度分析 大数据可视化系统

目录摘要开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;摘要 随着网络文学的快速发展&#xff0c;海量小说数据的高效采集与分析成为研究热点。本系统基于Python爬虫技术&#xff0c;结合大…

智慧果园树上腰果成熟度检测数据集VOC+YOLO格式4700张6类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件)图片数量(jpg文件个数)&#xff1a;4700标注数量(xml文件个数)&#xff1a;4700标注数量(txt文件个数)&#xff1a;4700标注类别…

基于Python的电商购物商城管理系统 0151px5p

目录项目概述核心功能技术架构特色与创新应用场景开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;项目概述 Python电商购物商城管理系统是一个基于Web的全功能电子商务平台&#xff0c;旨在…

FLUX.2 Klein:消费级GPU也能实现的亚秒级图像生成

2026年1月19日&#xff0c;Black Forest Labs&#xff08;黑森林实验室&#xff09;正式开源了FLUX.2系列中的轻量级成员——FLUX.2 [klein]模型家族。作为目前最快的图像生成模型系列之一&#xff0c;FLUX.2 [klein]以其亚秒级推理速度和消费级硬件友好性迅速吸引了开发者的关…

我的 ASP.NET 点餐系统项目实战之旅

asp.net的点餐系统项目&#xff0c;才答辩完&#xff0c;可以使用。 使用了百度API&#xff0c;也是三层架构&#xff0c;有管理员后台和商家前台 刚完成了 ASP.NET 点餐系统的答辩&#xff0c;现在来跟大家分享下这个有趣的项目经历。这个系统已经可以投入使用啦&#xff0c;而…

Openwork 入门指南:开源 AI 桌面助手快速上手

Openwork 入门指南&#xff1a;开源 AI 桌面助手快速上手 最近 Openwork 确实很火&#xff01;它是一个完全开源的 AI 桌面代理工具&#xff0c;被誉为 Claude Cowork&#xff08;Anthropic 的付费协作功能&#xff09;的免费平替。项目在 2026 年 1 月中旬发布后迅速爆火&…

边缘 AI 入门系列(一):Jetson Nano 深度学习环境搭建与 YOLOv5 部署

文章目录 一、概述:Jetson Nano深度学习环境构建全景图 为什么选择Jetson Nano? 环境配置的挑战与解决思路 二、部署流程:从系统刷机到AI框架配置的完整路径 2.1 Jetson Nano刷机流程 2.1.1 刷机前的准备工作 2.1.2 系统镜像选择 2.1.3 刷机操作步骤 2.1.4 刷机常见问题解决…

python的变量名变量名称空间

一、变量的三要素 1.变量名 2.赋值运算符 3.变量值二、声明一个变量的过程 X hello 1.在内存里面开辟了一个变量名X的名称空间 2.在内存里面创建了一个字符串hello 3.将字符串的内存地址和变量名X关联三、内存堆区和栈区 1.变量名称放在内存的栈区 2.变量的值的内存地址放在内…

大数据技术的基于Python+Selenium的招聘信息智能采集与分析系统开题任务书

目录摘要开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;摘要 随着互联网招聘信息的爆炸式增长&#xff0c;传统的人工采集与分析方式效率低下且难以满足精准化需求。基于大数据技术构建的招…

CVE-2025–24813:Apache Tomcat 路径等价性漏洞解析与赏金通告

免责声明&#xff1a;本文档仅用于教育目的。未经授权利用系统是非法行为&#xff0c;将受到法律制裁。保持道德&#xff0c;遵守法律&#xff0c;负责任地进行安全研究。 感谢大家阅读。享受快乐、符合道德的黑客技术&#xff01; 如果你喜欢我的工作或者需要利用脚本&#xf…

提升多尺度检测能力:YOLOv8 中 P2 小目标与 P6 超大目标检测头添加方法

蓝色线条为原模型,绿色线条为优化后的小目标模型,map提升4.5! 我们先理解什么叫做上下采样、再理解要选择哪一层作为检测头和分辨率越大检测目标越小等问题,然后再来添加小目标检测头就会容易理解很多! 原理介绍 一、上采样(Upsampling) 1. 定义 上采样是指将特征图…

TensorRT INT8 部署实战:CMake 工程化 + RTSP 推流的指定区域行人检测与人群密度分析

往期文章 RK3588+docker+YOLOv5部署:https://blog.csdn.net/FJN110/article/details/149673049 RK3588测试NPU和RKNN函数包装https://blog.csdn.net/FJN110/article/details/149669753 RK3588刷机:https://blog.csdn.net/FJN110/article/details/149669404 以及深度学习部署工…

BEV感知十年演进

你问 BEV 感知十年演进&#xff0c;其实已经不是在问一种“感知表示形式”的未来&#xff0c;而是在问&#xff1a; 当系统把世界“压扁成一张俯视图”之后&#xff0c;它还能不能对真实世界的风险负责。 下面这份内容&#xff0c;不是 BEV 从 LSS 到 Occupancy 的技术路线回顾…

【Coze 2.0深度技术解析】从AI辅助工具到主动协作伙伴的技术实现

文章目录目录引言一、Coze 2.0的升级背景&#xff1a;解决传统AI Agent的三大技术痛点二、Coze 2.0四大核心技术能力深度解析2.1 AgentSkills&#xff1a;行业经验的数字化封装&#xff0c;让通用AI快速“专精”2.2 AgentPlan&#xff1a;目标驱动的自主规划&#xff0c;实现长…

YOLOv8 集成 CBAM 实战:通道注意力(CAM)与空间注意力(SAM)详解

YOLOv8 效能再升级:深度解析与集成 CBAMBlock (Convolutional Block Attention Module) 文章目录 YOLOv8 效能再升级:深度解析与集成 CBAMBlock (Convolutional Block Attention Module) 1. 探索注意力机制的奥秘 2. CBAM (Convolutional Block Attention Module) 原理与结构…

RK3588 高分辨率多摄像头系统优化实战:48MP 单摄与双摄分时复用方案解析

文章目录 前言 一、RK3588摄像头硬件资源深度解析 1.1 MIPI PHY硬件架构 1.2 软件通路映射关系详解 1.3 关键配置要点 二、双ISP合成技术深度剖析 2.1 高分辨率处理的技术挑战 2.2 双ISP合成的系统配置 2.3 虚拟ISP节点的重要作用 三、48M分辨率单摄系统的完整实现 3.1 OV50C40…