向量 vector
支持对元素的下标访问,在尾部添加和删除元素,效率高, 类似数组.
1. 2. // C++学习笔记_15 线性容器-vector容器 3. 4. #include<vector> 5. #include<iostream> 6. #include<string> 7. #include<algorithm> //算法函数的头文件 8. 9. using namespace std; 10. template <typename T> 11. void PrintVecInfo(const vector<T> &tVec) 12. { 13. cout << "Empty :" << tVec.empty() << endl; //判断容器是否为空 14. cout << "Size :" << tVec.size() << endl; //容器中存的元素个数 15. cout << "Capacity:" << tVec.capacity() << endl;//容器可以存储的元素个数(申请了多少内存) 16. cout << "Max_size:" << tVec.max_size() << endl;//容器最大可以存储多少个元素 17. } 18. 19. template<typename T> 20. void PrintVec(vector<T> &tVec) 21. { 22. typename vector<T>::iterator it; 23. for (it = tVec.begin(); it != tVec.end(); it++){cout << *it << " ";} 24. cout << endl; 25. } 26. 27. class AAA 28. { 29. private: 30. int x;int y; 31. public: 32. AAA(int a, int b) :x(a), y(b){} 33. 34. friend ostream& operator << (ostream& os, const AAA &A) 35. { os << "(" << A.x << ", " << A.y << ")";return os; } 36. }; 37. 38. void TestVector() 39. { 40. vector<int> iVec1; //定义vector 里面没数据 41. vector<int> iVec2(10); //定义 10 个长度的 vector, 默认值是 0 42. //vector<AAA> aVec --> 由默认构造函数生成的对象 43. vector<int> iVec3(10, 1); //定义10个长度的 vector 默认值是 1 (string(5,'A') = "AAAAA"); 44. vector<int> iVec4(iVec3); //复制构造函数 45. vector<int> iVec5(iVec4.begin(), iVec4.begin() + 5); 46. // 使用迭代器区间进行构造 47. 48. //迭代器区间 的迭代器类型可以是任何迭代器 49. int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 50. vector<int> iVec6(arr, arr + 9); //--> 这个也是迭代器区间构造 51. 52. //直接赋值:可以理解为复制构造函数(这里 {} 不可以省略,表示{}内整体是一个变量) 53. // (这里 () 可以省略。vector<int> v{1,2,3}) 54. vector<int> iVec7({ 1, 3, 5, 7, 9, 2, 4, 6, 8 }); 55. 56. cout << "iVec7 info:" << endl; 57. PrintVecInfo(iVec7); 58. cout << endl; 59. 60. cout << "iVec7:"; 61. PrintVec(iVec7); 62. cout << endl; 63. //插入元素: 64. //1: 插入最后面 push_back(xxx) 65. //2: 指定位置插入元素 insert(it, xxx) 66. //3: 插入 N 个一样的元素, insert(it, N, xxx) 67. //4: 插入一段迭代器区间的元素 insert(it, itBegin, itEnd) 68. iVec7.push_back(11); //在尾部插入 11 69. iVec7.insert(iVec7.begin()+1, 22); //在第二个位置插入 22 70. iVec7.insert(iVec7.begin(), 3, 33); //在开始位置插入 3 个 33 71. iVec7.insert(iVec7.begin(), iVec2.begin(), iVec2.begin() + 3); 72. //开始位置插入 iVec2.begin 到 iVec2.begin() + 3 这段区间的元素 73. cout << "iVec7:"; 74. PrintVec(iVec7); 75. cout << endl; 76. 77. //删除元素: 78. //1: 删除尾部元素 pop_back() 79. //2: 删除指定位置元素 erase(it) 80. //3: 删除指定区间的元素 erase(itBegin, itEnd) 81. //4: 清空整个 vector clear() 82. iVec7.pop_back(); //删除尾部 83. cout << "iVec7:"; 84. PrintVec(iVec7); 85. 86. iVec7.erase(iVec7.begin() + 1); //删除第二个元素 87. cout << "iVec7:"; 88. PrintVec(iVec7); 89. 90. iVec7.erase(iVec7.begin(), iVec7.begin() + 6);//删除前面6个元素 91. cout << "iVec7:"; 92. PrintVec(iVec7); 93. 94. iVec7.clear();//删除所有元素 95. cout << "iVec7 info: "<<endl; 96. PrintVecInfo(iVec7); 97. cout << endl; 98. 99. //访问第 i 个元素 100. cout << "iVec6:"; 101. PrintVec(iVec6); 102. cout << "第三个元素:" << iVec6[2] << endl; 103. cout << "第四个元素:" << iVec6.at(3) << endl; 104. //可以下标[] 或者 at 方式访问第 i 个元素。 105. //有一点点区别:下标访问不会判断是否越界 --》越界则程序出错 106. // at() 方式访问,会做越界判断 --》越界抛出异常 107. 108. //第一个元素和最后一个元素 109. cout << "第一个元素:" << iVec6.front() << endl; 110. cout << "第末个元素:" << iVec6.back() << endl; 111. 112. cout << endl; 113. //事实上 iVec6.begin() 这个迭代器,就指向了 front() 这个元素 114. // iVec6.end()-1 这个迭代器,就指向了 back() 这个元素 115. cout << "第一个元素:" << *(iVec6.begin()) << endl; 116. cout << "第末个元素:" << *(iVec6.end()-1) << endl; 117. 118. //反向迭代器 rbegin(), rend() 119. // rbegin() 指向最后一个元素, 直到 rend() 指向第一个元素前面 120. // 从 rbegin 到 rend 也是使用 ++ 操作 121. cout << endl << "逆向迭代:" << endl; 122. vector<int>::reverse_iterator rIt; 123. for (rIt = iVec6.rbegin(); rIt != iVec6.rend(); rIt++){cout << *rIt << " ";} 124. cout << endl; 125. cout << "第一个元素:" << *(iVec6.rend()-1) << endl; 126. cout << "第末个元素:" << *(iVec6.rbegin()) << endl; 127. 128. //其他函数 129. iVec2.swap(iVec3); //成员函数。交换两个 vector --》交换的是 内存地址值 130. swap(iVec2, iVec3);//外部函数 std 里定义的 swap --》 交换的是元素值 131. //原则:容器内自己实现了的函数,不要调用外部实现(成员函数效率更高,处理的更好) 132. 133. //重置容器中的数据:assign 134. //使用方式类似于构造函数 135. cout << endl; 136. iVec1.assign({1,3,5,7,9}); //指定一系列数据 137. PrintVec(iVec1); 138. 139. iVec1.assign(5, 7); //指定连续 N 个数据 (5 个 7) 140. PrintVec(iVec1); 141. 142. iVec1.assign(iVec6.begin(), iVec6.end()); //指定一段区间 143. PrintVec(iVec1); 144. cout << endl; 145. 146. //resize(n) -->把容器中元素扩充(或者减少)到 n 个 147. // 1:如果扩充,则在容器后面增加元素,值是默认构造函数生成的 148. // 2:如果减少,则删除掉末尾若干个元素 149. iVec1.resize(12); 150. PrintVec(iVec1); 151. 152. iVec1.resize(5); 153. PrintVec(iVec1); 154. 155. //内部存储 AAA 对象 156. vector<AAA> aVec;//不可以使用 vector<AAA> aVec(5) 157. //因为 AAA 没有默认构造函数,这里 5 个元素的值是默认构造函数生成的 158. //error C2512: “AAA”: 没有合适的默认构造函数可用 159. //没有默认构造函数,不能通过 AAA() 生成元素默认的值; 160. //aVec.resize(5); 161. aVec.resize(5, AAA(1, 2));//扩充(或者减少)到5个元素,扩充的话,默认值是 AAA(1,2) 162. PrintVec(aVec); 163. cout << endl; 164. 165. //reserve(n) 预先给 vector 分配 n 个元素的空间 --》 改变的是 capacity 166. //vector 的内存管理:vector 申请里一段大小的内存 167. // 在插入元素的过程中,如果,内存空间不够 168. // --》 重新申请一片更大的内存,把已有数据拷贝过去后,再插入元素 169. 170. // --》如果我们已经知道我们的vector需要存多少元素(比如10000个) 171. // 直接存的话,可能涉及到频繁申请释放空间,和元素拷贝 172. // 如果用 reserve 预先申请内存 --》 避免重复的申请释放内存和元素拷贝 173. 174. PrintVecInfo(aVec); 175. cout << endl; 176. 177. //aVec.push_back(AAA(2,2)); 178. //aVec.reserve(100); 179. aVec.reserve(3); //原来元素个数是5,capacity是5,不能把 capacity变小 180. PrintVecInfo(aVec); 181. cout << endl; 182. 183. PrintVec(aVec); 184. 185. //事实上 aVec 的 capacity 无论任何情况,都只会增加,不会减少,哪怕调用了 clear() 清空容器 186. 187. } 188. 189. //判断是否偶数 190. bool IsEven(int x) 191. { 192. return x % 2 == 0; // 等价于: return !( x % 2); 193. // return !(x & 1); 194. } 195. 196. bool IsBigger2(int x) 197. { 198. return x > 2; 199. } 200. 201. class IsMore 202. { 203. private: 204. int x; 205. public: 206. IsMore(int a) :x(a){} 207. 208. //重载 括号运算符 (注意这里两对括号) 209. //--》IsMore 对象 XXX 调用 XXX(a) 就 返回了 a > x 210. bool operator () (int a){return a > x;} 211. }; 212. 213. void TestAlg() 214. { 215. //count : 统计容器中元素的个数 216. //count_if: 统计容器中满足某个条件的元素个数 217. vector<int> iVec{ 1, 2, 3, 4, 5, 5, 3, 2, 2, 1 }; 218. 219. int cnt; 220. 221. cnt = count(iVec.begin(), iVec.end(), 2); 222. cout << "统计 2 的个数:" << cnt << endl; 223. 224. //入参是一个函数 225. //一定注意不带 (), 带括号表示调用这个函数,然后取函数的返回值 226. //不带括号才表示这是一个变量 (变量类型是: 函数指针) 227. cnt = count_if(iVec.begin(), iVec.end(), IsEven); //入参是一个函数 228. //回调的概念:以函数指针作为函数 count_if 函数的入参 229. // 在 count_if 函数里面,调用函数 IsEven(xxx) 230. // 也就是说,count_if 函数,会对 [iVec.begin(), iVec.end()) 这个区间中的元素 *it 231. // 依次调用 IsEven(*it) 来判断这个值,是否满足条件 (满足则计数器++) 232. cout << "统计偶数的个数:" << cnt << endl; 233. 234. //统计iVec 中 大于 2 的元素个数: 235. cnt = count_if(iVec.begin(), iVec.end(), IsBigger2); 236. 237. //我们还想iVec中统计大于 3 的元素个数? 再写一个 IsBigger3 {return x>3} 238. //还想统计 大于 4 的元素个数? 大于 5 的元素个数 ?...... 239. //不能随便给 IsBigger 加参数,因为 count_if 调用的方式只会传入1个入参 240. //---> 使用仿函数的方式来实现 241. // --》定义一个类,然后,这个类的对象具有函数的功能(重载括号运算符) 242. //现在我们怎么调用? 243. cnt = count_if(iVec.begin(), iVec.end(), IsMore(3)); 244. cnt = count_if(iVec.begin(), iVec.end(), IsMore(4)); 245. cnt = count_if(iVec.begin(), iVec.end(), IsMore(5)); 246. //---> IsMore(3) 生成了一个你们对象 M 247. //---> count_if 函数中,依次调用 M(*it) 来判断元素是否满足条件 248. // ----》 调用了 () 运算符重载来判断 是否有:*it > 3 249. 250. //事实上,我们实现的 MySort 函数,只能升序排列 251. //我们想降序?字符串按长度排序呢? 都不可能通过 MySort 实现 252. //--> 改进的方法: 给 MySort 传入一个参数,作为比较条件 253. } 254. 255. int main() 256. { 257. //TestVector(); 258. TestAlg(); 259. 260. system("pause"); 261. return 0; 262. }