C++ STL vector详解

1. vector简介

template<class T, class Alloc = allocator<T>>
class vector;

vector是一个可以动态增长的数组,T是要存储的元素类型。vector可以像数组一样,用下标+[]来访问元素,如:

int arr[] = {1,2,3,4};
for (int i = 0; i < 4; ++i)cout << arr[i] << " ";
cout << endl;
vector<int> v = {1,2,3,4};
for (int i = 0; i < 4; ++i)cout << v[i] << " ";

输出:

1 2 3 4
1 2 3 4

2. vector的构造函数

本文不考虑allocator的问题,尽可能简化vector的使用方式。

2.1 无参构造

explicit vector();

最常见的构造,构造一个空的vector。

vector<int> v;

2.2 n个val

explicit vector(size_t n, const T& val = T());

用n个val来初始化vector。

vector<int> v1(5); // 用5个0来初始化vector
vector<int> v2(3, 1); // 用3个1来初始化vectorfor (auto e : v1)cout << e << " ";
cout << endl;
for (auto e : v2)cout << e << " ";

输出:

0 0 0 0 0
1 1 1 

由于explicit的作用,下面的写法并不是调用这个构造函数,而是initializer_list。

vector<int> v = {3,1};

2.3 拷贝构造

vector(const vector<T>& v);

用一个vector来拷贝初始化另一个vector。

vector<int> v1 = {1,2,3,4,5};
vector<int> v2(v1);
vector<int> v3 = v1; // 这里的v3调用的也是拷贝构造,等价于vector v3(v1);
for (auto e : v1) cout << e << " ";
cout << endl;
for (auto e : v2) cout << e << " ";
cout << endl;
for (auto e : v3) cout << e << " ";

输出:

1 2 3 4 5 
1 2 3 4 5 
1 2 3 4 5 

2.4 迭代器构造

template<class InputIterator>
vector(InputIterator first, InputIterator last);

用任意类型的迭代器构造。

int arr[] = {1,2,3};
vector<int> v(arr, arr + sizeof(arr)/sizeof(arr[0]));
for(auto e : v) cout << e << " ";

输出:

1 2 3

2.5 列表初始化

vector(initializer_list<T> il);

使用列表初始化。

vector<int> v1 = {1,2,3};
vector<int> v2{4,5,6};
for (auto e : v1) cout << e << " ";
cout << endl;
for (auto e : v2) cout << e << " ";

输出:

1 2 3
4 5 6

3. vector的迭代器

vector的迭代器是随机访问迭代器(random access iterator),提供begin,end,rbegin,rend等接口。

iterator begin();
const_iterator begin() const;iterator end();
const_iterator end() const;reverse_iterator rbegin();
const_reverse_iterator rbegin() const;reverse_iterator rend();
const_reverse_iterator rend() const;

举一个正向迭代器使用的例子:

vector<int> v = {1,2,3,4,5};
auto it = v.begin();
while (it != v.end)
{cout << *it << " ";++it;
}

输出:

1 2 3 4 5

上述代码和下面的代码是等价的,因为范围for的底层也是用迭代器实现的。

vector<int> v = {1,2,3,4,5};
for (auto e : v)cout << e << " ";

4. vector的容量相关接口

4.1 size

size_t size() const;

获取数据个数。

vector<int> v = {1,2,3,4,5};
cout << v.size() << endl;

输出:

5

4.2 capacity

size_t capacity() const;

获取容量大小。

vector<int> v;
for (int i = 0; i < 30; ++i) v.push_back(i);
cout << v.capacity() << endl;

VS2022的环境下输出:

42

4.3 empty

bool empty() const;

判断vector是否为空。

vector<int> v;
cout << v.empty() << endl; // true
v.push_back(1);
cout << v.empty() << endl; // false

输出:

1
0

4.4 resize

void resize(size_t n, const T& val = T());

把vector的size改为n。若n小于当前size,则只保留前n个数据;若n大于当前size,则插入val,直到size为n。

vector<int> v = {1,2,3,4};v.resize(6); // n大于当前size
for (auto e : v) cout << e << " ";
cout << endl;v.resize(2); // n小于当前size
for (auto e : v) cout << e << " ";
cout << endl;v.resize(4, 5); // n大于当前size
for (auto e : v) cout << e << " ";

输出:

1 2 3 4 0 0
1 2
1 2 5 5

