算法笔记
本笔记的参考教材是《算法竞赛 从入门到进阶》,算法新手一枚,以后每天都把自己学习笔记分享到这里😃😃
第二章 计算的资源
复杂度
程序运行需要的资源是有限的,oj上的题目一般会对时间和空间有说明,例如:
- Time Limit:2000/1000ms
- Memory Limit:65536/65536KB
队员拿到题目的第一时间就是分析程序需要的 计算时间 和 存储空间。
这是一个for循环的例子,输出的是程序运行的时间
# include<bits/stdc++.h>
using namespace std;
int main(){
int i,k,n = 1e8;
clock_t start,end;
start = clock();
for(i=0;i<n;i++) k++;
end = clock();
cout << double(end-start) / CLOCKS_PER_SEC << endl;
}
输出结果:0.179s
当将循环次数改成1e9的时候,结果变为1.794s
不同排序算法的复杂度对比
Ques: 给定n个整数,请按从大到小的顺序输出其中前n大的数
- 输入:数据有两行,第一行有两个数m、n,第二行是m个整数 [-500000,500000]
- 输出:前m大的数
冒泡排序
# include<bits/stdc++.h>
using namespace std;
int n,m;
int a[10001];
# define swap(a,b) {int temp = a; a=b; b=temp;}
void bubble_sort(){
for(int i=1;i<=n-1;i++){
for(int j=1;j<=n-1;j++){
if(a[j]>a[j+1]){
swap(a[j],a[j+1]);
}
}
}
}
int main(){
while(~scanf("%d %d",&n,&m)){
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
bubble_sort();
for(int i = n;i>=n-m+1 ;i--){
if(i == n-m+1) printf("%d\n",a[i]);
else printf("%d ",a[i]);
}
}
return 0;
}
结果:

时间上,时间复杂度为O(n^2),因为swap有两层循环;空间上,不耗费额外空间,只有a数组占空间
快速排序
快速排序是基于分治法的排序算法,可直接用STL里面的sort()函数,将上面冒泡排序的代码中的 bubble_sort 换成 sort(a+1,a+n+1)就行。
算法的时间复杂度是O(n*log2 n)
哈希算法
哈希算法以空间换取时间,在输入数字t的时候,在a[500000+t]这个位置上记录a[500000+t]=1,输出的时候逐个检查。
# include<bits/stdc++.h>
using namespace std;
const int MAX = 1000001;
int a[MAX];
int main(){
int n,m;
while(~scanf("%d %d",&n,&m)){
memset(a,0,sizeof(a));
for(int i=0;i<n;i++){
int t;
scanf("%d",&t);
a[500000+t]=1;
}
for(int i = MAX-1;m>0;i--){
if(a[i]){
if(m>1) printf("%d ",i-500000);
else printf("%d\n",i-500000);
m--;
}
}
}
return 0;
}
第三章 STL和基本数据结构
容器
顺序式容器
- vector : 动态数组,从末尾能快速插入和删除,直接访问任何元素
- list :双链表,从任何地方能快速插入和删除
- deque :双向队列,从前面或末尾能快速插入和删除,直接访问任何元素
- queue :队列,先进先出
- priority_queue :优先队列,最高优先级元素总是第一个出列
- stack :栈,先进后出
关联式容器
- set :集合,快速查找,不允许重复值
- multiset :快速查找,允许重复值
- map :一对一映射,不允许重复值
- multimap : 一对多映射,基于关键字查找,允许重复值
1. vector
数组是基本的数据结构,分为动态数组合静态数组。在竞赛中,一般采用的规则是:如果空间够用居勇静态数组,这样不容易出错;如果空间比较紧张,就用vector建立动态数组。
vector能在运行中改变数组的大小,在空间存储是连续的,所以索引在常数时间内是可以完成的,但在中间差值和删除操作会造成内存块的复制,会影响效率。
定义
| 功能 | 例子 | 说明 |
| 定义int型数组 | vector<int>a | 默认初始化,a为空 |
| vector<int>b(a) | 用a定义b | |
| vector<int>a(100) | a有100个值为0的元素 | |
| vector<int>a(100,6) | a有100个值为6的元素 | |
| 定义string型数组 | vector<string>a(10,"null") | 10个值为null的元素 |
| vector<string>a(10,"hello") | 10个值为hello的元素 | |
| vector<string>b(a.begin(),a.end()) | b是a的复制 | |
| 定义结构型数组 | struct point { int x,y; } vector <point> a | a用来存坐标 |
还可以定义二维数组:vector<int> a[MAXN];
这个数组的第一维是固定大小的,第二维是动态的,经常用语实现图的邻接表中
常用操作
| 功能 | 例子 | 说明 |
|---|---|---|
| 赋值 | a.push_back(100) | 在尾部添加元素100 |
| 元素个数 | int size = a.size() | 元素个数 |
| 是否为空 | bool isEmpty = a.empty() | 判断是否为空 |
| 打印 | cout << a[0] << endl; | 打印第一个元素 |
| 中间插入 | a.insert(a.begin()+i,k) | 把k插到第i个元素前面 |
| 尾部插入 | a.insert(a.end(),10,5) | 在尾部插入10个整数5 |
| 尾部删除 | a.pop_back() | 删除尾部元素 |
| 删除区间 | a.erase(a.begin()+i,a,a.bejin()+j) | 删除区间[i,j-1]的元素3 |
| 删除元素 | a.erase(a.begin()+2) | 删除第三个元素 |
| 调整大小 | a.resize(n) | 数组大小调整为n |
| 清空 | a.clear() | 清空数组 |
| 翻转 | a.reverse(a.begin(),a.end()) | 翻转数组 |
| 排序 | sort(a.begin(),a.end()) | 对数组进行从小到大的排序 |
vector 例题
圆桌上围坐着2n个人。其中n个人是好人,另外n个人是坏人。如果从第一个人开始数数,数到第m个人,则立即处死该人;然后从被处死的人之后开始数数,再将数到的第m个人处死…… 依此方法不断处死围坐在圆桌上的人。试问预先应如何安排这些好人与坏人的座位,能使得在处死n个人之后,圆桌上围坐的剩余的n个人全是好人。
输入: n ,m
输出:对于每一组数据,输出2n个大写字母,“G”表示好人,“B”表示坏人,五十个祖母为一行,不允许出现空白行,相邻数据间留有一个空行。
代码:
# include<bits/stdc++.h>
using namespace std;
int main(){
vector<int>table;
int n,m;
while(cin >> n >> m){
table.clear();
for(int i=0;i<2*n;i++) table.push_back(i);
int pos = 0;
for(int i=0;i<n;i++){
pos = (pos+m-1)%table.size(); //圆桌成环问题,取余就可以
// pos 存储的是应该标记成坏人的位置
table.erase(table.begin()+pos);
}
int j=0;
for(int i=0;i<2*n;i++){
if(!(i%50) && i) cout << endl; // 五十个为一行
if(j < table.size() && i == table[j]){
j++;
cout << "G" ;
}
else cout << "B";
}
cout << endl << endl;
}
return 0;
}
结果:

294

被折叠的 条评论
为什么被折叠?



