算法
堆--优先队列 && STL
手写堆:数组+函数实现
1.定义:完全二叉树的一种结构(数组下标连续)
2.性质:大根堆 and 小根堆 (根max,min)
3.含义:
heap【】:利用数组存储堆
now:新放入的数d的下标
next:父节点fa【】的下标
4.存入put:构建堆(从下到上)
①输入d,放入堆底 heap[++heap_size]=d;
②now记录d的下标
③while循环设置:一直向上找父节点,直到heap【now】>= heap【next】跳出循环
5.取出get:堆排序(从下到上)
每次取出并删除堆顶(max,min),达到堆排序
①取出根节点
②把堆底的结点放到堆顶的结点位置上,覆盖,同时堆长度len--
③把根结点置为当前父节点 fa
④while循环设置:根节点每次向下找,交换子节点中较小的那个值,直到无子节点则停止
STL:优先队列,调用c++库函数<queue>
大根堆 priority_queue<int>q:每次取出的堆顶为max
小根堆 priority_queue< int, vector<int>,greater<int> >q: 每次取出的堆顶为min
基本操作:
1.empty( ):如果队列为空返回ture
2.push( ):入队,加入一个元素
3.pop( ): 出队,删除队首元素 (先进先出)
4.size( ): 返回优先队列的元素个数
5.top( ): 返回优先队列中的队首元素(一般为max,min)
手写堆put:
//放入,建立堆
void put(int d) //heap【1】为堆顶
{
int now,next;
heap[++heap_size]=d; //先把d存入堆底
now=heap_size; //now:新放入的数d的下标
while(now>1) //下标 >1
{
next=now/2; //父节点下标
if(heap[now]>=heap[next]) break; //小根堆,达到则跳出循环
swap(heap[now],heap[next]);
now=next; //替换,一直向上找父节点
}
}
手写堆get:
//取出堆顶元素max,min(堆排序)
void get() //heap【1】为堆顶
{
int now=1,next,res=heap[1]; //now下标为1,堆顶
heap[1]=heap[heap_size]; //取出堆顶元素,将堆底最后一个元素放于堆顶
heap_size--;
while(now*2 <= heap_size) //存在左孩子(至少存在一个子节点)
{
next=now*2; //next为左孩子
if(next<heap_size && heap[next+1]<heap[next]) next++;//若右孩子更小,则next++
if(heap[now]<=heap[next]) break; //构成小根堆,跳出循环
swap(heap[now],heap[next]) //交换
now=next;
}
return res; //返回堆顶,max,min
}
STL基础模板:
#include<stdio.h>
#include<queue>
priority_queue<int> q; //建立一个小根堆
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
q.push(x); //加入元素
}
for(int i=1;i<=n;i++)
{
int res=q.top(); //获取堆顶元素
q.pop(); //出队
printf("%d",res); //输出堆顶(max,min)
}
return 0;
}
运算符重载函数:堆排序,类似sort函数
struct node//结构体名称
{
int w;
int pos;
bool operator </*小于号规则*/( const node &x )const//这里传的是地址
{
return x.w <w; //大根堆
}
};
STL进阶模板:涵盖dijk算法+链式前向星存储方式
const int N=1000010;
const int M=5000010;
int head[N];
int dis[N];
bool book[N];
long long int cnt=0;
int n,m,s;
struct node //链式前向星存图
{
int to;
int next;
int val;
} edge[M];
struct priority //运算重载符,利用结构体
{
int id; //序号
int dis; //距离
bool operator<(const priority &x)const //类似sort函数
{
return x.dis<dis; //大根堆,堆排序
}
};
void add(int u,int v,int w) //链式前向星存图
{
cnt++;
edge[cnt].to=v;
edge[cnt].next=head[u];
edge[cnt].val=w;
head[u]=cnt;
}
void dijk()
{
priority_queue <priority> q; //大根堆
for(int i=1; i<=n; i++)
{
dis[i]=2147483647; //初始化大值
}
dis[s]=0; //起点到起点的距离为0
int u;
int d;
q.push((priority){s,0}); //建队列
while(!q.empty())
{
priority temp=q.top(); //q.top()获取temp堆顶元素 (min,max)
q.pop(); //堆顶temp出队并被堆底替代,重新堆排序
u=temp.id; //不断找堆顶序号
d=temp.dis; //堆顶的距离
if(book[u]) //当这个点没被标记的时候(堆顶序号u)
continue;
book[u]=1; //堆顶标记
for(int i=head[u]; i; i=edge[i].next)//链式向前星
{
int k=edge[i].to;
if(dis[k]>dis[u]+edge[i].val)
{
dis[k]=dis[u]+edge[i].val; //更新距离dis【】
if(!book[k])
{
q.push((priority){k,dis[k]}); //push()入列
}
}
}
}
}
//
int main()
{
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
q.push(x); //加入元素,入堆(队)
}
}
刷题
1.送邮件
P1629 邮递员送信 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P16291.关键:(1->n) +( n->1)单源+多源转化为单源+单源
2.初始化:a【】【】初始化大值max,a【n】【n】自己无需初始大值(自己到自己)
3.二维数组存图:有距离输入距离,无距离为∞,自己到自己为0
4.dijk():两个for循环
①找当前最短距离:for()
②更新dis【】数组:for()
5.注意:逆向存图时的双重for循环方式
#include <stdio.h>
#include <string.h>
const int MAX=1e9;
int n,m;
int a[2010][2010];
int dis[2010],book[2010];
long long sum=0;
int min(int a,int b)
{
return a<b?a:b;
}
void dijk(int n)
{
memset(book,0,sizeof(book)) ; //标记book初始化为0
book[1]=1; //起点标记
dis[1]=0; //起点到起点最短距离为0
for(int i=1; i<=n; i++)
{
int minn=MAX;
int t=0;
for(int j=1; j<=n; j++)
{
if(!book[j]&&dis[j]<minn)
{
minn=dis[j]; //更新最短距离
t=j; //找到最短距离
}
}
book[t]=1;
for(int j=1; j<=n; j++)
{
if(!book[j]&&dis[j]>dis[t]+a[t][j])
{
dis[j]=dis[t]+a[t][j];
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
if(i!=j) //a【n】【n】横纵坐标一样的距离为0,相当于 1->1
{
a[i][j]=MAX; //初始化,大值
}
}
}
for(int i=1; i<=m; i++) //图存入二维数组
{
int c,b,w;
scanf("%d%d%d",&c,&b,&w);
a[c][b]=min(w,a[c][b]);
}
for(int i=1; i<=n; i++) //1-n
{
dis[i]=a[1][i]; //有距离更新距离,无距离为最初大值∞
}
dijk(n);
for(int i=1; i<=n; i++)
{
sum+=dis[i];
}
for(int i=1; i<=n; i++) //n-1,逆向思维转变为1-n
{
for(int j=i+1; j<=n; j++)
{
int t=0;
t=a[i][j];
a[i][j]=a[j][i];
a[j][i]=t;
}
}
for(int i=1; i<=n; i++) //n-1
{
dis[i]=a[1][i];
}
dijk(n);
for(int i=1; i<=n; i++)
{
sum+=dis[i];
}
printf("%lld\n",sum);
return 0;
}
2.单源最短路径(数据强化版)
P4779 【模板】单源最短路径(标准版) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P4779
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <queue>
using namespace std;
const int N=1000010;
const int M=5000010;
int head[N];
int dis[N];
bool book[N];
long long cnt=0;
int n,m,s;
struct node//链式前向星存图
{
int to;
int next;
int w;
} edge[M];
struct priority
{
int id;
int dis;
bool operator<(const priority &x)const
{
return x.dis<dis;
}
};
void add(int u,int v,int w)
{
cnt++;
edge[cnt].to=v;
edge[cnt].next=head[u];
edge[cnt].w=w;
head[u]=cnt;
}
priority_queue <priority> q; //建立大根堆
int main()
{
scanf("%d%d%d",&n,&m,&s);
for(int i=1; i<=n; i++)
{
dis[i]=2147483647;//根据没有答案输出 2^31-1 来初始化
}
dis[s]=0; //起点到起点的距离为0
for(int i=1; i<=m; i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);//存图;
add(a,b,c);
}
int u;
int d;
q.push((priority){s,0}); //入队
while(!q.empty()) //队里不为空
{
priority temp=q.top(); //取出堆顶
q.pop(); //取出并删除堆顶,重新排序,堆排序
u=temp.id; //相当于不断找源点
d=temp.dis;
if(book[u]) continue;
book[u]=1;
for(int i=head[u]; i; i=edge[i].next)
{
int k=edge[i].to;
if(dis[k]>dis[u]+edge[i].w)
{
dis[k]=dis[u]+edge[i].w;//更新距离
if(!book[k])
{
q.push((priority){k,dis[k]});//入列
}
}
}
}
for(int i=1; i<=n; i++)
{
printf("%d ",dis[i]);
}
return 0;
}