4.5 reserve

void reserve(size_t n);

改变capacity,至少改为n,保留足够的空间。

vector<int> v;
v.reserve(100);
cout << v.capacity() << endl;

输出示例:

100

5. vector的增删查改

5.1 push_back和pop_back

void push_back(const T& val);
void pop_back();

尾插和尾删,push_back在vector的最后插入val,pop_back删除vector的最后一个元素。

vector<int> v = {1,2,3,4,5};
v.pop_back();
for (auto e : v) cout << e << " ";
cout << endl;
v.push_back(6);
for (auto e : v) cout << e << " ";

输出:

1 2 3 4
1 2 3 4 6

5.2 全局find + vector的迭代器

template<class InputIterator, class T>
InputIterator find(InputIterator first, InputIterator last, const T& val);

使用algorithm中的find,配合vector的迭代器,查找[first,last)中有没有val,若找到了,返回指向val的迭代器;若没找到,返回last。

vector<int> v = {1,2,3,4,5};
auto it = find(v.begin(), v.end(), 3);
if (it != v.end()) cout << *it << endl;
else cout << "没找到" << endl;

输出:

3

5.3 insert + erase

iterator insert(iterator pos, const T& val);
iterator erase(iterator pos);

插入和删除元素。insert负责在pos位置插入val,并返回指向val的迭代器。erase负责删除pos位置的值,并返回指向删除元素的下一个元素的迭代器。

配合find,在3前面插入30,再删除所有偶数的代码如下:

vector<int> v = {6,2,3,4,5,10,12};
auto it = find(v.begin(), v.end(), 3);
if (it != v.end()) 
{it = v.insert(it, 30);// 此时it指向30cout << *it << endl;
}
for (auto e : v) cout << e << " ";
cout << endl;// 删除所有偶数
it = v.begin();
while (it != v.end())
{if (*it % 2 == 0) it = v.erase(it);else ++it;
}
for (auto e : v) cout << e << " ";

输出:

30
6 2 30 3 4 5 10 12
3 5

5.4 swap成员函数 + 全局函数

// 成员函数
void swap(vector<T> v);// 全局函数
template<class T>
void swap(vector<T>& v1, vector<T>& v2);

交换2个vector。

vector<int> v1 = {1,2,3,4,5};
vector<int> v2 = {6,7,8,9,0};
for (auto e : v1) cout << e << " ";
cout << endl;
for (auto e : v2) cout << e << " ";
cout << endl;v1.swap(v2);
for (auto e : v1) cout << e << " ";
cout << endl;
for (auto e : v2) cout << e << " ";
cout << endl;swap(v1, v2);
for (auto e : v1) cout << e << " ";
cout << endl;
for (auto e : v2) cout << e << " ";

输出:

1 2 3 4 5
6 7 8 9 0
6 7 8 9 0
1 2 3 4 5
1 2 3 4 5
6 7 8 9 0

5.5 operator[]

T& operator[](size_t pos);
const T& operator[](size_t pos) const;

像数组一样,使用下标+[]访问vector。

vector<int> v = {1,2,3,4,5};
for (size_t i = 0; i < v.size(); ++i)cout << v[i] << " ";

输出:

1 2 3 4 5

6. 迭代器失效问题

6.1 场景1:插入 + 扩容

所有的插入操作都有可能导致扩容,从而导致迭代器失效,如resize、reserve、insert、assign、push_back等。这是因为,扩容的步骤是:

  1. 开辟一块新的更大的空间。
  2. 把旧的空间的数据拷贝到新的空间中去。
  3. 释放旧的空间。

而扩容前,迭代器指向了旧的空间,扩容后,旧的空间被释放了,迭代器指向的空间已经被销毁,迭代器失效。

下面的代码中,在reserve之后,迭代器it失效,出现野指针问题。

vector<int> v = {1,2,3,4,5};
auto it = v.begin();
v.reserve(100);
while (it != v.end()) cout << *it++ << " ";

6.2 场景2:删除

删除操作,如erase,会挪动数据覆盖删除,此时迭代器指向的元素可能已经改变,甚至指向非法的空间。如:

vector<int> v = {1,2,3,4,5};
auto it1 = v.end() - 2; // 指向4
auto it2 = v.end() - 1; // 指向5v.erase(v.begin());

