大神请忽略~
顺序容器
通常情况下vector是个不错的选择.
容器的公共操作
迭代器范围
左闭合区间 [begin, end), beigin包含, end不包含. end指向最后一个元素的后一个位置.
左闭合范围的特点:
- 如果begin==end, 则范围为空
- 如果begin !=end 则范围至少包含一个元素, 且begin指向该范围中的第一个元素
- 可以 ++begin , 然后begin==end
begin(), rbegin() 反向迭代器第一个元素, cbegin() const迭代器
auto与begin, end 结合时, 获得的迭代器是否是const取决于容器的类型, 而c开头的begin,end等一定能获得const版本的迭代器.
当两个容器发生拷贝时, 两个容器的类型和元素类型都必须相同, 如果是使用迭代器, 则只需要元素能够转化即可.
array 42个int类型的数组. array必须同时指定元素的类型和大小. 其大小为array::size_type i;
默认构造的array是非空的, 它包含了与其大小一样多的元素.
array a = {0, 1, 2};
int b[3] = {0,1,2};
不能对内置数组进行拷贝或对象赋值, 但是array可以.
array<int, 3> a = {0, 1, 2};
int b[3] = {0,1,2}; // 正确
int b2[3] = b; // 错误 不能复制
array<int, 3>a2 = a; // 正确
int c[3]; // 未指定数组内每个数的初值, 值未知
c[3] = { 2 }; // 内存越界
赋值和swap swap(c1,c2)交换c1, c2中的元素,比从c2向c1拷贝快得多.
array<int, 3> a1 = { 1,2,3 };
array<int, 3> a2 = { 123 };// 11中 所有元素均为123, 14中 第一个元素123, 其余为0
a1 = a2;
a2 = { 3, 2 }; // 11中 错误不能将一个花括号列表赋值给数组; 14中可以, 第一个元素为3, 其余为0
for (auto a : a2) cout << "a2: " << a;
seq.assign(b,e) 将seq中的元素替换为迭代器b和e所表示的范围中的元素.
seq.assign(il) 将seq中的元素替换为初始化列表il中的元素
seq.assign(n,t) 将seq中的元素替换为n个值为t的元素.
赋值相关运算会导致指向左边容器内部的迭代器,引用和指针失效. 而swap操作将容器内容交换不会导致迭代器, 引用和指针失效(除了array和string)
assign 与赋值运算符的区别:
assign允许一个不同但相容的类型赋值,或者从容器的一个子序列赋值.
swap 只交换容器的内部数据结构 除了array. 迭代器,引用,指针不会失效 除string
最好统一使用非成员版本的swap
容器大小
- size 元素的个数
- empty 当size==0 时 返回true, 否则false
- max_size 返回一个大于或等于该类型容器所能容纳的最大元素数.
关系运算符
每个容器类型都支持 ==和!=, 无序关联容器以外的所有容器都支持<, <=, >, >=
容器类型不同不能比较, 如vector 和list, 需要编程逐一比较.
顺序容器的操作
容器的特定位置 插入元素
xx.insert(xx.begin(), "hello")
插入范围内元素
xx.insert(xx.begin(), 10, "hello"), xx.insert(xx.begin(), v.end()-2, v.end())
xx.insert(xx.end(), {1,2,3,4})
insert 返回指向第一个新加入元素的迭代器.
emplace_front, emplace, emplace_back , 对应 push_front, insert, push_back.
emplace 和insert的区别:
- push/insert 将元素类型的对象传递进去, 这些对象被拷贝到容器中. 会创建临时对象, 然后放入容器
- emplace 则是将参数传递给元素类型的构造函数.在容器内存空间中直接构建新对象
访问元素
在解引用一个迭代器或调用front或back之前检查是否有元素c.empty()
front, back(forward_list 不支持) 必须确保容器不能为空. 其返回值为引用.
string vector deque array支持下标的容器 可以使用at. at在编译时检查下标是否越界, 而[] 不会.
删除元素
删除元素不使用array.
forward_list 不支持pop_back, vector和string 不支持pop_front
c.erase(p) 删除迭代器p指定的元素.
c.erarse(b,e) 删除迭代器b, e指定范围的元素.
c.clear() 删除所有元素
deque删除除首尾之外的任何元素都会使所有迭代器,引用和指针失效
vector, string 删除点之后的迭代器,引用和指针都会失效
forward_list:
before_begin(), cbefore_begin(),
insert_after(p, t), insert_after(p, n, t) p 之后插入n个t
insert_after(p,b,e) 插入另一个迭代器b,e之间的元素
insert_after(p, il) 插入列表il {}
emplace_after(p, args)
erase_after(p) 删除p之后的元素
erase_after(b,e) 删除[b, e)之间的元素
改变容器大小(除了array)
如果变小, 则后面的元素被删除, 如果变大, 会被放到容器后部分.
c.resize(n), c.resize(n, t) 使用t初始化新添加的元素, c调整为n个元素.
如果元素不能默认初始化,必须提供默认构造函数.
迭代器失效的情况- 插入:
- vector和string, 指向插入点之后
- deque, 插入两端之外的地方, 整个都失效, 插入两端,两端的失效
迭代器失效的情况- 删除:
- 指向被删除的都会失效
- deque 首尾之外删除 都会失效, 如果删除尾, 尾后迭代器失效, 如果是首, 不受影响.
- vector, string 指向被删之后的会失效.
vector, string, deque, 如果对容器插入/删除要小心使用迭代器
advance(it, n) 移动迭代器向前n个位置.
vector 对象是如何增长的
vector会预留部分空间以免在频繁插入时频繁的重新申请空间,从而可以加快执行速度.
管理容器的成员函数
c.shrink_to_fit() 将capacity()减少为与size()相同的大小, 不保证退还多余的内存.
c.capacity() 不重新分配内存空间的话, c可以保存多少元素
c.reserve(n) 分配至少能容纳n个元素的内存空间
额外的string操作
const char *cp ="hello world!"
char noNull[] = {'h', 'e'};
string s1(noNull) //报错, noNull 没有以空字符结尾
substr 返回一个截取的字符串
搜索操作 都返回string::size_type值, 找不到返回const string::size_type 类型的npos, 并初始化值为-1;
find 大小写敏感
查找与给定字符串中任何一个字符匹配的位置.
string numbers("0123456789"), name("r2d2");
auto pos = name.find_first_of(numbers); // pos = 1, name 中第一个数字的下标.
string dept("03714p3");
auto pos = dept.find_first_not_of(numbers); // pos = 5, 字符p的下标, 第一个不在参数中的字符.
- s.find(args) 查找s中args第一次出现的位置
- s.rfind(args) 查找s中args最后一次出现的位置
- s.find_first_of(args) 查找args中任何一个字符第一次出现的位置
- s.find_first_not_of(args) 查找args中任何一个字符最后一次出现的位置
- s.find_last_of(args) 查找第一个不在args中的字符
- s.find_last_not_of(args) 查找最后一个不在args中出现的字符
args的形式:
- c,pos 从pos开始查找字符c, pos默认0
- s2, pos, 从pos查找字符串s2, pos 默认0
- cp,pos 从pos开始查找指针cp指向的以空字符串结尾的c风格字符串, pos默认0
- cp, pos, n 从pos开始查找指针cp指向的数组的前n个字符, pos和n 无默认值.
compare函数
- s1.compare(s2) 与字符串s2比较
- s1.compare(pos1, n1, s2) s从pos1开始的n1个字符与s2比较
- s1.compare(pos1, n1, s2, pos2, n2) s从pos1开始的n1个字符与s2中从pos2开始的n2个字符比较
- s1.compare(cp) s与cp指向的以空字符结尾的字符数组比较
- s1.compare(pos1, n1, cp) s中pos1开始的n1个字符与cp指向的以空字符结尾的字符数组比较
- s1.compare(pos1,n1,cp,n2) s中pos1开始的n1个字符与cp指向的以空字符结尾的n2个字符比较
数值转换
数值数据与标准库string之间的转换
- to_string(val) 返回数值val的string表示.
- stoi(s,p,b) 返回int, long, unsigned long, long long, unsigned long long. b是基数(8, 10, 16), p是size_t
- stol(s,p,b) 指针,用来保存s中第一个非数值字符的下标, 默认为0
- stoll(s,p,b)
- stoull(s,p,b)
- stof(s,p)
- stod(s,p)
- stold(s,p)
容器适配器
为了简化容器的操作???
stack, queue, priority_queue
- size_type 一种类型, 足以保存当前类型的最大对象的大小
- value_type 元素类型
- container_type 实现适配器的底层容器类型
- A a 创建一个名为a的空适配器
- A a(c); 创建一个名为a的适配器, 带有容器c的一个拷贝
- 关系运算符 ==, !=, <=, <, >, >=
- a.empty() 若a包含任何元素 返回false, 否则 true
- a.size() 返回a中的元素数目
- swap(a,b) 交换a和b的内容
- a.swap(b)
stack push_back,pop_back, back, 不能使用array forward_list
queue back, push_back, front, push_front 可以用于list和deque, 不能vector
priority_queue front, push_back, pop_back 随机访问 , 能用于vector和deque, 不能list
栈适配器stack的操作:
- s.pop() 删除栈顶元素, 不返回该元素值
- s.push(item) 创建一个新元素压入栈顶, 拷贝方式
- s.emplace(args) 有args构建新元素
- s.top() 返回栈顶元素, 但不将元素弹出栈.
队列适配器操作: 所有操作都不删除元素
queue默认基于deque实现, priority_queue(可以设定优先级)默认基于vector实现. 先进先出原则
- q.pop() 返回queue的首元素, 或者priority_queue的最高优先级元素, 不删除元素
- q.front() 返回首元素或尾元素, 不删除元素
- q.back() 只适用于 queue
- q.top() 返回最高优先级元素 不删除元素
- q.push(item) 在queue末尾或priority_queue恰当的位置 创建一个元素
- q.emplace(args)
9.6 的联系题比较难, 真没想到一个带小括号的四则运算就这么难处理.
几乎照抄了这位兄弟的实例 https://blog.csdn.net/qq_43152052/article/details/96333433,
int priority2(const char opt) {
if ('(' == opt) return 1;
if ('+' == opt || '-' == opt) return 2;
if ('*' == opt || '/' == opt) return 3;
return 0;
}
void calculate2(stack<int>& opdstack, const char opt)/*计算操作数栈的结果*/
{
int a = opdstack.top(); //取栈顶元素a
opdstack.pop();
int b = opdstack.top(); //取栈顶元素b
opdstack.pop();
if (opt == '+')
{
opdstack.push(a + b); //压入a+b的值
}
if (opt == '-')
{
opdstack.push(b - a); //压入b-a的值
}
if (opt == '*')
{
opdstack.push(a * b); //压入a*b的值
}
if (opt == '/')
{
opdstack.push(b / a); //压入b/a的值
}
}
int caleExpression2(string str) {
stack<int> stack_nums;
stack<char> stack_operator;
for (auto i = 0; i != str.length(); ++i) {
char char_opt = str[i];
if (char_opt == '+' || char_opt == '-' || char_opt == '*' || char_opt == '/') {
if (stack_operator.size() == 0) //操作符栈为空
stack_operator.push(char_opt);
else {
int curr_priority = priority2(char_opt);
int stack_top_priority = priority2(stack_operator.top());
if (curr_priority > stack_top_priority) {
stack_operator.push(char_opt);
}
else {
while (curr_priority <= stack_top_priority) {
calculate2(stack_nums, stack_operator.top());
stack_operator.pop(); //删除处理过的运算符
if (stack_operator.size() > 0) {
stack_top_priority = priority2(stack_operator.top());
}
else break;
}
stack_operator.push(char_opt);
}
}
}
else if (char_opt == '(') {
stack_operator.push(char_opt);
}
else if (char_opt == ')') {
// 出栈操作
while (stack_operator.top() != '(') {
calculate2(stack_nums, stack_operator.top());
stack_operator.pop(); // 删除操作符
}
stack_operator.pop(); // 删除最后的 '('
}
else {
// 如果是操作数, 就压入到操作数栈
stack_nums.push(atoi(&char_opt));
}
}
while (stack_operator.size() != 0) {
calculate2(stack_nums, stack_operator.top());
stack_operator.pop();
}
return stack_nums.top();
}
int main(int argc, char** argv) {
//9.52
string source = "(2*4+1)*2/(3-2)-1";// "(1+3)*3/(2-1)";
vector<string> tokens = { "(","2", "*", "4", "+", "1",")", "*", "2", "/", "(","3", "-", "2",")", "-", "1" };// { "(", "1", "+", "3", ")", "*", "3", "/", "(", "2", "-", "1", ")" };
for (auto i = 0; i != source.size(); ++i)
cout << source[i];
cout << endl;
cout << caleExpression2(source) << endl;
}