目录
1.题目
顺序表ADT模板设计及简单应用:将顺序表中前 m 个元素和后 n 个元素进行互换
作者: 冯向阳时间限制: 1S章节: DS:线性表
问题描述 :
目的:使用自行设计的顺序表ADT或STL中的vector模板,设计并实现顺序表应用场合的一些简单算法设计。
应用1:试设计一个算法,用尽可能少的辅助空间将非空顺序表中前 m 个元素和后 n 个元素进行互换,即将线性表(a1,a2,…,am,b1,b2,…,bn) 改变成(b1,b2,…,bn,a1,a2,…,am)。假定m始终是有效值。
参考函数原型:
(1)顺序表ADT版本
template<class ElemType>
void Exchange( SqList<ElemType> &A, int m ); // 本算法实现顺序表中前 m 个元素和后 n 个元素的互换
(2)vector版本
template<class ElemType>
void Exchange( vector<ElemType> &A, int m );// 本算法实现顺序表中前 m 个元素和后 n 个元素的互换输入说明 :
第一行:顺序表的数据元素类型标记(0:int;1:double;2:char;3:string;其余值:输出err)
第二行:待处理顺序表的数据元素(数据元素之间以空格分隔)
第三行:逆置位置m
输出说明 :
第一行:逆置前顺序表的遍历结果(数据元素之间以“,”分隔)
空行
第三行:逆置后顺序表的遍历结果(数据元素之间以“,”分隔)
输入范例 :
0
13 5 27 9 32 123 76 98 54 87
5输出范例 :
13,5,27,9,32,123,76,98,54,87
123,76,98,54,87,13,5,27,9,32
2.题解
- 读题:题目要求通过vector或者手写adt来实现顺序表,在此基础上完成一个交换元素的算法
- 实现顺序表:手写adt还挺麻烦的哈(等有时间再去实现),所以本大懒人选择现成的vector。表的数据的录入还挺麻烦的,不同数据类型录入的方法都不同(把输入部分储存为string类型然后再去转换类型。int考虑字符串转数字,double除了转数字外还有小数,string读入不读空格。而且还要考虑正负)。还有记得考虑空表问题,别把'/0'这个元素插到vector里面然后出错。
- 实现交换算法:这个容易,只需要进行三步翻转。1.把表中的所有元素翻转 2.把上一步之后的表中后面的m个元素翻转 3.最后把前面n-m个翻转就ok。当然也可以直接枚举一个一个放到对应的位置,但是题目要求少用空间。
3.代码实现
- 数据录入构成表:
先用string来存储接下来要录入的元素 int n; string s; cin>>n; getchars(); getline(cin,s); 下面是不同数据类型的录入方法 //int型数据的录入(没有考虑负数情况,也没有考虑空表) vector<int> a; int num=0; for(int i=0; i<s.size(); i++)//遍历输入的string { if(s[i]==' ')//每当遇到空格时就把前一个数字元素num插入到vector中 { a.push_back(num);//插入 num=0;//数字num刷新归零,以便下一次使用 } else//每当遇到非空格就计算该num的值(实现string转换为int) { num=num*10+s[i]-'0';//num位权+1后再加上当前字符的数值 } } a.push_back(num);//用于插入最后一个元素。因为最后一个元素后面没有空格,还没有把最后一个元素插入vector就跳出循环了,所以在循环外面把最后一个元素插入 //double型数据录入(没有考虑负数情况,也没有考虑空表) vector<double> b; double num=0; int flag=0,t=0;//flag为1表示在计算小数部分数值,t表示小数点后的数字长度 for(int i=0; i<s.size(); i++) { if(s[i]==' ') { num=num/pow(10,t); b.push_back(num); t=0,num=0,flag=0;//刷新归零 } else if(s[i]=='.') { flag=1; } else { num=num*10+s[i]-'0'; if(flag==1) t++; } } num=num/pow(10,t); b.push_back(num); //char型数据录入 vector<char> c; for(int i=0; i<s.size(); i++) { if(s[i]!=' ') c.push_back(s[i]); } //string型数据录入(没有考虑空表) vector<string> d; string str; str.clear(); for(int i=0; i<s.size(); i++) { if(s[i]==' ') { d.push_back(str); str.clear(); } else str+=s[i]; } d.push_back(str);
- 模版算法部分:
//print函数 template<class T> void print(vector<T> &A) { //法一:用迭代器遍历 typename::vector<T> ::iterator it;//注意迭代器定义的语句 for(it=A.begin();it!=A.end();it++) { if(it==A.begin())cout<<(*it); else cout<<","<<(*it); } cout<<endl; //法二:用长度遍历 int i; for(i=0; i<A.size()-1; i++) cout<<A[i]<<","; cout<<A[i]<<endl; } //交换算法,reverse函数要algorithm头文件 template<class T> void change(vector<T> &A) { print(A); cout<<endl; int n=A.size()-m; reverse(A.begin(),A.end()); reverse(A.begin(),A.begin()+n); reverse(A.begin()+n,A.end()); print(A); }
-
完整代码
#include <iostream> #include <cstdio> #include <vector> #include <cstring> #include <cmath> #include <algorithm> using namespace std; int n,m; string s; template<class T> void print(vector<T> &A) { int i; for(i=0;i<A.size()-1;i++) cout<<A[i]<<","; cout<<A[i]<<endl; } template<class T> void change(vector<T> &A) { print(A); cout<<endl; int n=A.size()-m; reverse(A.begin(),A.end()); reverse(A.begin(),A.begin()+n); reverse(A.begin()+n,A.end()); print(A); } int main() { cin>>n; getchar(); getline(cin,s); cin>>m; if(n==0) { vector<int> a; int num=0; for(int i=0;i<s.size();i++) { if(s[i]==' ') { a.push_back(num); num=0; } else { num=num*10+s[i]-'0'; } } a.push_back(num); change(a); } else if(n==1) { vector<double> b; double num=0; int flag=0,t=0; for(int i=0;i<s.size();i++) { if(s[i]==' ') { num=num/pow(10,t); b.push_back(num); t=0,num=0,flag=0; } else if(s[i]=='.') { flag=1; } else { num=num*10+s[i]-'0'; if(flag==1)t++; } } num=num/pow(10,t); b.push_back(num); change(b); } else if(n==2) { vector<char> c; for(int i=0;i<s.size();i++) { if(s[i]!=' ')c.push_back(s[i]); } change(c); } else if(n==3) { vector<string> d; string str; str.clear(); for(int i=0;i<s.size();i++) { if(s[i]==' ') { d.push_back(str); str.clear(); } else str+=s[i]; } d.push_back(str); change(d); } else cout<<"err"<<endl; return 0; }
4.改进
在课堂听完老师的讲解之后发现有两处可以进行优化或者钻研
- 首先是造表,可以用stringstream实现函数模版实现代码复用,并且不需要讨论不同类型数据的录入,方便得很
- 第二是不能说是优化,只是想尝试手写一个模版类,毕竟老师说从底层理解vector是怎么实现的也更能熟练掌握vector运用嘛,我觉得他说得很对。
以下是代码
- stringstream使用
//模版造表函数,在主函数中运用就不需要写这么长的代码,stringstream头文件sstream template<class T> void CreatList(stringstream &in,vector<T> &sqlist) { T tmp; while(in>>tmp) sqlist.push_back(tmp); } //以int类型为例 int main() { cin>>n; cin.ignore(); getline(cin,s); stringstream input(s); if(n==0) { vector<int> a; CreatList(input,a); change(a); }
使用stringstream的完整代码,并且进行代码赏析
#include <iostream> #include <sstream> #include <cstring> #include <algorithm> #include <vector> using namespace std; int n,m; string s; template<class T> void print(vector<T> &A) { typename std::vector<T>::iterator it=A.begin(); for(it;it!=A.end()-1;it++) cout<<*it<<","; cout<<*it<<endl; } template<class T> void change(stringstream &input,vector<T> &A) { T str;//用来临时储存单个数据 while(input>>str) //“stringstream对象 + >>“就是提取字符串中单个数据 A.push_back(str);//每每提取到单个数据就插入到vector中 print(A); cout<<endl; int x=A.size()-m; reverse(A.begin(),A.end());//翻转是用swap实现的,而swap只需要另外开辟一个额外空间,也就是说整个这个操作只多使用了一个空间,因此符合题目要求 reverse(A.begin(),A.begin()+x); reverse(A.begin()+x,A.end()); print(A); } int main() { cin>>n; getchar();//也可以用cin.ignore()都是用来消除上一个n的输入后保存在缓冲区的回车 getline(cin,s);//读入string一般用这个读入,不用cin是为了防止碰到string中的空格就读入结束 stringstream input(s);//创建一个stringstream对象input,并把s的数据存入这个流对象input,因为stringstream对象可以进行单个字符串提取的操作,这么做是方便了提取单个数据来造表 cin>>m; if(n!=0&&n!=1&&n!=2&&n!=3) cout<<"err"<<endl; else if(n==0) { vector<int> a; change(input,a); } else if(n==1) { vector<double> b; change(input,b); } else if(n==2) { vector<char> c; change(input,c); } else { vector<string> d; change(input,d); } return 0; }
- 手写adt