上面的代码中,erase之后,it1指向的元素已经不是4了(此时it1指向5),而it2指向了非法的空间。这是因为,erase的底层会覆盖删除1,会把1后面的元素向低地址处挪动1格。

erase前
1   2   3   4   5^   ^it1 it2
erase后
2   3   4   5^   ^it1 it2

6.3 解决方案

迭代器失效后,要对迭代器重新赋值。

下面的程序,本意是删除vector中所有的偶数,但是erase之后,迭代器失效了,程序的行为是未定义的。

vector<int> v = {2,4,5,6,7,8,10};
auto it = v.begin();
while (it != v.end())
{if (*it % 2 == 0)v.erase(it);++it;
}

解决方案:在erase之后,对it重新赋值。注意到erase会返回指向删除元素的下一个元素的迭代器,当找到偶数并删除后,it应该接受erase的返回值;若it指向的不是偶数,it++即可。

vector<int> v = {2,4,5,6,7,8,10};
auto it = v.begin();
while (it != v.end())
{if (*it % 2 == 0)it = v.erase(it);else++it;
}for (auto e : v) cout << e << " ";

输出:

5 7

7. 练习

7.1 只出现一次的数字I

只出现一次的数字I原题链接icon-default.png?t=N7T8https://leetcode.cn/problems/single-number/description/使用范围for,取出所有数字,异或到一起。根据异或的特性,相同的数字会被抵消,最后的结果就是只出现一次的数字。

class Solution {
public:int singleNumber(vector<int>& nums) {int ret = 0;for (auto n : nums) ret ^= n;return ret;}
};

7.2 杨辉三角

杨辉三角原题链接icon-default.png?t=N7T8https://leetcode.cn/problems/pascals-triangle/description/总共numRows行,每行有i+1个元素,两端的元素是1,其余元素(i,j)是(i-1,j-1)和(i-1,j)相加的结果。 遍历vv,类似于遍历二维数组,可以用下标+[],注意边界由size决定。对于vector类型,v[0]等价于v.front(),v[size()-1]等价于v.back()。

class Solution {
public:vector<vector<int>> generate(int numRows) {vector<vector<int>> vv(numRows); // numRows行//for (int i = 0; i < vv.size(); ++i)for (int i = 0; i < numRows; ++i){// 每行有i+1个元素vv[i].resize(i + 1);vv[i].front() = vv[i].back() = 1;//for (int j = 1; j < vv[i].size() - 1; ++j)for (int j = 1; j < i; ++j){vv[i][j] = vv[i-1][j-1] + vv[i-1][j];}}return vv;}
};

7.3 删除有序数组中的重复项

删除有序数组中的重复项原题链接icon-default.png?t=N7T8https://leetcode.cn/problems/remove-duplicates-from-sorted-array/description/由于数组是有序的,考虑使用下标i遍历数组,若遇到和前一个元素不相同的元素,就存储到下标j对应的空间中。注意i和j都要从1开始。

class Solution {
public:int removeDuplicates(vector<int>& nums) {int j = 1;for (int i = 1; i < nums.size(); ++i){if (nums[i] != nums[i-1]){nums[j++] = nums[i];}}return j;}
};

7.4 只出现一次的数字II

只出现一次的数字II原题链接icon-default.png?t=N7T8https://leetcode.cn/problems/single-number-ii/description/最简单的想法是,用哈希表存储每个元素出现的次数(7.1、7.5和7.6也可以用这种方式解决)。

class Solution {
public:int singleNumber(vector<int>& nums) {unordered_map<int, int> um;for (auto n : nums) ++um[n];for (auto& [n, cnt] : um)if (cnt == 1) return n;return 0;}
};

受到7.1题目的启发,考虑使用位运算求解本题。由于只有一个数字ret出现一次,其余数字出现三次,那么假设对所有数字二进制补码中的第i位求和为sum,就有两种情况:

