算法笔记3——提高篇模板

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

树型判定条件调整
LLBF(root) = 2, BF(root->lchild) = 1R(root)
LRBF(root) = 2, BF(root->lchild) = -1L(root->lchild); R(root)
RRBF(root) = -2, BF(root->lchild) = -1L(root)
RLBF(root) = -2, BF(root->lchild) = 1R(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,
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值