C++STL标准库学习笔记(十四)算法(下)
前言:
在这个笔记中,我把大多数代码都加了注释,我的一些想法和注解用蓝色字体标记了出来,重点和需要关注的地方用红色字体标记了出来。
在这一篇文章中,我们主要对STL中的算法进行简单的介绍,这篇文章接着上一篇文章(https://www.cnblogs.com/AwakeFantasy/p/15859234.html)。
正文:
2.3. 删除算法
删除容器中的某些元素
删除--不会使容器中的元素减少
将所有应该被删除的元素看作空位置
用留下的元素从后往前移,依次去填空位置
元素前移后,它原来的位置也就算是空位置
也应由后面的留下的元素来填上
最后,没有被填上的空位置,维持其原来的值不变
删除算法不应作用于关联容器
remove:删除区间中等于某个值的元素
remove_if:删除区间中满足某种条件的元素
remove_copy:拷贝区间到另一个区间,等于某个值的元素不拷贝
remove_copy_if:拷贝区间到另一个区间,符合某种条件的元素不拷贝
unique:删除区间中连续相等的元素,只留下一个(可自定义比较器),unique返回最后一个元素后面的迭代器,用它减begin就可以知道长度
unique_copy:拷贝区间到另一个区间,连续相等的元素,只拷贝第一个到目标区间()
算法复杂度都是O(n)
unique
template<class Fwdit>
Fwdit unique(Fwdit first, Fwdit last);
用==判断是否相等
templast<class Fwdit, class Pred>
Fwdit unique(Fwdit first, Fwdit last, Pred pr);
用pr(x,y)为true说明x和y相等
对[first, last)这个序列中连续相等的元素,只留下第一个
返回值是迭代器,指向元素删除后的区间的最后一个元素的后面
样例:(remove的)
1 #include<iostream> 2 #include<vector> 3 #include<iterator> 4 #include<algorithm> 5 using namespace std; 6 int main(int argc, char const *argv[]) 7 { 8 int a[5] = {1,2,3,2,5}; 9 int b[6] = {1,2,3,2,5,6}; 10 ostream_iterator<int> oit(cout,","); 11 int* p = remove(a,a+5,2); 12 cout<<"1)"; 13 copy(a,a+5,oit); 14 cout<<endl;//输出:1)1,3,5,2,5,//这里就能看出前面说的删除移位了 15 cout<<"2)"<<p-a<<endl;//输出:2)3 16 vector<int> v(b,b+6); 17 remove(v.begin(),v.end(),2); 18 cout<<"3)"; 19 copy(v.begin(),v.end(),oit); 20 cout<<endl;//输出:3)1,3,5,6,5,6, 21 cout<<"4)"; 22 cout<<v.size()<<endl;//输出:4)6 23 return 0; 24 }
样例中删除了a中的两个“2”,结果导致3前移了一位,而5补上了3原来的位置,而另一次删除的时候,vector的大小是没动的。
2.4. 变序算法
变序算法改变容器中的元素的顺序
但是不改变元素的值
变序算法不适用于关联容器
算法复杂度都是O(n)的
reverse:颠倒区间的前后次序
reverse_copy:把一个区间颠倒后的结果拷贝到另一个区间,源区间不变
rotate:将区间进行循环后移
rotate_copy:将区间以首尾相接的形式进行旋转后的结果拷贝到另一个区间,源区间不变。
next_permutation:将区间改为下一个排列(可自定义比较器)(如123,132,213之类,这是排列,而它们之间有大小关系,这就是上一个(前面小)下一个(前面大)了)
prev_permutation:将区间改为上一个排列(可自定义比较器)
random_shuffle:随机打乱区间内元素的顺序
partition:把区间内满足某个条件的元素移到前面,不满足该条件的移到后面
stable_patition
把区间内满足某个条件的元素移到前面
不满足该条件的移到后面
而对这两部分元素,分别保持它们原来的先后次序不变
random_shuffle
template<class Ranit>
void random_shuffle(Ranit first, Ranit last);
随机打乱[first, last)中的元素,适用于能随机访问的容器
reverse
template<class Bidlt>
void reverse(Bidlt first, Bidlt last);
颠倒[first,last)顺序
next_permutation
template<class init>
bool next_permutaion(init first, init last);
求下一个排列
样例:(next_permutation)
1 #include<iostream> 2 #include<algorithm> 3 #include<string> 4 using namespace std; 5 int main(int argc, char const *argv[]) 6 { 7 string str = "231"; 8 char szStr[] = "324"; 9 while (next_permutation(str.begin(),str.end())) 10 { 11 cout<<str<<endl; 12 } 13 cout<<"****"<<endl; 14 while (next_permutation(szStr,szStr+3)) 15 { 16 cout<<szStr<<endl; 17 } 18 sort(str.begin(),str.end()); 19 cout<<"****"<<endl; 20 while (next_permutation(str.begin(),str.end())) 21 { 22 cout<<str<<endl; 23 } 24 25 return 0; 26 } 27 /* 28 输出; 29 312 30 321 31 **** 32 342 33 423 34 432 35 **** 36 132 37 213 38 231 39 312 40 321 41 */
样例2:(也是这个函数的)
1 #include<iostream> 2 #include<algorithm> 3 #include<string> 4 #include<list> 5 #include<iterator> 6 using namespace std; 7 int main(int argc, char const *argv[]) 8 { 9 int a[] = {8,7,10}; 10 list<int> ls(a,a+3); 11 while (next_permutation(ls.begin(),ls.end())) 12 { 13 list<int>::iterator i; 14 for ( i = ls.begin(); i != ls.end(); i++) 15 { 16 cout<<*i<<" "; 17 } 18 cout<<endl; 19 } 20 21 return 0; 22 }
2.5. 排序算法
比前面的变序算法复杂度更高,一般是O(nlog(n))
排序算法需要随机访问迭代器的支持
不适用于关联容器和list
sort:将区间从小到大排序(可自定义比较器)
stable_sort:将区间从小到大排序,并保持相等元素之间的相对次序(可自定义比较器)
partial_sort:对区间部分排序,直到最小的n个元素就位(可自定义比较器)
partial_sort_copy:将区间前n个元素的排序结果拷贝到别处,源区间不变(可自定义比较器)
nth_element:对区间部分排序,使得第n小的元素(n从0开始算)就位,而且比它小的都在它前面,比它大的都在它后面(可自定义比较器)
make_heap:使区间成为一个“堆”(可自定义比较器)
push_heap:将元素加入一个“堆”区间(可自定义比较器)
pop_heap:从“堆”区间删除堆顶元素(可自定义比较器)
sort_heap:将一个“堆”区间进行排序,排序结束后,该区间就是普通的有序区间,不再是“堆”了(可自定义比较器)
sort快速排序
template<class Ranlt>
void sort(Ranlt first, Ranlt last)
按升序排序
判断x是否应比y靠前,就看x<y是否为true
template<class Ranlt, class Pred>
void sort(Ranlt first, Ranlt last, Pred pr);
按升序排序
判断x是否应比y靠前,就看pr(x,y)是否为true
(这些前面的笔记也是有讲过用法的)
样例:
1 #include<iostream> 2 #include<algorithm> 3 using namespace std; 4 class Myless 5 { 6 public: 7 bool operator()(int n1,int n2) 8 { 9 return(n1 % 10)<(n2 % 10); 10 } 11 }; 12 13 int main(int argc, char const *argv[]) 14 { 15 int a[] = {14,2,9,111,78}; 16 sort(a,a+5,Myless()); 17 int i; 18 for ( i = 0; i < 5; i++) 19 { 20 cout<<a[i]<<" "; 21 } 22 cout<<endl;//输出:111 2 14 78 9 23 sort(a,a+5,greater<int>()); 24 for ( i = 0; i < 5; i++) 25 { 26 cout<<a[i]<<" "; 27 } 28 //输出:111 78 14 9 2 29 return 0; 30 }
sort实际上是快速排序,时间复杂度O(n*log(n))
平均性能最优
但是最坏的情况下,性能可能非常差
如果要保证“最坏情况下”的性能,那么可以使用
stable_sort
stable_sort实际上是归并排序,特点是能保持相等元素之间的先后次序
在有足够存储空间的情况下,复杂度为n*log(n),否则复杂度为n*log(n)*log(n)
stable_sort用法和sort相同
排序算法要求随机存取迭代器的支持,所以list不能使用排序算法,要使用list::sort
2.6. 有序区间算法
要求所操作的区间是已经从小到大排好序的
要求随机访问迭代器的支持
有序区间算法不能用于关联容器和list
binary_search:判断区间中是否包含某个元素
includes:判断是否一个区间中的每个元素,都在另一个区间中
lower_bound:查找最后一个不小于某值的元素的位置
upper_bound:查找第一个大于某值的元素的位置
equal_range:同时获取lower_bound和upper_bound
merge:合并两个有序区间到第三个区间
(这些东西在我的之前的笔记中有提到过,红色字体的部分都是Olog(n)的,include要看区间大小,mergeO(n))
set_union:将两个有序区间的并拷贝到第三个区间
set_intersection:将两个有序区间的交拷贝到第三个区间
set_difference:将两个有序区间的差拷贝到第三个区间
set_symmetric_difference:将两个有序区间的对称差拷贝到第三个区间
inplace_merge:将两个连续的有序区间原地合并为一个有序区间(就合并嘛)
binary_search
折半查找
要求容器已经有序且支持随机访问迭代器,返回是否找到
template<class Fwdlt, class T>
bool binary_search(Fwdlt first, Fwdlt last, const T& val);
上面这个版本,比较两个元素x,y大小时,看x<y
template<class Fwdlt, class T, class Pred>
bool binary_search(Fwdlt first, Fwdlt last, const T& val, Pred pr);
上面这个版本,比较两个元素x,y大小时,若pr(x,y)为true,则认为x小于y
lower_bound
template<class Fwdlt, class T>
Fwdlt lower_bound(Fwdlt first, Fwdlt last, const T& val);
要求[first,last)是有序的
查找[first,last) 中的,最大的位置Fwdlt,使得[first,Fwdlt)中所有的元素都比val小
equal_range
template<class Fwdlt, class T>
pair<Fwdlt, Fwdlt> equal_range(Fwdlt first, Fwdlt last, const T& val);
要求[first,last)是有序的,
返回值是一个pair,假设为p,则:
[first,p.first)中的元素都比val小
[p.second,last)中的所有元素都比val大
p.first就是lower_bound的结果
p.last就是upper_bound的结果
(照我看就只有最后两句有用哈哈哈,毕竟最后两句浅显易懂)
merge
template<class lnlt1, class lnlt2, class Outlt>
Outlt merge(lnlt1 first1, lnlt1 last1, lnlt2 first2, lnlt2 last2,Outlt x);
用<作比较器
template<class lnlt1, class lnlt2, class Outlt, class Pred>
Outlt merge(lnlt1 first1, lnlt1 last1, lnlt2 first2, lnlt2 last2, Outlt x, Pred pr);
用pr作比较器
把[first1,last1),[first2,last2)两个升序序列合并,形成第三个升序序列,第三个升序序列以x开头
includes
template<class lnlt1, class lnlt2>
bool includes(lnlt1 first1, lnlt1 last1, lnlt2 first2, lnlt2 last2);
template<class lnlt1, class lnlt2, class Pred>
bool includes(lnlt1 first1, lnlt1 last1, lnlt2 first2, lnlt2 last2, Pred pr);
判断[first2,last2)中的每个元素,是否都在[first1, last1)中
第一个用<作比较器
第二个用pr作比较器,pr(x,y)==true说明x,y相等
set_difference
template<class lnlt1, class lnlt2, class Outlt>
Outlt set_difference(lnlt1 first1, lnlt1 last1, lnlt2 first2, lnlt2 last2, Outlt x);
template<class lnlt1, class lnlt2, class Outlt, class Pred>
Outlt set_difference(lnlt1 first1, lnlt1 last1, lnlt2 first2, lnlt2 last2, Outlt x, Pred pr);
(这些模板敲得挺累,其实就是为了说明它们应该传入什么参数)
求出[first1,last1)中,不在[first2,last2)中的元素,放到从x开始的地方
如果[first1,last1)里有多个相等元素不在[first2,last2)中,则这多个元素也都会被放入x代表的目标区间里
set_intersection
template<class lnlt1, class lnlt2, class Outlt>
Outlt set_intersection(lnlt1 first1, lnlt1 last1, lnlt2 first2, lnlt2 last2, Outlt x);
template<class lnlt1, class lnlt2, class Outlt, class Pred>
Outlt set_intersection(lnlt1 first1, lnlt1 last1, lnlt2 first2, lnlt2 last2, Outlt x, Pred pr);
求出[first1,last1),[first2,last2)中共有的元素,放到从x开始的地方
如果某个元素e在[first1,last1)里出现n1次,在[first2,last2)中出现n2次,则该元素在目标区间里出现min(n1,n2)次
set_symmetric_difference
template<class lnlt1, class lnlt2, class Outlt>
Outlt set_symmetric_difference(lnlt1 first1, lnlt1 last1, lnlt2 first2, lnlt2 last2, Outlt x);
template<class lnlt1, class lnlt2, class Outlt, class Pred>
Outlt set_symmetric_difference(lnlt1 first1, lnlt1 last1, lnlt2 first2, lnlt2 last2, Outlt x, Pred pr);
把两个区间相互不在另一区间里的元素放入x开始的地方
set_union
template<class lnlt1, class lnlt2, class Outlt>
Outlt set_union(lnlt1 first1, lnlt1 last1, lnlt2 first2, lnlt2 last2,Outlt x);
用<比较大小
template<class lnlt1, class lnlt2, class Outlt, class Pred>
Outlt set_union(lnlt1 first1, lnlt1 last1, lnlt2 first2, lnlt2 last2, Outlt x, Pred pr);
用pr比较大小
求两个区间的并,放到x开始的位置
如果某个元素e在[first1,last1)里出现n1次,在[first2,last2)中出现n2次,则该元素在目标区间里出现max(n1,n2)次
无法分类的东东:
bitset
template<size_t N>
class bitset
{
.......
}
实际使用的时候,N是一个常型整数
如:
bit<40>bst;
bst是一个由40位组成的对象
用bitset的函数可以方便地访问任何一位
bitset的成员函数:
bitset<N>&operator&=(const bitset<N>& rhs);
bitset<N>&operator|=(const bitset<N>& rhs);
bitset<N>&operator^=(const bitset<N>& rhs);
bitset<N>&operator<<=(size_t num);
bitset<N>&operator>>=(size_t num);
(位运算)
bitset<N>&set();//全部设成1
bitset<N>&set(size_t pos, bool val = true);//设置某位
bitset<N>&reset();//全部设成0
bitset<N>&set(size_t pos);//设置某位为0
bitset<N>&flip();//全部翻转
bitset<N>&flip(size_t pos);//翻转某位
reference operator[](size_t pos);//返回对某位的引用
bool operator[](size_t pos)const;//判断某位是否为1
reference at(size_t pos);
bool at(size_t pos)const;
unsigned long to_ulong()const;//转换成整数
string to_string()const;//转换成字符串
size_t count()const;//计算1的个数
size_t size()const;
bool operator==(const bitset<N>& rhs)const;
bool operator!=(const bitset<N>& rhs)const;(判断两个是否不等)
bool test(size_t pos)const;//测试某位是否为1
bool any()const;//是否有某位为1
bool none()const;//是否全部为0
bitset<N> operator<<(size_t pos)const;
bitset<N> operator>>(size_t pos)const;
bitset<N> operator~()const;
static const size_t bitset_size = N;
注意:第0位在最右边
后记:
long long说的就是这两集,实在太长了啊啊啊啊,不过也好,整理出来了完整的笔记,最近过年摸鱼也没少摸,从老家回来后就要回归高效率的生活了,希望新的一年里能做到最好吧。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构