  1. sum%3==0,说明ret的第i位是0;
  2. sum%3==1,说明ret的第i位是1。

这样就能获取到ret的每一位了。

class Solution {
public:int singleNumber(vector<int>& nums) {int ret = 0;for (int i = 0; i < 32; ++i){// 所有数字的第i位求和int sum = 0;for (auto n : nums)sum += ((n >> i) & 1);// 若求和后不是3的倍数,ret的第i位是1if (sum % 3)ret |= (1 << i);}return ret;}
};

7.5 只出现一次的数字III

只出现一次的数字III原题链接icon-default.png?t=N7T8https://leetcode.cn/problems/single-number-iii/description/可以使用哈希表解决,但最优解依然是使用位运算。若把所有数异或到一块得到sum,假设sum二进制中的第i位是1。那么就可以把所有数字分成两组,其中一组的第i位是1,另一组的第i位是0,那么只出现一次的两个数字就被分到了不同的组中。把其中一组的所有数异或到一块去,就能得到其中一个只出现一次的数字,再把这个数字异或sum就能得到另一个只出现一次的数字。

其中,n&-n可以取出n的二进制中最低位的1,但是n=INT_MIN不能这么算,因为-n越界了,要单独考虑。INT_MIN的最高位是1,其余位都是0。

class Solution {
public:vector<int> singleNumber(vector<int>& nums) {int sum = 0;for (auto n : nums) sum ^= n;// 取出sum最低位的1// INT_MIN最低位的1是它本身// n&-n可以取出n最低位的1int bit = sum == INT_MIN ? INT_MIN : sum & -sum;int i = 0;for (auto n : nums)if (n & bit)i ^= n;return {i, i ^ sum};}
};

7.6 数组中出现次数超过一半的数字

数组中出现次数超过一半的数字原题链接icon-default.png?t=N7T8https://www.nowcoder.com/practice/e8a1b01a2df14cb2b228b30ee6a92163?tpId=13&&tqId=11181&rp=1&ru=/activity/oj&qru=/ta/coding-interviews/question-ranking本题依然可以使用哈希表统计次数;也可以先排序,再找出中位数。

这里介绍一种算法:候选法。定义n为可能的众数,cnt为当前数字出现的次数。遍历数组,若当前元素和n相等,那么cnt++;若当前元素不等于n,那么cnt--。当cnt减到0时,就把n设置成当前元素,cnt设置成1。遍历完数组后,若有元素出现次数超过数组长度的一半,那么这个元素一定是n。由于题目描述中说明,一定有一个元素出现次数超过数组长度的一半,所以n为满足题目要求的元素。

class Solution {
public:int MoreThanHalfNum_Solution(vector<int>& numbers) {int n = 0, cnt = 0;for (auto num : numbers)if (cnt == 0) n = num, cnt = 1;else n == num ? ++cnt : --cnt;return n;}
};

7.7 电话号码的数字组合

电话号码的数字组合原题链接icon-default.png?t=N7T8https://leetcode.cn/problems/letter-combinations-of-a-phone-number/description/维护一个字符串,取出每个数字字符对应的所有字母字符,并插入到字符串中。当digits中的所有数字字符都遍历完时,把得到的字符串插入到vector中,回退到上一层,找其余的字母排列。

如何取出每个数字对应的所有字母字符呢?可以考虑使用unordered_map,我这里使用vector,用下标对应数字,改下标对应的字符串表示所有可能的字母字符。建议定义成静态的成员变量,因为只需要维护一份vector,节省空间。

vector<string>和要维护的string建议先用reserve保留足够的空间,虽然本题用处不大,因为digits的size是在[0,4]的范围,扩容消耗不大。

class Solution {
public:vector<string> letterCombinations(string digits) {if (digits.empty()) return {};vector<string> coms;// 计算容量int newCapacity = 1;for (auto ch : digits){int n = ch - '0';newCapacity *= numToStr[n].size();}coms.reserve(newCapacity);string com;com.reserve(digits.size());Combinations(digits, 0, com, coms);return coms;}
private:void Combinations(const string& digits, int idx, string& com, vector<string>& coms){if (idx == digits.size()){coms.push_back(com);return;}int n = digits[idx] - '0';const string& str = numToStr[n];for (auto ch : str){com.push_back(ch);Combinations(digits, idx+1, com, coms);com.pop_back();}}static const vector<string> numToStr;
};const vector<string> Solution::numToStr = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};

8. 模拟实现vector

8.1 迭代器

我们可以用3个迭代器来维护vector(SGI版本的STL就是这么实现的),并且给nullptr的缺省值。

iterator _start = nullptr;
iterator _finish = nullptr;
iterator _end_of_storage = nullptr;

其中iterator就是原生的指针。

typedef T* iterator;
typedef const T* const_iterator;

其中,_start标识空间的起始位置,_finish标识最后一个有效数据的位置,_end_of_storage标识空间的结束位置。举个例子,假设此时vector中的size是4,capacity是8,那么内存的大概分布如下:

   1      2      3      4      ?      ?      ?      ?      !^                           ^                           ^
_start                      _finish                     _end_of_storage

 那么几个关键位置就一目了然了。

iterator begin()
{return _start;
}iterator end()
{return _finish;
}const_iterator begin() const
{return _start;
}const_iterator end() const
{return _finish;
}

8.2 构造函数和析构函数

注意,v(5,10)可能会匹配迭代器区间构造函数,而不是按照n个val初始化的构造函数,所以要提供一个vector(size_t, const T&)的版本。

vector() = default;template<class InputIterator>
vector(InputIterator first, InputIterator last)
{while (first != last){push_back(*first);++first;}
}vector(size_t n, const T& val = T())
{reserve(n);while (n--){push_back(val);}
}vector(int n, const T& val = T())
{reserve(n);while (size() < n){push_back(val);}
}~vector()
{if (_start){delete[] _start;_start = _finish = _end_of_storage = nullptr;}
}

8.3 拷贝构造和operator=

对于拷贝构造,我没有用现代写法,因为使用迭代器构造时的扩容会有消耗。注意不能使用memcpy直接按字节拷贝,因为T可能是自定义类型,memcpy会导致浅拷贝。

void swap(vector<T>& v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);
}vector(const vector<T>& v):_start(new T[v.size()])
{_end_of_storage = _finish = _start + v.size();iterator it = begin();for (auto e : v){*it++ = e;}
}// 现代写法
//vector(const vector<T>& v)
//{
//	vector<T> tmp(v.begin(), v.end());
//	swap(tmp);
//}vector<T>& operator=(vector<T> tmp)
{swap(tmp);return *this;
}

8.4 容量相关接口

reserve在扩容时,不能使用memcpy拷贝数据,因为T可能是自定义类型,memcpy会导致浅拷贝。另外,由于size和capacity是指针相减计算得来的,扩容后更新_finish时不能写_finish=_start+size(),因为此时的_start已经改变,但是_finish还未更新,size会算出错误的结果。正确的做法是,提前记录size。

size_t size() const
{return _finish - _start;
}size_t capacity() const
{return _end_of_storage - _start;
}bool empty()
{return _start == _finish;
}void resize(size_t n, const T& val = T())
{if (n <= size()){_finish = _start + n;return;}if (n > capacity()){// 至少扩容2倍reserve(max(n, 2 * capacity()));}while (size() < n){push_back(val);}
}void reserve(size_t n)
{if (n > capacity()){// 扩容size_t sz = size();iterator tmp = new T[n];if (_start){// 拷贝数据for (size_t i = 0; i < sz; ++i){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + sz;_end_of_storage = _start + n;}
}

8.5 访问相关接口

T& operator[](size_t pos)
{assert(pos < size());return _start[pos];
}const T& operator[](size_t pos) const
{assert(pos < size());return _start[pos];
}T& front()
{return *_start;
}const T& front() const
{return *_start;
}T& back()
{return *(_finish - 1);
}const T& back() const
{return *(_finish - 1);
}

8.6 插入和删除

insert插入时,先挪动数据再插入。注意reserve会导致迭代器失效,需要提前记录pos相对于_start的偏移量,reserve后需要对pos重新赋值。erase直接挪动数据覆盖即可。

挪动数据不能使用memcpy,因为T可能是自定义类型,memcpy会导致浅拷贝。

void push_back(const T& val)
{insert(end(), val);
}void pop_back()
{erase(end() - 1);
}iterator insert(iterator pos, const T& val = T())
{assert(pos >= _start);assert(pos <= _finish);if (_finish == _end_of_storage){// 扩容导致迭代器失效size_t len = pos - _start;reserve(capacity() == 0 ? 4 : 2 * capacity());pos = _start + len;}// 挪动数据iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = val;++_finish;return pos;
}iterator erase(iterator pos)
{assert(pos >= _start);assert(pos < _finish);// 挪动数据,覆盖删除iterator begin = pos + 1;while (begin < _finish){*(begin - 1) = *begin;++begin;}--_finish;return pos;
}

8.7 测试

观察调试窗口:

8.8 完整实现

#include<iostream>
#include<assert.h>
using namespace std;namespace xbl
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}vector() = default;template<class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);++first;}}vector(size_t n, const T& val = T()){reserve(n);while (n--){push_back(val);}}vector(int n, const T& val = T()){reserve(n);while (size() < n){push_back(val);}}~vector(){if (_start){delete[] _start;_start = _finish = _end_of_storage = nullptr;}}void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);}vector(const vector<T>& v):_start(new T[v.size()]){_end_of_storage = _finish = _start + v.size();iterator it = begin();for (auto e : v){*it++ = e;}}// 现代写法//vector(const vector<T>& v)//{//	vector<T> tmp(v.begin(), v.end());//	swap(tmp);//}vector<T>& operator=(vector<T> tmp){swap(tmp);return *this;}size_t size() const{return _finish - _start;}size_t capacity() const{return _end_of_storage - _start;}bool empty(){return _start == _finish;}void resize(size_t n, const T& val = T()){if (n <= size()){_finish = _start + n;return;}if (n > capacity()){// 至少扩容2倍reserve(max(n, 2 * capacity()));}while (size() < n){push_back(val);}}void reserve(size_t n){if (n > capacity()){// 扩容size_t sz = size();iterator tmp = new T[n];if (_start){// 拷贝数据for (size_t i = 0; i < sz; ++i){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + sz;_end_of_storage = _start + n;}}T& operator[](size_t pos){assert(pos < size());return _start[pos];}const T& operator[](size_t pos) const{assert(pos < size());return _start[pos];}T& front(){return *_start;}const T& front() const{return *_start;}T& back(){return *(_finish - 1);}const T& back() const{return *(_finish - 1);}void push_back(const T& val){insert(end(), val);}void pop_back(){erase(end() - 1);}iterator insert(iterator pos, const T& val = T()){assert(pos >= _start);assert(pos <= _finish);if (_finish == _end_of_storage){// 扩容导致迭代器失效size_t len = pos - _start;reserve(capacity() == 0 ? 4 : 2 * capacity());pos = _start + len;}// 挪动数据iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = val;++_finish;return pos;}iterator erase(iterator pos){assert(pos >= _start);assert(pos < _finish);// 挪动数据,覆盖删除iterator begin = pos + 1;while (begin < _finish){*(begin - 1) = *begin;++begin;}--_finish;return pos;}private:iterator _start = nullptr; // 有效数据起始位置iterator _finish = nullptr; // 有效数据结束位置iterator _end_of_storage = nullptr; // 空间结束位置};void test_vector(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);for (size_t i = 0; i < v.size(); ++i){cout << v[i] << " ";}cout << endl;vector<int>::iterator it = v.begin();while (it != v.end()){cout << *it << " ";++it;}cout << endl;for (auto e : v){cout << e << " ";}cout << endl;vector<int> v1(v);for (auto e : v1){cout << e << " ";}cout << endl;vector<int> v2;v2 = v1;for (auto e : v2){cout << e << " ";}cout << endl;vector<string> vs1;vs1.push_back("11111");vs1.push_back("22222");vs1.push_back("33333");vs1.push_back("44444");vs1.push_back("55555");for (auto e : vs1){cout << e << " ";}cout << endl;vector<string> vs2(vs1);for (auto e : vs2){cout << e << " ";}cout << endl;vector<string> vs3;vs3 = vs2;for (auto e : vs2){cout << e << " ";}cout << endl;vs3.pop_back();for (auto e : vs3){cout << e << " ";}cout << endl;vs3.pop_back();vs3.pop_back();vs3.pop_back();vs3.pop_back();//vs3.pop_back();vs3.push_back("666");vs3.push_back("777");vs3.push_back("888");for (auto e : vs3){cout << e << " ";}cout << endl;auto pos = find(vs3.begin(), vs3.end(), "777");if (pos != vs3.end()){vs3.erase(pos);}else{cout << "没找到" << endl;}for (auto e : vs3){cout << e << " ";}cout << endl;vector<int> v3;v3.resize(5);for (auto e : v3){cout << e << " ";}cout << endl;v3.resize(3);for (auto e : v3){cout << e << " ";}cout << endl;v3.resize(6, 1);for (auto e : v3){cout << e << " ";}cout << endl;vector<int> v4(10, 2);for (auto e : v4){cout << e << " ";}cout << endl;// 杨辉三角int height = 8;vector<vector<int>> vv1(8, vector<int>());for (int i = 0; i < vv1.size(); ++i){vv1[i].resize(i + 1, 0);vv1[i].front() = vv1[i].back() = 1;for (int j = 1; j < vv1[i].size() - 1; ++j){vv1[i][j] = vv1[i - 1][j - 1] + vv1[i - 1][j];}}for (const auto& v : vv1){for (auto n : v){cout << n << " ";}cout << endl;}vector<vector<int>> vv2(vv1);for (const auto& v : vv2){for (auto n : v){cout << n << " ";}cout << endl;}vector<vector<int>> vv3;vv3 = vv2;for (const auto& v : vv3){for (auto n : v){cout << n << " ";}cout << endl;}}
}

