认识堆和堆排序

10、堆

image-20210901091131546

10.1、完全二叉树

image-20210901091628693

10.2、大跟堆

在一个完全二叉树里,每一颗子树的最大值就是头结点的值,满足这个条件就是大跟堆。

image-20210901091733660

10.3、小跟堆

在一个完全二叉树中,每一颗子树的最小值就是他的头结点,满足这个条件就是小跟堆。

怎么让一个完全二叉树变成大跟堆呢?

一开始heapsize是为0的,然后5进来的时候,就放到heapsize为0的位置,然后heapsize变为1了,再进来一个数3的时候,将其放到heapsize为1的位置,然后heapsieze变为2;在进来6的时候,将其放到heapsize为2的位置,heapsize变为3;这时候根据父结点的寻找公式来确定子节点的父结点是啥,所以6可以确定他的父结点是5,这时候就要比较父结点跟子节点,子节点要是比父结点大,就得交换两者位置,如果小的话就不用交换位置,继续处理进来的另一个数。通过父结点的比较可以每次进来一个数都能将其调整为大跟堆的形式。

image-20210901092213122

image-20210901092614705

image-20210901092920985

要将大跟堆中的最大值拿掉,怎么让原本的大跟堆还保持大跟堆的状态。

大跟堆的头结点跟最后一个结点的值交换,然后heapsize减小1;heapsize减一之后就以为这之前的头结点也就是换到最后一个结点的值是不存在大跟堆了,因为heapsize减一后如果还要最后一个结点就是下标越界了。

调换之后可能之前的大跟堆已经不再是大跟堆的结构了,所以需要再次调整为大跟堆,怎么调整呢?

从头结点开始,找他的子节点中的最大值,然后最大值跟父结点交换。

image-20210901094027930

image-20210901094045899

问题:如果现在要修改大跟堆中的i位置的某个值为其他的值,那么怎么保证这个完全二叉树还是大跟堆呢?

首先看他修改后的值跟之前的值比较,如果比之前的大,那么就跟上面的进行一个比较,也就是headfy操作。如果比之前的小,就跟下面的进行比较。

image-20210901094548783

堆其实就是只有两个操作,一个是heapsize操作一个是headfy操作。

package 左神算法.;

public class Heap {
    public static void main(String[] args) {
        int[] arr = {7, 3, 6, 5, 8};
        int index = 4;
        int heapSize = 0;
        heapInsert(arr, index);
        heapify(arr, index, heapSize);
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
    }
//某个数现在处在index位置,往上继续移动
    public static void heapInsert(int[] arr, int index) {
        while (arr[index] > arr[(index - 1) / 2]) {
            swap(arr, index, (index - 1) / 2);
            index = (index - 1) / 2;
        }
    }

    //某个数在index位置,能否往下移动
    public static void heapify(int[] arr, int index, int heapSize) {
        int left = index * 2 + 1;//左孩子的下标
        while (left < heapSize) {//下方还有孩子的时候
//            两个孩子中,谁的值大,把下标给largest
            int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left;
//            父和孩子之间,谁的值大,把下标给largest
            largest = arr[largest] > arr[index] ? largest : index;
            if (largest == index) {
                break;
            }
            swap(arr, largest, index);
            index = largest;
            left = index * 2 + 1;
        }
    }
    public static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}
/*
8 7 6 5 3 
 */

时间复杂度:O(logN)

11、堆排序

image-20210901095439991

时间复杂度:O(NlogN)

如果是给定了所有的元素在堆里面,然后调整成大跟堆,就可以先调整下面的然后继续往上。结果时间复杂度可以计算如下:完全二叉树中的叶子结点是N/2;

image-20210901100405131

image-20210901100621208

11.1、堆排序扩展题目

image-20210901100704998

假设k=6的时候,数组中的0-6中的7个元素加入到小跟堆中,因为k=6,移动距离不能超过6,所以将0-6位置的元素从小跟堆弹出,然后再加入7后面的元素。最后数组中的元素都会从小跟堆弹出到一个数组中

时间复杂度O(N*logK)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值