堆应用—堆排序和迪杰斯特拉算法优化

1. 堆排序

1.1 基本介绍

堆排序是利用堆这种数据结构而设计的一种排序算法,是一种选择排序,平均复杂度为O(nlogn)

堆是一种特殊的完全二叉树,满足每个结点的值都不小于或者不大于其左右孩子结点的值,对左右孩子结点的值的大小关系则不作要求。

将完全二叉树转化成数组,则有如下关系:

小顶堆:arr[i]<=arr[2i+1]&&arr[i]<=arr[2i+2]

大顶堆:arr[i]>=arr[2i+1]&&arr[i]>=arr[2i+2]

如此,可利用如上的关系进行堆排序,且升序采用大顶堆,降序采用小顶堆

1.2 思想

(1)将待排序序列构造成一个大顶堆

(2)整个序列的最大值就是堆顶的根节点

(3)将其与末尾元素进行交换,此时末尾就是最大值

(4)将剩下的元素重新构造,再完成交换即可,如此循环,即可完成。

1.3 代码

public class Order {
    public static void main(String[] args) {
        int ans[]=new int[]{34,1,24,54,25,64,742,42};
           heapSort(ans);
           System.out.print(Arrays.toString(ans));
    }
    //编写一个堆排序的方法
    public static void heapSort(int[] ans){
        int temp=0;
        //将完整的序列变成大顶堆
             for(int i=(ans.length/2) -1;i>=0;i--){
                 adjustHeap(ans,i,ans.length);
             }
        //完成最大值转移,并减少长度再进行大顶堆构造
             for(int j=ans.length-1;j>0;j--){
                 temp=ans[j];
                 ans[j]=ans[0];
                 ans[0]=temp;
                 adjustHeap(ans,0,j);
             }
    }
    
    //完成将以i对应的非叶子结点的树调整成大顶堆
    public  static void adjustHeap(int arr[],int i,int length){
        //i表示非叶子结点在数组中的索引
        //length表示对多少个元素进行调整
        int temp=arr[i];//取出当前元素的值
        for(int k=i*2+1;k<length;k=k*2+1){

                if(k+1<length&&arr[k]<arr[k+1])//左子结点小于右子结点,
                {
                    k++;
                }
                if(arr[k]>temp)//子结点大于父结点
                {
                    arr[i]=arr[k];
                    i=k;
                }
                else
                    break;
        }
        arr[i]=temp;
    }
}

对应输出为:
堆排序对应输出

2. 迪杰斯特拉算法堆优化

2.1 问题描述

当使用常规的迪杰斯特拉算法的时候,遇到数据量巨大,会造成超时,所以通过查询资料,我们使用堆对该算法进行优化。下面是应用场景:
最短旅游路径

2.2 使用工具

1.采用优先队列得到起点,这样就避免了循环得到每一轮的最短路径。因为这种队列每次poll出来的必然是满足权要求的路径。

但这样是有一个问题的,就是对于一个路径类,如何将其添加到优先路径中呢?

class Path implements Comparable<path>{
    int cost;
    int dis;
    int end;
    public Path(int end,int dis,int cost){
        this.end=end;
        this.dis=dis;
        this.cost=cost;
    }
    @Override
    public int compareTo(Path path) {
        if(this.dis==Path.dis)
            return this.cost-Path.cost;
        return this.dis-Path.dis;
    }
}

这里通过援引Comparable接口来完成这一点,这里需要重写compareTo方法,将需要的优先级顺序给出即可。

  1. 避免使用邻接数组,这样对于每一个起点寻找对应的路径就不需要对应列去遍历,这里可以使用Arraylist

2.3 代码(主函数部分)

public class Main {
    public static void main(String[] args) {
        int N,M,S,D;
        Scanner in=new Scanner(System.in);
        N=in.nextInt();//表示共有N个城市
        M=in.nextInt();//表示在N个城市间有M条路径
        S=in.nextInt();//起点,取值为0-N-1
        D=in.nextInt();//终点,取值为0-N-1
        int [] already=new int[N];//访问过则为1
        int []cost=new int[N];//出发点到每个城市的花费
        int []dis=new int[N];//出发点到每个城市的最小距离
        ArrayList<ArrayList<path>> edge=new ArrayList<>();
        for(int i=0;i<N;i++){
            ArrayList<path> tem=new ArrayList<>();
            edge.add(tem);//将每一个起点对应的path集合都选好
        }
        //使用path类保存
        for(int i=0;i<M;i++){
            int start=in.nextInt();
            int end=in.nextInt();
            int di=in.nextInt();
            int cos=in.nextInt();
            edge.get(start).add(new path(end,di,cos));
            edge.get(end).add(new path(start,di,cos));
        }
        //之后是,优先队列
        Queue<path> queue=new PriorityQueue<>();
        queue.add(new path(S,0,0));//输入起点
        int bound=100000;//这里不建议使用Max_Value,因为再相加会导致溢出
        Arrays.fill(cost,bound);
        Arrays.fill(dis,bound);
        cost[S]=0;
        dis[S]=0;
        int index=S;
        while(!queue.isEmpty()){
            path cur=queue.poll();
            int tem=cur.end;
            if(already[tem]!=1){
                already[tem]=1;
                if(tem==D)
                    break;
                for(int i=0;i<edge.get(tem).size();i++){
                    int to=edge.get(tem).get(i).end;
                    if(already[to]==1)
                        continue;
                    int cur1=edge.get(tem).get(i).dis;
                    int cur2=edge.get(tem).get(i).cost;
                    if(dis[tem]+cur1<dis[to]){
                        dis[to]=dis[tem]+cur1;
                        cost[to]=cost[tem]+cur2;
                        queue.add(new path(to,dis[to],cost[to]));
                    }else if(dis[tem]+cur1==dis[to]&&cost[to]>cost[tem]+cur2)
                    {
                        cost[to]=cost[tem]+cur2;
                        queue.add(new path(to,dis[to],cost[to]));
                    }
                }
            }
        }
        System.out.print(dis[D]+" "+cost[D]);
        }
            
}

欢迎来我的博客看看

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值