输出:

1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
11111 22222 33333 44444 55555
11111 22222 33333 44444 55555
11111 22222 33333 44444 55555
11111 22222 33333 44444
666 777 888
666 888
0 0 0 0 0
0 0 0
0 0 0 1 1 1
2 2 2 2 2 2 2 2 2 2
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/699888.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

搜索专项---双向DFS模型

文章目录 送礼物 一、送礼物OJ链接 本题思路: #include <bits/stdc.h>typedef long long LL;constexpr int N1<<25;int n,m,k; int g[50]; int weight[N],cnt; int ans;void dfs1(int u,int s) {if(uk){weight[cnt]s;return;}dfs1(u1,s);if(g[u](LL)s<m) dfs1…

[NCTF2019]True XML cookbook --不会编程的崽

题目的提示很明显了&#xff0c;就是xxe攻击&#xff0c;直接抓包。 <?xml version "1.0"?> <!DOCTYPE ANY [ <!ENTITY xxe SYSTEM "file:///etc/passwd" > ]> <user><username> &xxe; </username><passwor…

EasyRecovery2024个人免费版本电脑手机数据恢复软件下载

EasyRecovery是一款功能强大的数据恢复软件&#xff0c;能够帮助用户恢复丢失、删除、格式化或损坏的数据。无论是由于误操作、病毒攻击、硬盘故障还是其他原因导致的数据丢失&#xff0c;EasyRecovery都能提供有效的解决方案。 该软件支持从各种存储介质恢复数据&#xff0c;…

