因为最近在刷leetcode,没有输入输出,但是一些机考都是需要自定义数据结构输入与输出的。
我刷题用的c++,虽然c++有cin和cout,但我个人不推荐使用这个,还是建议使用c语言的scanf与printf(ps:如果题目涉及到字符串的输入读取,我建议用cin,因为字符串的读取用scanf容易出错),只要加上头文件#include<cstdio>即可。
格式控制说明
%d 十进制有符号整数
%u 十进制无符号整数
%f 浮点数
%s 字符串
%c 单个字符
%p 指针的值
%e 指数形式的浮点数
%x, %X 无符号以十六进制表示的整数
%0 无符号以八进制表示的整数
%g 自动选择合适的表示法
以我的刷题经验来看,一般题目用%d(接受int),%f(接受float,double),%c(接受char)比较多,其他很少,所以着重熟悉一下常用的就行,记少不记混。
一些特殊规定字符
\n 换行
\f 清屏并换页
\r 回车
\t Tab符
\xhh 表示一个ASCII码用16进表示,
其中hh是1到2个16进制数
这里的话也就\n与\t用得多,因为一般题目重心是靠算法,所以对这方面要求不高,知道基本输出格式就行。
scanf
格式
scanf(“格式控制字符串”, ¶m1,¶m2,···);
格式控制字符:包含普通字符、格式控制说明、特殊规定字符。
ps:
①后面的参数个数要与格式控制说明个数、顺序一一对应。
②输入的参数是变量的地址,需要在变量前加&
printf
格式
scanf(“格式控制字符串”, param1,param2,···);
格式控制字符:包含普通字符、格式控制说明、特殊规定字符。
和scanf()基本一样,不过输出的是参数,不是其地址,所以参数前不用加&。
存储结构
对于结构为一维数组或者二维数组,我个人喜欢使用vector<T>与vector<vector<T>>,而不喜欢使用a[]和a[][]。
然后虽然stl包含的数据结构不少。但是实际题目用到的不会很多很杂。
基本vector、queue、stack、unordered_map就能解决大部分问题。
vector
使用:加上头文件 #include<vector>
初始化:
vector<int> a(5),长度为5的整型向量,无初值
vector<int> a(5,1),在上面的基础上多了一个赋上初值1。
vector<int> a(b),若b是同是向量,则用b整体复制给a。
vector<int> a(b.begin(),b.begin()+3),只是把b的部分复制给a。
vector<int> a{1,2,3,4,5},直接指定a里面的值
常用api函数:
a.push_back(v) | 在向量末尾加入v |
a.pop_back() | 删除向量末尾元素 |
a.front() | 返回向量第一个元素 |
a.back() | 返回向量最后一个元素 |
a.resize(5,2) | 重置向量a,元素个数为5,初始值为2 |
其实还有很多api,但是其他的我觉得用的少,因为机考题一般考算法居多。且我这个不是资料,是总结,没必要写全,网上有更详细
一些常用特殊操作(需要加上头文件#include<algorithm>)
反转向量中的元素 | reverse(a.begin(),a.end()) |
求向量中所有元素的和 | accumulate(a.begin(),a.end(),0) 第三个参数表示设置累加初值 |
排序向量 | sort(a.begin(),a.end()) |
二维向量排序,尤其是区间问题 | sort(intervals.begin(), intervals.end(), [](vector a, vector b) { return a[1] < b[1]; }); 表示以区间末尾升序排列 |
queue
使用:加上头文件 #include<queue>
初始化:queue<类型> q
常用api函数
q.push(v) | 向队尾加入v |
q.pop() | 弹出队头元素 |
q.front() | 返回队头元素 |
q.back() | 返回队尾元素 |
stack
使用:加上头文件 #include<stack>
初始化:stack<类型> s
常用api函数
s.push(v) | v入栈 |
s.pop() | 弹出栈顶元素 |
s.top() | 获得栈顶元素 |
unordered_map
使用:加上头文件 #include<unordered_map>
初始化:unordered_map<类型,类型> m
常用api函数:
m.count(v) | 返回v出现的次数,一般用这个mp.count(v)!=0来查找v是否在map中 |
m.find(x) | 查找一个元素,一般用mp.find(x)!=mp.end()来查找x是否存在 |
例子
一般线性数据的题目输入用vector来存储就好了,但是如果碰到树形结构呢?
情况一:以二维空间代指树形
例子1
1 5 4 4 -1 4 5 3 6
这上面一串表示树,第i项的值表示其父亲的索引。也就是说第4项其值为-1,-1表示无父节点,那么第四项就是根节点,值为4的就是该根节点的子节点,分别是第2,3,5项,依次类推,树形如下:
真正装载的话,不需要用树,用二维向量或向量数组模拟即可
vector<vector<int> > tree(N);
int a[N];
//获取该行数据
for(int i=0;i<N;i++) {
scanf("%d",&a[i]);
}
//根据输入建图
for(int i=0;i<N;i++) {
if(a[i]==-1){
root=i;//确定root得到索引(儿子的值)
}
else
{
tree[a[i]].push_back(i);//构造图结构,通过索引关系来构造
//类似于二维数组,父亲索引(对应自己的值)和自己的索引(对应儿子的值)
}
}
这样遍历树的时候只需要从root开始通过二维向量就能轻松遍历节点的儿子。
例子2
21 1 23
01 4 03 02 04 05
03 3 06 07 08
06 2 12 13
13 1 21
08 2 15 16
02 2 09 10
11 2 19 20
17 1 22
05 1 11
07 1 14
09 1 17
10 1 18
每一行代表有孩子的人的id,其孩子个数以及各孩子的id
树如下:
存储的时候用向量数组即可,数组索引存的是节点id,该数组索引对应的向量为儿子的id集合。
vector<int> v[100];
for(int i=0;i<m;i++){
scanf("%d %d",&a,&b);
for(int j=0;j<b;j++){
scanf("%d",&c);
v[a].push_back(c);
}
}
这样根据该向量数组的关系能轻松遍历树中节点的儿子。
情况二:直接建议一个树形结构
struct TreeNode{
int val;
TreeNode* left;
TreeNode* right;
};
TreeNode* t;
//使用t
//或者自定义类型Tree,那么后面定义的时候不需要带上*
typedef TreeNode* Tree;
//直接
Tree t
//使用t
输入一串数组,使用其建二叉搜索树
//插入建树
Tree insert(Tree t,int x){
//若为空,new一个
if(!t){
t=new(TreeNode);
t->val=x;
t->left=t->right=NULL;
}
//小于该值,则去找其左子树
else if(x<t->val){
t->left=insert(t->left,x);
//去找其右子树
}else{
t->right=insert(t->right,x);
}
return t;
}
//建初始树
Tree t=NULL;
for(int i=0;i<n;i++){
scanf("%d",&x);
//调用插入函数建树
t=insert(t,x);
}