每日总结之最短路径4.4+堆(手写堆+STL大法)

算法

 堆--优先队列 && 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;
}
 
 
 
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值