【C++ 第十四章】红黑树

2024-08-21 06:44
文章标签 c++ 红黑树 第十四章

本文主要是介绍【C++ 第十四章】红黑树,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言:

学习本章,需要先学习 AVL树的 旋转,因为 红黑树也需要旋转调整来平衡,下面讲解将不赘述 旋转的原理和操作

红黑树的旋转 和 AVL树的旋转 唯一不同的是:旋转的判断使用逻辑

AVL树的旋转 可以通过 平衡因子 判断使用哪一种旋转

红黑树的旋转 则 直接通过 判断 爷爷 grandfather、父亲 parent、自己 cur 三种节点之间的位置关系 来 判断使用哪一种 旋转


其实原理都一样,只不过AVL树有了平衡因子,可以直接借助平衡因子判断,其核心还是爷父子三者位置关系


🦖1. 红黑树的概念

        红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是 Red或 Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路 径会比其他路径长出俩倍,因而是接近平衡的

(即 最长路径 长度一定 小于等于 最短路径的*2 )



🦖2. 红黑树的 4 条性质(决定代码实现逻辑)

(1)二叉搜索树的结构

(2)根和叶子(NULL)都是黑色

(3)不存在连续的两个红色结点

(4)任一结点到叶所有路径黑结点数量相同


四条性质可以总结成 四条口诀

左根右、根叶黑、不红红、黑路同


🦖⭐(1)左根右

即 左根右 的 二叉搜索树结构

🦖⭐(2)根叶黑

       根和叶子(NULL)都是黑色

        划蓝色虚线的 节点 10 即为 根节点

        划蓝色虚线的 长方形节点,空节点 NULL

        这两种节点都必须是 黑色!



🦖⭐(3)不红红

不存在连续的两个红色结点

下图中 节点 7 和 节点 5 两个连续红节点的情况不合法,需要进一步调整(后序讲解)


🦖⭐(4)黑路同

任一结点到叶所有路径黑结点数量相同

下图中,每条路径的 黑色节点个数 都是 3 ,这就是合法

注意:一条路径的终点一定是 NULL 空节点!!!


🦖3. 为什么说 "红黑树的最长路径不会超过最短路径的两倍 "

因为所有路径黑色节点的数量必须相同(黑路同),同时 红色节点不能连续出现(不红红)

因此 最长路径一定是 一黑一红 的排列

则 最长的那条路径:即使下面再加一个 红色节点,也只是刚好 是最短路径的两倍,而绝对不会超过 最左边的最短路径


🦖4. 红黑树节点 定义

// 设置颜色枚举值
enum Colour {RED,BLACK
};template<class K, class V>
struct RBTreeNode
{typedef RBTreeNode<K, V> Node;pair<K, V> _data;  Node* _left;Node* _right;Node* _parent;Colour _col;RBTreeNode(const T& data):_data(data), _left(nullptr), _right(nullptr), _parent(nullptr){}
};

🦖5. 红黑树的插入

🦖5.1 插入的节点 默认是 红色

为什么?看下面例子

🦖⭐当 插入的节点 7 为 黑色时

黑色节点 的 插入,必然会违反 ”黑路同“ 的性质,因此要对其他节点都进行调整(其他节点都要再加一个黑色节点)


当 插入的节点 7 为 红色 ,且 该节点插在 黑色节点 8 下面时

可以发现,插入一个 红色节点并没有影响 任何一个 红黑树的性质,即不用做出调整


当 插入的节点 7 为 红色 ,且 该节点插在 红色节点 8 下面时

若 在 红色节点后面插入一个 红色节点,会违反 ”不红红“ 的性质,则需要调整


综上所述,插入黑色节点就一定需要调整,插入红色节点却可能不需要调整

因此,插入红色节点的性价比最高

🦖5.2 插入节点后,若性质被破坏,分三种情况调整

(注意:是性质被破坏了,才需要调整,没被破坏不需要调整)

🦖(1)插入结点是根结点:

        直接将 节点变黑就行

如果非根节点:看叔叔颜色

🦖(2)插入结点的叔叔 uncle 是红色


该情况处理步骤:

1、将叔父爷变色(即除了自己 cur 以外的三个节点)


2、再将 cur 指向 爷爷

(然后继续对这个 cur 进行这 红黑树 4条性质的判定,看是否违反,即 从 cur 开始 继续向上调整)


