为了准备保研夏令营的机式,这段时间都会搞一下算法练下手,不免遇到很多需要运用栈和队列解决的问题比如迷宫问题等,今天遇到一个计算表达式的问题,感觉又要用栈又要用到队列,好麻烦,我写代码的风格是不喜欢定义太多的变量和用到过多的数据结构,因为这样会使得代码看起来很复杂,而且自己也会搞晕。能否找到一个可以代替栈和队列的数据结构呢?
我们知道栈是满足先进后出(FILO),而队列满足先进先出(FIFO),因此如果我在解题的过程当中既要能够让该数据结构可以满足先进后出又要满足先进先出怎么办,答案是目前还真没有现成做好的数据结构自己拿去用,因为这样破坏了整个程序设计的体系。
但是办法总是有的,我们除了自己定义数据结构来满足自身需求以外(实际在程序竞赛期间那有那么多时间给你定义这样的结构,最好用现成的数据结构,不仅方便而且不会出错),我们还可以使用已有的数据结构来进行稍加改造即可。
今天要讲数据结构可能有时候我们不常用,对的,就是Vectors,自己花了一下午功夫好好研究了一下,感觉这个玩意好像可以既当做栈使用,也可以当做队列使用,只是操作大家注意一下即可。
Vectors 包含着一系列连续存储的元素,其行为和数组类似。访问Vector中的任意元素或从末尾添加元素都可以在常量级时间复杂度内完成,而查找特定值的元素所处的位置或是在Vector中插入元素则是线性时间复杂度。
与它相关的操作还是很多,咱们慢慢来讲,把这篇文章看完其实其余的数据机构如STL(Standard Template Library,标准模板库)当中的栈和队列什么操作你会更加熟悉。
1. Constructors —— 构造函数
这个是建立vector的第一步,我们在声明的时候可以有如下的几种方式(假设内部存储的是int类型):
1.1 vector ve;
这个是最常用的方法,就是简单声明一个内部装着int类型的vector;
1.2 vector ve(5, 1);
这个代表建立了一个包含5个int空间的且初始值为1的vector;
1.3 vector ve(ve2); (其中ve2的类型为vector,即与ve一样)
这个代表建立了和ve2一样元素的vector,只是他们的地址不同
1.4 (其实还有,但是不常用,就不列了)
xxxxxxxxxxxx
2. Operators —— 对vector进行赋值或比较
建立两个vector后内部的元素可以直接进行比较,C++ Vectors能够使用标准运算符: ==, !=, <=, >=, <, 和 >. 要访问vector中的某特定位置的元素可以使用 [] 操作符.
例如:
#include <iostream>
#include <vector>
using namespace std;
int main(void){
vector<int> ve1(5, 1);
vector<int> ve2(4, 0);
if (ve1[0] == ve2[0]){
cout<<"equal"<<endl;
}
else {
cout<<"not equal"<<endl;
}
return 0;
}
这个代码运行的结构就是“not equal”,也就是不相等,很简单,不多解释;
另外两个vectors被认为是相等的,如果: 1、它们具有相同的容量 ; 2、所有相同位置的元素相等.
同时vectors之间大小的比较是按照词典规则.
3. assign() —— 对Vector中的元素赋值
这个其实和上面是构造函数类似,有两种用法
3.1 void assign( input_iterator start, input_iterator end );
还是直接看例子吧;
#include <iostream>
#include <vector>
using namespace std;
int main(void){
vector<int> ve1(5, 1);
vector<int> ve2(4, 0);
cout<<"之前的状态:"<<endl;
cout<<"ve1的状态"<<endl;
for (vector<int>::iterator it = ve1.begin(); it != ve1.end(); it++){
cout<<*it<<" ";
}
cout<<endl;
cout<<"ve2的状态"<<endl;
for (vector<int>::iterator it = ve2.begin(); it != ve2.end(); it++){
cout<<*it<<" ";
}
cout<<endl<<endl;
cout<<"之后的状态:"<<endl;
ve1.assign(ve2.begin(), ve2.end());
cout<<"ve1的状态"<<endl;
for (vector<int>::iterator it = ve1.begin(); it != ve1.end(); it++){
cout<<*it<<" ";
}
cout<<endl;
cout<<"ve2的状态"<<endl;
for (vector<int>::iterator it = ve2.begin(); it != ve2.end(); it++){
cout<<*it<<" ";
}
return 0;
}
输出结果如下:
不用解释,很好理解,就是复制操作,如果你想把ve1复制为ve2的前三个元素可以这样:
ve1.assign(ve2.begin(), ve2.begin() + 3);
3.2 void assign( size_type num, const TYPE &val );
这个就和构造函数vector ve(5, 1)原理类似
4. at() —— 返回指定位置的元素
例如:ve.at(2)
返回的就是ve索引为2的元素;
其实用ve[2]这种方法也可以访问,但是at() 函数 比 [] 运算符更加安全, 因为它不会让你去访问到Vector内越界的元素。例如, 考虑下面的代码:
vector<int> ve;
ve.push_back(2);
ve.push_back(4);
ve.push_back(6);
cout<<endl;
for (int i = 0; i < 10; i++){
cout<<ve[i]<<" ";
}
这段代码访问了vector末尾以后的元素,这将可能导致很危险的结果.以下的代码将更加安全:
vector<int> ve;
ve.push_back(2);
ve.push_back(4);
ve.push_back(6);
cout<<endl;
for (int i = 0; i < 10; i++){
cout<<ve.at(i)<<" ";
}
5. back() —— 返回最末一个元素
例如这个代码:
vector<int> ve;
ve.push_back(2);
ve.push_back(4);
ve.push_back(6);
int obj = ve.back();
cout<<obj;
它返回的结果就是6;
6. begin() —— 返回第一个元素的迭代器
begin()函数返回一个指向当前vector起始元素的迭代器.
这个在遍历的时候非常有用,与之相似的还有end(),用法看下面即可,其实就是一种数据结构:
#include <iostream>
#include <vector>
using namespace std;
int main(void){
vector<int> ve;
ve.push_back(2);
ve.push_back(4);
ve.push_back(6);
for (vector<int>::iterator it = ve.begin(); it != ve.end(); it++){
cout<<*it<<" ";
}
cout<<endl;
return 0;
}
代码的输出是:
2 4 6
说白了就是遍历整个vector,然后begin()就是指向vector的头部,而end()为尾部,在后面我们还会用到它在删除某些元素。
7. capacity() —— 返回vector所能容纳的元素数量(在不重新分配内存的情况下)
这个的话感觉没什么实质性作用,你每次插入一个元素或者几个元素后它的返回值还是会增多,有待一日问问老师吧。
8. clear() —— 清空所有元素
这个就比较简单了,就是把你之前插入的元素全部清空
9. empty() —— 判断Vector是否为空(返回true时为空)
如果当前vector没有容纳任何元素,则empty()函数返回true,否则返回false;
我们也可以使用它来进行遍历,例如,以下代码清空一个vector,并按照逆序显示所有的元素:
#include <iostream>
#include <vector>
using namespace std;
int main(void){
vector<int> ve;
ve.push_back(2);
ve.push_back(4);
ve.push_back(6);
ve.push_back(8);
ve.push_back(10);
while (ve.empty() != true){
cout<<ve.back()<<" ";
ve.pop_back();
}
return 0;
}
程序的输出就是10 8 6 4 2
10. end() —— 返回最末元素的迭代器(译注:实指向最末元素的下一个位置)
这个在讲begin()的时候已经讲过,用来进行遍历使用,不过后面我们还有另外用途,现在你就记住他的返回值是最后一个元素的后一个位置的地址即可。
11. erase() —— 删除指定元素
这个的作用是用来删除指定位置的元素,有两种用法:
11.1 iterator erase( iterator loc );
输入是待删除的元素的位置,因此前提是你要知道待删除的位置在哪儿;
但是我们曾经在前面讲过我们要建立一个既可以当栈使用又可以当队列使用的数据结构,因此vector既要满足可以删除队头元素,也要可以删除队尾元素,这样才能模拟栈和队列的出栈和出队的操作,但是对于vector只有移除最后一个元素的相关函数,但是我们可以根据上面讲的begin()和end()来模拟栈和队列的操作;
#include <iostream>
#include <vector>
using namespace std;
int main(void){
vector<int> ve;
ve.push_back(2);
ve.push_back(4);
ve.push_back(6);
ve.push_back(8);
ve.push_back(10);
ve.erase(ve.begin()); // 删除vector前面的第一个元素
ve.erase(ve.end() - 1); // 删除vector尾部的最后一个元素
for (vector<int>::iterator it = ve.begin(); it != ve.end(); it++){
cout<<*it<<" ";
}
cout<<endl;
return 0;
}
上面的代码运行结果是4 6 8
,即既可以删除开头的元素,又可以删除尾部元素,那么不管是栈而言还是对于队列而言出栈出队就实现了。不仅如此,我们还可以删除任意位置的元素,只要你知道该元素的位置,例如你想删除开始部位的后一个元素,则可以写成ve.erase(ve.begin() + 1);
11.2 iterator erase( iterator start, iterator end );
这个是删除start与end之间的所有元素,例如如果删除整个元素你可以写成ve.erase(ve.begin(), ve.end()); 代码我就不贴了,自己尝试一下即可。
12. front() —— 返回第一个元素
这个对应于队列的话就是返回头元素,而返回末尾元素就是back(),上文已经提及。
但是你如果对begin()和end()理解很深了话其实还可以用 *ve.begin() 和 *(ve.end() - 1) 来代替上面的两个函数,因此begin()返回的就是头元素的地址,end()返回的是末尾的后一位的地址。
13. insert() —— 插入元素到Vector中
insert() 函数有以下三种用法:
13.1 iterator insert( iterator loc, const TYPE &val );
第一个是要指定某个位置插入某个元素,例如ve.insert(ve.begin(), 0);代表在ve的头部插入元素0,而ve.insert(ve.end(), 12);代表在尾部插入元素12,对应于栈和队列就是进栈和进队列的情况,当然还可以再首元素和末元素的某个位置插入;
#include <iostream>
#include <vector>
using namespace std;
int main(void){
vector<int> ve;
ve.push_back(2);
ve.push_back(4);
ve.push_back(6);
ve.push_back(8);
ve.push_back(10);
ve.insert(ve.begin(), 0);
ve.insert(ve.end(), 12);
for (vector<int>::iterator it = ve.begin(); it != ve.end(); it++){
cout<<*it<<" ";
}
cout<<endl;
return 0;
}
例如以上代码的运行结果就是0 2 4 6 8 10 12
;
13.2 void insert( iterator loc, size_type num, const TYPE &val );
在指定位置loc前插入num个值为val的元素,例如:
#include <iostream>
#include <vector>
using namespace std;
int main(void){
vector<int> ve;
ve.push_back(2);
ve.push_back(4);
ve.push_back(6);
ve.push_back(8);
ve.push_back(10);
ve.insert(ve.begin(), 3, 0);
for (vector<int>::iterator it = ve.begin(); it != ve.end(); it++){
cout<<*it<<" ";
}
cout<<endl;
return 0;
}
运行的结果就是0 0 0 2 4 6 8 10
,原理大家应该也懂了,我就不多说了;
13.3 void insert( iterator loc, input_iterator start, input_iterator end );
在指定位置loc前插入区间[start, end)的所有元素,例如下面代码:
#include <iostream>
#include <vector>
using namespace std;
int main(void){
vector<int> ve;
ve.push_back(2);
ve.push_back(4);
ve.push_back(6);
ve.push_back(8);
ve.push_back(10);
ve.insert(ve.begin(), ve.begin(), ve.end());
for (vector<int>::iterator it = ve.begin(); it != ve.end(); it++){
cout<<*it<<" ";
}
cout<<endl;
return 0;
}
输出的结果就是2 4 6 8 10 2 4 6 8 10
,相当于把ve赋值了一份在自己的空间里面;
14. pop_back() —— 移除最后一个元素
删除当前vector最末的一个元素,这个就是和ve.erase(ve.end() - 1);功能一样,对应于栈就是出栈,不多讲。
15. push_back() —— 在Vector最后添加一个元素
添加一个值的元素到当前vector末尾,这个和ve.insert(ve.end(), 12);类似,也不多讲。
16. rbegin() —— 返回Vector尾部的逆迭代器
rbegin函数返回指向当前vector末尾的逆迭代器,这个和begin()类似,只不过他指向尾部,然后从尾部开始遍历到首部,即倒序输出,与之配合使用的是rend()函数。
#include <iostream>
#include <vector>
using namespace std;
int main(void){
vector<int> ve;
ve.push_back(2);
ve.push_back(4);
ve.push_back(6);
ve.push_back(8);
ve.push_back(10);
cout<<"从头至尾输出:"<<endl;
for (vector<int>::iterator it = ve.begin(); it != ve.end(); it++){
cout<<*it<<" ";
}
cout<<endl<<endl;
cout<<"倒序输出:"<<endl;
for (vector<int>::reverse_iterator it = ve.rbegin(); it != ve.rend(); it++){
cout<<*it<<" ";
}
cout<<endl;
return 0;
}
输出的结果为:
你要是不习惯也可以这样倒序输出:
for (vector<int>::iterator it = ve.end() - 1; it != ve.begin() - 1; it--){
cout<<*it<<" ";
}
其实都是相通的。
17. size() —— 返回Vector元素数量的大小
size() 函数返回当前vector所容纳元素的数目,这个很简单,就不讲了。
18. swap() —— 交换两个Vector
这个就是交换两个vector变量的内容,当然你也可以像交换两个变量一样,定义一个临时变量来进行,只不过这个只用写1行代码即可。
所有的vector都讲完了,讲了这么多举个栗子,就把上面的内容运用到表达式的求解里面吧!
题目是输入一串表达式,求解值,要考虑乘法和除法的优先级,不考虑除零和括号的情况,并且只考虑整数,例如输入:
3-48+24/42-12+1
输出为:
-28
过程就要建立两个栈,一个符号栈一个字符栈,遇到数字进栈,当遇到+或者-号时符号入栈,当遇到*或者/号时就继续取下个数,然后数字栈出栈一个元素与该数进行运算后再入栈,最后扫描完整个表达式,
之后再进行简单的加减运算,我们可以从栈的底部开始,这时候可以把栈当做队列操作,每次队列头出一个元素,然后符号出一个元素,进行运算后再接着下一轮,直到符号栈为空。
代码如下:
#include <iostream>
#include <vector>
#include <iomanip>
using namespace std;
int main(void){
string s;
cin>>s;
vector<int> ve;
vector<char> vec;
for (int i = 0; i < s.length(); i++){
if (s[i] >= '0' && s[i] <= '9'){
int index = i + 1;
while (index < s.length() && s[index] >= '0' && s[index] <= '9') index++;
int num = 0;
for (int j = i; j < index; j++){
num *= 10;
num += (s[j] - '0');
}
ve.push_back(num);
i = index - 1;
}
else {
if (s[i] == '+' || s[i] == '-'){
vec.push_back(s[i]);
}
else {
int index = i + 1, num = 0;
while (index < s.length() && s[index] >= '0' && s[index] <= '9') index++;
for (int j = i + 1; j < index; j++){
num *= 10;
num += (s[j] - '0');
}
int num2 = ve.back();
ve.erase(ve.end() - 1);
if (s[i] == '*'){
ve.push_back(num2 * num);
}
else {
ve.push_back(num2 / num);
}
i = index - 1;
}
}
}
cout<<"-----"<<endl;
for (vector<int>::iterator it = ve.begin(); it != ve.end(); it++){
cout<<setw(2)<<*it<<" ";
}
cout<<endl;
cout<<" ";
for (vector<char>::iterator it = vec.begin(); it != vec.end(); it++){
cout<<setw(2)<<*it<<" ";
}
cout<<endl;
cout<<"-----"<<endl;
int sum = ve.front();
ve.erase(ve.begin());
while (vec.size() != 0){
if (vec.front() == '+'){
sum += ve.front();
}
else {
sum -= ve.front();
}
ve.erase(ve.begin());
vec.erase(vec.begin());
}
cout<<sum<<endl;
return 0;
}
例如输入3-48+24/42-12+1,结果为:
中间的
-----
3 32 12 12 1
- + - +
-----
代表第一次运算完后数字栈和符号栈内的情况。
我也就不多讲了,大家慢慢研究,还是比较简单的,只是今天用到了vector。没用stack和queue;
不早了,我也要回寝室准备睡了,明天周一,又是新的一个星期,又要开始忙碌了。
如果写的有问题请及时指正,谢谢!
欢迎大家访问我的主页。