C++算法之旅、04 基础篇 | 第一章 基础算法
2023年8月23日
#include <头文件>
cstdio
有两个函数 printf,scanf 用于输出和输入
int : %d
float : %f
double : %lf
char : %c
long long : %lld
iostream
有 cin 读入,cout 输出
using namespace std;
使用了std命名空间,cin、cout定义在该命名空间中,不引入空间会找不到导致出错
int main()
函数执行入口
基本类型
a+b
⭐所有 cout、cin 都能用 scanf、printf 替换,但反过来,由于cout、cin效率可能较低会导致超时
⭐ printf %c 会读入空格跟回车,需要手动加一个空格跟回车;cin 不会读入空格跟回车
#include <iostream>
using namespace std;
int main() {
int a, b; // ^ 定义两个变量
cin >> a >> b; // ^ 输入(把cin里的值拿到a里面去、b里面去)
cout << a + b << endl; // ^ 输出
return 0;
}
#include <cstdio>
#include <iostream>
using namespace std;
int main() {
int a, b;
scanf("%d%d", &a, &b);
printf("%d %d\n", a + b, a * b);
return 0;
}
%
C++ 中取模看前面的数,如果负数则负数;而在Lua中取模的结果不可能为负数
cout << 5 % 2 << endl; // 1
cout << -5 % 2 << endl; // -1
类型转换
float , double 转 int 下取整 ;int 转 char 整数取ASCII表;
char类型跟int类型运算,结果是int类型;int类型与float型运算会变成float型
隐性类型转换:把精度低的类型转换成另外一个精度高的类型
604
题目要求保留四位小数,考虑到float有效数字位只有6~7位,可能出现精度不准确,故改用 double(未来题目看到浮点数也优先使用double)
#include <cstdio>
int main() {
double r;
scanf("%lf", &r);
printf("A=%.4lf", 3.14159 * r * r);
return 0;
}
653
#include <cstdio>
int main() {
int m;
scanf("%d", &m);
printf("%d\n", m);
printf("%d nota(s) de R$ 100,00\n", m / 100);
m %= 100;
printf("%d nota(s) de R$ 50,00\n", m / 50);
m %= 50;
printf("%d nota(s) de R$ 20,00\n", m / 20);
m %= 20;
printf("%d nota(s) de R$ 10,00\n", m / 10);
m %= 10;
printf("%d nota(s) de R$ 5,00\n", m / 5);
m %= 5;
printf("%d nota(s) de R$ 2,00\n", m / 2);
m %= 2;
printf("%d nota(s) de R$ 1,00\n", m / 1);
return 0;
}
617
由于隐性类型转换,结果为double,用%d表示数字不正确,需要用%lf
#include <cstdio>
int main() {
int a;
scanf("%d", &a);
printf("%.0lf minutos", a / 30.0 * 60);
return 0;
}
618
看数据范围,最大可以到 10 ^ 14 次方远远大于 2 ^ 31,需要改用 double(8字节)
#include <cstdio>
int main() {
long a, b;
scanf("%ld%ld", &a, &b);
printf("%.3lf", a * b / 12.0);
return 0;
}
656
一开始想用两个整数读整数跟小数(%d.%d),但小数位输入可能是 .1 或 .01,读出来都是1,不能处理,只能按浮点数来读
#include <cstdio>
int main() {
double n;
int m;
scanf("%lf", &n);
m = (int)(n * 100);
printf("NOTAS:\n");
printf("%d nota(s) de R$ 100.00\n", m / 10000);
m %= 10000;
printf("%d nota(s) de R$ 50.00\n", m / 5000);
m %= 5000;
printf("%d nota(s) de R$ 20.00\n", m / 2000);
m %= 2000;
printf("%d nota(s) de R$ 10.00\n", m / 1000);
m %= 1000;
printf("%d nota(s) de R$ 5.00\n", m / 500);
m %= 500;
printf("%d nota(s) de R$ 2.00\n", m / 200);
m %= 200;
printf("MOEDAS:\n");
printf("%d moeda(s) de R$ 1.00\n", m / 100);
m %= 100;
printf("%d moeda(s) de R$ 0.50\n", m / 50);
m %= 50;
printf("%d moeda(s) de R$ 0.25\n", m / 25);
m %= 25;
printf("%d moeda(s) de R$ 0.10\n", m / 10);
m %= 10;
printf("%d moeda(s) de R$ 0.05\n", m / 5);
m %= 5;
printf("%d moeda(s) de R$ 0.01\n", m / 1);
return 0;
}
2023年8月24日
字符串的输入
#include <iostream>
using namespace std;
int main() {
string a, b, c;
cin >> a >> b >> c;
666
多注意题目意思,如果...否则...,而不是如果、否则两种情况都输出
#include <cstdio>
#include <iostream>
using namespace std;
int main() {
double a, b, c, tmp;
cin >> a >> b >> c;
if (b >= a && b >= c) {
tmp = a;
a = b;
b = tmp;
} else if (c >= a && c >= b) {
tmp = c;
c = a;
a = tmp;
}
if (a >= b + c)
cout << "NAO FORMA TRIANGULO" << endl;
else {
if (a * a == b * b + c * c) cout << "TRIANGULO RETANGULO" << endl;
if (a * a > b * b + c * c) cout << "TRIANGULO OBTUSANGULO" << endl;
if (a * a < b * b + c * c) cout << "TRIANGULO ACUTANGULO" << endl;
if (a == b && a == c && b == c) cout << "TRIANGULO EQUILATERO" << endl;
if (a == b && c != b || a == c && b != c || b == c && a != b)
cout << "TRIANGULO ISOSCELES" << endl;
}
return 0;
}
658
容易忘记最后 2 * a
的括号,导致先除后乘
#include <cmath>
#include <cstdio>
int main() {
double a, b, c;
scanf("%lf%lf%lf", &a, &b, &c);
if (b * b - 4 * a * c < 0 || a == 0) {
printf("Impossivel calcular");
} else {
printf("R1 = %.5lf\n", (-b + sqrt(b * b - 4 * a * c)) / (2 * a));
printf("R2 = %.5lf", (-b - sqrt(b * b - 4 * a * c)) / (2 * a));
}
return 0;
}
2023年8月25日
读入个数未知
读入的个数未知时,可以用while(cin >> x)
或while(scanf("%d", &x) != -1)
或while(~scanf("%d", &x))
来输入。
如果输入的最后一个为0且该0不处理,输入语句可以用while(cin >> x && x)
或while(cin >> x, x)
,逗号表达式取最后一个值。
714
algorithm 函数库的使用 , % 相关的概念
#include <algorithm>
#include <cstdio>
#include <iostream>
using namespace std;
int main() {
int x, y;
cin >> x >> y;
if (x > y) {
swap(x, y);
}
int total = 0;
for (x++; x < y; x++) {
if (abs(x) % 2 == 1) total += x;
}
cout << total;
return 0;
}
725
设一个公约数 d 那么另一个公约数 x / d , d <= x/d 得 d <= 根号x;另外还要考虑1的特殊情况。
#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;
int main() {
int n, x;
cin >> n;
while (cin >> x, n--) {
int sum = 0;
for (int i = 1; i * i <= x; i++) {
if (x % i == 0) {
if (i < x) sum += i;
if (x / i != i && x / i < x) sum += x / i;
}
}
if (sum == x)
cout << x << " is perfect" << endl;
else
cout << x << " is not perfect" << endl;
}
return 0;
}
2023年8月26日
reverse
在 alogorithm
库中,用于翻转数组。涉及题目(翻转整个,然后两边各自再翻转)
memset
在 cstring
库中,用于数组的初始化,速度比循环初始化要快。参数是数组指针,值(字节),长度(字节)
memset(a,-1,sizeof a)
memcpy
在 cstring
库中,用于复制数组。参数是目标数组指针,被复制数组指针,长度(字节)
753
技巧,需要看各个格子距离上下左右边的最短距离。下面两道也是技巧性题目
#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;
int main() {
int n;
while (cin >> n, n) {
for (int i = 1; i <= n; i++) {
for (int k = 1; k <= n; k++) {
cout << min(min(i, k), min(n + 1 - k, n + 1 - i)) << " ";
}
cout << endl;
}
cout << endl;
}
return 0;
}
754
#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;
int main() {
int n;
while (cin >> n, n) {
for (int i = 1; i <= n; i++) {
for (int k = 1; k <= n; k++) {
cout << max(i - k + 1, k - i + 1) << " ";
}
cout << endl;
}
cout << endl;
}
return 0;
}
755
#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;
int main() {
long n, m[31] = {1};
for (int i = 1; i < 31; i++) {
m[i] = m[i - 1] * 2;
}
while (cin >> n, n) {
for (int i = 1; i <= n; i++) {
for (int k = 1; k <= n; k++) {
cout << m[i + k - 2] << " ";
}
cout << endl;
}
cout << endl;
}
return 0;
}
756 ⭐
涉及到状态的转变,逻辑题
解题法-1 传统逻辑解题
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int main() {
int a[101][101];
memset(a, 0, sizeof a);
int n, m;
cin >> n >> m;
int i = 0, k = 0;
int c = 1;
int mode = 1;
while (i - 1 >= 0 && a[i - 1][k] == 0 || i + 1 < n && a[i + 1][k] == 0 ||
k - 1 >= 0 && a[i][k - 1] == 0 || k + 1 < n && a[i][k + 1] == 0 ||
a[i][k] == 0) {
if (mode == 1) {
a[i][k] = c++;
if (k + 1 == m || a[i][k + 1] != 0) {
mode = 2;
i++;
} else
k++;
} else if (mode == 2) {
a[i][k] = c++;
if (i + 1 == n || a[i + 1][k] != 0) {
mode = 3;
k--;
} else
i++;
} else if (mode == 3) {
a[i][k] = c++;
if (k - 1 < 0 || a[i][k - 1] != 0) {
mode = 4;
i--;
} else
k--;
} else {
a[i][k] = c++;
if (i - 1 < 0 || a[i - 1][k] != 0) {
mode = 1;
k++;
} else
i--;
}
}
for (int i = 0; i < n; i++) {
for (int k = 0; k < m; k++) {
cout << a[i][k] << " ";
}
cout << endl;
}
return 0;
}
解题法-2 将移动方式保存到数组中
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int res[101][101];
int main() {
int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0};
int m, n;
cin >> n >> m;
int mode = 0;
// 每一个点都会走过一遍,算次数即可
for (int k = 1, x = 0, y = 0; k <= m * n; k++) {
res[x][y] = k;
// 先看看新坐标
int a = x + dx[mode];
int b = y + dy[mode];
// 如果新坐标不行就更新状态并重新计算坐标
if (a < 0 || a >= n || b < 0 || b >= m || res[a][b]) {
mode = (mode + 1) % 4;
a = x + dx[mode];
b = y + dy[mode];
}
x = a;
y = b;
}
for (int i = 0; i < n; i++) {
for (int k = 0; k < m; k++) {
cout << res[i][k] << " ";
}
cout << endl;
}
return 0;
}
2023年8月27日
类型 | 函数 | 输入 | 读入 |
---|---|---|---|
string s | getline(cin,s) | ab cd | ab cd |
char s[100] | cin.getline(s,100) | ab cd | ab cd |
char s[100] | fgets(s,100,stdin)但是要注意最后会读入一个回车 | ab cd | ab cd\n |
fgets回车去除方法
if(s[strlen(s)-1]=='\n')
s[strlen(s)-1]=0;
fgets
默认读入字符串遇到空格就会停止。
fgets用于读入一整行,参数为字符数组地址、最大读多少字符、从哪读,会读入最后的回车符
fgets(s,100000,stdin);
cin
cin.getline用于读入一整行,参数为字符数组地址、最大读多少字符
cin.getline(s,1000);
getline
getline用于string类型读入一整行
getline(cin,s);
puts
puts 输出字符数组,包括换行符
puts(s);
cstring 库函数
库里有 strlen
,strcmp
、strcpy
与 memcpy
类似
strlen(s); // 只计算字符串的元素,\0不计入其中
strcmp(a,b); // 字典序比较,小于返回-1(可能其他负数),等于返回0,大于返回1(可能其他正数)
strcpy(a,b); // 把b复制给a
两层循环优化
在这种循环下,每次循环 strlen(s)
都要执行一遍,是两层循环。
for (int i = 0; i < strlen(s) ; i++)
改成
for (int i = 0, len=strlen(s) ; i < len; i++)
或直接
for (int i = 0; s[i]; i++)
80%的情况用string处理
c++ string 的简单用法_c++ string sprintf_ddancoo的博客-CSDN博客
string s1; // 默认的空字符串
string s2 = s1; // s2是s1的副本
string s3 = "hiya"; // s3是该字符串字面值的副本
string s4(10,'c'); // s4的内容是 ccccc
cin >> s1 >> s2;
// string 不能用scanf读但可以用printf输出
printf("%s\n",s1.c_str());
cout << s1 << s2;
cout << s1.empty() << endl; // 布尔值返回 1
cout << s3.empty() << endl; // 布尔值返回 0
cout << s3.size() << endl; // 数组长度,O(1)复杂度
// 支持比较运算符,相加
string s3 = s3 + s4;
s3 += s4;
/* 当把string对象和字符字面值及字符串字面值混在一条语句中使用时,
必须确保每个加法运算符的两侧的运算对象至少有一个是string*/
s3 = s3 + " is great!" + '!'; // 这里不能用 +=
// 加法运算符两边必须有一个 string 类型
// 遍历 string
for (char c : s) cout << c << endl; // 等价于 for 循环体里 char c = str[i]
// 引用符号(可以改变 c 的值)
for (char &c : s)
// 让编译器去猜类型
auto s = "Hello World!" // char[]
for (auto c : s)
裁剪字符串
使用 string
类型的 substr
函数
cout << a.substr(0, max_index + 1) + b + a.substr(max_index + 1)
<< endl;
764 输出字符串 🍔
简单,记得给b加结束符
c++string的输出异常_c++ mexfunction string 没有输出-CSDN博客
其中的s1+='a',就可以在s1初始为空白的情况下往后追加字符,并且扩充他的size,所以可以正确输出
以前使用string的时候只是把它当做字符数组来用,却没有去深入了解string这个东西,才会导致今天弄了这么大的麻烦,以后要长记性
#include <cstdio>
#include <iostream>
using namespace std;
int main() {
string a, b;
getline(cin, a);
for (int i = 0; i < a.size(); i++) {
b += a[i] + a[(i + 1) % a.size()];
//cout << b[i];
}
// 疑惑
b = b + '\0';
cout << b;
return 0;
}
770 sstream 库
不用库的话,判断空格位置,比较麻烦
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int main() {
string s, a, b, word, res;
getline(cin, s);
cin >> a >> b;
int index = 0;
for (int i = 0; i <= s.size(); i++) {
if (s[i] == ' ' || s[i] == 0) {
if (strcmp(word.c_str(), a.c_str()) == 0) {
res = res + b + ' ';
} else
res = res + word + ' ';
word = "";
} else
word = word + s[i];
}
cout << res;
return 0;
}
用库的话,把读入的字符串再当成一个流(类似于split操作了)
#include <cstdio>
#include <cstring>
#include <iostream>
#include <sstream>
using namespace std;
int main() {
string s, a, b, word, res;
getline(cin, s);
cin >> a >> b;
stringstream ssin(s);
string str;
while (ssin >> str)
if (str == a)
cout << b << ' ';
else
cout << str << ' ';
cout << res;
return 0;
}
771 ⭐ 第一类双指针算法
771. 字符串中最长的连续出现的字符 - AcWing题库
#include <cstdio>
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
while (n--) {
int max_count = 0;
char max_char;
string a;
cin >> a;
for (int i = 0; i < a.size(); i++) {
int j = i;
while (j < a.size() && a[i] == a[j]) j++;
int diff = j - i;
if (max_count < diff) {
max_char = a[i];
max_count = diff;
}
// IMPORTANT
i = j - 1; // i 还会再++一次
}
cout << max_char << " " << max_count << endl;
}
return 0;
}
774 back()、pop_back() ⭐
#include <cstdio>
#include <iostream>
using namespace std;
int main() {
string word, s_max;
// s.back() 获取最后一个字符 s.pop_back() 去掉最后一个字符
while (cin >> word) {
if (word.back() == '.') word.pop_back();
if (word.size() > s_max.size()) s_max = word;
}
cout << s_max;
return 0;
}
这里还有一个思想是按词处理而不是先读入一整串再sstream,类似下面的
#include <cstdio>
#include <iostream>
using namespace std;
int main() {
string res, word;
while (cin >> word) {
res = word + " " + res;
}
cout << res;
return 0;
}
776 ⭐
#include <algorithm>
#include <cstdio>
#include <iostream>
using namespace std;
int main() {
string a, b;
cin >> a >> b;
if (b.size() > a.size()) swap(a, b);
for (int i = 0; i < a.size(); i++) {
a = a.substr(1) + a[0];
int k = 0;
// 第一类双指针
for (int i = 0; i + b.size() <= a.size(); i++) {
while (k < b.size() && a[i + k] == b[k]) k++;
if (k == b.size()) {
puts("true");
return 0;
} else
k = 0;
}
}
puts("false");
return 0;
}
2023年8月30日
777
#include <cstdio>
#include <iostream>
using namespace std;
int main() {
string s;
while (cin >> s, s != ".") {
for (int i = 1; i <= s.size(); i++) {
if (s.size() % i != 0) continue;
string a = s.substr(0, i);
string res;
int times = s.size() / i;
// 拼合字符串然后做比较
for (int i = 1; i <= times; i++) {
res += a;
}
if (res == s) {
cout << times << endl;
break;
}
}
}
return 0;
}
也可以通过字符串移位的贪心算法实现
#include <cstdio>
#include <iostream>
using namespace std;
int main() {
string s;
while (cin >> s, s != ".") {
string res = s;
for (int i = 1; i <= res.size(); i++) {
res = res.substr(1) + res[0];
if (res == s) {
cout << res.size() / i << endl;
break;
}
}
}
return 0;
}
778
如何处理输入的问题
string s, s1, s2;
char c;
while (cin >> c, c != ',') s += c;
while (cin >> c, c != ',') s1 += c;
while (cin >> c) s2 += c;
// 或
string s, s1, s2;
getline(cin, s, ',');
getline(cin, s1, ',');
getline(cin, s2);
s.find(); // 在字符串s上从前往后找
s.rfind(); // 从后往前
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
string s, s1, s2;
getline(cin, s, ',');
getline(cin, s1, ',');
getline(cin, s2);
int temp = s.find(s1);
int ans = s.rfind(s2);
if (temp == -1 || ans == -1)
{
cout << -1 << endl;
return 0;
}
int len = ans - temp - s1.size();
if (len < 0) cout << -1 << endl;
else cout << len << endl;
return 0;
}
这题没难度
#include<bits/stdc++.h>
using namespace std;
int n;
string a[209];
int main(){
while(cin>>n){
if(n==0) return 0;
for(int i=0;i<n;++i) cin>>a[i],reverse(a[i].begin(),a[i].end());
sort(a,a+n);
string v="";
for(int i=0;i<a[0].length();++i){
if(a[0][i]==a[n-1][i]) v=a[0][i]+v;
else break;
}
cout<<v<<"\n";
}
}
函数默认参数
默认值需要是后面连续的几个参数,或全部都有默认值
void foo(int a,int b = 5,int c = 10)
sizeof 数组问题
main函数显示的是数组长度( 40字节),foo函数显示的是指针的长度(64位8字节)。
void foo(int b[]){
cout << sizeof b;
}
int main(){
int a[10];
cout << sizeof a << endl;
foo(a);
return 0;
}
/*
40
8
*/
inline
加在函数定义前,相当于函数展开,适用于函数调用次数小的情况。对递归函数无效
823 简单递归问题
#include <cstdio>
#include <iostream>
using namespace std;
const int N = 10;
int n;
int pl(int index, int a[], bool h[]) {
if (index == n) {
for (int i = 0; i < n; i++) {
cout << a[i] << " ";
}
cout << endl;
} else
for (int i = 1; i <= n; i++) {
if (!h[i]) {
h[i] = 1;
a[index] = i;
pl(index + 1, a, h);
h[i] = 0;
}
}
}
int main() {
cin >> n;
int a[10] = {0};
bool h[10] = {0};
pl(0, a, h);
return 0;
}
指针、引用
int a = 3;
int* p = &a; // 指针
int& p2 = a; // 引用、别名
链表与指针 ⭐
#include <iostream>
using namespace std;
struct Node {
int val;
Node* next;
Node(int _val) : val(_val), next(NULL) {}
};
int main() {
Node* p = new Node(1); // ^ 加new返回地址,不加new返回变量
auto p2 = new Node(2);
auto p3 = new Node(3);
p->next = p2; // ^ 规定:如果p是指针用->、是变量用.
p2->next = p3;
// IMPORTANT 头结点:第一个结点的地址而不是值
Node* head = p;
for (Node* i = head; i; i = i->next) {
cout << i->val << endl;
}
// 添加节点
Node* u = new Node(4);
u->next = p;
head = u;
for (Node* i = head; i; i = i->next) {
cout << i->val << endl;
}
// ^ 链表删除是指在遍历时遍历不到这个点
head->next = head->next->next;
for (Node* i = head; i; i = i->next) {
cout << i->val << endl;
}
return 0;
}
84
条件表达式脑筋急转弯
class Solution {
public:
int getSum(int n) {
int res = n;
n > 0 && (res += getSum(n - 1));
return res;
}
};
28
class Solution {
public:
void deleteNode(ListNode* node) {
node->val = node->next->val; // 伪装成下一个点
node->next = node->next->next; // 将node删除(访问不到)
// *(node) = *(node->next);
}
};
36 ⭐
二路归并,赋值从右往左
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* merge(ListNode* l1, ListNode* l2) {
auto dummy = new ListNode(-1), tail = dummy;
while (l1 && l2) {
if (l1->val < l2->val) {
tail = tail->next = l1;
l1 = l1->next;
} else {
tail = tail->next = l2;
l2 = l2->next;
}
}
if (l1)
tail->next = l1;
else
tail->next = l2;
return dummy->next;
}
};
87 ⭐
class Solution {
public:
int strToInt(string str) {
int i = 0, flag = 1;
while (i < str.size() && str[i] == ' ') i++;
if (i == str.size())
return 0;
else if (str[i] == '-') {
flag = -1;
i++;
} else if (str[i] == '+') {
flag = 1;
i++;
}
long long sum = 0;
while (i < str.size() && str[i] >= '0' && str[i] <= '9') {
sum *= 10;
sum += str[i] - '0';
i++;
if (sum * flag > INT_MAX) {
return INT_MAX;
} else if (sum * flag < INT_MIN)
return INT_MIN;
}
sum = sum * flag;
return (int)sum;
}
};
for (; i < str.size(); i++) {
if (str[i] != ' ') break;
}
for 循环均可修改为while减少代码量 ⭐
while (i < str.size() && str[i] == ' ') i++;
35 ⭐
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if (!head || !head->next) return head; // ^ 特殊情况
auto A = head;
auto B = head->next;
A->next = NULL;
while (B) {
auto C = B->next;
B->next = A;
A = B, B = C;
}
return A;
}
};
递归写法
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if (!head || !head->next) return head; // ^ 特殊情况
auto tail = reverseList(head->next);
head->next->next = head;
head->next = NULL;
return tail;
}
};
66 ⭐⭐
class Solution {
public:
ListNode *findFirstCommonNode(ListNode *headA, ListNode *headB) {
auto A = headA, B = headB;
while (A != B) {
if (A)
A = A->next;
else
A = headB;
if (B)
B = B->next;
else
B = headA;
}
return A;
}
};
29
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* deleteDuplication(ListNode* head) {
if (!head || !head->next) return head;
auto tmp = new ListNode(-1);
tmp->next = head;
auto A = tmp, B = head;
while (B && B->next) {
auto C = B->next;
if (C->val == B->val) {
while (C && C->val == B->val) C = C->next;
A->next = C;
B = C;
} else {
A = B;
B = B->next;
}
}
return tmp->next;
}
};
因为B可以由A推导出,又可以改为
class Solution {
public:
ListNode* deleteDuplication(ListNode* head) {
auto tmp = new ListNode(-1);
tmp->next = head;
auto A = tmp;
while (A->next) {
auto C = A->next;
while (C->next && A->next->val == C->next->val) C = C->next;
if (C != A->next)
A->next = C->next;
else
A = A->next;
}
return tmp->next;
}
};
vector
int q[100000][100000]
需要大量空间,用可变长数组可以节省很多空间。结尾插入O(1),开头插入O(n),自带比较,按字典序比
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> a({1, 2, 3});
vector<int> b[233];
struct Rec {
int x, y;
};
vector<Rec> c;
// a.size();
// a.empty();
// a.clear();
vector<int>::iterator it = a.begin();
// 当成指针来看,it 相当于访问 a[0], [begin,end)
// a.end() 最后位置的下一个位置
// *a.begin() 取值 :star:
for (int i = 0; i < a.size(); i++) {
}
for (auto i = a.begin(); i < a.end(); i++) {
}
for (int x : a) {
}
// a.front() 等价于 a[0] 等价于 *a.begin()
// a.back() 等价于 a[a.size()-1]
// star
// a.push_back(4); 在数组最后添加元素 O(1)
// a.pop_back(); 删除末尾元素
return 0;
}
queue
普通队列取队尾是 front(),优先队列取最大\最小是 top()
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
int main() {
queue<int> q;
queue<double> a;
struct Rec {
int x, y;
// 优先队列
// 大根堆,自定义结构体要重载小于号
// 小根堆,重载大于号
bool operator<(const Rec& t) const { return x < t.x; }
};
queue<Rec> c;
// ^ 优先队列,每次优先弹最大值,大根堆
priority_queue<int> a;
// ^ 小根堆
priority_queue<int, vector<int>, greater<int>> b;
priority_queue<pair<int, int>> b;
// 普通队列
a.push(3); // 队尾插入元素
a.pop(); // 队头弹出元素
a.front(); // 返回队头元素
a.back(); // 返回队尾元素
// 优先队列
b.push(1);
b.top(); // 取最大值
b.pop(); // 删除最大值
// IMPORTANT 队列、优先队列、栈没有clear函数
// 可以重新初始化
q = queue<int>();
return 0;
}
stack
#include <iostream>
#include <stack>
using namespace std;
int main() {
stack<int> stk;
stk.push(1);
stk.top();
stk.pop(); // 弹出但不返回
return 0;
}
deque
在队头队尾插入都是O(1)
#include <deque>
#include <iostream>
using namespace std;
int main() {
deque<int> a;
a.begin(), a.end();
a.front(), a.back();
a.push_back(1), a.push_front(1);
a[0];
a.pop_back(), a.pop_front();
a.clear();
return 0;
}
set
红黑树
#include <iostream>
#include <set>
using namespace std;
int main() {
set<int> a; // 元素不能重复
multiset<int> b; // 元素可以重复
set<int>::iterator it = a.begin();
it++;
it--; // 前驱、后继
++it, --it;
a.end();
a.insert(1);
a.find(1); // 返回迭代器,找不到返回 a.end() // 判断x在a中是否存在
// :star:
a.lower_bound(1); // IMPORTANT 找到大于等于x的最小的元素的迭代器
a.upper_bound(1); // 找到大于x的最小的元素的迭代器
a.count(1); // 存在x返回1,不存在返回0,如果是multiset则返回个数
a.erase(1); // 删除
//
struct Rec {
int x, y;
bool operator<(const Rec& t) const { return x < t.x; }
};
set<Rec> c; // size/empty/clear/ 也有迭代器
return 0;
}
map
#include <iostream>
#include <map>
#include <vector>
using namespace std;
int main() {
map<int, int> a;
a[1] = 2;
a[10000000] = 3;
a.insert({100, 1});
cout << a[10000000] << endl;
map<string, vector<int>> b;
b["test"] = vector<int>({1, 2, 3, 4});
b.insert({"t", {}});
b.find("t");
return 0;
}
unordered
unordered_set使用哈希表
#include <iostream>
#include <unordered_map>
#include <unordered_set>
#include <vector>
using namespace std;
int main() {
unordered_set<int> a; // 比set效率高些,但不支持二分
unordered_multiset<int> b; // 比set效率高些,但不支持二分
unordered_map<int, int> c; // 比map效率高些,但不支持二分
return 0;
}
bitset
很长的01二进制串
#include <bitset>
#include <iostream>
using namespace std;
int main() {
bitset<1000> a, b;
a[0] = 1;
a[1] = 1;
a.set(3); // 某位设为1
a.reset(3); // 某位设为0
// 支持位运算
a &= b;
return 0;
}
pair
#include <bitset>
#include <iostream>
using namespace std;
int main() {
pair<int, string> a;
a = {3, "test"};
cout << a.first << " " << a.second << endl;
// 老版本 make_pair(4,"abc")
// pair 支持比较,先比较first然后比较second
return 0;
}
与、或、取反、异或
& | ~ ^
常用二进制操作 ⭐
求某一位数字(如下求第三位数字)
int i = a >> 2 & 1;
返回最后一个1 ⭐
a & (~a + 1) // 0000001000
// 又因为 -a 等同 ~a+1
a & -a
常用库函数 ⭐
#include <algorithm>
#include <ctime>
#include <iostream>
#include <vector>
using namespace std;
struct Rec {
int x, y;
bool operator<(const Rec &t) const { return x < t.x; }
} rec[5];
bool cmp(int a, int b) { // a是否应该排在b的前面
return a < b;
}
bool cmp2(Rec a, Rec b) { // a是否应该排在b的前面
return a.x < b.x;
}
int main() {
// ^ reverse
vector<int> a({1, 2, 3, 4});
reverse(a.begin(), a.end());
for (int x : a) cout << x << ' ';
cout << endl;
int b[] = {1, 2, 2, 4, 5};
reverse(b, b + 5); // 第二个参数写数组最后一个位置的下一个
// ^ unique :star:
int m = unique(b, b + 5) - b; // 不同元素数量个数
int m = unique(a.begin(), a.end()) - a.begin(); // 不同元素数量个数 :star:
a.erase(unique(a.begin(), a.end()), a.end()); // 把没有用的部分删掉
// ^ random_shuffle
srand(time(0));
random_shuffle(a.begin(), a.end()); // 打乱数组
// ^ sort
sort(a.begin(), a.end()); // 从小到大
sort(a.begin(), a.end(), greater<int>()); // 从大到小 :star:
sort(a.begin(), a.end(), cmp);
for (int i = 0; i < 5; i++) {
rec[i].x = -i;
rec[i].y = i;
}
sort(rec, rec + 5, cmp2);
sort(rec, rec + 5); // 重载小于号
// ^ lower_bound/upper_bound
int *p = lower_bound(b, b + 5, 3); // 返回迭代器
int *p = upper_bound(b, b + 5, 3); // 返回迭代器
return 0;
}
范围遍历
比for循环快一点点
int cnt = 0;
for (int x : nums) {
if (x == k) cnt++;
}
68
class Solution {
public:
int getMissingNumber(vector<int>& nums) {
unordered_set<int> s;
for (int i = 0; i <= nums.size(); i++) s.insert(i);
for (auto x : nums) s.erase(x);
return *s.begin();
}
};
双指针法 ⭐ 32
32. 调整数组顺序使奇数位于偶数前面 - AcWing题库
class Solution {
public:
void reOrderArray(vector<int> &array) {
int i = 0, j = array.size() - 1;
while (i < j) {
while (i < j && array[i] % 2) i++;
while (i < j && array[j] % 2 == 0) j--;
if (i < j) swap(array[i], array[j]);
}
}
};
2023年8月31日
20
class MyQueue {
public:
/** Initialize your data structure here. */
stack<int> a, b;
MyQueue() {}
/** Push element x to the back of queue. */
void push(int x) { a.push(x); }
/** Removes the element from in front of queue and returns that element. */
int pop() {
while (a.size() > 1) b.push(a.top()), a.pop();
int t = a.top();
a.pop();
while (b.size()) a.push(b.top()), b.pop();
return t;
}
/** Get the front element. */
int peek() {
while (a.size() > 1) b.push(a.top()), a.pop();
int t = a.top();
while (b.size()) a.push(b.top()), b.pop();
return t;
}
/** Returns whether the queue is empty. */
bool empty() { return a.empty(); }
};
75
使用哈希表,只需要遍历一遍数组
class Solution {
public:
vector<int> findNumbersWithSum(vector<int>& nums, int target) {
unordered_set<int> s;
for (auto x : nums) {
if (s.count(target - x)) return {target - x, x};
s.insert(x);
}
}
};
51 next_permutation ⭐
返回一个序列的下一个序列,如果是最大了返回false
class Solution {
public:
vector<vector<int>> permutation(vector<int>& nums) {
sort(nums.begin(), nums.end());
vector<vector<int>> res;
do {
res.push_back(nums);
} while (next_permutation(nums.begin(), nums.end()));
return res;
}
};
26 lowbit
class Solution {
public:
int NumberOf1(int n) {
int res = 0;
for (int i = 0; i < 32; i++) {
if (n >> i & 1) res++;
}
return res;
}
};
class Solution {
public:
int NumberOf1(int n) {
int res = 0;
while (n) {
n -= n & -n;
res++;
}
return res;
}
};
第二种方法快一些
420 火星人 ⭐⭐
#include <algorithm>
#include <cstdio>
#include <iostream>
using namespace std;
const int N = 10010;
int n, m;
int q[N];
int main() {
cin >> n >> m;
for (int i = 0; i < n; i++) cin >> q[i];
while (m--) next_permutation(q, q + n);
for (int i = 0; i < n; i++) cout << q[i] << " ";
return 0;
}
#include <algorithm>
#include <cstdio>
#include <iostream>
using namespace std;
const int N = 10010;
int n, m;
int q[N];
int main() {
cin >> n >> m;
for (int i = 0; i < n; i++) cin >> q[i];
while (m--) {
int k = n - 1;
while (q[k - 1] > q[k]) k--;
int min_max = k;
for (int i = k; i < n; i++) {
if (q[min_max] > q[i] && q[i] > q[k - 1]) min_max = i;
}
swap(q[k - 1], q[min_max]);
reverse(q + k, q + n);
}
for (int i = 0; i < n; i++) cout << q[i] << " ";
return 0;
}
862
#include <algorithm>
#include <iostream>
using namespace std;
struct Data {
int x;
double y;
string z;
bool operator<(const Data &t) const { return x < t.x; }
} a[10010];
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) cin >> a[i].x >> a[i].y >> a[i].z;
sort(a, a + n);
for (int i = 0; i < n; i++) {
printf("%d %.2lf %s\n", a[i].x, a[i].y, a[i].z.c_str());
}
return 0;
}