写点什么

陪你手撕源码系列之 STL set 相关算法

用户头像
herongwei
关注
发布于: 2020 年 12 月 12 日
陪你手撕源码系列之 STL set 相关算法

点赞再看,养成习惯


文章每周持续更新,可以微信搜索「herongwei」第一时间阅读和催更,本文 GitHub https://github.com/rongweihe/MoreThanCode 已经收录,有一线大厂面试点思维导图,也整理了很多我的文档,欢迎 star 和完善。一起加油,变得更好!


大家好,我是小贺。


C++ STL set 相关算法


STL 一共提供了四种与 set 集合相关的算法,分别是并集,交集,差集,对称差集。


但要注意的是:此处的 set 不同于数学中的集合。数学中的集合允许元素以任意次数、任意次序出现,但此处的不允许元素重复出现,而且所有元素按序出现。这四种算法处理的结构也是有序的。


有关 set 的四个算法提供两个版本。版本一接收五个迭代器,前四个分别两两确定一个集合区间,还有一个迭代器用于处理运算的结果。版本二和版本一的不同之处在于,版本二可以自定义元素的比较方式(模板参数定制比较器)。它们的返回值相同,都返回运算结果的尾端迭代器。


这四个算法虽然以 set 命名,但实际上也不局限于 set, 只要是元素有序出现,且提供迭代器的容器都可以用此算法,这一点才稍后的源码中可以看到。


1、set_union


算法 set_union 提供两个集合 S1,S2 之并集,即 S1 U S2 。


set_union 是一种稳定的操作,输入区间内的每个元素的相对顺序不会改变。


STL 提供两个版本,差异在于如何定义某个元素小于另一个元素:


  • 第一个版本使用 operator< 进行比较。

  • 第二个版本采用仿函数 comp 进行比较。


源码


template<class InputIterator1, class InputIterator2, class OutputIterator>OutputIterator set_union(InputIterator1 first1, InputIterator1 last1,                        InputIterator2 first2, InputIterator2 last2,                        OutputIterator result) {    //当两个区间都尚未到达尾端时,执行以下操作    while(first1 != last1 && first2 != last2) {        //在两个区间内分别移动迭代器 首先将元素较小者(a)记录于目标区        //然后移动 a 区迭代器前进;同时保持另一个迭代器不动;进行下一次比较        //记录小值,迭代器移动...直到两区中有一区达到尾端        //如果元素相等 可以任取一个迭代器所指元素 然后同时移动两个迭代器        if (*first1 < *first2) {            *result = *first1;            ++first1;        } else if (*first2 < *first2) {            *result = *first2;            ++first2;        } else {            *result = *first1;            ++first1;            ++first2;        }        ++result;    }    //只要两个区间有一个到达尾端就结束while循环    //将其中一个没有到达尾端的区间的剩余元素全部拷贝到目的区间。    return std::copy(first2, last2, std::copy(first1, last1, result));}
复制代码


2、set_intersection


算法 set_intersection 提供两个集合 S1,S2 之交集,即 S1 S2 。


set_intersection 是一种稳定的操作,输入区间内的每个元素的相对顺序不会改变。


STL 提供两个版本,差异在于如何定义某个元素小于另一个元素:


  • 第一个版本使用 operator< 进行比较。

  • 第二个版本采用仿函数 comp 进行比较。


源码


template<class InputIterator1, class InputIterator2, class OutputIterator>OutputIterator set_intersection(InputIterator1 first1, InputIterator1 last1,                        InputIterator2 first2, InputIterator2 last2,                        OutputIterator result) {    //当两个区间都尚未到达尾端时,执行以下操作    while(first1 != last1 && first2 != last2) {        //在两个区间内分别移动迭代器 取交集也很好理解 当遇到相等的元素才记录        if (*first1 < *first2) {            ++first1;        } else if (*first2 < *first2) {            ++first2;        } else {            *result = *first1;            ++first1;            ++first2;            ++result;        }    }    return result;}
复制代码


3、set_difference


算法 set_difference 提供两个集合 S1,S2 之差集,即 S1 - S2 。


set_difference 是一种稳定的操作,输入区间内的每个元素的相对顺序不会改变。


STL 提供两个版本,差异在于如何定义某个元素小于另一个元素:


  • 第一个版本使用 operator< 进行比较。

  • 第二个版本采用仿函数 comp 进行比较。


源码


//差集:计算 存在于[first1, last1) 且不存在于 [first2,last2)所有元素template<class InputIterator1, class InputIterator2, class OutputIterator>OutputIterator set_difference(InputIterator1 first1, InputIterator1 last1,                        InputIterator2 first2, InputIterator2 last2,                        OutputIterator result) {    //当两个区间都尚未到达尾端时,执行以下操作    while(first1 != last1 && first2 != last2) {        //在两个区间内分别移动迭代器 当第一区间的元素等于第二区间 就让两区间同时前进        //当第一区间元素大于第二区间的元素 就让第二区间往前走        //有了上面两步就能保证当第一区间的元素小于第二区间时 第一区间的元素只存在于第一区间而不存在第二区间        //于是将它记录于目标区间        if (*first1 < *first2) {            *result = *first1;            ++first1;            ++result;        } else if (*first2 < *first2) {            ++first2;        } else {            ++first1;            ++first2;        }    }    return std::copy(first1, last1, result);}
复制代码


4、set_symmetric_difference


算法 set_symmetric_difference 提供两个集合 S1,S2 之对称差集,即 (S1 - S2) U (S2 - S1) 。


set_symmetric_difference 是一种稳定的操作,输入区间内的每个元素的相对顺序不会改变。


由于 S1,S2 内的每个元素都不需要唯一,因此如果某个值在 S1 出现 n 次,在 S2 中出现 m 次,那么改值在输出区间出现 |n-m| 次。


STL 提供两个版本,差异在于如何定义某个元素小于另一个元素:


  • 第一个版本使用 operator< 进行比较。

  • 第二个版本采用仿函数 comp 进行比较。


源码


template<class InputIterator1, class InputIterator2, class OutputIterator>OutputIterator set_symmetric_difference(InputIterator1 first1, InputIterator1 last1,                        InputIterator2 first2, InputIterator2 last2,                        OutputIterator result) {    //当两个区间都尚未到达尾端时,执行以下操作    while(first1 != last1 && first2 != last2) {        //在两个区间内分别移动迭代器 当两个区间元素相等 就让两区间同时前进        //当两个区间元素不等,记录较小值于目标区间 并让较小值区间继续前进        if (*first1 < *first2) {            *result = *first1;            ++first1;            ++result;        } else if (*first2 < *first2) {            *result = *first2;            ++first2;            ++result;        } else { // *first1 == *first2            ++first1;            ++first2;        }    }    return std::copy(first2, last2, std::copy(first1, last1, result));}
复制代码


5、结尾

如果觉得文章对你有帮助,欢迎分享给你的朋友,一键三连,谢谢各位。

我是 herongwei ,是男人,就对自己狠一点,祝大家周末愉快,我们下期见。



文章每周持续更新,可以微信搜索「herongwei」第一时间阅读和催更,本文 GitHub https://github.com/rongweihe/MoreThanCode 已经收录,有一线大厂面试点思维导图,也整理了很多我的文档,欢迎 star 和完善。一起加油,变得更好!

发布于: 2020 年 12 月 12 日阅读数: 32
用户头像

herongwei

关注

五道口搬砖程序员 2018.06.12 加入

公众号:【herongwei】分享计算机编程、算法,职场经验、生活感悟。

评论

发布
暂无评论
陪你手撕源码系列之 STL set 相关算法