1.什么是STL
2.容器(Containers)
int mian(){vector<int> num1;//定义了一个名为num1,大小为 0,存放int类型数据的数组vector<double> num2;//定义了一个名为num2,大小为0,存放double类型数据的数组vector<Node> num3;//还可以存放自己定义的结构体类型return 0;
}
int mian(){vector<int> a(n);//定义了长度为n的动态数组a,下标和一般数组一样,从0开始vector<int> b(n,0);//定义了长度为n的动态数组b,并且初值全为0vector<int> c(n.100);//定义了长度为n的动态数组c,并且初值全为100return 0;
}
int mian(){vector<int> a={1,4,2,7,5,8};//数组大小为6,元素顺序为1,4,2,7,5,8vector<int> b=a;//与一般数组不同,动态数组可以直接拷贝,而普通的数组不行哦return 0;
} //元素的初始化
定义二维数组
int mian(){vector<int> a[5];// 本质等于定义了5个 vector, 分别是a[0], a[1] ... a[4] // 显然这种方式是混合C的数组的,所以显然这个5就 不能改变了,但是a[0] ~ a[4] 的大小是可以改变的 // 这种写法只有列可以改变大小,行不能改变大小return 0;
}
这里对于二维数组有个形象生动的比喻,以上面的动态二维数组为例,我们将用vector<int> 定义的5个vector.a[0]--a[4]看成一根葡萄藤上的5个节点,将每个vector.a里的元素个数看成每个节点下结的若干个葡萄,这样是不是就好理解多了,节点是不能改变的,而节点下结的葡萄个数却是可以动态增多的。
所以我们有下面的定义方式
int mian(){vector<vector<int>> a(5); // 这样就有一个 行列都可变长的二维数组了 vector<vector<int>> a(5,vector<int> (10, 0)); // 初始化列就等于又初始化一个 vector//定义了一个二维数组,其中初始化有5个葡萄节点,节点数量可以改变,每 个节点下初始化有10个葡萄,葡萄数量可以改变,每个葡萄重量初始化为0。return 0;
}
这时候大家就会发现,如果要定义多维的数组就要嵌套多个vector,非常麻烦,因此C++ 17之后支持的初始化方式
vector a(5, vector<int>(10));
访问元素:基本和C数组一样,通过下标访问、遍历
vector<int> a(n);//定义一个长度为n的数组
for(int i=0;i<n;i++){cin>>a[i]; //这里循环输入和普通数组一样
}
int res=a[0]; //访问首个元素
C++还提供了智能指针,能自动按顺序访问所有元素(特别注意,sdnuoj系统非常古老,所以提交智能指针会pe)
vector<int> a(n);//定义一个长度为n的数组
for(int i:a){cout<<i<<'\n';
} //如果你甚至懒得想这个数组是什么类型的,还可以 用auto
for(auto i:a){cout<<i<<'\n';
}
另外STL里的容器也可以使用迭代器访问元素,所谓迭代器可以理解为一种更为强大的指针就行了。(为什么要用迭代器呢?因为STL里面的容器需要实现功能很多,传统指针不够用)
vector<int> a(n);
vector<int> :: iterator i = a.begin();//定义了一个vector<int>类型的迭代器,并且指向了a数组的开头
cout<< *i <<'\n';//输出i所指向的内容;for(i=a.begin();i!=a.end();i++){cout<<*i<<'\n';
}// 同样如果懒得写上面这一坨类名并且还有一堆不明含义 的冒号,也可以使用auto
auto i = a.begin(); // 遍历同理也可以用这种
for(auto i : a){cout << i << '\n';
}
此外关于vector的函数就太多了,这里浅浅的列举出一些常用的,建议用一个带有代码补全的编辑器,学会看 函数原型以及用户文档来了解。
附上C++的用户手册:https://cplusplus.com/reference/
例题: P1066、P1614
#include <set>
set<int> st; // 定义一个名为set,存放int类型数据的集合
set<int> st2 = st; //也可以使用类似vector一样的拷贝赋值
set<int> st;
st.insert(5);
st.insert(0);st.insert(3);
st.insert(1);
ST.insert(3);
st.insert(5);//由于原先已经存入5了,所以这次存入会被忽略cout << st.size() << '\n'; // 输出st的大 小,很明显是4
cout << st.count(5) << '\n'; // count(x)查询x出现的次数,但是set里元素只能出 现一次, // 所以可以 用来判断x是否在x里出现
注意:set与vector不同,不能通过下标来访问,因此就必须使用迭代器或者智能指针
set<int> :: iterator i = st.begin();
cout<< *i << endl;//输出第一个元素
i = next(i); //迭代器后移,访问下一个元素
cout<< *i <<endl; //输出下一个元素//当然可以通过迭代器遍历输出
for(i=st.begin(); i!=st.end() ; i++){cout<< *i <<endl;
}//或者智能指针
for(auto i : st){cout<< *i <<endl;
}
再来介绍一些关于set的函数
由set衍生来的一些容器:
unordered_set<int> a; // 无序集合,元素无序,但是只能出现一次
multiset<int> b; //多重集合,可以存在多个元素
unordered_multiset<int> c //无序多重集合
例题:sdnu1209、sdnu1058
2.3 pair (二元组)
#include<utility>
pair<int, int> p; // 定义了一个first元素为 int, second元素为int的二元组
p = {5, 3}; // 讲first元素赋值为5, second 元素赋值为3(在一些老版本的编译器上可能用不了) //如果需要将一个5和3结合成二元组,可以使用函数 make_pair();
p = make_pair(5, 3);
访问元素
pair<int, int> p = {5, 3};// 要访问第一个元素就使用p.first, 要访问第二个 元素就使用p.second; int a = p.first, b = p.second; // pair<int, int> 其实等价于下面的结构体 struct pair{ int first, second;};
2.4 map(映射)
#include<map>map<int,int> mp; //定义了一个int映射int的map;
map<int,string> mp; //定义了一个string映射int的map;
map<char,int> mp; //定义了一个char映射int的map;
map<int, node> mp // 定义了一个int映射node的map(node是结构体)
基本操作
map<int,int> mp;
mp.insert(make_pair(1,2)); // 插入key为 1,value为2的映射
mp.insert({1,2}); // 在C++11版本之后,可 以用花括号来生成结构体了cout<< mp[1] <<endl; //输出1的映射,显然是2mp[1]=3; //把原先的1映射2,改成1映射3
cout<< mp[1] <<endl; //显然这时候会输出3cout<< mp[2]<<endl; //如果原先没有定义2的映射,那么自动生成2映射0;
//遍历map
map<int, int> :: iterator i;
for(i = mp.begin() ; i!= mp.end(); i++){cout<< *i.first <<' ' << *i.second;
}//智能指针
for( auto i: mp){cout<< i.first <<' ' << i.second;
}//另一种智能指针
for(auto[x ,y] : mp){cout<< x << ' ' << y <<'\n' ;
}
这里再介绍一些其他关于map的操作
unorder_map<int, int> mp; // 无序