机试练习(二)——一些细节的总结
1.在循环输出多行的过程中对空格和换行通常需要分开处理
题目描述
输入一个正整数n,输出n行,每行都是从1到n的所有正整数。
输入描述
一个正整数n(1<=n<=10)。
输出描述
共行,每行是从1到的所有正整数。整数间用一个空格隔开,行末不允许有多余的空格。
样例1
输入
3
输出
1 2 3
1 2 3
1 2 3
点击查看代码
#include <cstdio>
int main() {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
printf("%d", j);
printf(j < n ? " " : "\n");
}
}
return 0;
}
总结:因为每个数之后是空格,但是如果是一行的最后一位的话,是回车,因此空格和回车要分开来讨论;
写代码时如果条件满足可以尝试着多使用一下条件表达式,更加简练。
2.algorithm头文件中的swap、sort、reverse函数
swap(a[j], a[j + 1])交换数组中两元素的值
sort函数对[first, last)区间的函数进行排序,查看源码可知使用的是快速排序法。
reverse(first, last)函数反转[first, last)区间的数据。
3.关于string s和char s[]区别
(一)#include<string.h>和#include以及#include<string.h>区别
<string>是C++标准定义的头文件,它定义了一个string的字符串类,里面包含了string类的各种操作,如s.size(), s.erase(), s.insert()等。但<string>又包含了老的C版本的字符串操作如strcpy、strcat等,这就相当于,在<string>的文件中除了定义自己的string类之外,还加了一个#include<string.h>一句包含了C版本的字符串操作。
(二)读取/输出方式
点击查看代码
#include<string>
#include<stdio.h>
#include<iostream>
using namespace std;
int main(){
string str,str1;
char a[100],b[100];
cin>>str;
scanf("%s",str1);//错误!!!string类型不可以用scanf输入
cin>>a;
scanf("%s",b);
}
(三)string类和char数组间互相转换
- string转char数组
点击查看代码
string str="gdfd";
char *p=str.c_str();
- char数组转string:直接赋值即可
点击查看代码
#include<string>
#include<stdio.h>
#include<iostream>
using namespace std;
int main(){
string str;
char p[100];
cin>>p;
str=p;
cout<<str<<endl;
}
(四)字符串可以直接比较大小
如下示例中,string类可以直接比较大小
点击查看代码
#include <iostream>
using namespace std;
int main() {
string a, b;
cin >> a;
cin >> b;
if (a > b) {
cout << ">";
} else if (a == b) {
cout << "=";
} else {
cout << "<";
}
return 0;
}
如下示例中,char s[]则需要使用strcmp比较大小
点击查看代码
#include <cstdio>
#include <cstring>
const int MAXN = 60;
char s1[MAXN], s2[MAXN];
int main() {
scanf("%s", s1);
scanf("%s", s2);
int compareResult = strcmp(s1, s2);
if (compareResult < 0) {
printf("<");
} else if (compareResult > 0) {
printf(">");
} else {
printf("=");
}
return 0;
}
(五)字符串拼接
string类可以直接用+拼接,而char s[]则需要使用strcat函数拼接
- string类进行字符串拼接
点击查看代码
#include <iostream>
using namespace std;
int main() {
string s1, s2;
cin >> s1;
cin >> s2;
cout << s1 + s2;
}
- char s[]进行字符串拼接
点击查看代码
#include <cstdio>
#include <cstring>
const int MAXN = 100 + 1;
char s1[MAXN], s2[MAXN];
int main() {
scanf("%s%s", s1, s2);
strcat(s1, s2);
printf("%s", s1);
return 0;
}
4.scanf(" %c"),在%c前加空格
标准输入流(stdin)是文件指针,scanf()就 是从标准输入流获取数据。
以上面代码为例,当执行到scanf()时,开始等待用户输入,当我们输入数据,在按下回车键前会把数据储存在缓冲区,按下回车键后,会把数据以及换行符(\n)一起传送到标准输入流(stdin)中,scanf)就 是从标准输入流中或许数据,上面 第一个scanf()要读入一个整数,它从标准输入流中查找,跳过空白符(空格、换行符、制表符),如果遇到数字,则开始读入,直到遇到非数字的字符结束,最后计算读取到的数值,储存到相关变量中。
5.while(scanf("%d%d",&n,&m)==2&&n&&m)
第一个数输入的返回值 两个%d, 意思为只有输出两个整数后才返回2 所以 scanf() == 2
&& 的意思为: 不但要输入2个整数int类型的数 还有n且m不等于0;
例如: 我们输入 0 0 并按回车 这样循环条件是不成立的
scanf == 2 成立
但n和m没有满足都不是0的条件
例如:我们输入 2 0 并按回车 一样m不成立 n 和scanf 是都成立的
例如:我们输入 a 1 并按回车 scanf 不成立 应为输入的不是整数 scanf == 1
6.sscanf(str,"",&,&)==
sscanf是从字符串中格式化输入
scanf是从标准输入输出中输入
点击查看代码
#include <iostream>
using namespace std;
const int MAXN = 50;
char str[MAXN];
int main() {
cin.getline(str, MAXN);
int a, b, c;
if (sscanf(str, "%d is greater than %d", &a, &b) == 2) {
cout << (a > b ? "Yes" : "No");
} else if (sscanf(str, "%d is equal to %d plus %d", &a, &b, &c) == 3) {
cout << (a == b + c ? "Yes" : "No");
} else {
cout << "???";
}
return 0;
}
7.结构体->和.区别
->:用在结构体指针的时候
. :用在结构体后面(".“这个运算符可以理解为中文里"的”)
点击查看代码
#include <stdio.h>
struct Student{
int age;
}st,*p,**p1;
int main(void)
{
p = &st;
p1 = &p;
st.age = 90;
printf("%d\n",p->age);
printf("%d\n",(*p).age);
printf("%d\n",st.age);
// 上述三行代码等价
printf("%d\n",(*p1)->age);
// (*p1)->age = (*(*p1)).age = (*p).age = st.age
}
8.getchar()的使用
一般在scanf与gets()或getline()之间加getchar(),消除回车符的影响。
scanf()、gets()遇空格结束,而getline()可以输入一行字符串
getline的用法:
点击查看代码
//main函数前声明:
const int MAXN=10001;
char str[MAXN];
//main函数中使用
cin.getline(str,MAXN);
9.浮点数比较大小,不能直接比较
要设置精度(一般为1e-8),然后相减,看结果绝对值是否小于精度
点击查看代码
#include <cstdio>
#include <cmath>
const double EPS = 1e-8;
bool greater(double a, double b) {
return a - b > EPS;
}
bool less(double a, double b) {
return a - b < -EPS;
}
int main() {
double a, b, c, d;
scanf("%lf%lf%lf%lf", &a, &b, &c, &d);
double result1 = a * asin(sqrt(b) / 2);
double result2 = c * asin(sqrt(d) / 2);
if (greater(result1, result2)) {
printf("1");
} else if (less(result1, result2)) {
printf("2");
} else {
printf("0");
}
return 0;
}
10.在循环输入多组数据时,可以使用while(t--),也可以使用for循环,但是标程里给的都是while方法
题目描述
输入两个正整数和,求的值。
输入描述
第一行为一个非负整数()。
接下来行,每行一组数据,为用空格隔开的两个正整数、(、)。
输出描述
每组数据一行,输出的值。
样例1
输入
3
2 3
1 8
7 9
输出
5
9
16
点击查看代码
#include <cstdio>
int main() {
int numCase, a, b;
scanf("%d", &numCase);
while (numCase--) {
scanf("%d%d", &a, &b);
printf("%d\n", a + b);
}
return 0;
}
11.STL库介绍
(一)vector用法
push_back,erase,insert,begin,end
注:vector没有find方法,如需要使用,要用find泛型
错误:
if (vec.find(1) != vec.end())
正确:
if (find(vec.begin(), vec.end(), 1) != vec.end())
点击查看代码
#include <cstdio>
#include <vector>
using namespace std;
int main() {
int n, x, k1, k2;
scanf("%d", &n);
vector<int> v;
for (int i = 0; i < n; i++) {
scanf("%d", &x);
v.push_back(x);
}
scanf("%d%d%d", &x, &k1, &k2);
v.insert(v.begin() + k1, x);
v.erase(v.begin() + k2);
for (int i = 0; i < (int)v.size(); i++) {
printf("%d", v[i]);
if (i < (int)v.size() - 1) {
printf(" ");
}
}
return 0;
}
(二)set用法
遍历需要迭代器
点击查看find、erase代码
#include <cstdio>
#include <set>
using namespace std;
int main() {
int n, x;
scanf("%d%d", &n, &x);
set<int> s;
for (int i = 0; i < n; i++) {
int a;
scanf("%d", &a);
s.insert(a);
};
set<int>::iterator it = s.find(x);
if (it != s.end()) {
s.erase(it);
}
for (set<int>::iterator it = s.begin(); it != s.end(); it++) {
if (it != s.begin()) {
printf(" ");
}
printf("%d", *it);
}
return 0;
}
点击查看insert、clear代码
#include <cstdio>
#include <set>
using namespace std;
int main() {
int n, x;
scanf("%d%d", &n, &x);
set<int> s;
for (int i = 0; i < n; i++) {
int a;
scanf("%d", &a);
s.insert(a);
};
s.clear();
printf("%d", (int)s.size());
return 0;
}
(三)string用法
注意:cin.getline(str,MAXN)与getline(cin,str)的区别
- cin.getline()用法:接收一个字符串,可以接收空格并输出
- getline()用法:接收一个字符串,可以接收空格并输出,需包含“#include<string>”
- s.length()输出s中的字符个数;
- s.clear()将s清空;
cout << s.length();
s.clear();
- +操作符将s1和s2拼接,可以赋值给一个新的string s3
string s3 = s1 + s2;
- s.insert()函数在s的号位置处插入一个字符c
- s.erase()函数删除s的号位置的字符
s.insert(s.begin() + k1, c);
s.erase(s.begin() + k2);
- 字符串可以直接用>或者<或者==比较大小
- s.substr()函数获取从s的下标从开始、长度为len的子串,赋值给一个新的string s2
string s2 = s.substr(k, len);
- s1.find(s2)函数判断s2是否是s1的子串,如果是,输出s2第一次在s1中出现的起始位置;如果不是,则输出-1
int pos = s1.find(s2);
- s1.replace()函数将s1的下标从开始、长度为len的子串替换为另一字符串s2,并将结果赋值给一个新的string s3
string s3 = s1.replace(k, len, s2);
(四)map用法(相当于python里的字典)
- 遍历赋值
点击查看代码
map<char, int> mp;
for (int i = 0; i < n; i++) {
cin >> c >> x;
mp[c] = x;
}
for (map<char, int>::iterator it = mp.begin(); it != mp.end(); it++) {
cout << it -> first << " " << it -> second << endl;
}
- mp.find()函数寻找一个指定的键
点击查看代码
for (int i = 0; i < n; i++) {
cin >> c >> x;
mp[c] = x;
}
cin >> c;
if (mp.find(c) != mp.end()) {
cout << mp[c];
} else {
cout << -1;
}
- mp.erase()函数删除一个指定的键;
- mp.clear()函数清空整个mp
- mp.size()函数输出mp中的键的个数
- mp的访问与操作
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
string str;
map<string,int> mp;
cin>>n;
for(int i=0;i<n;i++){
cin>>str;
if(mp.find(str)!=mp.end()) mp[str]++;
else mp[str]=1;
}
for(map<string,int>::iterator it=mp.begin();it!=mp.end();it++){
cout<<it->first<<" "<<it->second<<endl;
}
}
(五)queue用法
初始化
queue<int> q;
队列只能读取队头和队尾元素
push、front、back、pop、empty、size操作
(六)priority_queue
在优先队列中,优先级高的元素先出队列,并非按照先进先出的要求,类似一个堆(heap)。
默认按照从小到大排列。所以top()返回的是最大值而不是最小值!即如果把后面两个参数缺省的话,优先队列就是大顶堆,队头元素最大。
使用greater<>后,数据从大到小排列,top()返回的就是最小值而不是最大值!如果使用了第三个参数,那第二个参数不能省,用作保存数据的容器!!!!
priority_queue<int,vector<int> , greater<>> pq;
注:greater
-
初始化
priority_queue<int>
priority_queue<int, vector<int>, greater<int> > q;
-
q.push()函数元素入队
-
q.top()函数输出队首元素
-
q.empty()函数判断q是否为空
-
q.size()函数输出q中的元素个数
-
当优先队列定义为结构体时,需要在结构体中重载比较操作符函数。
点击查看代码
#include <iostream>
#include <queue>
using namespace std;
struct Fruit {
string name;
int price;
Fruit(string _name, int _price) {
name = _name;
price = _price;
}
friend bool operator < (Fruit f1, Fruit f2) {
return f1.price > f2.price;
}
};
int main() {
int n, price;
string name;
cin >> n;
priority_queue<Fruit> q;
for (int i = 0; i < n; i++) {
cin >> name >> price;
q.push(Fruit(name, price));
}
Fruit topFruit = q.top();
cout << topFruit.name << " " << topFruit.price;
return 0;
}
(七)stack用法
- 初始化
stack<int> s;
- s.push()
- s.top()
- s.pop()
- s.empty()函数判断s是否为空
- s.size()函数输出s中的元素个数
(八)pair用法
点击查看代码
#include <iostream>
#include <utility>
using namespace std;
int main() {
string str;
int k;
cin >> str >> k;
pair<string, int> p = make_pair(str, k);
cout << p.first << " " << p.second;
return 0;
}
点击查看代码
#include <iostream>
#include <utility>
using namespace std;
int main() {
int k1, k2, k3, k4;
cin >> k1 >> k2 >> k3 >> k4;;
pair<int, int> p1 = make_pair(k1, k2);
pair<int, int> p2 = make_pair(k3, k4);
if (p1 < p2) {
cout << "Yes";
} else {
cout << "No";
}
return 0;
}
(九)algorithm库用法
- max()、min(),返回两个数的最大最小值
- abs(x)返回x的绝对值。注意:x必须是整数,浮点型的绝对值请用math头文件下的fabs
- swap(x,y)用来交换x和y的值
- reverse(it,it2)可以将数组指针在[it,it2)之间的元素或容器的迭代器在[it,it2)范围内的元素进行反转。(左闭右开)
点击查看代码
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int n, x;
scanf("%d", &n);
vector<int> v;
for (int i = 0; i < n; i++) {
scanf("%d", &x);
v.push_back(x);
}
reverse(v.begin(), v.end());
for (int i = 0; i < (int)v.size(); i++) {
printf("%d", v[i]);
if (i < (int)v.size() - 1) {
printf(" ");
}
}
return 0;
}
点击查看代码
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main() {
string str;
cin >> str;
reverse(str.begin(), str.end());
cout << str;
return 0;
}
- next_permutation()给出一个序列在全排列中的下一个序列
点击查看代码
#include<cstdio>
#include<algorithm>
using namespace std;
int main(){
int a[10] = {1,2,3};
//a[0]~a[2]之间的序列需要求解next_permutation
do{
printf("%d%d%d\n",a[0],a[1],a[2]);
} while(next_permutation(a,a+3));
return 0;
- fill()可以把数组或者容器中的某一段区间赋为某个相同的值。和memset不同,这里的赋值可以是数组类型对应范围内的任意值。
- sort()可以排列数组,string,vector等
点击查看代码
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
struct Node {
int x, y;
Node(int _x, int _y) {
x = _x;
y = _y;
}
};
bool cmp(Node a, Node b) {
if (a.x != b.x) {
return a.x < b.x;
} else {
return a.y < b.y;
}
}
int main() {
int n, x, y;
scanf("%d", &n);
vector<Node> v;
for (int i = 0; i < n; i++) {
scanf("%d%d", &x, &y);
v.push_back(Node(x, y));
}
sort(v.begin(), v.end(), cmp);
for (int i = 0; i < n; i++) {
printf("%d %d\n", v[i].x, v[i].y);
}
return 0;
}
- lower_ bound()和upper_ bound()需要用在一个有序数组或容器中
lower_ bound(first,last,val)用来寻找在数组或容器的[first,last)范围内第一个值大于等于val的元素的位置,如果是数组,则返回该位置的指针;如果是容器,则返回该位置的迭代器。
upper_bound(first,last,val)用来寻找在数组或容器的[first,last)范围内第一个值大于val的元素的位置,如果是数组,则返回该位置的指针;如果是容器,则返回该位置的送代器。