当前位置: 首页 > wzjs >正文

石家庄网站建设seo郴州网站建设网络推广渠道

石家庄网站建设seo,郴州网站建设网络推广渠道,西安网站制作哪家便宜又好,辽宁建设工程信息网那个Hello大家好&#xff01;很高兴我们又见面啦&#xff01;给生活添点passion&#xff0c;开始今天的编程之路&#xff01; 我的博客&#xff1a;<但凡. 我的专栏&#xff1a;《编程之路》、《数据结构与算法之美》、《题海拾贝》、《C修炼之路》 这期我们模拟实现一下基于红黑…

        Hello大家好!很高兴我们又见面啦!给生活添点passion,开始今天的编程之路!

我的博客:<但凡.

我的专栏:《编程之路》、《数据结构与算法之美》、《题海拾贝》、《C++修炼之路》

        这期我们模拟实现一下基于红黑树的map和set。

目录

1、封装set和map,解决KeyOfT

2、iterator

3、key不支持修改的问题

4、operator[]


 

1、封装set和map,解决KeyOfT

        首先我们先把set和map的大类实现一下:

map:

#pragma once
#include"RBTree.h"template<class K,class V>
class map
{
public:private:RBTree<K, pair<K, V>> _t;};

set:

#pragma once
#include"RBTree.h"template<class K>
class set
{
public:private:RBTree<K, K> _t;
};

        对于传入红黑树的两个参数,第一个我们传入关键值key,第二个传入我们存储的实际类型,如果是key就再传入一次,如果是key_value就传入pair类型。

        我们需要一个KeyOfT,将key_value里面的key值取出来,因为我们不知道存储的值到底是key还是key_value类型,所以对于map和set我们都需要一个对应的KeyOfT。

map:

struct MapKeyOfT
{const K& operator()(const pair<K, V>& kv){return kv.first;}
};

 set:

struct SetKeyOfT
{const K& operator()(const K& key){return key;}
};

         这样的话,我们在传入红黑树时需要往模板中传入三个参数,所以我们对红黑树进行改造:

template<class K,class T,class KeyOfT>
class RBTree
{typedef  RBTreeNode<T> Node;public:pair<Iterator,bool> Insert(const T& data){if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;//根节点必须是黑return { Iterator(_root,_root),true };}KeyOfT kot;Node* parent = nullptr;Node* cur = _root;while (cur){//这访函数返回T值if (kot(cur->_data) < kot(data)){parent = cur;cur = cur->_right;}else if (kot(cur->_data) > kot(data)){parent = cur;cur = cur->_left;}else{return { Iterator(cur,_root),false };//隐式类型转换}}cur = new Node(data);Node* newnode = cur;cur->_col = RED;//新增的必须是redif (kot(parent->_data) < kot(data)){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{//u不存在或u存在且为黑//单边高if (cur == parent->_left){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}//对应的是AVL树左边右边高的情况else{RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else{Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED){uncle->_col = BLACK;parent->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return { Iterator(newnode,_root),false };}......Iterator Find(const K& key){KeyOfT kot;Node* cur = _root;while (cur){if (kot(cur->_data) < key){cur = cur->_right;}else if (kot(cur->_data) > key){cur = cur->_left;}else{return Iterator(cur,_root);}}return End();}private:......
private:Node* _root = nullptr;
};

        为了节省篇幅我把所有不涉及改动的函数都先去掉了。另外insert里面涉及的迭代器我们之后再说。

2、iterator

