四则运算与堆栈:
-
后缀表达式通过堆栈运算:运算数压入,运算符弹出两个运算数运算后压入结果,最终栈顶即为结果
-
中缀表达式要通过堆栈转化为后缀表达式:
-
运算数:输出
-
运算符:如果优先级>栈顶运算符,压入该运算符;如果<=,弹出并输出栈顶运算符直到该运算符优先级>栈顶运算符,压入该运算符。
-
左括号:优先级栈外最高,栈内最低。
-
右括号:弹出并输出运算符直到遇到左括号。
-
括号不输出
最终弹出并输出栈内运算符
-
-
与二叉树的关系
先序得前缀,中序得中缀,后序得后缀
/*dfs搜索+string的拼接*/
/*输出中缀表达式加括号*/
string dfs(int root)
{
if(tree[root].left==-1&&tree[root].right==-1) return tree[root].data;
if(tree[root].left==-1&&tree[root].right!=-1) return "("+tree[root].data+dfs(tree[root].right)+")";
if(tree[root].left!=-1&&tree[root].right!=-1) return "("+dfs(tree[root].left)+tree[root].data+dfs(tree[root].right)+")";
}
- 2019秋季
/*Postfix Expression */
string dfs(int root)
{
if(tree[root].L==-1&&tree[root].R==-1) return "("+tree[root].data+")";
if(tree[root].L==-1&&tree[root].R!=-1) return "("+tree[root].data+dfs(tree[root].R)+")";
if(tree[root].L!=-1&&tree[root].R!=-1) return "("+dfs(tree[root].L)+dfs(tree[root].R)+tree[root].data+")";
}
//output:
(((a)(b)+)((c)(-(d))*)*)
//思路:如需加括号,将所有情况列出来
查找
二分查找 O(logN)
1、有序
2、模板
//解决“寻找有序序列第一个满足条件的元素的位置”问题
//二分区间[left,right]
int solve(int left,int right){
int mid;
while(left<right){//找到唯一位置
mid=(left+right)/2;
if(条件成立){//满足条件元素位置>=mid
right=mid;
}
else left=mid+1;
}
return left;
}
3、函数
#include<algorithm>
//返回指针/迭代器!!!!!first,last为指针
lower_bound(first,last,val);//[first,last)范围内第一个>=val的元素
upper_bound(first,last,val);//第一个>val
two pointers(利用数列递增的思想)复杂度O(n)
- 序列合并问题(如多项式加法)
- 序列中a+b=M问题
树的遍历
两种题型:dfs和利用先中后序遍历序列特点写递归
/*先序中序转后序*/
void post(int root,int left,int right){//left,right标记中序子列,root标记先序序列
if(left>right) return;
int i=left;
while(i<=right&&pre[root]!=in[i])i++;
post(root+1,left,i-1);
post(root+1+i-left,i+1,right);
printf("%d ",pre[root]);
}
/*先序(后序)中序转层序,利用vector+sort*/
struct node{
int index,value;
};
bool cmp(node a,node b){
return a.index<b.index;
}
vector<node> ans;
vector<int> post,in;
void pre(int root,int left,int right,int index)
{
if(left>right) return;
int i=left;
while(i<=right&&post[root]!=in[i]) i++;
int nl=i-left;
int nr=right-i;
ans.push_back({index,post[root]});
pre(root-1-nr,left,i-1,index*2);
pre(root-1,i+1,right,index*2+1);
}
post.resize(n+1);
in.resize(n+1);
pre(n,1,n,1);
sort(ans.begin(),ans.end(),cmp);
/*模板*/
/*先(后)中序建树*/
/*启发性题目:1143——BST和先序序列的关系、1151——BT的结构*/
void XXXorder(int root,int left,int right)//注意:root标记中序,left、right标记先(后)序
{
if(left>right) return;//出口
while(i<=right&&没有找到根) i++;
XXXorder(左树);
XXXorder(右树);
}
/*dfs 注意剪枝(1103)*/
void dfs(int root,A,B,C)
{
if(root==-1){
更新A,B,C;
return;//!!!!!
}
path.push_back(root);
dfs(左树);
dfs(右树);
path.pop_back();
}
******几个想法:*******
/*1、可见树的遍历(除bfs)本质是递归的应用*/
/*2、注意先序的特点:先序为进入顺序*/
/*3、BST与先序:可知进入顺序,可由BST特点(比较*值大小*推知LCA(BT需由*序列位置*推知,用map)*/
/*4、BST与中序:中序为小->大排序*/
******几个题目:*******
1103、1115、1119(先后->中)??、1130(中缀表达式)、1135(dfs路径)、1143(BST与先序)、1151(BT树结构)
BST
/*二叉搜索树BST*/
/*查找、插入、建树、删除*/
struct node{
int data;
node *left,*right;
};
/*建树*/
node* build(node* root,int v)/*注意如果函数类型是void,要写作void build(node* &root)*/
{
if(root==NULL){
root=new node();//int* p=new int;
root->data=v;
root->left=root->right=NULL;
}
else if(v<=root->data) root->left=build(root->left,v);
else root->right=build(root->right,v);
return root;
}
node* root=NULL;//注意置NULL
for(int i=0;i<n;i++){
int t;
scanf("%d",&t);
root=build(root,t);
}
/*删除以root为根节点,权值为x的结点*/
node* findmax(node* root)
{
while(root->right!=NULL) root=root->right;
return root;
}
void delete(node* &root,int x)
{
if(root->data==x){
if(root->left==NULL&&root->right==NULL)
root=NULL;
else if(root->left!=NULL){
node* pre=findmax(root->left);
root->data=pre->data;
delete(root->left,pre->data);
}
else{
node* next=findmin(root->right);
root->data=next->data;
delete(root->right,next->data);
}
}
else if(root->data<x) delete(root->right,x);
else delete(root->left,x);
}
并查集
/*并查集操作*/
/*main中千万别忘记initial()*/
int fa[n];
void initial(){
for(int i=0;i<n;i++)
fa[i]=-1;
}
int find(int x)
{
if(fa[x]<0) return x;
int F=find(fa[x]);
fa[x]=F;
return F;
}
void Union(int a,int b)
{
int faA=find(a);
int faB=find(b);
if(faA!=faB){
if(fa[faA]<fa[faB]){
fa[faA]+=fa[faB];
fa[faB]=faA;
}
else {
fa[faB]+=fa[faA];
fa[faA]=faB;
}
}
}
堆
-
结构特点:完全二叉树!!!!!
-
最大堆、最小堆
判别方法:
/*判断是大顶堆还是小顶堆*/ for(int i=2;i<=n;i++){ if(a[i/2]>a[i]) isMin=0; if(a[i/2]<a[i]) isMax=0; } if(isMin==1&&isMax==0) printf("Min Heap\n"); else printf("%s",isMax==1&&isMin==0?"MaxHeap":"Not Heap");
-
堆的建立
typedef struct HNode *Heap; /* 堆的类型定义 */ struct HNode { ElementType *Data; /* 存储元素的数组 */ int Size; /* 堆中当前元素个数 */ int Capacity; /* 堆的最大容量 */ }; #define MAXDATA 1000 /* 该值应根据具体情况定义为大于堆中所有可能元素的值 */ MaxHeap CreateHeap( int MaxSize ) { /* 创建容量为MaxSize的空的最大堆 */ MaxHeap H = new HNode(); H->Data = (ElementType *)malloc((MaxSize+1)*sizeof(ElementType)); H->Size = 0; H->Capacity = MaxSize; H->Data[0] = MAXDATA; /* 定义"哨兵"为大于堆中所有可能元素的值*/ return H; } void PercDown( MaxHeap H, int p ) { /* 下滤:将H中以H->Data[p]为根的子堆调整为最大堆 */ int Parent, Child; ElementType X; X = H->Data[p]; /* 取出根结点存放的值 */ for( Parent=p; Parent*2<=H->Size; Parent=Child ) { Child = Parent * 2; if( (Child!=H->Size) && (H->Data[Child]<H->Data[Child+1]) ) Child++; /* Child指向左右子结点的较大者 */ if( X >= H->Data[Child] ) break; /* 找到了合适位置 */ else /* 下滤X */ H->Data[Parent] = H->Data[Child]; } H->Data[Parent] = X; } void BuildHeap( MaxHeap H ) { /* 调整H->Data[]中的元素,使满足最大堆的有序性 */ /* 这里假设所有H->Size个元素已经存在H->Data[]中 */ int i; /* 从最后一个结点的父节点开始,到根结点1 */ for( i = H->Size/2; i>0; i-- ) PercDown( H, i ); }
图
概述
图的题目分为:
-
dfs、bfs遍历或并查集判断连通性1021、1034、1076(bfs)
-
dfs解决无权图最短路径问题1131(易与上一点代码混淆)
-
dijkstra+dfs解决有权图最短路径问题1018(dijkstra不熟练,找bug时间长)
-
考察图的定义和特点 1139?、1142、1154(逻辑不清晰、做题过程混乱)
图不够熟练,平均做题速度>50min
图的遍历
/*深度优先搜索遍历图*/
/*伪码*/
dfs(u)
{
vis[u]=true;
for(从u出发能到达的所有顶点v)
if(vis[v]==false)
dfs(v);
}
dfstraversal(G)
{
for(G的所有顶点u)
if(vis[u]==false)
dfs(u);
}
/*邻接矩阵*/
void dfs(int u,int depth)
{
vis[u]=true;
//对u的操作
for(int v=0;v<n;v++){
if(vis[v]==false&&G[u][v]!=inf)
dfs(v,depth+1);
}
}
void dfstraversal()
{
for(int u=0;u<n;u++)
if(vis[u]==false)
dfs(u,1);
}
/*邻接表*/
void dfs(int u,int depth)
{
vis[u]=true;
for(int v=0;v<G[u].size();v++)
if(vis[G[u][v]]==false) dfs(G[u][v],depth+1);
}
/*BFS*/
/*伪码描述*/
BFS(u)
{
queue q;
q.push(u);
inq[u]=true;//入队即标记
while(q.empty()==false){
访问队首元素;
q.pop();
for(从u出发可到达的所有顶点v)
if(inq[v]==false){//注意是把没入队的加入而不是没访问过的加入
q.push(v);
inq[v]=true;
}
}
}
BFSTraversal(G)
{
for(G的所有顶点u)
if(inq[u]==false)
BFS(u);
}
/*带层数的时候*/
struct node{
int v,layer;
};
next.layer=top.layer+1;
dfs解决无权图最短路径
vector<int> temp,ans;
void dfs(int index,标尺A,B,C)
{
if(枚举结束){
if(该方案能使A,B,C条件更优)
更新结果
return;
}
if(剪枝条件) return;
temp.push_back(index);
dfs(index+1,更新A,B,C);//选index的情况,如果index可重复选择,则改为index
temp.pop_back();
dfs(index+1,A,B,C);//不选index的情况
}
/*在图中求满足条件的方案并输出路径时*/
vector<int> temp;//记录临时路径
void dfs(int start,int end1,int depth)
{
if(start==end1){
temp.push_back(start);
/***/
temp.pop_back();
return;
}
vis[start]=true;
temp.push_back(start);
for(int i=0;i<G[start].size();i++)
if(vis[G[start][i]]==false) dfs(G[start][i],end1,depth+1);
vis[start]=false;//!!!!!注意与遍历图中所有节点算法的不同!A1131
temp.pop_back();
}
Dijkstra求有权图最短路径
/*不能求带负权边的最短路径*/
/*dfs可以求一个点到另一个点的最短路径的长度*/
/*伪码*/
void dijkstra(int s)
{
初始化;
for(循环n次){
u=使d[u]最小的还未被访问的顶点标号;
vis[u]=true;
for(从u出发能到达的所有顶点v)
if(v未被访问过且以u为中介能使d[v]更优)
优化d[v];
}
}
/*邻接矩阵*/
/*Dijkstra&DFS*/
#include<algorithm>/*fill函数头文件*/
using namespace std;
const int INF=0x3fffffff;
vector<int> path[maxN];
bool vis[maxN];
int dis[maxN];
int G[maxN][maxN],N;
//初始化图在main中已完成
void Dijkstra(int s)
{
fill(dis,dis+maxN,INF);
fill(vis,vis+maxN,false);
dis[s]=0;//注意赋值时s不是0
for(int i=0;i<N;i++){
int u=-1;int mind=INF;
for(int j=0;j<N;j++)
if(vis[j]==false&&dis[j]<mind){
u=j;
mind=dis[j];
}
if(u==-1) return;//not connect
vis[u]=true;
for(int v=0;v<N;v++){
if(vis[v]==false&&G[u][v]!=INF){//attention!!!不能写入循环条件中,注意是vis[v]
if(d[u]+G[u][v]<d[v]){
d[v]=d[u]+G[u][v];
path[v].clear();
path[v].push_back(u);
}
else if(d[u]+G[u][v]==d[v]){
path[v].push_back(u);
}
}
}
}
}
int optvalue;
vector<int> temp,ans;
void DFS(int v)
{
if(v==s)//s为起点
{
temp.push_back(v);
int value;
/*计算temp路径的value*/
if(value优于optvalue){
optvalue=value;
ans=temp;
}
temp.pop_back();
return;
}
temp.push_back(v);//**
for(int i=0;i<path[v].size();i++)//注意是path哦,不是G
DFS(path[v][i]);//注意别写错
temp.pop_back();
}
/*邻接表*/
struct node{
int v,d;
};
vector<node> G[maxn];
int n;
int dis[maxn];
bool vis[maxn];
/.../
//与邻接矩阵唯一不同的地方
for(int i=0;i<G[u].size();i++){
int v=G[u][i].v;//
if(vis[v]==false&&dis[u]+G[u][i].d<dis[v]){//有标尺的时候要把vis[v]==false放外面
dis[v]=dis[u]+G[u][i].d;
}
}
/*无需输出路径的dijkstra和其标尺统计*/
fill(num,num+maxn,0);
fill(vw,vw+maxn,0);
for(int j=0;j<G[u].size();j++){
int v=G[u][j].v;
if(vis[v]==false){
if(d[u]+G[u][j].weight<d[v]){
d[v]=d[u]+G[u][j].weight;
num[v]=num[u];
vw[v]=vw[u]+teams[v];
}
else if(d[u]+G[u][j].weight==d[v]){
num[v]+=num[u];//路径数统计
if(vw[u]+teams[v]>vw[v])//点权
vw[v]=vw[u]+teams[v];
}
}
}
图的连通性判断(DFS或并查集)
/*DFS*/
vector<int> G[maxN];
bool vis[maxN]={false}
int occupy;
void DFS(int u)
{
if(u==occupy) return;//delete the occupied node
vis[u]=true;
for(int i=0;i<G[u].size();i++)
if(vis[G[u][i]]==false)
DFS(G[u][i]);
}
int block=0;
for(int i=1;i<=N;i++){
if(i!=occupy&&vis[i]==false){
DFS(i);
block++;
}
}
/*并查集*/
for(int u=1;u<=N;u++){
for(int v=0;v<G[u].size();v++){
if(u==occupy||G[u][v]==occupy) continue;//跳过被occupy的点
Union(u,G[u][v]);
}
}
int block=0;
for(int i=1;i<=N;i++){
if(i==occupy) continue;
int fa_i=father(i);
if(vis[fa_i]==false) block++;
vis[fa_i]=true;
}
拓扑排序
void TopSort()
{
for(图中每个顶点 V){
if(Indegree[v]==0)
Enqueue(v,Q);
}
while(!isEmpty(Q)){
v=Dequeue(Q);
输出V;
cnt++;
for(V的每个邻接点W)
if(--Indegree[w]==0)
Enqueue(W,Q);
}
if(cnt!=|V|) Error("图中有回路");
}
C++
输入输出
-
double 输入%lf 输出%f long double 输入出%Lf
-
输入一行字符串带空格:
**如果是
string s
则用getline(cin,s);
头文件#include<string>
**如果是
char str[100]
则用cin.getline(str,100);
头文件#include<iostream>
;若该行前面有不需要部分,可先单独输入不需要部分,再输入getline
-
!!!!!getline(cin,s)
前面有换行输入时,一定要在前面加上getchar()
或者scanf
-
%c
会读入空格
和回车
-
sscanf
和sprintf
:-
头文件
#include<stdio.h>
-
作用:字符数组
str[100]
与"%d:%lf,%s"
的转换 -
#include<stdio.h> char str[100]="2020:3.14,hello".str2[100]; sscanf(str,"%d:%lf,%s",&n,&db,&str2); printf(n,db,str2); sprintf(str,"%d:%.2f,%s",n,db,str2); printf(str); /*如果str是string类型*/ sscanf(str.c_str(),"",)
-
四舍五入取整:
+0.5后(int)
-
math.h头文件
log(double)以e为底
ceil(double)向上取整 floor向下取整
fabs(double x)
algorithm头文件
fill()
reverse(string.begin(),string .end());
sort(a,a+n,greater<int>());
swap(a,b);
max(x,y)/min(x,y)//x,y同类型
abs(int x)//fabs(double)
lower_bound(first,last,val)//>=val
upper_bound(first,last,val)//>val
cctype头文件
tolower(char c);
isalpha(char c);
isalnum(char c);
STL
string
/*substr的用法*/
string s2=s.substr(4);//从下标4到结束
string s3=s.substr(5,3)//从下标5开始,3个
/*find*/
string.find(str,pos)//返回下标值,如果没有==string::npos
if(s.find('a',5)!=string::npos)
int index=s.find('a',5);
s.find('a');//返回a第一次出现的下标
s.find('a',5);//从下标5开始,寻找a第一次出现的下标
/*erase*/
s.erase(pos,cnt);
s.erase(s.begin()+n);
/*insert*/
s.insert(pos,string);//注意:insert到pos前!!!!
/*string转int*/
stoi(string);//string可带“+/-”
/*int/double/float转string*/
string s=to_string(int/double/float);
map(按照键从小到大排序)
map<> mp;
for(auto it=mp.begin();it!=mp.end();it++)
vector
vector的拼接
vector<int> v1,v2;
v1.insert(v1.end(),v2.begin(),v2.end());//将v2拼接在v1后面
v1.insert(v1.begin(),v2.begin(),v2.end());//将v2放在v1前面
运算符重载
struct node{
int index,freq;
bool operator <(const node &a) const{
return (freq!=a.freq)?freq>a.freq:index<a.index;
}
};
set
set.size();
引用和传参
修改函数传入的参数,可用指针或引用。
对引用变量的操作即为对原变量的操作
bool cmp(node &a,node &b)//排序传参建议⽤引⽤传参
简单数学
素数的判断及素数表
bool isprime(int n)
{
if(n<=1) return false;
int sqr=(int)sqrt(n*1.0);
for(int i=2;i<=sqr;i++)
if(n%i==0) return false;
return true;
}
//素数表
//初始化标记为1(是素数),外层循环从2到sqrt(n),内层循环从2到j*i<n(把j的i倍全部标记为0)
vector<int> isprime(50000,1);
for(int i=2;i*i<50000;i++)
for(int j=2;j*i<50000;j++)
isprime[j*i]=0;
大整型运算
struct bign{
int d[maxn],len;
bign(){fill(d,d+maxn,0);len=0;}
};
bign change(string s)
{
bign a;
for(int i=0;i<s.length();i++)
a.d[a.len++]=s[s.length()-1-i]-'0';
return a;
}
bign add(bign a,bign b)
{
bign c;
int carry=0;
for(int i=0;i<a.len||i<b.len;i++){
int tmp=a.d[i]+b.d[i]+carry;
c.d[c.len++]=tmp%10;
carry=tmp/10;
}
if(carry!=0) c.d[c.len++]=carry;
return c;
}
//减法、乘法、除法!!!!!
分数及其四则运算
//**结构**//
struct node{
long int up,down;
};
//**化简**//
//**三种情况**//
long int gcd(long int a,long int b)
{
if(b==0) return a;
else return gcd(b,a%b);
}
node reduct(node r)//分母不为0
{
if(r.down<0){r.up=-r.up;r.down=-r.down;}
if(r.up==0) r.down=1;
else{
long int d=gcd(abs(r.up),r.down);//abs!!!!!
r.up/=d;r.down/=d;
}
return r;
}
/*四则运算:除法要注意分母不为0*/
void output(node r)
{
r=reduct(r);
if(r.down==1) printf("%ld",r.up);
else if(abs(r.up)>r.down)
printf("%ld %ld/%ld",r.up/r.down,abs(r.up)%r.down,r.down);
else printf("%ld/%ld",r.up,r.down);
}
质因子分解
/*质因子分解*/
struct factor{
int x,cnt;
}fac[10];//对于int型来说数组大小为10即可
/*根据结论:对一个正整数来说,其质因子最多只有一个>sqrt(n),其余均<=sqrt(n)*/
1、枚举[2,sqrt(n)]内n的质数,判断其是否是n的质因子,设为p,加入factor结构体,初始化cnt=0;
只要p还是n的因子,令n不断除p,cnt++,直到p不再是n的因子;
if(n%prime[i]==0){
fac[num].x=prime[i];
fac[num].cnt=0;
while(n%prime[i]==0){
fac[num].cnt++;
n/=prime[i];
}
num++;
}
2、若上述枚举结束后n>1,则说明n有一个>sqrt(n)的因子;
if(n!=1){
fac[num].x=n;
fac[num].cnt=1;
}
哈希表
/*哈希映射+平方探测法*/
/*插入*/
int flag=0;
for(int i=0;i<=tsize;i++)//插入次数<=tsize+1
{
int pos=(a+i*i)%tsize;
if(v[pos]==0){
v[pos]=a;
flag=1;
break;
}
}
if(flag==0) printf("cannot be inserted");
/*计算查找时间*/
int cnt=0;
for(int i=0;i<=tsize;i++)//
{
cnt++;
int pos=(a+i*i)%tsize;
if(v[pos]==a||v[pos]==0) break;
}
错误集锦
Tips
-
vector之间可以赋值
-
10^9以内或32bit——int
-
long long(64bit)输入输出%lld
-
getline()若有其他输入一定要
scanf("%d\n",&n);
输入\n
-
链表:注意可能输入数据有无用点,故输入的数据数可能不是有效结点数(A1074)
-
注意统计条件:
- A1071:非alnum间隔有效字符串,注意最后一个有效字符串需要特殊考虑(因为后面没有非alnum了)
-
do while()的运用 A1069
-
溢出知识的运用:A1065
-
distinct numbers不同的数字