C++:set和map的底层封装模拟实现

目录

底层对比:

底层红黑树结构和set、map: 

底层模拟:

传值调用:

迭代器:

operator ++()

 find函数

operator() 、仿函数 

set和map的仿函数 :

图解: 

insert函数:

构造函数,析构函数: 

 析构函数:

拷贝构造函数: 

赋值函数: 

map的封装:

set的封装: 

红黑树的修改: 



底层对比:

上图以kv模型为例 

通过底层可以看出:

  • set和map的底层调用都是调用了红黑树 rb_tree
  • set和map在底层最大的区别就是value的不同,set的value仍然是key类型的,但是map的value是pair<const key,T>类型的
  • 也因为类型的不同,二者使用的template 类模板也相差了一个class T ,而这个class T表示的其实就是map的value中的second的数值类型
  • 同时因为value的不同,所以导致了map和set在红黑树中存储的数据也并不相同

底层红黑树结构和set、map: 

底层模拟:

传值调用:

迭代器:

  • 不论是set还是map 的迭代器,关键得是看红黑树的迭代器
  • 树的内部迭代器其实就是和链表相差不多,需要进行一个内部的重载调用
  • 也就是说 operator-> operator * operator ++ operator --都是需要进行内部重载,然后进行封装的!
 RBTree.htemplate<class T, class Ref, class Ptr>
struct __RBTreeIterator
{typedef RBTreeNode<T> Node;typedef __RBTreeIterator<T, Ref, Ptr> Self;Node* _node;__RBTreeIterator(Node* node):_node(node){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const Self& s){return _node != s._node;}Self& operator++(){if (_node->_right){// 下一个,右树最左节点Node* leftMin = _node->_right;while (leftMin->_left){leftMin = leftMin->_left;}_node = leftMin;}else{// 下一个,孩子等于父亲左的那个祖先Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}
};

operator ++()

是实现迭代器的难点之一,因为红黑树的本质其实是二叉搜索树,所以遍历是使用中序遍历,所以++和--都是使用 中序遍历的节点,如下图所示,it所在位置的下一个节点是15 

 首先我们需要知道,中序遍历的过程是 左子树 根节点 右子树 所以可以通过下图右可以得出:

  • 当前it所在的位置是它父亲节点的左节点,那么下一个需要遍历的节点就是it的父亲节点
  • 当前节点的右子树是空的,那么表示这个节点的右子树结束遍历,下一个需要遍历和++递达的节点,应该往上进行查找祖先节点
  • 如果当前节点的右子树是空的,且当前节点是其父亲节点的右节点,那么表示要去父亲节点更往上的方向寻找下一个需要遍历的节点
  • 如果当前节点的右子树是空的/遍历完了,且当前节点是其父亲节点的左节点,那么表示当前节点结束,返回父亲节点且下一个访问的节点就是父亲节点,且同时访问完后需要取父亲节点的右子树中进行遍历和寻找下一个++位置的节点
  • 最后一个单独的处理,那就是走到最后一个节点的时候,走到最后一个节点的右子树是空的,就一直返回到根节点,这时候就需要给迭代器置空了!

 

  • ++的下一个节点是该节点右子树中的最左节点!
  • leftMin = node->right 表示进入右子树中寻找最左节点,当然现在的条件是右子树存在,如果右子树存在就是上面,如果不存在就需要往上边寻找,直到找到某个节点是它父亲节点的左节点为止,或者说找到的某个节点,他没有父亲节点它是根节点为止!

  • 要求当前的节点是他父亲节点的右节点,如果一直是这样就需要一直往上走,直到当前节点是它父亲节点的左节点时,返回父亲节点
  • 且需要注意如果当前节点是根节点,那么它的父亲就是空的,空的不能再继续往上寻找了,所以直接跳出循环,最后返回父亲节点!
RBTree.h
//红黑树中调动迭代器的函数方法
typedef __RBTreeIterator<T, T&, T*> Iterator;
typedef __RBTreeIterator<T, const T&, const T*> ConstIterator;Iterator Begin(){Node* leftMin = _root;while (leftMin && leftMin->_left){leftMin = leftMin->_left;}return Iterator(leftMin);}Iterator End(){return Iterator(nullptr);}ConstIterator End() const{return ConstIterator(nullptr);}ConstIterator Begin() const{Node* leftMin = _root;while (leftMin && leftMin->_left){leftMin = leftMin->_left;}return ConstIterator(leftMin);}

 find函数

RBTree.hIterator 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 Iterator(cur);}}return End();}