        接着我们实现map和set的迭代器。首先我们在红黑树所在的头文件中把迭代器定义出来:

template<class T,class ptr,class ref>
class RBTreeIterator
{
public:typedef RBTreeNode<T> Node;typedef RBTreeIterator<T,ptr,ref> Self;RBTreeIterator(Node* node, Node* root):_node(node),_root(root){}ref operator*() {return _node->_data;}ptr operator->(){return &_node->_data;}bool operator==(const Self& s) const{return _node == s._node;}bool operator!=(const Self& s) const{return _node != s._node;}Self& operator++(){if (_node->_right){//节点右不为空,下一个节点就是右子树中序第一个(最左节点Node* minleft = _node->_right;while (minleft->_left){minleft = minleft->_left;}_node = minleft;}else{//节点右为空,找找祖先,并且该祖先是其父亲的左Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right)//parent是空也结束{cur = parent;parent = parent->_parent;}//如果parent为nullptr,nullptr为end()_node = parent;}return *this;}Self& operator--(){if (_node == nullptr) // end(){// --end(),特殊处理,⾛到中序最后⼀个结点,整棵树的最右结点 Node* rightMost = _root;while (rightMost && rightMost->_right){rightMost = rightMost->_right;}_node = rightMost;}else if (_node->_left){// 左⼦树不为空,中序左⼦树最后⼀个 Node* rightMost = _node->_left;while (rightMost->_right){rightMost = rightMost->_right;}_node = rightMost;}else{// 孩⼦是⽗亲右的那个祖先 Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_left){cur = parent;parent = cur->_parent;}_node = parent;}return *this;}private:Node* _node;Node* _root;
};

        对于迭代器这个类,++,--两个操作需要特殊说明一下。

        对于++操作,首先,因为我们map和set++是有序数的下一个,对应的就是红黑树的中序遍历的下一个节点。那么我们不可能因为一个++就遍历整个红黑树去找下一个节点吧?所以我们分情况讨论一下,看看红黑树的下一个节点理应出现在哪。如果当前节点右子树不为空,下一个节点就应该是右子树的最左节点。如果当前节点的右子树为空,下一个节点就应该是父节点,并且该父节点一定是他的父节点(父节点的父节点)的左子树。如果一直查找直到nullptr,就说明++之后应该是end,那就返回nullptr就可以了。

        对于--操作,首先我们要对当前节点是end()的情况做特殊处理,这个节点的上一个节点应该是整棵树的最右节点。第二种情况就是左子树不为空,如果左子树不为空,说明他的上一个节点为左子树的最右节点。第三种情况就是左子树为空,此时他的上一个节点应该是父节点,并且此父节点一定是他的父节点(父节点的父节点)的右子节点。

接下来我们对红黑树进行改造,新增迭代器的部分:

typedef  RBTreeIterator<T,T*,T&> Iterator;
typedef  RBTreeIterator<T,const T*,const T&> ConstIterator;
Iterator Begin()
{Node* minleft = _root;while (minleft && minleft->_left)//这棵树可能是空的{minleft = minleft->_left;}return Iterator(minleft,_root);
}
Iterator End()
{return Iterator(nullptr,_root);
}
ConstIterator Begin() const
{Node* minleft = _root;while (minleft && minleft->_left)//这棵树可能是空的{minleft = minleft->_left;}return ConstIterator(minleft, _root);
}
ConstIterator End() const
{return ConstIterator(nullptr, _root);
}

接下来我们对insert进行改造,使他的返回值和源码一样:

pair<Iterator,bool> Insert(const T& data)
{if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;//根节点必须是黑return { Iterator(_root,_root),true };}KeyOfT kot;Node* parent = nullptr;Node* cur = _root;while (cur){//这访函数返回T值if (kot(cur->_data) < kot(data)){parent = cur;cur = cur->_right;}else if (kot(cur->_data) > kot(data)){parent = cur;cur = cur->_left;}else{return { Iterator(cur,_root),false };//隐式类型转换}}cur = new Node(data);Node* newnode = cur;cur->_col = RED;//新增的必须是redif (kot(parent->_data) < kot(data)){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){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else{Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED){uncle->_col = BLACK;parent->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return { Iterator(newnode,_root),false };
}

         接下来我们再去map和set的头文件中,对迭代器进行进一步的封装:

map:

typedef typename RBTree < K, pair<K, V>, MapKeyOfT>::Iterator iterator;
typedef typename RBTree < K, pair<K, V>, MapKeyOfT>::ConstIterator const_iterator;
pair<iterator,bool> insert(const pair<K, V>& kv)
{return _t.Insert(kv);
}
iterator begin()
{return _t.Begin();
}
iterator end()
{return _t.End();
}
const_iterator begin() const 
{return _t.Begin();
}
const_iterator end() const 
{return _t.End();
}
iterator find(const K& key)
{return _t.Find(key);
}

set:

typedef typename RBTree < K, K, SetKeyOfT>::Iterator iterator;
typedef typename RBTree < K, K, SetKeyOfT>::ConstIterator const_iterator;
pair<iterator, bool> insert(const K& key)
{ return _t.Insert(key);
}
iterator begin()
{return _t.Begin();
}
iterator end()
{return _t.End();
}
const_iterator begin() const
{return _t.Begin();
}
const_iterator end() const
{return _t.End();
}
iterator find(const K& key)
{return _t.Find(key);
}

        注意这里我们typedef的迭代器一定要是在红黑树的大类中我们已经封装好的迭代器,不然没有对应的Begin,End的操作。并且我们要用typename标记,不然编译器不知道iterator到底是一个类还是一个变量。

3、key不支持修改的问题

        这个问题就很好解决了,对于set,我们只需要在第二个参数加上const 就行了:

RBTree<K,const K, SetKeyOfT> _t;

        对于map,我们在第二个参数pair中的key值之前加上const:

RBTree<K, pair<const K, V>, MapKeyOfT> _t;

 4、operator[]

        这里的[]本质还是对insert的复用,因为[]本身也可以插入,如果插入成功,就返回新插入的值对应的value值,如果插入失败,就返回原有的该值的value值(具体可以看map和set的使用这一篇)。那这个操作完全可以通过复用insert来实现:

map:

V& operator[](const K& key)
{pair<iterator, bool> t = insert({ key ,V()});//int的缺省值是0auto it = t.first;return it->second;//这里会省略一个->
}

set:

        set不支持operator[]。

        好了,到这里我们就基本实现完了map和set,现在解决大部分读者在学习这里的时候会遇到的一个疑问。为啥模板类中要传入一个K再传入一个T呢?假设在set中,我们存储的值类型为int,那么传两个int进来不是妥妥的重复吗?

        首先,我们模拟实现要和源码尽可能保持一致,源码是这么写的,所以我们也这么写。那么源码为什么要这么写呢?

        确实,对于set来说就是重复传了。但是对于map来说,第一个K的作用是指示key值的类型。因为find和erase操作的函数参数都是K类型的。也就是说我们得用一个类型来指示我们形参是什么类型的。假设map中我们只传一个pair进来,我们根本无法确定key值的类型是什么。

        并且,本着能用一套逻辑就用一套逻辑的原则,通过传一个key的类型来解决这个问题,总比写两套逻辑,一个是key类型红黑树,一个是key_value类型红黑树要方便得多。

         完整代码放在我的gitee了: 

         RBTree · 主线学习简单项目 - 码云 - 开源中国

        好了,今天的内容就分享到这,我们下期再见!

 

http://www.dtcms.com/wzjs/134836.html

相关文章:

  • 做网站维护有什么要求网址大全浏览器
  • 网站开发工程师 下载培训班学员培训心得
  • 采购公告 校园网站建设整站排名优化品牌
  • b2b网站网址sem是什么检测分析
  • 高端网站建设定制种子搜索神器下载
  • 智慧网站建设建议品牌网络推广
  • 光谷做网站免费个人网站建设
  • 什么公司需要建立网站吗免费外链网站
  • 用shopify 做网站google下载手机版
  • 万网域名指向网站宁波seo托管公司
  • 帝国cms 商城网站视频教程新网站seo外包
  • 如何找做网站的客户优化网站建设
  • 俱乐部网站模板app线下推广怎么做
  • 枣庄网站建设 网站设计 网站制作正规网站优化哪个公司好
  • 网站建设制作哪家好百度一下官网网址
  • 手机怎么做自己的网站西安市网站
  • 湖南移动网站建设益阳网络推广
  • 企业商务网站设计与开发百度指数的数据来源
  • 哪家网站建设搜索引擎营销的特点有
  • 网站开发api平台发布软文
  • 做万词霸屏后网站关键词没有排名厦门网站快速排名优化
  • 重庆奉节网站建设公司推荐谷歌浏览器 官网下载
  • 哪个网站网页做的好看在百度上怎么打广告
  • 做租人网站犯法吗百度自然排名优化
  • 提供信息门户网站制作全球十大搜索引擎排名
  • 网站推广排名教程汕头seo关键词排名
  • b站推广入口mba智库在线观看今天刚刚发生的新闻最新新闻
  • wordpress多站点开启属于b2b的网站有哪些
  • 切片工具做网站怎么做谷歌seo网站建设
  • html5手机版seo优化服务