数据结构_堆

堆(优先队列)

1.概述

堆是一颗完全二叉树

一般都是用数组来模拟堆,堆中的索引满足下面的关系,假设一个节点的索引为x,那么它的左儿子节点为 2 * x 右儿子节点索引为 2 * x + 1所以为了方便操作一般根节点的索引都是从1开始
在这里插入图片描述

大根堆与小根堆

为何堆又称为优先队列,因为我们根据某个条件将堆中最符合条件的值动态调整到根节点的位置 ,所以堆可以用来排序,我们调整每个父节点的值都小于或者等于两个儿子节点 所以根节点的值就是堆中的最小值,这就是小根堆

大根堆顾名思义,每个父节点的值都大于或者等于两个儿子节点的值

2.实现堆排序

堆排序是不稳定的

AcWing838.堆排序 ——>题解

输入一个长度为 n的整数数列,从小到大输出前 m 小的数。

我们使用堆排序,先构造一个堆,每次总是满足根节点的值是最小的(down方法),然后让其出队(删除),然后将最后一个节点的值赋给根节点,然后再次调整使其最小且堆中的所有节点都满足小于等于儿子节点,(这个就是删除头结点的做法)

2.1实现down方法

down方法就是调整某一个节点,使其满足大于(小于)或者等于两个儿子节点的值

如下图所示,7这个节点显然是不满足小于两个儿子节点的是,所以与两个儿子节点比较值的大小,然后调整其位置,让其与值为7的节点交换后满足条件
在这里插入图片描述

down方法实现

由于我们要满足删除头节点后堆中所有的节点还都满足小于两个儿子节点,所有我们要从上到下调整,我们可以用递归和迭代两种方式做。

迭代版

    //down方法 传来的是父节点的索引 迭代版
    private static void downIter(int x){
        int t = x;
        while(true){
          //判断左右两个儿子节点的值是否小于父节点
          if(2 * x <= size && heap[2 * x] < heap[t]) t = 2 * x;
          if(2 * x + 1 <= size && heap[2 * x + 1] < heap[t]) t = 2 * x + 1;
          //最小值的索引不是父节点索引 说明得调整
          if(t != x){
             int temp = heap[t];
             heap[t] = heap[x];
             heap[x] = temp;
             x = t;
           }
           //最小值就是自己 往下就不用比较了 直接break了
           else break;
        }
    }

递归版

    //down函数 递归版
    private static void downRecu(int x){
         int t = x;
         if(2 * x <= size && heap[2 * x] < heap[t]) t = 2 * x;
         if(2 * x + 1 <= size && heap[2 * x + 1] < heap[t]) t = 2 * x + 1;
         if(t != x){
             int temp = heap[t];
             heap[t] = heap[x];
             heap[x] = temp;
             //递归做
             downRecu(t);
         }
    }

2.2构造堆

这里构造堆有技巧,因为最后一层没有子节点,我们可以直接从倒数第二层的最后一个元素开始down,一直down到根节点 时间复杂度是O(n)

import java.util.Scanner;

public class Main{
	
    //数组 模拟堆
    private static int[] heap = new int[100010];
    //元素个数 也代表索引
    private static int size;

    public static void main(String[] args){
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();
        //先将所有的元素读到数组中
        for(int i = 1; i <= n; i ++ )heap[i] = in.nextInt();
        size = n;
		
        //构造堆 从倒数第二层开始down  
        for(int i = n / 2; i != 0; i --) downIter(i);
        
        //此时的堆就是一个小根堆 所有的父节点都小于或者等于儿子节点
        
        //输出前m个最小值
        while(m -- > 0 ){
            //直接输出根节点的值就是最小值 
            System.out.print(heap[1] + " ");
            
            //删除根节点 将最后一个节点值赋给根节点,然后down根节点
            heap[1] = heap[size--];
            downIter(1);
        }
    }
}

2.3额外实现up操作

将一个节点向上调整 即当插入一个数时,这个数首先在最后,为了保证特性,这时必须向上调整

void up(int x ){
    //跟它的父节点比较
    while(x / 2 != 0 && heap[x / 2] > heap[x]){
      //交换heap[x] heap[x / 2]
      swap(heap[x],heap[x / 2]);
      //继续向上
      x / = 2;
   } 
}

4.Java中的PriorityQueue

Java中的优先队列底层是一个Object类型的数组,默认初始化容量是11,当第一次插入元素时才会创建这个数组对象

transient Object[] queue;

构造器

常用的一个构造器,可以传入一个比较器,作用就是指定优先的规则

public PriorityQueue(Comparator<? super E> comparator)

常用方法

//添加 add底层就是offer
public boolean add(E e) 
public boolean offer(E e) 
    
//删除某个元素    
public boolean remove(Object o)

//查看队头元素 element底层调的就是peek 只是队头是null报异常
public E element()    
public E peek() 

//队头元素出队 (重新调整堆)    
public E poll() {  
        
//是否包含某一个元素
public boolean contains(Object o)    
//判断是否一个存在一个集合的元素    
public boolean containsAll(Collection<?> c) {
    
//清空队列
public void clear() { 
 
//转成一个指定类型的数组    
public <T> T[] toArray(T[] a)   
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shstart7

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值