反序列化字符串逃逸 [安洵杯 2019]easy_serialize_php1

打开题目 $_SESSION是访客与整个网站交互过程中一直存在的公有变量 然后看extract()函数的功能&#xff1a; extract($_POST)就是将post的内容作为这个函数的参数。 extract() 函数从数组中将变量导入到当前的符号表(本题的作用是将_SESSION的两个函数变为post传参) function…

【Unity】提示No valid Unity Editor liscense found.Please active your liscense.

有两个软件&#xff0c;如果只有一个&#xff0c;点黑的不会有效果、、、、&#xff08;楼主是这个原因&#xff0c;可以对号入座一下&#xff09; 简而言之&#xff0c;就是去下载Unity Hub&#xff0c;再里面激活管理通行证 问题情境&#xff1a; 点击unity出现以下弹窗&a…

类型转换(C++)

一、C语言中的类型转换 在C语言中&#xff0c;如果赋值运算符左右两侧类型不同&#xff0c;或者形参与实参类型不匹配&#xff0c;或者返回值类型与 接收返回值类型不一致时&#xff0c;就需要发生类型转化&#xff0c;C语言中总共有两种形式的类型转换&#xff1a;隐式类型 …

MATLAB环境下使用滤波自适应算法进行主动噪声消除

滤波作为自适应滤波系统中信号处理等研究领域的重要组成模块&#xff0c;主要被应用于信道均衡、系统识别、声学回波抵消、生物医学、雷达、波束形成等模块。在自适应滤波系统中&#xff0c;当信息数据统计方面的相关先验知识是已知的情况下&#xff0c;滤波器才能处理相关的输…