operator() 、仿函数 

 使用仿函数的原因其实是在insert函数中需要使用operator()的原因,因为在insert函数中,需要使用比较大小之间的问题,而为了让红黑树同时适应set和map(主要是针对map的pair内部问题),需要对取值之间进行正确取值的操作。

如上图所示,在set中data可以直接对于set中的value,但是对于map来说,需要在pair中进行取值,所以map需要一个详细的对应关系,这就造成了仿函数Key0fT的诞生

RBTree.h//树的最终的基础结构
template<class K, class T, class KeyOfT>
class RBTree
{typedef RBTreeNode<T> Node;public:typedef __RBTreeIterator<T, T&, T*> Iterator;typedef __RBTreeIterator<T, const T&, const T*> ConstIterator;
private:Node* _root = nullptr;
};

set和map的仿函数 :

        //Myset.hstruct SetKeyOfT{const K& operator()(const K& key){return key;}};//Mymap.hstruct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};

图解: 

insert函数:

pair<Iterator, bool> Insert(const T& data){if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return make_pair(Iterator(_root), true);}KeyOfT kot;//调用了仿函数,方便之后的比较Node* parent = nullptr;Node* cur = _root;while (cur){// K// pair<K, V>// kot对象,是用来取T类型的data对象中的keyif (kot(cur->_data) < kot(data)){parent = cur;cur = cur->_right;}else if (kot(cur->_data) > kot(data)){parent = cur;cur = cur->_left;}else{return make_pair(Iterator(cur), false);}}cur = new Node(data);Node* newnode = cur;cur->_col = RED; // 新增节点给红色if (kot(parent->_data) < kot(data)){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = 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//            cif (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//		g//   u     p//      cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return make_pair(Iterator(newnode), true);}

构造函数,析构函数: 

 析构函数:

~RBTree(){Destroy(_root);_root = nullptr;}private:void Destroy(Node* root){if (root == nullptr)return;Destroy(root->_left);Destroy(root->_right);delete root;root = nullptr;}

拷贝构造函数: 

 拷贝构造函数使用的是前序遍历,且内部需要三条链接的拷贝和构造,也就是左子树、右子树、和父亲节点,同时也需要进行节点颜色的拷贝操作,所以拷贝构造的内部其实是一个前序遍历+颜色的拷贝+左右子树的拷贝和链接+递归遍历

