算法 - 排序 - 堆排序

11 篇文章 0 订阅

1. 简介

堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。

堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质:
1.堆中某个节点的值总是不大于或不小于其父节点的值;
2.堆总是一棵完全二叉树。

常见的堆有二叉堆、斐波那契堆等。

堆的定义:n个元素的序列{k1,k2,ki,…,kn}当且仅当满足下关系时,称之为堆。
(ki <= k2i,ki <= k2i+1)或者(ki >= k2i,ki >= k2i+1), (i = 1,2,3,4…n/2)

2. 算法演示

排序动画过程解释

  1. 首先,将所有的数字存储在堆中

  2. 按大顶堆构建堆,其中大顶堆的一个特性是数据将被从大到小取出,将取出的数字按照相反的顺序进行排列,数字就完成了排序

  3. 在这里数字 5 先入堆

  4. 数字 2 入堆

  5. 数字 7 入堆, 7 此时是最后一个节点,与最后一个非叶子节点(也就是数字 5 )进行比较,由于 7 大于 5 ,所以 7 和 5 交互

  6. 按照上述的操作将所有数字入堆,然后从左到右,从上到下进行调整,构造出大顶堆

  7. 入堆完成之后,将堆顶元素取出,将末尾元素置于堆顶,重新调整结构,使其满足堆定义

  8. 堆顶元素数字 7 取出,末尾元素数字 4 置于堆顶,为了维护好大顶堆的定义,最后一个非叶子节点数字 5 与 4 比较,而后交换两个数字的位置

  9. 反复执行调整+交换步骤,直到整个序列有序

3. 代码


/**
 * Created by lizq on 2020/3/3.
 */
public class HeapSort {

    public static void main(String[] args) {
        long[] arrs = RandomArr.createLongArr(20, 0, 200);
        long[] arrs2 = Arrays.copyOf(arrs, arrs.length);
        Arrays.stream(arrs).forEach(l -> {
            System.out.print(l + " ");
        });
        System.out.println();
        sort(arrs);

        Arrays.stream(arrs).forEach(l -> {
            System.out.print(l + " ");
        });
        System.out.println();
        Arrays.stream(arrs2).sorted().forEach(l -> {
            System.out.print(l + " ");
        });
    }

    public static void sort(long[] arrs) {
        buildHeap(arrs);
        int len = arrs.length;
        for (int i = len - 1; i > 0; i--) {
            swap(arrs, 0, i);
            len--;
            heapify(arrs, 0, len);
        }
    }


    public static void buildHeap(long[] arrs) {
        // 找到最后一个没有子树的节点
        for (int i = (int) Math.floor(arrs.length / 2); i >= 0; i--) {
            heapify(arrs, i, arrs.length);
        }
    }


    public static void heapify(long[] arrs, int i, int len) {

        int l = getLeft(i);
        int r = getRight(i);
        long tmp;
        int largest = i;

        // 如果左子树大于根节点,则把左子树设置最大值
        if (l < len && arrs[l] > arrs[largest]) {
            largest = l;
        }
        // 如果右子树大于根节点,则把右子树设置最大值
        if (r < len && arrs[r] > arrs[largest]) {
            largest = r;
        }
        if (largest != i) {
            // 只交换最大那个分支
            swap(arrs, i, largest);
            // 递归修正换了节点那个分支
            heapify(arrs, largest, len);
        }
    }

    /**
     * 获取左子节点的索引
     *
     * @param i
     * @return
     */
    public static int getLeft(int i) {
        return i * 2 + 1;
    }


    /**
     * 获取右子节点的索引
     *
     * @param i
     * @return
     */
    public static int getRight(int i) {
        return i * 2 + 2;
    }

    /**
     * 获取父亲节点
     *
     * @param i
     * @return
     */
    public static int getParent(int i) {
        // 根节点
        if (i == 0) {
            return -1;
        }
        return (i - 1) / 2;
    }

    /**
     * 交换元素
     *
     * @param arrs
     * @param i
     * @param j
     */
    public static void swap(long[] arrs, int i, int j) {
        // 交换元素
        long tmp = arrs[i];
        arrs[i] = arrs[j];
        arrs[j] = tmp;
    }

}

4. 总结

构建堆的过程就是把最大值(最小值) 从最底层升到最上层, 根节点一定是最大值(最小值)

排序过程把首节点和尾节点互换,然后对之前的再次进行从上倒下构造堆

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值