数据价值在线化丨TiDB 在企查查数据中台的应用及 v7.1 版本升级体验

本文介绍了企查查在数据中台建设中使用 TiDB 的经验和应用。通过从 MySQL 到 TiDB 的迁移&#xff0c;企查查构建了基于 TiDB Flink 的实时数仓框架 &#xff0c;充分利用了 TiDB 的分布式架构、MySQL 兼容性和完善的周边工具等特性&#xff0c;实现了数据的在线化处理。2023 年…

【mysql】时间戳与date互转

查看当前时区 show variables like %time_zone%;时间戳与date互转的注意事项 UNIX_TIMESTAMP() 与 FROM_UNIXTIME() 是一对儿时区要相同不指定时区时&#xff0c;使用mysql配置的默认时区。参考“查看当前时区”。 date转时间戳 时间戳转date 参考 https://dev.mysql.com/d…

Linux学习方法-框架学习法——Linux驱动架构的演进

配套视频学习链接&#xff1a;https://www.bilibili.com/video/BV1HE411w7by?p4&vd_sourced488bc722b90657aaa06a1e8647eddfc 目录 Linux驱动演进的过程 Linux驱动的原始架构(Linux V2.4) 平台总线架构(platform) Linux设备树 Linux驱动演进的趋势 Linux驱动演进的过程…