RBTree(const RBTree<K, T, KeyOfT>& t){_root = Copy(t._root);}private:Node* Copy(Node* root){//因为要有父亲节点的链接,所以需要一个反向链接//其次也需要进行颜色的拷贝!因为是红黑树!if (root == nullptr)return nullptr;Node* newroot = new Node(root->_data);newroot->_col = root->_col;newroot->_left = Copy(root->_left);if (newroot->_left)newroot->_left->_parent = newroot;newroot->_right = Copy(root->_right);if (newroot->_right)newroot->_right->_parent = newroot;return newroot;}

赋值函数: 

//RBTree.hRBTree<K, T, KeyOfT>& operator=(RBTree<K, T, KeyOfT> t){swap(_root, t._root);return *this;}

map的封装:

namespace bit
{template<class K, class V>class map{struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};public:typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::Iterator iterator;typedef typename RBTree<K, const K, MapKeyOfT>::ConstIterator const_iterator;const_iterator begin() const{return _t.Begin();}const_iterator end() const{return _t.End();}iterator begin() {return _t.Begin();}iterator end() {return _t.End();}iterator find(const K& key){return _t.Find(key);}pair<iterator, bool> insert(const pair<K, V>& kv){return _t.Insert(kv);}V& operator[](const K& key){pair<iterator, bool> ret = _t.Insert(make_pair(key, V()));return ret.first->second;}private:RBTree<K, pair<const K, V>, MapKeyOfT> _t;};

set的封装: 

namespace bit
{template<class K>class set{struct SetKeyOfT{const K& operator()(const K& key){return key;}};public:typedef typename RBTree<K, const K, SetKeyOfT>::Iterator iterator;typedef typename RBTree<K, const K, SetKeyOfT>::ConstIterator const_iterator;const_iterator begin() const{return _t.Begin();}const_iterator end() const{return _t.End();}iterator begin(){return _t.Begin();}iterator end(){return _t.End();}iterator find(const K& key){return _t.Find(key);}pair<iterator, bool> insert(const K& key){return _t.Insert(key);}private:RBTree<K, const K, SetKeyOfT> _t;};void PrintSet(const set<int>& s){for (auto e : s){cout << e << endl;}}

红黑树的修改: 

 

#pragma once
#include<vector>enum Colour
{RED,BLACK
};template<class T>
struct RBTreeNode
{RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;T _data;Colour _col;RBTreeNode(const T& data):_left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _col(RED){}
};template<class T, class Ref, class Ptr>
struct __RBTreeIterator
{typedef RBTreeNode<T> Node;typedef __RBTreeIterator<T, Ref, Ptr> Self;Node* _node;__RBTreeIterator(Node* node):_node(node){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const Self& s){return _node != s._node;}Self& operator++(){if (_node->_right){// 下一个,右树最左节点Node* leftMin = _node->_right;while (leftMin->_left){leftMin = leftMin->_left;}_node = leftMin;}else{// 下一个,孩子等于父亲左的那个祖先Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}
};template<class K, class T, class KeyOfT>
class RBTree
{typedef RBTreeNode<T> Node;public:typedef __RBTreeIterator<T, T&, T*> Iterator;typedef __RBTreeIterator<T, const T&, const T*> ConstIterator;RBTree() = default;RBTree(const RBTree<K, T, KeyOfT>& t){_root = Copy(t._root);}// t2 = t1RBTree<K, T, KeyOfT>& operator=(RBTree<K, T, KeyOfT> t){swap(_root, t._root);return *this;}~RBTree(){Destroy(_root);_root = nullptr;}Iterator Begin(){Node* leftMin = _root;while (leftMin && leftMin->_left){leftMin = leftMin->_left;}return Iterator(leftMin);}Iterator End(){return Iterator(nullptr);}ConstIterator End() const{return ConstIterator(nullptr);}ConstIterator Begin() const{Node* leftMin = _root;while (leftMin && leftMin->_left){leftMin = leftMin->_left;}return ConstIterator(leftMin);}Iterator 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 Iterator(cur);}}return End();}// 20:10pair<Iterator, bool> Insert(const T& data){if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return make_pair(Iterator(_root), true);}KeyOfT kot;Node* parent = nullptr;Node* cur = _root;while (cur){// K// pair<K, V>// kot对象,是用来取T类型的data对象中的keyif (kot(cur->_data) < kot(data)){parent = cur;cur = cur->_right;}else if (kot(cur->_data) > kot(data)){parent = cur;cur = cur->_left;}else{return make_pair(Iterator(cur), false);}}cur = new Node(data);Node* newnode = cur;cur->_col = RED; // 新增节点给红色if (kot(parent->_data) < kot(data)){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = 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//            cif (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//		g//   u     p//      cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return make_pair(Iterator(newnode), true);}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;_root->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = subL;}else{ppNode->_right = subL;}subL->_parent = ppNode;}}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;_root->_parent = nullptr;}else{if (ppNode->_right == parent){ppNode->_right = subR;}else{ppNode->_left = subR;}subR->_parent = ppNode;}}void InOrder(){_InOrder(_root);cout << endl;}bool IsBalance(){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);}private:Node* Copy(Node* root){if (root == nullptr)return nullptr;Node* newroot = new Node(root->_data);newroot->_col = root->_col;newroot->_left = Copy(root->_left);if (newroot->_left)newroot->_left->_parent = newroot;newroot->_right = Copy(root->_right);if (newroot->_right)newroot->_right->_parent = newroot;return newroot;}void Destroy(Node* root){if (root == nullptr)return;Destroy(root->_left);Destroy(root->_right);delete root;root = nullptr;}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);}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_InOrder(root->_right);}private:Node* _root = nullptr;//size_t _size = 0;
};

 

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

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

相关文章

地平线-旭日X3派(RDK X3)上手基本操作

0. 环境 - win10笔记本 - RDK X3 1.0&#xff08;地平线旭日X3派&#xff0c;后来改名为代号RDK X3&#xff09; 1. 下载资料 https://developer.horizon.ai/resource 主要下载镜像 http://sunrise.horizon.cc/downloads/os_images/2.1.0/release/ 下载得到了 ubuntu-prei…

vs无法打开或包括文件”QTxxx“

vs创建项目时默认引入core、gui、和widgets等模块&#xff0c;在需要网络通讯或者图表等开发时需要添加相应模块。 点击扩展 -> QT VS Tools -> QT Project Setting->Qt Modules&#xff0c;添加相应模块即可

奇瑞控股携手契约锁推动客户、供应商及内部业务全程数字化

奇瑞控股集团是安徽省排名第一的制造业企业&#xff0c;同时入选中国企业家协会发布的中国500强、《财富》中国500强&#xff0c;连续21年位居中国品牌乘用车出口第一。 面对汽车行业“新四化”主题及“数字化”时代变革&#xff0c;奇瑞控股持续创新求变&#xff0c;率先引入电…

Java18的新特性介绍

一、概况 Java 18是Java编程语言的最新版本&#xff0c;它于2022年9月发布。Java 18引入了许多新特性和改进&#xff0c;以下是其中一些重要的新特性。 元编程功能&#xff1a;Java 18引入了元注释和元类型声明的功能&#xff0c;使开发人员能够在编译时对注解进行元处理。这为…

【C++】位图/布隆过滤器+海量数据处理

目录 一、位图 1.1 位图的概念 1.2 位图的实现 1.3 位图的应用&#xff08;面试题&#xff09; 二、布隆过滤器 2.1 布隆过滤器的引入 2.2 布隆过滤器概念 2.3 布隆过滤器的插入和查找 2.4 布隆过滤器的实现 2.5 布隆过滤器的优点和缺陷 2.6 布隆过滤器的应用&#…

Servlet 的 API

HttpServlet init&#xff1a;当 tomcat 收到了 /hello 这样的路径是请求后就会调用 HelloServlet&#xff0c;于是就需要对 HelloServlet 进行实例化&#xff08;只实例一次&#xff0c;后续再有请求也不实例了&#xff09;。 destory&#xff1a;如果是通过 smart tomcat 的停…

洗地机品牌哪个牌子好?实力派洗地机品牌TOP10榜单

洗地机依靠其洗、拖、吸、烘为一体的功能&#xff0c;能高效的完成地面清洁的工作&#xff0c;深受大家的喜爱。但是洗地机的型号越来越多&#xff0c;功能也越来越多&#xff0c;对于不想花大价钱&#xff0c;又想要高性价比的精致人群来说实在不友好&#xff0c;所以笔者今天…

C++ 中重写重载和隐藏的区别

重写&#xff08;override&#xff09;、重载&#xff08;overload&#xff09;和隐藏&#xff08;overwrite&#xff09;在C中是3个完全不同的概念。我们这里对其进行详细的说明 1、重写&#xff08;override&#xff09;是指派生类覆盖了基类的虚函数&#xff0c;这里的覆盖必…

实例展示vue单元测试及难题解惑

通过生动详实的例子带你排遍vue单元测试过程中的所有疑惑与难题。 技术栈&#xff1a;jest、vue-test-utils。 共四个部分&#xff1a;运行时、Mock、Stub、Configuring和CLI。 运行时 在跑测试用例时&#xff0c;大家的第一个绊脚石肯定是各种undifned报错。 解决这些报错…

【HarmonyOS4学习笔记】《HarmonyOS4+NEXT星河版入门到企业级实战教程》课程学习笔记(九)

课程地址&#xff1a; 黑马程序员HarmonyOS4NEXT星河版入门到企业级实战教程&#xff0c;一套精通鸿蒙应用开发 &#xff08;本篇笔记对应课程第 16 节&#xff09; P16《15.ArkUI-状态管理-任务统计案例》 1、实现任务进度卡片 怎么让进度条和进度展示文本堆叠展示&#xff1…

./scripts/Makefile.clean 文件分析

文章目录 目标 $(subdir-ymn)目标__clean $(clean-dirs):     make -f ./scripts/Makefile.clean obj$(patsubst _clean_%,%,$) $(clean-dirs)$(patsubst _clean_%,%,$)_clean_api _clean_cmd _clean_common _clean_disk _clean_drivers _clean_drivers/ddr/altera _clean_d…

react中的useEffect()的使用

useEffect()是react中的hook函数&#xff0c;作用是用于创建由渲染本身引起的操作&#xff0c;而不是事件的触发&#xff0c;比如Ajax请求&#xff0c;DOM的更改 首先useEffect()是个函数&#xff0c;接受两个参数&#xff0c;第一个参数是一个方法&#xff0c;第二个参数是数…

vue3 路由跳转 携带参数

实现功能&#xff1a;页面A 跳转到 页面B&#xff0c;携带参数 路由router.ts import { createRouter, createWebHistory } from "vue-router";const routes: RouteRecordRaw[] [{path: "/demo/a",name: "aa",component: () > import(&quo…

x264 码率控制原理:x264_ratecontrol_start 函数

x264_ratecontrol_start 函数 函数原理 函数功能:编码一帧之前,为当前帧选择一个量化 QP,属于帧级别码率控制;这对于控制视频质量和文件大小至关重要。通过调整QP,编码器可以在保持视频质量的同时,尽可能减小输出文件的大小。函数参数:x264_t *h: 编码器上下文结构体指…

地信遥感测绘电子书

《地理信息系统概论》&#xff0c;黄杏元&#xff0c;马劲松编著&#xff0c;第三版&#xff0c;高等教育出版社&#xff0c;2008年 《地理信息系统》&#xff08;第二版&#xff09;汤国安&#xff0c;赵牡丹&#xff0c;杨昕等编&#xff0c;高等教育出版社&#xff0c;2010…

【stm32/CubeMX、HAL库】嵌入式实验五:定时器(2)|PWM输出

参考&#xff1a; 【【正点原子】手把手教你学STM32CubeIDE开发】 https://www.bilibili.com/video/BV1Wp42127Cx/?p13&share_sourcecopy_web&vd_source9332b8fc5ea8d349a54c3989f6189fd3 《嵌入式系统基础与实践》刘黎明等编著&#xff0c;第九章定时器&#xff0c…

8操作系统定义、分类及功能+设备管理+作业管理 软设刷题 软考+

操作系统定义、分类及功能设备管理作业管理 知识点1-55-1010-1515-2020-2525-3030-35 刷题操作系统定义、分类及功能1-55-1010-15作业管理1-5设备管理1-55-10 知识点 1-5 1 嵌入式操作系统的特点&#xff1a; 1.微型化&#xff0c;从性能和成本角度考虑&#xff0c;希望占用的…

145.栈和队列:删除字符串中的所有相邻重复项(力扣)

题目描述 代码解决 class Solution { public:string removeDuplicates(string s) {// 定义一个栈来存储字符stack<char> st;// 遍历字符串中的每一个字符for(int i 0; i < s.size(); i){// 如果栈为空或栈顶字符与当前字符不相同&#xff0c;则将当前字符入栈if(st.e…

Jenkins的Pipeline流水线

目录 前言 流水线概念 什么是流水线 Jenkins流水线 pipeline node stage step 创建一个简单的流水线 创建Pipeline项目 选择模板 测试 前言 提到 CI 工具&#xff0c;首先想到的就是“CI 界”的大佬——Jenkjns,虽然在云原生爆发的年代,蹦出来了很多云原生的 CI 工具…

Hive的窗口函数

定义&#xff1a; 聚合函数是针对定义的行集(组)执行聚集,每组只返回一个值.如sum()、avg()、max() 窗口函数也是针对定义的行集(组)执行聚集,可为每组返回多个值.如既要显示聚集前的数据,又要显示聚集后的数据.步骤&#xff1a; 1.将记录分割成多个分区. 2.在各个分区上调用窗…