🦖(3)插入结点的叔叔 uncle 是黑色:旋转 + 变色

注意:黑色节点也可以是 NULL 空节点


直接通过 判断 爷爷 grandfather、父亲 parent、自己 cur 三种节点之间的位置关系 来 判断使用哪一种 旋转

因为 单旋 LL型 和 RR 型的原理一致,双旋 LR 型 和 RL 型的原理一致

下面我就以 LL 型 和 LR 型举例讲解旋转的步骤

🦖LL 型:


1、以爷爷为旋转点,向右旋转

2、变色:爷变红,父变黑

🦖LR 型


1、先 以 father 为旋转点 旋转,再以 爷爷 为旋转点 旋转

2、爷变红,cur 变黑


🦖小结:都是固定步骤

单旋 LL型 和 RR 型

        旋转:以爷爷为旋转点 左旋 或 右旋(父亲为 旋转中心轴)

        变色:爷变红,父变黑

双旋 LR 型 和 RL 型

        旋转:先 旋转 父亲 father,再 旋转 爷爷

        变色:爷变红,cur 变黑


🦖5.3 插入节点中 旋转变色逻辑 代码讲解

根据上面的讲解,可以发现,决定红黑树 旋转 or 变色 的是  爷父子的位置关系 和 叔叔的颜色

因此代码逻辑 也要 以这两点为中心 设计

🦖伪代码:

因为插入节点是 红色,父亲为空时,违反 "不红红" 的性质,则进入循环执行调整

while ( 父亲不为空  同时  父亲的颜色为红色 )

{

        if ( 父亲是  爷爷 的 孩子 ) 

                if ( 叔叔是 色 ) 

                else if ( 叔叔是 色 ) 

                         if ( cur 是 父亲的 )

                         else if ( cur 是 父亲的 )

        else if ( 父亲是  爷爷 的 孩子 ) 

                if ( 叔叔是色 ) 

                else if ( 叔叔是 色 ) 

                         if ( cur 是 父亲的 左 )

                         else if ( cur 是 父亲的 )

}

🦖实际代码:

// 变色调整:
while (parent && parent->_col == RED) {Node* Grandfather = parent->_parent;/*gp     u*/// 父亲是  爷爷 的左孩子if (parent == Grandfather->_left) {Node* Uncle = Grandfather->_right;// 叔叔是 红色:三人变色,cur指爷if (Uncle && Uncle->_col == RED) {parent->_col = BLACK;Uncle->_col = BLACK;Grandfather->_col = RED;cur = Grandfather;parent = cur->_parent;}// 叔叔是 黑色:旋转后变色else if (Uncle == nullptr || Uncle->_col == BLACK) {// 看 cur 的位置:决定单旋 or 双旋if (cur == parent->_left) {/*  右单旋 + 变色gp       uc*/rotateLL(Grandfather);// 爷变红,父变黑Grandfather->_col = RED;parent->_col = BLACK;}else if (cur == parent->_right) {/*  双旋(先左旋后右旋) + 变色gp        uc*/rotateRR(parent);  // p 先 左旋rotateLL(Grandfather);  //  g 再右旋// 爷变红,cur 变黑Grandfather->_col = RED;cur->_col = BLACK;}break;  // 旋转后,退出循环}}// 父亲是  爷爷 的右孩子else if (parent == Grandfather->_right) {Node* Uncle = Grandfather->_left;// 叔叔是 红色:三人变色,cur指爷if (Uncle && Uncle->_col == RED) {parent->_col = BLACK;Uncle->_col = BLACK;Grandfather->_col = RED;cur = Grandfather;parent = cur->_parent;}// 叔叔是 黑色:旋转后变色else if (Uncle == nullptr || Uncle->_col == BLACK) {// 看 cur 的位置:决定单旋 or 双旋if (cur == parent->_right) {/*  左单旋 + 变色gu       pc*/rotateRR(Grandfather);// 爷变红,父变黑Grandfather->_col = RED;parent->_col = BLACK;}else if (cur == parent->_left) {/*  双旋(先右旋后左旋) + 变色gu         pc*/rotateLL(parent);  // p 先 右旋rotateRR(Grandfather);  //  g 再左旋// 爷变红,cur 变黑Grandfather->_col = RED;cur->_col = BLACK;}break;  // 旋转后,退出循环}}
}// 根节点强制变色
_root->_col = BLACK;

🦖5.4 insert 函数 总代码

