机试练习(二)——一些细节的总结

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数组均可以使用cin/cout进行输入输出操作。但是string不能通过scanf/printf, 以及puts()和gets()进行输入输出操作。而char数组可以。

(三)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>”
  1. s.length()输出s中的字符个数;
  2. s.clear()将s清空;
    cout << s.length();
    s.clear();
  3. +操作符将s1和s2拼接,可以赋值给一个新的string s3
    string s3 = s1 + s2;
  4. s.insert()函数在s的号位置处插入一个字符c
  5. s.erase()函数删除s的号位置的字符
    s.insert(s.begin() + k1, c);
    s.erase(s.begin() + k2);
  6. 字符串可以直接用>或者<或者==比较大小
  7. s.substr()函数获取从s的下标从开始、长度为len的子串,赋值给一个新的string s2
    string s2 = s.substr(k, len);
  8. s1.find(s2)函数判断s2是否是s1的子串,如果是,输出s2第一次在s1中出现的起始位置;如果不是,则输出-1
    int pos = s1.find(s2);
  9. s1.replace()函数将s1的下标从开始、长度为len的子串替换为另一字符串s2,并将结果赋值给一个新的string s3
    string s3 = s1.replace(k, len, s2);

(四)map用法(相当于python里的字典)

  1. 遍历赋值
点击查看代码
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;
}
注: rbegin 指向倒序起始(即末尾) rend 指向倒序末尾(即起始)
  1. 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;
}
  1. mp.erase()函数删除一个指定的键;
  2. mp.clear()函数清空整个mp
  3. mp.size()函数输出mp中的键的个数
  4. 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与greater() 的区别,这要根据函数原型要求参数是函数对象类型还是要求参数是结构类型。greater<int>对应于结构的类型,greater<int>()对应于没有参数且返回类型更大的函数的类型。比如multimap中使用不带括号的,sort使用带括号的。

  1. 初始化
    priority_queue<int>
    priority_queue<int, vector<int>, greater<int> > q;

  2. q.push()函数元素入队

  3. q.top()函数输出队首元素

  4. q.empty()函数判断q是否为空

  5. q.size()函数输出q中的元素个数

  6. 当优先队列定义为结构体时,需要在结构体中重载比较操作符函数。

点击查看代码
#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用法

  1. 初始化
    stack<int> s;
  2. s.push()
  3. s.top()
  4. s.pop()
  5. s.empty()函数判断s是否为空
  6. 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库用法

  1. max()、min(),返回两个数的最大最小值
  2. abs(x)返回x的绝对值。注意:x必须是整数,浮点型的绝对值请用math头文件下的fabs
  3. swap(x,y)用来交换x和y的值
  4. 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;
}
  1. 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;
  1. fill()可以把数组或者容器中的某一段区间赋为某个相同的值。和memset不同,这里的赋值可以是数组类型对应范围内的任意值。
  2. 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;
}
  1. lower_ bound()和upper_ bound()需要用在一个有序数组或容器中
    lower_ bound(first,last,val)用来寻找在数组或容器的[first,last)范围内第一个值大于等于val的元素的位置,如果是数组,则返回该位置的指针;如果是容器,则返回该位置的迭代器。
    upper_bound(first,last,val)用来寻找在数组或容器的[first,last)范围内第一个值大于val的元素的位置,如果是数组,则返回该位置的指针;如果是容器,则返回该位置的送代器。
posted @ 2022-07-07 11:30  Xmasker^_^  阅读(35)  评论(0编辑  收藏  举报