算法学习(c++)
- 前言(重要)
- 一、c和c++的基本知识学习
- 1、scanf函数的用法(注意:scanf和printf与cin和cout最好不要混用,否则可能会出现一些神奇的bug)
- 2、printf函数的用法
- 3、printf函数的三种输出格式
- 4、getchar()和putchar()函数的用法
- 5、gets()(已过时)、puts()、gets_s()、fgets()的用法
- 6、math函数的使用
- 7、数组使用时的注意(全局数组)
- 8、memset函数,对数组中每个元素赋相同的值(与fill()函数相似)
- 9、string.h头文件中的一些函数(c中字符串的一些函数)
- 10、sscanf()和sprintf()函数的使用
- 11、结构体的新认识(构造函数)
- 12、cin(读取整行)和cout(控制精度)的使用(这里推荐使用c的输入输出):
- 13、浮点数的比较(有误差)
- 14、多点测试——数据的3种输入方法
- 15、变量的取值范围
- 16、字符串转换为int型变量的方法
- 17、与字符有关的isalpha、isalnum、islower、isupper函数详解
- 18、如果程序输入数据的个数无法确定,用istringstream将会非常方便
- 二、一些简单算法实现
- 三、纠错心得
前言(重要)
1. 为了能够更好的调试程序,可以将输入流和输出流重定向为指向文件
#include<bits/stdc++.h>
using namespace std;
int main(){
freopen("in.txt", "r", stdin); //输入重定向,从文件中读入
freopen("out.txt", "w", stdout); //输出重定向,输出到文件中去
int a;
double b;
string s;
cin>>a>>b>>s;
cout<<s<<"##"<<a<<"##"<<b<<endl;
return 0;
}
运行结果为:
**2. 当然在oj上提交程序时,将一些用于调试的输出语句删除也是很麻烦的,于是可以使用脚手架来方便完成
#include<bits/stdc++.h>
using namespace std;
#define DEBUG 1 //调试使用,提交时改为0
int main(){
freopen("in.txt", "r", stdin); //输入重定向,从文件中读入
#if DEBUG //这就是脚手架框架
freopen("out.txt", "w", stdout); //输出重定向,输出到文件中去
#endif
int a;
double b;
string s;
cin>>a>>b>>s;
cout<<s<<"##"<<a<<"##"<<b<<endl;
return 0;
}
运行结果:
1、当宏定义DEBUG为1时,脚手架编译时不会删除,因此会从文件中读入,并读出到文件中去:
2、当宏定义DEBUG为0时,程序编译时会将脚手架删除,因此会从文件中读入,并输出到屏幕上:
一、c和c++的基本知识学习
1、scanf函数的用法(注意:scanf和printf与cin和cout最好不要混用,否则可能会出现一些神奇的bug)
2、printf函数的用法
3、printf函数的三种输出格式
- (1)%md的用法:
int a = 123;
printf("%5d", a);
输出结果为:
123 //注意:123前面是有2个空格的
- (2)%0md的用法:
int a = 123;
printf(%05d", a);
输出结果为:
00123 //右对齐,并以0填充
- (3)%.mf的用法:
double a = 123.456789;
printf("%.2f\n", a);
printf("%.4f\n", a);
输出结果为:
123.45 //保留2位小数
123.4567 //保留4位小数
4、getchar()和putchar()函数的用法
getchar()和putchar()是分别输入和输出单个字符
并且getchar()能够输入’\n’即换行符
用getchar()函数读走换行符’\n’:
#include <bits/stdc++.h>
using namespace std;
const int MAX = 51;
char str[MAX];
int main(){
int n;
cin>>n;
getchar(); //需要把之前的换行符读掉
while(n--){
cin.getline(str, MAX);
printf("%s\n", str);
}
return 0;
}
*小技巧:*用scanf()函数本身读走换行符’\n’
#include <bits/stdc++.h>
using namespace std;
const int MAX = 51;
char str[MAX];
int main(){
int n;
scanf("%d%*c", &n); // 利用 %*c 处理掉换行
while(n--){
cin.getline(str, MAX);
printf("%s\n", str);
}
return 0;
}
利用%*c输入格式来处理换行,含义是输入一个字符、但是这个字符不存储到变量中。
5、gets()(已过时)、puts()、gets_s()、fgets()的用法
# 现在的c和c++标准已经将gets()函数给废除了,因为它不安全,而改用了以下方法:
char str[MAX_LEN]; //定义字符数组
1、只c++可用:
cin.getline(str, MAX_LEN);
2、只c可用:
gets_s(str, MAX_LEN);
3、c和c++均可用:
scanf("%[^\n]", str); //这种方式不会把碰到的换行符处理掉
4、c和c++均可用:
fgets(str, MAX_LEN, stdin);
str[strcspn(str, "\n")] = '\0'; //将换行符替换掉
示例如下:
#include <bits/stdc++.h>
int main(){
int n;
char str[10];
scanf("%d", &n);
getchar(); //将换行符去掉
gets(str);
puts(str);
return 0;
}
输入:
2
hello world!
输出:
hello world!
6、math函数的使用
需要包含头文件#include <math.h>或者#include < cmath >
- fabs(double x):用于求绝对值
- floor(double x):用于向下取整
- ceil(double x):用于向上取整
- pow(double r, double p):用于求rp
- sqrt(double x):用于求x的算术平方根
- log(double x):用于求以自然对数为底的对数,如果要求任意底数的对数,需要换底公式logab = logeb / logea
- sin(double x)、cos(double x)、tan(double x):三角函数
- asin(double x)、acos(double x)、atan(double x):反三角函数
- round(double x):用于将x四舍五入,返回值也是double类型
7、数组使用时的注意(全局数组)
8、memset函数,对数组中每个元素赋相同的值(与fill()函数相似)
9、string.h头文件中的一些函数(c中字符串的一些函数)
char str[MAX_LEN], str2[MAX_LEN]; //定义字符数组
1. strlen(str); //得到字符数组中第一个\0前的字符的个数
2. strcmp(str, str2); //str < str2返回一个负数,str = str2返回0, str > str2返回一个正数
3. strcpy(str, str2); //把str2复制给str
4. strcat(str, str2); //把str2拼接到str后面
10、sscanf()和sprintf()函数的使用
可以将scanf()和printf()理解成:
scanf(screen, “%d”, &n);
printf(screen, “%d”, n);
它们以屏幕为目标进行输入输出
再来看sscanf()和sprintf(),它们是:
sscanf(str, “%d”, &n);
sprintf(str, “%d”, n);
以字符数组str为目标进行输入输出,即对str进行操作,不从屏幕进行输入输出
第二、第三个参数的操作和scanf和printf的操作相同
11、结构体的新认识(构造函数)
1. 结构体的定义:
# 定义一个表示坐标的结构体
struct Point{
int x;
int y;
};
2. 结构体的默认构造函数(c++特有):
# 每个结构体都有一个默认构造函数,是不可见的
# 上面定义的结构体其实是这样的
struct Point{
int x;
int y;
Point(){}
};
# 小tips:
# 构造函数没有返回值,可以有形参
# 构造函数名必须和结构体名相同
# 构造函数形参名不能和结构体内变量名相同
3. 结构体构造函数的重载(c++特有):
# 可以自己定义构造函数,且可重载,只要参数个数和类型不完全相同即可
struct Point{
int x;
int y;
Point(){}
Point(_x, _y):x(_x), y(_y){}
/*也可以这样写
Point(_x, _y){
x = _x;
y = _y;
}
*/
};
12、cin(读取整行)和cout(控制精度)的使用(这里推荐使用c的输入输出):
1. cin.getline()和getline()的用法区别(读取整行):
// cin.getline()是需要以字符数组作为参数
char str[10];
cin.getline(str, 10);
// getline()是需要以string容器作为参数
string str;
getline(cin, str);
2. cout控制精度的输出:
//程序简写
#include <iomanip>
cout<<setiosflags(ios::fixed)<<setprecision(2)<<123.4567<<endl;
输出结果为:
123.45
13、浮点数的比较(有误差)
1. 浮点数存储的误差
浮点型数据的存储结果:
double pi = 3.14;
pi可能在计算机中存储为3.1400000000001
或者3.1399999999999
2. c和c++中的==
==的操作是完全相同才能判定为true
像上面的数据,用==进行比较,就会判为false
3. 极小数eps,用来表示误差可接受的范围
经验表明,eps取10^-8^是个合适的数字
所以可以这样比较两个浮点数的大小:
坐标轴如下:
-------|-----0-----|----------
小于 -eps 等于 eps 大于
const double eps = 1e-8;
//等于的实现:
#define equ(a, b) ((fabs((a) - (b))) < (eps))
//大于的实现:
#define equ(a, b) (((a) - (b)) > (eps))
//小于的实现:
#define equ(a, b) (((a) - (b)) < (-eps))
//大于等于的实现:
#define equ(a, b) (((a) - (b)) > (-eps))
//小于等于的实现:
#define equ(a, b) (((a) - (b)) < (eps))
14、多点测试——数据的3种输入方法
1. while···EOF型:适用读文件末尾
#读文件末尾,不需要其他操作
#读黑框框,需要按ctrl+z组合键来手动触发EOF
while(scanf("%d", &n) != EOF){
}
//读字符串
while(gets(str) != NULL){
}
2. while···break型
while(scanf("%d%d", &a, &b) != EOF){
if(a == 0 && b == 0) break;
}
#或者可以这样
while(scanf("%d%d", &a, &b) , a || b){
}
#这是当a与b有一个不为0时就继续循环
3. while(T–)型
int T;
scanf("%d", &T);
while(T--){
}
15、变量的取值范围
16、字符串转换为int型变量的方法
- c++中推荐——用stoi()方法,参数为string对象
#include <iostream>
#include <string>
using namespace std;
int main(){
string str = "12345";
int a = stoi(str);
cout<<a;
return 0;
}
- c中推荐——用atio()方法,参数为字符数组
#include <iostream>
#include <string>
using namespace std;
int main(){
string str = "12345";
char* c = str.c_str(); //转换为字符数组
int a = atoi(c);
cout<<a;
return 0;
}
17、与字符有关的isalpha、isalnum、islower、isupper函数详解
- int isalpha(char)函数用来判断一个字符是否为字母,如果是字母则返回非零,否则返回零
- int isalnum(char)函数用来判断一个字符是否为数字或字母,是则输出非零,否则输出零
- int islower(char)函数用来判断一个字符是否为小写字母
- int ispuuer(char)函数用来判断一个字符是否为大写字母
18、如果程序输入数据的个数无法确定,用istringstream将会非常方便
注意:istringstream也是根据空格来分割数据的
#include <bits/stdc++.h>
// #include <sstream> //istringstream需要包含这个库
using namespace std;
int main() {
string str;
getline(cin, str);
istringstream ss(str); //构造一个对象
int a;
double f;
string s;
ss>>a; //从ss中读取一个int型数据
ss>>f;
ss>>s;
cout<<s<<"###"<<f<<"###"<<a<<endl;
return 0;
}
输入数据:
1234 123.456 abcde
输出为:
abcde###123.456###1234
二、一些简单算法实现
1、冒泡排序
#include <bits/stdc++.h> //万能头文件
using namespace std;
int main(){
//输入数据
int n;
scanf("%d", &n);
int a[n];
for(int i = 0; i < n; i++){
scanf("%d", &a[i]); //输入数据
}
//冒泡排序算法主体
bool flag = true;
while(flag){
flag = false;
for(int i = 0; i < n-1; i++){
if(a[i] > a[i+1]){
swap(a[i], a[i+1]);
flag = true;
}
}
}
//输出排序结果
for(int i = 0; i < n-1; i++){
printf("%d ", a[i]);
}
printf("%d", a[n-1]);
return 0;
}
2、选择排序
#include<bits/stdc++.h>
using namespace std;
void selectSort(int a[], int n);
int main(){
//输入数据
int n;
scanf("%d", &n);
int a[n];
for(int i = 0; i < n; i++){
scanf("%d", &a[i]);
}
//选择排序
selectSort(a, n);
//输出排序结果
for(int i = 0; i < n; i++){
printf("%d ", a[i]);
}
return 0;
}
void selectSort(int a[], int n){
int max = 0;
for(int i = 0; i < n; i++){
max = i;
for(int j = i; j < n; j++){
if(a[j] > a[max]){
max = j;
}
}
swap(a[i], a[max]);
}
}
3、插入排序
#include<bits/stdc++.h>
using namespace std;
void insertSort(int a[], int n);
int main(){
//输入数据
int n;
scanf("%d", &n);
int a[n];
for(int i = 0; i < n; i++){
scanf("%d", &a[i]);
}
//插入排序
insertSort(a, n);
//输出排序结果
for(int i = 0; i < n; i++){
printf("%d ", a[i]);
}
return 0;
}
void insertSort(int a[], int n){
//升序排序
for(int i = 1; i < n; i++){
for(int j = 0; j < i; j++){
if(a[i] < a[j]){
int temp = a[i];
for(int k = i; k > j; k--){
a[k] = a[k-1];
}
a[j] = temp;
}
}
}
}
三、纠错心得
1、变量转换问题(这个错误很难发现,需要强制类型转换)
## 错误样例
int a = 3, b = 2;
double c = a / b;
long long d = a*b; //这里也是同样的错误,当a、b的值过大时,得到的结果d是溢出的,如a=b=1000000,则结果d为-727379968
cout<<c;
### 得到的结果是1
### 原因是a/b先是计算的结果为int型,然后转换成double型
## 正确样例
int a = 3, b = 2;
double c = a*1.0 / b;
## 或者
double c = (double)a / b; //强转
long long d = (long long)a*b;
cout<<c;
### 得到的结果是1.5
### 原因是a/b直接是按double型计算,所以结果也直接为double型