stack
- 后缀表达式的生成以及计算(算法笔记P249)
对于op的优先级可以使用map生成映射
对于前缀表达式生成后缀表达式,
1 操作数输出
2 op优先级高于栈顶则入栈,优先级小于或等于栈顶则栈顶元素出出栈。
3 '('入栈
4 ‘)‘一直出栈到遇见’(’
后缀表达式计算
遇见数字入栈,遇见op则出栈两个操作数,运算结果入栈
树
//查找某个结点的所有祖先结点
bool print_pre_node(Node *root, int val) {
if(root == NULL) return false;
if(root -> val == val) { return true; }
if(print_pre_node(root -> left, val) || print_pre_node(root -> right, val)) {
cout<<root -> val<<ends;
return true;
}
return false;
}
AVL P326
树型 | 判定条件 | 调整 |
---|---|---|
LL | BF(root) = 2, BF(root->lchild) = 1 | R(root) |
LR | BF(root) = 2, BF(root->lchild) = -1 | L(root->lchild); R(root) |
RR | BF(root) = -2, BF(root->lchild) = -1 | L(root) |
RL | BF(root) = -2, BF(root->lchild) = 1 | R(root->rchild); L(root) |
涉及到的函数
struct node{
int data,h;
node *lchild,*rchild;
};
node* newNode(int x){}
int getH(node* root){}
int getB(node* root){}
void updataH(node* root){}
void L(node* &root){}
void R(node* &root){}
void insert(node* &root,int x){}
堆排序
const int manx=100;
int heap[maxn];
void downAdjust(int low,int high){
int i=low,j=i*2;
while(j<=high){
if(j+1<=high&&heap[j+1]>heap[j]) j+=1;
if(heap[i]<heap[j]){
swap(heap[i],heap[j]); i=j; j=i*2;
}else break;
}
}
void createHeap(){
for(int i=n/2;i>=1;i--) downAdjust(i,n);
}
void heapSort(){
createHeap();
for(int i=n;i>1;i--){ swap(heap[i],heap[1]); downAdjust(1,i-1); }
}
哈夫曼编码
赫夫曼数的最小带权路径是非叶子节点或者非根节点的权值之和
#include<cstdio>
#include<queue>
using namespace std;
priority_queue<long long,vector<long long>, greater<long long> > q;
int main(){
int n;
long long temp,x,y,ans=0;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%lld",&temp);
q.push(temp);
}
while(q.size()>1){
x=q.top(); q.pop();
y=q.top(); q.pop();
q.push(x+y);
ans+=x+y;//累计求的最小带权路径
}
printf("%lld\n",ans);
return 0;
}
图的遍历
fill(G[0],G[0]+maxn*maxn,INF);
- DFS
const int maxv=1000;
const int INF=1000000000;
int n;
bool vis[maxv]={false};
void DFSTrave(){
for(int u=0;u<n;u++){ if(vis[u]==false) DFS(u,1); }
}
邻接矩阵版
int G[maxv][maxv];
void DFS(int u,int depth){
vis[u]=true;
for(int v=0;v<n;v++){
if(vis[v]==false&&G[u][v]!=INF) DFS(v,depth+1); }
}
邻接表版
vector<int> Adj[maxv];
void DFS(int u,int depth){
vis[u]=true;
for(int i=0;i<Adj[u].size();i++){
int v=Adj[u][i];
if(vis[v]==false) DFS(v,depth+1);
}}
- BFS
#include<queue>
#include<vector>
using namespace std;
const int maxv=1000;
const int INF=1000000000;
bool inq[maxv]={false};
int n;
void BFSTrave(){
for(int u=0;u<n;u++){ if(inq[u]==false) BFS(u); }
}
邻接矩阵版
int G[maxv][maxv];
void BFS1(int u){
queue<int> q; q.push(u); inq[u]=true;
while(!q.empty()){
int u=q.front; q.pop();
for(int v=0;v<n;v++){
if(inq[v]==false&&G[u][v]!=INF){
q.push(v); inq[v]=true; }
} }}
邻接矩阵版
vector<int> Adj[maxv];
void BFS2(int u){
queue<int> q;
q.push(u); inq[u]=true;
while(!q.empty()){
int u=q.front(); q.pop();
for(int i=0;i<Adj[u].size();i++){
int v=Adj[u][i];
if(inq[v]==false){ q.push(v); inq[v]=true; }
} }}
并查集
int findF(int x){
if(x==father[x]) return x;
int F=findF(father[x]); father[x]=F; return F;
}
int findF(int x){
int a=x;
while(father[x]!=x) x=father[x];
while(a!=father[a]){ int z=a; a=father[a]; father[z]=x;}
return x;
}
void Union(int a,int b){
int faA=findF(a); int faB=findF(b);
if(faA!=faB) father[faA]=faB;
}
void inti(){
for(int i=1;i<=n;i++){ vis[i]=false; father[i]=i; }
}
//统计集合的个数
int res = 0;//记录集合的个数
int isRoot[maxn]={0};//记录是否为根节点
for(int i=1;i<=n;i++)
isRoot[findFather(i)]++;//i所在的根节点的记录加1
for(int i = 0; i < N; i++){
if(isRoot[i] != 0)//计算朋友圈的个数
res++;
}
Dijkstra
- 模板
const int maxn=1000;
const int INF=1000000000;
int n,G[maxn][maxn],d[maxn],pre[maxn];
bool vis[maxn]={false};
void Dijkstra(int s){
fill(d,d+maxn,INF);
for(int i=0;i<n;i++) pre[i]=i;
d[s]=0;
for(int i=0;i<n;i++){
int u=-1,min=INF;
for(int j=0;j<n;j++){
if(vis[j]==false&&d[j]<min){ u=j; min=d[j];}
}
}
if(u==-1) return ;
vis[u]=true;
for(int v=0;v<n;v++){
if(vis[v]==false&&G[u][v]!=INF&&d[u]+G[u][v]<d[v]){
d[v]=d[u]+G[u][v]; pre[v]=u;
}
}
}
- 输出路径
void DFS(int s,int v){
if(v==s){
printf("%d\n",s); return ;
}
DFS(s,pre[v]);
printf("%d\n",v);
}
- 每边新增边权,要求最短路径上的花费最小
fill(c,c+maxn,INF);c[s]=0;
for(int v=0;v<n;v++){
if(vis[v]==false&&G[u][v]!=INF){
if(d[u]+G[u][v]<d[v]){
d[v]=d[u]+G[u][v]; c[v]=c[u]+cost[u][v];
}else if(d[u]+G[u][v]==d[v]&&c[u]+cost[u][v]<c[v]){
c[v]=c[u]+cost[u][v]; }
}
}
- 新增点权,要求最短路径上的点权最大,设置weight[u]和w[u],初始w[s]=weight[s],其余为0
- 求最短路径的数量
int num[maxn]={0};
num[s]=1;
for(int v=0;v<n;v++){
if(vis[v]==false&&G[u][v]!=INF){
if(d[u]+G[u][v]<d[v]){
d[v]=d[u]+G[u][v]; num[v]=num[u];
}else if(d[u]+G[u][v]==d[v]){ num[v]+=num[u]; }
}
}
- DFS计算第二标尺,叶子节点st为路径的起点
#include<vector>
using namespace std;
const int maxn=1010;
const int INF=1000000000;
int n,m,st,ed,optvalue,numPath=0;
int d[maxn],G[maxn];
bool vis[maxn]={false};
vector<int> pre[maxn],path,temp;
void DFS(int v){//v是终点
if(v==st){
numPath++;//最短路径的条数
temp.push_back(v);
int value=0;
计算路径temp上的value;
if(value优于optvalue){ optvalue=value; path=temp; }
temp.pop_back();
return ;
}
temp.push_back(v);
for(int i=0;i<pre[v].size();i++) DFS(pre[v][i]);
temp.pop_back();
}
//边权之和
for(int i=temp.size()-1;i>0;i--){
int id=temp[i],idnext=temp[i-1];
value+=V[id][idnext];
}
//点权之和
for(int i=temp.size()-1;i>=0;i--){
int id=temp[i];
value+=W[id];
}
//Dijkstra中的更新
void Dijkstra(int s){//s为起点
fill(d,d+maxn,INF);
d[s]=0;
for(int i=0;i<n;i++){
int u=-1,min=INF;
for(int j=0;j<n;j++){
if(vis[j]==false&&d[j]<min){
u=j; min=d[j];
}
}
if(u==-1) return ;
vis[u]=true;
for(int v=0;v<n;v++){
if(vis[v]==false&&G[u][v]!=INF){
if(d[u]+G[u][v]<d[v]){
d[v]=d[u]+G[u][v];
pre[v].clear();
pre[v].push_back(u);
}else if(d[u]+G[u][v]==d[v])
pre[v].push_back(u);
}
}
}
}
//main():
fill(G[0],G[0]+maxn*maxn,INF);
Dijkstra(st);
dfs(ed);
for(int i=path.size()-1;i>=0;i=--)
printf("%d ",path[i]);
Bellman-Ford算法和SPFA算法 有到达源点的负环
struct Node{ int v,dis;};
vector<Node> adj[maxn];
int n,d[maxn];
bool Bellman(int s){//s为源点
fill(d,d+maxn,INF); d[s]=0;
for(int i=0;i<n-1;i++)//进行n-1次操作
for(int u=0;u<n;u++)
for(int j=0;j<adj[u].size();j++){
int v=adj[u][j].v; int dis=adj[u][j].dis;
if(d[u]+dis<d[v]) d[v]=d[u]+dis;
}
for(int u=0;u<n;u++)
for(int j=0;j<adj[u].size();j++){
int v=adj[u][j].v; int dis=adj[u][j].dis;
if(d[u]+dis<d[v]) return false;
}
return true;
}
vector<int> adj[maxn];
int n,d[maxn],num[maxn];//num表示结点入队的次数,次数大于n-1代表存在负环
bool inq[maxn];
bool SPEA(int s){
fill(inq,inq+maxn,false); fill(num,num+maxn,0); fill(d,d+maxn,INF);
queue<int> q;
q.push(s); inq[s]=true; num[s]++;
d[s]=0;
while(!q.empty()){
int u=q.front; q.pop();
inq[u]=false;
for(int j=0;j<adj[u].size();j++){
int v=adj[u][j].v; int dis=adj[u][j].dis;
if(d[u]+dis<d[v]){
d[v]=d[u]+dis;
if(!inq[v]){
q.push(v);inq[v]=true; num[v]++;
if(num[v]>=n) return false;
}
}
}
}
return true;
}
Floyd
void Floyd(){//n<200
for(int k=0;k<n;k++)//存在中间结点k,使得ij距离缩短
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
if(dis[i][k]!=INF&&dis[k][j]!=INF&&dis[i][k]+dis[k][j]<dis[i][j])
dis[i][j]=dis[i][k]+dis[k][j];
}
生成树算法
prim最小生成树(稠密图)
int n,G[maxn][maxn];
int d[maxn];//顶点与集合S的最短距离
bool vis[maxn]={false};
int prim(){
fill(d,d+maxn,INF); d[0]=0;
int ans=0;
for(int i=0;i<n;i++){
int u=-1,MIN=INF;
for(int j=0;j<n;j++){
if(vis[j]==false&&d[j]<MIN){ u=j; MIN=d[j]; }
}
}
if(u==-1) return -1;
vis[u]=true; ans+=d[u];
for(int v=0;v<n;v++)//u作为中介顶点
if(vis[v]==false&&G[u][v]!=INF&&G[u][v]<d[v])
d[v]=G[u][v];
}
kruskal最小生成树(稀疏图)
struct edge{
int u,v,cost;
}E[maxe];
bool cmp(edge a,edge b){
return a.cost<b.cost;
}
int father[maxn];
int findFather(int x){}
int kruskal(int n,int m){//n为顶点数,m为边数
int ans=0,num=0;
for(int i=1;i<=n;i++) father[i]=i;
sort(E,E+m,cmp);
for(int i=0;i<m;i++){
int faU=findFather(E[i].u); int faV=findFather(E[i].v);
if(faU!=faV){
father[faU]=faV;
ans+=E[i].cost; num++;
if(num==n-1) break;
}
}
if(num!=n-1) return -1;//无法连同时返回-1
return ans;
}
有向无环图
拓扑排序
tor<int> G[maxn];
int n,m,inDegree[maxn];//顶点数、入度
bool topologicalSort(){
int num=0;//记录加入拓扑排序的顶点数
queue<int> q;
for(int i=0;i<n;i++)//先将入度为零的点入队
if(inDegree[i]==0) q.push(i);
while(!q.empty()){
int u=q.front(); q.pop(); printf("%d",u);
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
inDegree[v]--;
if(inDegree[v]==0) q.push(v);
}
G[u].clear();//清空顶点u的所有边,可以不加
num++;
}
if(num==n) return true;
else return false;
}
关键路径
AOV:顶点表示活动
AOE:边表示活动
对于i->j的路径有
顶点(事件)的最早发生时间:ve[j]=max{ve[i]+length[i->j]}
顶点(事件)的最晚发生时间:vl[i]=min{vl[j]-length[i->j]}
边(活动)的最早发生时间:e[i->j]=ve[i]
边(活动)的最晚发生时间:l[i->j]=vl[j]-length[i->j]
- 计算事件j的最早发生时间
vector<node> G[maxn];
int n,inDegree[maxn];//顶点数、入度
stack<int> topOrder;//记录拓扑排序
bool topologicalSort(){
queue<int> q;
for(int i=0;i<n;i++)//先将入度为零的点入队
if(inDegree[i]==0) q.push(i);
while(!q.empty()){
int u=q.front(); q.pop();
topOrder.push(u);
for(int i=0;i<G[u].size();i++){
int v=G[u][i].v;
inDegree[v]--;
if(inDegree[v]==0) q.push(v);
}
if(ve[u]+G[u][i].w>ve[v])//使用ve[u]来更新u的后继结点v
ve[v]=ve[u]+G[u][i].w;//ve[j]=max{ve[i]+length[i->j]}
}
if(topOrder.size()==n) return true;
ekse return false;
}
- 关键路径的主体
int CriticalPath(){
fill(ve,ve+maxn,0);
if(topologicalSort()==false) return -1;
fill(vl,vl+maxn,ve[n-1]);//初始值为汇点的ve,若汇点编号不清楚,则求ve数组的最大值
while(!topOrder.empty()){
int u=topOrder.top(); topOrder.pop();
for(int i=0;i<G[u].size;i++){
int v=G[u][i].v;
if(vl[v]-G[u][i].w<vl[u])//vl[i]=min{vl[j]-length[i->j]}
vl[u]=vl[v]-G[u][i].w;
}
}
for(int u=0;u<n;u++){
for(int i=0;i<G[u].size();i++){
int v=G[u][i].v, w=G[u][i].w;
int e=ve[u], l=vl[v]-w;//e[i->j]=ve[i]、、l[i->j]=vl[j]-length[i->j]
if(e==l) printf("%d->%d\n",u,v);
}
}
return ve[n-1];//返回关键路径的长度
}
错题整理
- A1052 P272 链表排序。可以设置flag表示结点是否有效,cmp排序时,flag大的排在前面然后再比较data。!!注意考虑结点全部无效的情况!!
思路整理
- A1074 P265反转链表 静态链表和order的应用,以及链表的反转
- A1064 P320 构建完全二叉排序树,求层序遍历,使用数组构建,数据排序之后,中序遍历,为空的判断是
root > n
根节点标号1. - A1107 P330并查集的应用,以及course数组的应用
- A1098 堆排序P333
- A1021 P343 使用并查集求连通分量的个数,以及求作为树根使树最高的结点(选择任意节点求可以达到的最深集合A,A中取一个元素,求可以到达的最深顶点集合B,答案为A和B的并集)。联通的边的N-1的结点一定是一个图,使用DFS遍历时,记录pre以免走“回头路”。!!注意N=1时,输出1。
- A1034 P348边权和点权的和!!!姓名和编号的联系使用map,