Java 中堆的操作

 

当前数组中,给定一个节点,如何找到该节点的子节点?根据下标找规律

如果当前下标为i,左子树下标就是2*i+1,右字数下标就是2*i+2

如果当前节点下标为i,父节点下标为(i-1)/2

 

A下标0,左子树1,右子树2

B下标1,左子树3,右子树4,

C下标2,左子树5,右子树6,

D下标3,左子树7,右子树8.

 

使用数组表示二叉树的缺点:

1、如果一棵树有很多的空节点

 

 

 

A B  C D

0 2 6 14

 

如果一棵树是完全二叉树,使用数组表示就非常合适,针对完全二叉树使用数组表示,此时空间就没有被浪费

 

 

本质上就是一个特殊的二叉树

1、是完全二叉树

2、要求对于树的中的任意节点来说,

当前节点的值必须是大于左右孩子的值=>大堆/大根堆/大顶堆

当前节点的值必须是小于左右孩子的值=>小堆/小根堆/小顶堆

注意:

1、不能是有些节点满足当前节点大于左右孩子节点,有些节点满足当前节点小于孩子节点

2、堆中规则只是父节点和子节点的关系,左右子树之间没有约束

 

堆通常都是使用数组的方式来表示

 

堆最主要的用处就是找到一个集合中的最大值/最小值

 

 

借助堆,还能高效的找到第二大/第二小的值。

借助堆,还能高效的找到第三大/第三小的值。

……                                                                        ====》对对结构进行变换才能看出来

借助堆,还能高效的找到第k大/第k小的值。=>非常经典的 top k问题

 

堆相关操作:

堆的调整操作:

给定一个数组,当前这个数组不满足堆的特性,经过一定的操作调整,把它变成一个堆

向下调整(下沉式调整):

要求当前这个树中,除了根节点之外,其他节点都符合堆的要求,此时就可以使用向下调整

 

根据下面这个树来演示:

 

 

除了根节点之外,其他节点都已经符合 大堆 的要求了,此时要想让这个结构变成堆,就需要从根节点出发,进行向下调整

以大堆为例。从当前节点出发,先找到左右子树根节点,比较大的值,把当前节点和比较大的这个子节点进行交换

交换一次,不能完成调整,此时数组仍不是一个堆

就从左右子树中找到一个最大的数进行交换

 

 

 

public static void shiftDown(int[]array,int size,int index){
    int parent = index;
    int child = 2 * parent +1;
        //如果child 对应节点存在,就继续调整,
       //如果超过size 说明当前parent已经是叶子结点
    while(child  < size){
        //查看一下右子树
        if(child +1 < size && array[child +1] > array[child]){
            child =child +1;
        }
        //child经过上面的条件,已经不知道指向的是parent的左子树还是右子树了
        //child 肯定是左右子树中较大的那个
        if(array[child] > array[parent]){
            int tmp = array[child];
            array[child] = array[parent];
            array[parent] = tmp;
        }else {
            break;
        }
        parent = child;
        child = 2 * parent + 1;
    }
}

向上调整(上浮式调整):

private void shiftUp(int[] array, int size, int index) {  //上浮调整
    int child = index;
    int parent =  (child - 1) / 2;
    // 若果child 为 0 说明 child 已经是根节点了,根节点就没有父节点
    //调整到这里已经就到顶了
    while (child > 0){
        if(array[parent] < array[child]){
            int tmp = array[parent];
            array[parent] = array[child];
            array[child] = tmp;
        }else {
            break;
        }
        child = parent;
        parent = (child - 1) / 2;
    }
}

建堆

随便给一个数组,都能创建出一个堆来

从后往前遍历,从最后的一个节点的父节点出发(最后一个非叶子节点)

一次从当前位置进行向下调整即可

public static void creatHeap(int[] array,int size){
    //从后往前遍历,从最后一个而非叶子节点出发,依次进行向下调整
    //size -1 得到的是最后一个元素的下标,
    //再次 -1 / 2 视为了根据最后一个节点下标,找到该节点对应的父亲节点下标
    for(int i = (size -1 -1) / 2;i >= 0;i--){
        shiftDown(array,size,i);
    }
}

基于向下调整的方式建堆,必须从后往前遍历,

基于向上调整的方式建堆,必须从前往后遍历。


优先队列

实现优先队列的过程中进一步完成堆的其他操作

普通队列是“先进先出”

优先队列/优先级队列:每次出队列的元素都是优先级最高的元素

进行入队列的时候,就把元素插到数组末尾,然后进行向上调整

进行出队列的时候,删除堆顶元素,然后向下调整


删除操作:

1、去最后一个元素,复制到[0] 的元素上

2、尾删最后的元素

3、从 [0] 出发,进行向下调整

public Integer poll(){
    if(size <= 0){
        return null;
    }
    int ret = array[0];
    array[0] =array[size -1];
    size--;
    shiftDown(array,size,0);
    return ret;
}
private void shiftDown(int[] array,int size,int index){
    int parent = index;
    int child = 2 * parent +1;
    while (child < size){
        if(child +1 < size && array[child + 1 ]> array[child]){
            child = child +1;
        }
        if(array[child] > array[parent]){
            int tmp = array[child];
            array[child] = array[parent];
            array[parent] =tmp;
        }else{
            break;
        }
        parent =child;
        child = parent * 2 +1;
    }
}

 

时间复杂度

入队列 ==> 向上调整 ===》O(logN)

出队列 ==> 向下调整 ===》O(logN)

取堆首元素 ===》O(1)

建堆操作:看起来是O(NlogN) 实际上是===》O(N)

 

java 标准库中已经提供了一个优先队列

标准库中的优先队列默认是一个小堆,每次出队里的元素都是最小值

相当于标准库中,定义了“值越小,优先级越高”这样的规则

也可以有办法修改这个规则

手动定义一个比较器对象,借助比较器对象来描述比较规则(描述何种数据优先级高)

下面这个代码中创建了一个匿名内部类(定义在方法中的类),下面的代码可以直接替代上方的代码,不知道类名但知道这个类实现了 Compartor 接口

PriorityQueue<Integer> queue = new PriorityQueue<>(new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o2 - o1;
    }
});

下面更简洁的方法:

点击后就可以得到下面的代码:

PriorityQueue<Integer> queue = new PriorityQueue<>((Integer o1,Integer o2) -> o2 - o1);

lambda表达式 ,Java8后添加的,相当于一个匿名的方法,由于省略的东西太多了代码看起来不好理解,我们可以展开成下面的代码:

public static void main(String[] args) {
    PriorityQueue<Integer> queue = new PriorityQueue<>((Integer o1,Integer o2) -> {
        return o2 - o1;
    });

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值