// 插入
bool insert(const pair<K, V>& kv) {if (_root == nullptr) {_root = new Node(kv);_root->_col = BLACK; // 根节点一定是黑的return true;}Node* cur = _root;Node* parent = cur;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 的位置插入该节点cur = new Node(kv);cur->_col = RED;  // 新增节点给 红的// 父连子,子连父if (parent->_kv.first > kv.first) parent->_left = cur;else  parent->_right = cur;cur->_parent = parent;// 变色调整:while (parent && parent->_col == RED) {Node* Grandfather = parent->_parent;/*gp     u*/// 父亲是  爷爷 的左孩子if (parent == Grandfather->_left) {Node* Uncle = Grandfather->_right;// 叔叔是 红色:三人变色,cur指爷if (Uncle && Uncle->_col == RED) {parent->_col = BLACK;Uncle->_col = BLACK;Grandfather->_col = RED;cur = Grandfather;parent = cur->_parent;}// 叔叔是 黑色:旋转后变色else if (Uncle == nullptr || Uncle->_col == BLACK) {// 看 cur 的位置:决定单旋 or 双旋if (cur == parent->_left) {/*  右单旋 + 变色gp       uc*/rotateLL(Grandfather);// 爷变红,父变黑Grandfather->_col = RED;parent->_col = BLACK;}else if (cur == parent->_right) {/*  双旋(先左旋后右旋) + 变色gp        uc*/rotateRR(parent);  // p 先 左旋rotateLL(Grandfather);  //  g 再右旋// 爷变红,cur 变黑Grandfather->_col = RED;cur->_col = BLACK;}break;}}// 父亲是  爷爷 的右孩子else if (parent == Grandfather->_right) {Node* Uncle = Grandfather->_left;// 叔叔是 红色:三人变色,cur指爷if (Uncle && Uncle->_col == RED) {parent->_col = BLACK;Uncle->_col = BLACK;Grandfather->_col = RED;cur = Grandfather;parent = cur->_parent;}// 叔叔是 黑色:旋转后变色else if (Uncle == nullptr || Uncle->_col == BLACK) {// 看 cur 的位置:决定单旋 or 双旋if (cur == parent->_right) {/*  左单旋 + 变色gu       pc*/rotateRR(Grandfather);// 爷变红,父变黑Grandfather->_col = RED;parent->_col = BLACK;}else if (cur == parent->_left) {/*  双旋(先右旋后左旋) + 变色gu         pc*/rotateLL(parent);  // p 先 右旋rotateRR(Grandfather);  //  g 再左旋// 爷变红,cur 变黑Grandfather->_col = RED;cur->_col = BLACK;}break;}}}// 修改一:根节点强制变色_root->_col = BLACK;return false;
}

🦖6. 红黑树的删除

红黑树的删除本节不做讲解,有兴趣的同学可参考:《算法导论》或者《STL源码剖析》 http://www.cnblogs.com/fornever/archive/2011/12/02/2270692.html

🦖7. 红黑树与AVL树的比较

        之前讲到的AVL树,它是左右子树的高度相差不会超过一,可以发现 AVL树 对于平衡的要求会更加严格,因此 AVL树在树高上面要比红黑树控制的更加平衡,查询节点的时间复杂度为 logN

因为红黑树的最长路径 可以为最短路径的2倍,因此 查询节点的时间复杂度为 log 2*N

        所以在查询上面红黑树要略逊于  AVL树,当然在时间复杂度上都是同一个数量级,都是O(logN),差距不会太大

        恰恰因为 AVL树 要严格的控制树的平衡,因此 插入删除 操作后,旋转的次数较多

        而 红黑树 插入删除 操作中,旋转的次数较少

        所以相比之下, AVL树 在查询上边呢更高效;红黑树 在插入删除上边更高效

        在实际应用当中呢,红黑树 用的更广泛一些,比如说 C++的STL 当中的 map 和 set 都是基于红黑树实现的(下一个章节会讲解 【map 和 set 对红黑叔的封装】)

Java 库 、 linux内核 、其他一些库 都有使用 红黑树

🦖8. ⭐红黑树的完整代码

#pragma once
#include<iostream>
#include<vector>
#include<assert.h>
using namespace std;///// 设置颜色枚举值
enum Colour {RED,BLACK
};template<class K, class V>
struct RBTreeNode
{typedef RBTreeNode<K, V> Node;pair<K, V> _kv;Node* _left;Node* _right;Node* _parent;Colour _col;RBTreeNode(const pair<K, V>& data):_kv(data), _left(nullptr), _right(nullptr), _parent(nullptr){}
};template<class K, class V>
class RBTree
{
public:typedef RBTreeNode<K, V> Node;RBTree() = default;~RBTree() {destory(_root);_root = nullptr;}// 查找Node* find(const K& key) {Node* cur = _root;while (cur) {if (key > cur->_kv.first) {cur = cur->_right;}else if (key < cur->_kv.first) {cur = cur->_left;}else {return cur;}}return nullptr;}// 插入bool insert(const pair<K, V>& kv) {if (_root == nullptr) {_root = new Node(kv);_root->_col = BLACK; // 根节点一定是黑的return true;}Node* cur = _root;Node* parent = cur;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 的位置插入该节点cur = new Node(kv);cur->_col = RED;  // 新增节点给 红的// 父连子,子连父if (parent->_kv.first > kv.first) parent->_left = cur;else  parent->_right = cur;cur->_parent = parent;// 变色调整:while (parent && parent->_col == RED) {Node* Grandfather = parent->_parent;/*gp     u*/// 父亲是  爷爷 的左孩子if (parent == Grandfather->_left) {Node* Uncle = Grandfather->_right;// 叔叔是 红色:三人变色,cur指爷if (Uncle && Uncle->_col == RED) {parent->_col = BLACK;Uncle->_col = BLACK;Grandfather->_col = RED;cur = Grandfather;parent = cur->_parent;}// 叔叔是 黑色:旋转后变色else if (Uncle == nullptr || Uncle->_col == BLACK) {// 看 cur 的位置:决定单旋 or 双旋if (cur == parent->_left) {/*  右单旋 + 变色gp       uc*/rotateLL(Grandfather);// 爷变红,父变黑Grandfather->_col = RED;parent->_col = BLACK;}else if (cur == parent->_right) {/*  双旋(先左旋后右旋) + 变色gp        uc*/rotateRR(parent);  // p 先 左旋rotateLL(Grandfather);  //  g 再右旋// 爷变红,cur 变黑Grandfather->_col = RED;cur->_col = BLACK;}break;}}// 父亲是  爷爷 的右孩子else if (parent == Grandfather->_right) {Node* Uncle = Grandfather->_left;// 叔叔是 红色:三人变色,cur指爷if (Uncle && Uncle->_col == RED) {parent->_col = BLACK;Uncle->_col = BLACK;Grandfather->_col = RED;cur = Grandfather;parent = cur->_parent;}// 叔叔是 黑色:旋转后变色else if (Uncle == nullptr || Uncle->_col == BLACK) {// 看 cur 的位置:决定单旋 or 双旋if (cur == parent->_right) {/*  左单旋 + 变色gu       pc*/rotateRR(Grandfather);// 爷变红,父变黑Grandfather->_col = RED;parent->_col = BLACK;}else if (cur == parent->_left) {/*  双旋(先右旋后左旋) + 变色gu         pc*/rotateLL(parent);  // p 先 右旋rotateRR(Grandfather);  //  g 再左旋// 爷变红,cur 变黑Grandfather->_col = RED;cur->_col = BLACK;}break;}}}// 修改一:根节点强制变色_root->_col = BLACK;return false;}// RR型:左单旋void rotateRR(Node* parent) {Node* subR = parent->_right;Node* subRL = subR->_left;Node* parentParent = parent->_parent;// 1、subRL变成parent的右孩子parent->_right = subRL;// subRL 是有可能为 空的if (subRL) {subRL->_parent = parent;}// 2、parent变成subR的左孩子subR->_left = parent;parent->_parent = subR;// 3、subR变成当前子树的根// parentParent 是指 刚开始的 parent 的父亲:若 parent 是 _root 则 parentParent 为空,否则不为空,则该树就是子树if (parentParent) {if (parent == parentParent->_right)parentParent->_right = subR;else parentParent->_left = subR;subR->_parent = parentParent;}// 如果 parentParent == nullptr:说明 parent 是该树的 _root,_root 的 parent 是空else {_root = subR;subR->_parent = nullptr;}}// LL型:右单旋void rotateLL(Node* parent) {Node* subL = parent->_left;Node* subLR = subL->_right;Node* parentParent = parent->_parent;// 1、subLR变成parent的左孩子parent->_left = subLR;// subRL 是有可能为 空的if (subLR) {subLR->_parent = parent;}// 2、parent变成subL的右孩子subL->_right = parent;parent->_parent = subL;// 3、subL 变成当前子树的根// parentParent 是指 刚开始的 parent 的父亲:若 parent 是 _root 则 parentParent 为空,否则不为空,则该树就是子树if (parentParent) {if (parent == parentParent->_right)parentParent->_right = subL;else parentParent->_left = subL;subL->_parent = parentParent;}// 如果 parentParent == nullptr:说明 parent 是该树的 _root,_root 的 parent 是空else {_root = subL;subL->_parent = nullptr;}}// LR 型:subL 先 左旋, parent 右旋void rotateLR(Node* parent) {rotateRR(parent->_left);rotateLL(parent);}// RL 型:subR 先 右旋, parent 左旋void rotateRL(Node* parent) {rotateLL(parent->_right);rotateRR(parent);}// 中序遍历void InOrder() {_InOrder(_root);cout << '\n';}// 获取该树的高度int Height() {return _Height(_root);}// 获取节点个数int Size() {return _Size(_root);}// 判断是否是 红黑树bool IsValidRBTree() {if (_root == nullptr) return false;else if (_root && _root->_col == RED) return false;// 遍历一条路,记录一条路上一共固定有多少个黑色节点int cnt = 0;Node* cur = _root;while (cur) {if (cur->_col == BLACK) cnt++;cur = cur->_left;}return _IsValidRBTree(_root, 0, cnt);}private://  判断是否是 红黑树bool _IsValidRBTree(Node* pRoot, size_t k, const size_t blackCount){// 1、看根节点是否是 黑的// 2、看每条路径的 黑色节点数量是否相同// 3、检查是否有连续的红节点:遇到一个红节点就判断其父亲是否是 红的//走到null之后,判断 k 和 blackCount 是否相等:即一条路径上的 黑色节点数量是否为固定值if (pRoot == nullptr){if (k != blackCount){cout << "违反性质四:每条路径中黑色节点的个数必须相同" << endl;return false;}return true;}// 统计黑色节点的个数if (pRoot->_col == BLACK)k++;// 检测当前节点与其双亲是否都为红色Node* pParent = pRoot->_parent;if (pParent && pParent->_col == RED && pRoot->_col == RED){cout << "违反性质三:没有连在一起的红色节点" << endl;return false;}return _IsValidRBTree(pRoot->_left, k, blackCount) && _IsValidRBTree(pRoot->_right, k, blackCount);}int _Size(Node* pRoot) {if (pRoot == nullptr) return 0;//if (pRoot->_left == nullptr && pRoot->_right == nullptr) return 1;return 1 + _Size(pRoot->_left) + _Size(pRoot->_right);}int _Height(Node* pRoot) {if (pRoot == nullptr)return 0;return 1 + max(_Height(pRoot->_left), _Height(pRoot->_right));}// 销毁一棵树:后序遍历void destory(Node* root) {if (root == nullptr) {return;}destory(root->_left);destory(root->_right);delete root;}void _InOrder(const Node* root) {if (root == nullptr) {return;}_InOrder(root->_left);cout << (root->_kv).first << " : " << (root->_kv).second << '\n';_InOrder(root->_right);}Node* _root = nullptr;
};

参考文献和资料

B站 up :蓝不过海呀

【红黑树 - 定义, 插入, 构建】https://www.bilibili.com/video/BV1Xm421x7Lg?vd_source=bea8fdb0eb9c0c7d500ffd191a292977

这篇关于【C++ 第十四章】红黑树的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1092419

相关文章

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现

c++的初始化列表与const成员

初始化列表与const成员 const成员 使用const修饰的类、结构、联合的成员变量,在类对象创建完成前一定要初始化。 不能在构造函数中初始化const成员,因为执行构造函数时,类对象已经创建完成,只有类对象创建完成才能调用成员函数,构造函数虽然特殊但也是成员函数。 在定义const成员时进行初始化,该语法只有在C11语法标准下才支持。 初始化列表 在构造函数小括号后面,主要用于给

2024/9/8 c++ smart

1.通过自己编写的class来实现unique_ptr指针的功能 #include <iostream> using namespace std; template<class T> class unique_ptr { public:         //无参构造函数         unique_ptr();         //有参构造函数         unique_ptr(