最小堆排序_python3_堆优化dijkstra

编程对于工科学生的重要性不言而喻,如果你喜欢这篇文章,欢迎一起交流学习!欢迎交流:mate595@126.com

写在前面:我写C写惯了。python总是会加分号,像强迫症一样,大家别介意啊。

本文主要记录最小堆,堆优化dijkstra在百度文库有另一个版本,id: geek595,均原创。

本文先给出基本最小堆排序,然后再给出堆优化dijkstra(大二写的代码,很丑也没修改,求别喷)。

 

堆排是一种非常高效的排序,可以用在dijkstra、prim等算法中,优化算法复杂度。

相比较于桶排,push与pop复杂度稍有提高,但是空间复杂度却非常合理。

堆排利用了完全二叉树的性质。完全二叉树常常用数组保存(下标从1开始)。

当前节点:i;

父节点: i / 2;

左子节点:2*i;     又子节点:2*i+1;

例如,下图转换为数组就是:

元素3849101291113
下标123456789

1 push元素入堆的步骤:

首先建立一个空堆,将元素插入进去。

         如上图,假设此时C节点是我们要插入的节点。

  1. 首先,将C节点插入二叉树的尾部。
  2. 接着,根据C节点的键值,与其父节点进行比较,如果它小于父节点键值,则将其与父节点交换。改变其数组元素下表。再次判断,直到其父节点键值小于C节点键值。
  3. 边界条件为不能超过数组下表“1”,即C节点最高位置为根节点。

2 pop元素出堆

这里我们还采用上面的二叉树图进行讲解。由堆的特性,根节点A为我们要删除的元素。

  1. 首先,从数组中删除第一个元素,将数组中最后一个元素(C节点)放到A的位置。
  2. 接着,比较该节点与两个子节点的大小,选择小的儿子进行交换,并修改数组下标。循环进行,直到它的两个儿子都比它的键值大为止。
  3. 边界条件为不能超过当前数组中元素个数。

下面就是最激动人心的代码啦!

# -*- coding: utf-8 -*-

# Min Heap Sort