【Linux进阶之路】Socket —— “UDP“ “TCP“

文章目录 一、再识网络1. 端口号2. 网络字节序列3.TCP 与 UDP 二、套接字1.sockaddr结构2.UDP1.server端1.1 构造函数1.2 Init1.3 Run 2.客户端1.Linux2.Windows 3.TCP1. 基本接口2. 客户端3. 服务端1.版本12.版本23.版本34.版本4 三、守护进程尾序 温馨提示&#xff1a;文章较…

使用向量数据库pinecone构建应用04:混合搜索 Hybrid Search

Building Applications with Vector Databases 下面是这门课的学习笔记&#xff1a;https://www.deeplearning.ai/short-courses/building-applications-vector-databases/ Learn to create six exciting applications of vector databases and implement them using Pinecon…

ARM处理器有哪些工作模式和寄存器?各寄存器作用是什么?ARM异常中断处理流程?

《嵌入式工程师自我修养/C语言》系列——ARM处理器有哪些工作模式和寄存器&#xff1f;各寄存器作用是什么&#xff1f; 一、ARM处理器的工作模式及寄存器1.1 ARM处理器的工作模式1.2 ARM处理器中的寄存器 二、ARM 异常中断处理2.1 什么是异常&#xff1f;异常向量表是什么&…

用关联规则学习之购物篮分析

&#x1f349;CSDN小墨&晓末:https://blog.csdn.net/jd1813346972 个人介绍: 研一&#xff5c;统计学&#xff5c;干货分享          擅长Python、Matlab、R等主流编程软件          累计十余项国家级比赛奖项&#xff0c;参与研究经费10w、40w级横向 文…

【Python_Zebra斑马打印机编程学习笔记(二)】基于BarTender将btw文件转换为zpl文件

基于BarTender将btw文件转换为zpl文件 基于BarTender将btw文件转换为zpl文件前言一、BarTender1、BarTender 介绍2、BarTender 安装 二、导出 ZPL 文件1、导出 ZPL 文件步骤2、Zebra 打印机驱动安装 基于BarTender将btw文件转换为zpl文件 前言 本文介绍如何基于 BarTender 软…

深入理解JS的执行上下文、词法作用域和闭包(中)

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

.NET指定图片地址下载并转换Base64字符串

需求描述 需要调用第三方图片上传接口上传图片&#xff0c;对方图片格式只能接收Base64字符串。所以我们需要将系统服务器的图片通过Url下载下来&#xff0c;然后转换成Base64字符串。接下来我们将使用HttpClient类库下载图片并将其转换为Base64格式的字符串。 代码示例 /// &…

新手入门C语言之移位操作符和位操作符

在C语言中&#xff0c;移位操作符和位操作符是专门针对二进制的数字进行&#xff0c;因此&#xff0c;在描述移位操作符和位操作符之前&#xff0c;我们先来了解十进制&#xff0c;二进制&#xff0c;八进制&#xff0c;十六进制等的含义以及相互之间的转化。 一.进制以及相互…

《隐私计算简易速速上手小册》第7章:隐私计算与云计算/边缘计算(2024 最新版)

文章目录 7.1 云计算中的隐私保护7.1.1 基础知识7.1.2 主要案例:使用 Python 实现云数据的安全上传和访问7.1.3 拓展案例 1:实现基于角色的访问控制7.1.4 拓展案例 2:使用 Python 保护 API 安全7.2 边缘计算的隐私问题7.2.1 基础知识7.2.2 主要案例:使用 Python 实现边缘设…