# Insert elements in the heap.
def heap_sort_push(myList):
    # Because of complete binary trees' character,don't use the heap's first element.
    heap = [-1];
    for i in range(len(myList)):
        t = i+1;
        heap.append(myList[i]);
        # Bubbling up
        while heap[t] < heap[t//2]: # In python3, use '//' to replace '/'. 
            heap[t//2], heap[t] = heap[t], heap[(t)//2];
            t = t//2;
    return heap;
    

def find_smallest(x,y,t):
    if x >= y:
        return 2*t+1;
    else:
        return 2*t;    
# Pop an element from the heap
def heap_sort_pop(heap):
    popElement = heap[1];
    # Move the first element and last one.
    heap[1] = heap.pop(len(heap)-1);
    # Bubbling down.
    for i in range(len(heap)):
        t = i+1;
        if heap[t] > heap[t*2] or heap[t] > heap[t*2+1]:
            s = find_smallest(heap[t*2],heap[t*2+1],t)
            heap[t],heap[s] = heap[s],heap[t];
        else:
            return (popElement,heap);
        
        
myList = [11,3,8,10,9,13,12,4];
heap = heap_sort_push(myList);
print(heap[1:]);
(firstElement,heap) = heap_sort_pop(heap);
print(heap[1:]);

 

以上代码复制粘贴即可运行。效果:

下面是堆优化dijkstra,写的很烂,但是能跑,大佬们可略过避免辣眼睛

#include <iostream>
#include <time.h>
#include <stdio.h>
#include <string.h>
using namespace std;
#define maxnum 1000
int link[maxnum][maxnum];//记录每个点的邻接边
int c[maxnum][maxnum]; //邻接矩阵
int A[maxnum],n;
struct node
{
    int s;//起点
    int e;//终点
    int w;//权
};
typedef struct
{
    int s[maxnum];
    int e[maxnum];
    int w[maxnum];
    int length;
}nodee;
int top(nodee *l,int i)
{
	if(i==1)
        printf("(%d,%d)-%d\n",l->s[1],l->e[1],l->w[1]);
}
void insert_tail(nodee *l,int s,int e,int w)//插入新元素
{
    int x,ts,te,tw,ww;
    l->length=l->length+1;
    l->s[l->length]=s;
    l->e[l->length]=e;
    l->w[l->length]=w;
    /*开始向上冒泡*/
    x=l->length;
    while(x>1)
    {
        if(x/2==0)
            continue;
    	if(l->w[x]<l->w[x/2])
    	{
    		ts=l->s[x];
    		te=l->e[x];
			tw=l->w[x];
    		l->s[x]=l->s[x/2];
    		l->e[x]=l->e[x/2];
    		l->w[x]=l->w[x/2];
    		l->s[x/2]=ts;
    		l->e[x/2]=te;
    		l->w[x/2]=tw;
    		x=x/2;
	    }
	    else
	    	break;
    }
}
void d_elete(nodee *l)
{
	int ts,te,tw,x,f1=0,f2=0,f3=0;
	l->s[1]=l->s[l->length];
	l->e[1]=l->e[l->length];
	l->w[1]=l->w[l->length];
	l->s[l->length]=65535;
	l->e[l->length]=65535;
	l->w[l->length]=65535;
	l->length=l->length-1;
	/*向下冒泡*/
	x=1;
    while((2*x)<=l->length)/*判断父亲和两个儿子的权重大小*/
    {
    	
	    if(l->w[x]>l->w[2*x+1]) f1=1;
        if(l->w[x]>l->w[2*x]) f2=1;
        if(l->w[2*x]>l->w[x*2+1]) f3=1;
        if(f3==1&&f1==1&&f2==1)
        {
            ts=l->s[x];
                te=l->e[x];
                tw=l->w[x];
                l->s[x]=l->s[x*2+1];
                l->e[x]=l->e[x*2+1];
                l->w[x]=l->w[x*2+1];
                l->s[x*2+1]=ts;
                l->e[x*2+1]=te;
                l->w[x*2+1]=tw;
                x=2*x+1;
        }
        if(f1==1&&f2==1&&f3==0)
        {
            ts=l->s[x];
    		te=l->e[x];
			tw=l->w[x];
    		l->s[x]=l->s[x*2];
    		l->e[x]=l->e[x*2];
    		l->w[x]=l->w[x*2];
    		l->s[x*2]=ts;
    		l->e[x*2]=te;
    		l->w[x*2]=tw;
			x=2*x;
        }
        if(f1==0&&f2==0)
            break;
        if(f1==0&&f2==1)
        {
            ts=l->s[x];
    		te=l->e[x];
			tw=l->w[x];
    		l->s[x]=l->s[x*2];
    		l->e[x]=l->e[x*2];
    		l->w[x]=l->w[x*2];
    		l->s[x*2]=ts;
    		l->e[x*2]=te;
    		l->w[x*2]=tw;
			x=2*x;
        }
        if(f1==1&&f2==0)
        {
            ts=l->s[x];
                te=l->e[x];
                tw=l->w[x];
                l->s[x]=l->s[x*2+1];
                l->e[x]=l->e[x*2+1];
                l->w[x]=l->w[x*2+1];
                l->s[x*2+1]=ts;
                l->e[x*2+1]=te;
                l->w[x*2+1]=tw;
                x=2*x+1;
        }
        f1=0;f2=0;f3=0;
    }
}
void dijkstra(int s)
{
    int i,total;
    int vis[maxnum];//用来标记节点已访问
    memset(vis,0,sizeof(vis));//将vis中成员标记为0
    struct node nn;
    nodee heap;//建立堆
    heap.length=0;
    memset(heap.w,65535,sizeof(heap.w));
    memset(heap.s,65535,sizeof(heap.s));
    memset(heap.e,65535,sizeof(heap.e));
    total  = 1;//统计已经加入的节点数
    vis[s] = 1;//初始化第一个节点为已探索

    while(total < n)
    {
        for(i=1;i<link[s][0];i++)//探索每个节点的邻接边
            if(!vis[link[s][i]])
            {
                nn.s = s;
                nn.e = link[s][i];
                nn.w = c[s][nn.e];
                insert_tail(&heap,nn.s,nn.e,(nn.w+A[nn.s]));
            }
        while(vis[heap.e[1]])
            d_elete(&heap);
        s = heap.e[1];
        cout<<heap.s[1]<<'~'<<heap.e[1]<<endl;
        A[heap.e[1]]=heap.w[1];
        vis[s] = 1;//标记为集合内的元素
        d_elete(&heap);
        total++;
    }
}

int main()
{
    int i;
    int line,len;
    int t,s,d,p,q;
    cin>>n>>line;
    for(i=1;i<=n;i++)
    link[i][0] = 1;
    for(i=1;i<=line;i++)
    {
        cin>>p>>q>>len;
        c[p][q] = c[q][p] = len;
        link[p][link[p][0]++] = q;
        link[q][link[q][0]++] = p;
    }
    cout<<"s:"<<endl;
    memset(A,65536,sizeof(A));
    cin>>i;
    A[i]=0;
    dijkstra(i);//以源节点为起点
    while(1)
    {
        cout<<"e:"<<endl;
        cin>>i;
        if(i==0)
            break;
        cout<<"w:"<<A[i]<<endl;;
    }
    return 0;
}

/
#include <iostream>
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
using namespace std;
#define maxnum 1000
int link[maxnum][maxnum];//记录每个点的邻接边
int c[maxnum][maxnum]; //邻接矩阵
int A[maxnum],n;
struct node
{
    int s;//起点
    int e;//终点
    int w;//权
};
typedef struct
{
    int s[maxnum];
    int e[maxnum];
    int w[maxnum];
    int length;
}nodee;
int top(nodee *l,int i)
{
	if(i==1)
        printf("(%d,%d)-%d\n",l->s[1],l->e[1],l->w[1]);
}
void insert_tail(nodee *l,int s,int e,int w)//插入新元素
{
    int x,ts,te,tw,ww;
    l->length=l->length+1;
    l->s[l->length]=s;
    l->e[l->length]=e;
    l->w[l->length]=w;
    /*开始向上冒泡*/
    x=l->length;
    while(x>1)
    {
        if(x/2==0)
            continue;
    	if(l->w[x]<l->w[x/2])
    	{
    		ts=l->s[x];
    		te=l->e[x];
			tw=l->w[x];
    		l->s[x]=l->s[x/2];
    		l->e[x]=l->e[x/2];
    		l->w[x]=l->w[x/2];
    		l->s[x/2]=ts;
    		l->e[x/2]=te;
    		l->w[x/2]=tw;
    		x=x/2;
	    }
	    else
	    	break;
    }
}
void d_elete(nodee *l)
{
	int ts,te,tw,x,f1=0,f2=0,f3=0;
	l->s[1]=l->s[l->length];
	l->e[1]=l->e[l->length];
	l->w[1]=l->w[l->length];
	l->s[l->length]=65535;
	l->e[l->length]=65535;
	l->w[l->length]=65535;
	l->length=l->length-1;
	/*向下冒泡*/
	x=1;
    while((2*x)<=l->length)/*判断父亲和两个儿子的权重大小*/
    {
    	
	    if(l->w[x]>l->w[2*x+1]) f1=1;
        if(l->w[x]>l->w[2*x]) f2=1;
        if(l->w[2*x]>l->w[x*2+1]) f3=1;
        if(f3==1&&f1==1&&f2==1)
        {
            ts=l->s[x];
                te=l->e[x];
                tw=l->w[x];
                l->s[x]=l->s[x*2+1];
                l->e[x]=l->e[x*2+1];
                l->w[x]=l->w[x*2+1];
                l->s[x*2+1]=ts;
                l->e[x*2+1]=te;
                l->w[x*2+1]=tw;
                x=2*x+1;
        }
        if(f1==1&&f2==1&&f3==0)
        {
            ts=l->s[x];
    		te=l->e[x];
			tw=l->w[x];
    		l->s[x]=l->s[x*2];
    		l->e[x]=l->e[x*2];
    		l->w[x]=l->w[x*2];
    		l->s[x*2]=ts;
    		l->e[x*2]=te;
    		l->w[x*2]=tw;
			x=2*x;
        }
        if(f1==0&&f2==0)
            break;
        if(f1==0&&f2==1)
        {
            ts=l->s[x];
    		te=l->e[x];
			tw=l->w[x];
    		l->s[x]=l->s[x*2];
    		l->e[x]=l->e[x*2];
    		l->w[x]=l->w[x*2];
    		l->s[x*2]=ts;
    		l->e[x*2]=te;
    		l->w[x*2]=tw;
			x=2*x;
        }
        if(f1==1&&f2==0)
        {
            ts=l->s[x];
                te=l->e[x];
                tw=l->w[x];
                l->s[x]=l->s[x*2+1];
                l->e[x]=l->e[x*2+1];
                l->w[x]=l->w[x*2+1];
                l->s[x*2+1]=ts;
                l->e[x*2+1]=te;
                l->w[x*2+1]=tw;
                x=2*x+1;
        }
        f1=0;f2=0;f3=0;
    }
}
void dijkstra(int s)
{
    int i,total;
    int vis[maxnum];//用来标记节点已访问
    memset(vis,0,sizeof(vis));//将vis中成员标记为0
    struct node nn;
    nodee heap;//建立堆
    heap.length=0;
    memset(heap.w,65535,sizeof(heap.w));
    memset(heap.s,65535,sizeof(heap.s));
    memset(heap.e,65535,sizeof(heap.e));

    total  = 1;//统计已经加入的节点数
    vis[s] = 1;//初始化第一个节点为已探索

    while(total < n)
    {
        for(i=1;i<link[s][0];i++)//探索每个节点的邻接边
            if(!vis[link[s][i]])
            {
                nn.s = s;
                nn.e = link[s][i];
                nn.w = c[s][nn.e];
                insert_tail(&heap,nn.s,nn.e,(nn.w+A[nn.s]));
            }
        while(vis[heap.e[1]])
            d_elete(&heap);
        s = heap.e[1];
        //cout<<heap.s[1]<<'~'<<heap.e[1]<<endl;
        A[heap.e[1]]=heap.w[1];
        vis[s] = 1;//标记为集合内的元素
        d_elete(&heap);
        total++;
    }
}

int main()
{
    time_t tic,toc;
    int i;
    int line,len;
    int t,s,d,p,q;
    cin>>n>>line;
    for(i=1;i<=n;i++)
    link[i][0] = 1;
    for(i=1;i<=line;i++)
    {
        cin>>p>>q>>len;
        c[p][q] = c[q][p] = len;
        link[p][link[p][0]++] = q;
        link[q][link[q][0]++] = p;
    }
    //cout<<"s:"<<endl;
    memset(A,65536,sizeof(A));
    //cin>>i;
    i=1;
    A[i]=0;
    tic=clock();
    for(int j=0;j<10000;j++)
        dijkstra(i);//以源节点为起点
    toc=clock();
    cout<<toc-tic<<"ms"<<endl;
    /*while(1)
    {
        cout<<"e:"<<endl;
        cin>>i;
        if(i==0)
            break;
        cout<<"w:"<<A[i]<<endl;;
    }*/
    return 0;
}

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值