学习笔记一、数组:堆排序算法

业务逻辑:初始化数组 \rightarrow 创建堆 \rightarrow 堆序列化  \rightarrow 堆数组排序

1.数据结构

堆(英语:heap)是计算机科学中一类特殊的数据结构的统称。

堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质:
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.业务代码

public class HeapSort<T extends Integer> {

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

    /**
     * 获取右孩子
     *
     * @param i 索引
     * @return 节点
     */
    public int right(int i) {
        return (i + 1) * 2;
    }

    /**
     * 获取父节点
     *
     * @param i 索引
     * @return 节点
     */
    public int parent(int i) {
        // 当i为根节点
        if (i == 0) {
            return -1;
        }
        return (i - 1) / 2;
    }

    /**
     * 大根堆
     *
     * @param a          数组
     * @param i          索引
     * @param heapLength 堆长度
     */
    public void bigHeapify(T[] a, int i, int heapLength) {
        int l = left(i);
        int r = right(i);
        int largest = -1;

        /**
         * 下面两个if条件句用来找到三个元素的最大元素的位置largest;
         * l < heapLength 说明l在数组内,i非叶子节点;
         */
        if (l < heapLength && a[i].compareTo(a[l]) < 0) {
            largest = l;
        } else {
            largest = i;
        }
        // r < heapLength 说明r在数组内
        if (r < heapLength && a[largest].compareTo(a[r]) < 0) {
            largest = r;
        }
        // 如果i处元素不是最大的,就把i处的元素与最大处的原交换,交换会使元素下降
        if (i != largest) {
            T temp = a[i];
            a[i] = a[largest];
            a[largest] = temp;
            // 交换元素后,以a[i]为根的树就可能不在满足大根堆性质,于是递归调用该方法
            bigHeapify(a, largest, heapLength);
        }
    }

    /**
     * 大根堆
     *
     * @param a          数组
     * @param i          索引
     * @param heapLength 堆长度
     */
    public void smallHeapify(T[] a, int i, int heapLength) {
        int l = left(i);
        int r = right(i);
        int least = -1;

        /**
         * 下面两个if条件句用来找到三个元素的最小元素的位置largest;
         * l < heapLength 说明l在数组内,i非叶子节点;
         */
        if (l < heapLength && a[i].compareTo(a[l]) > 0) {
            least = l;
        } else {
            least = i;
        }
        // r < heapLength 说明r在数组内
        if (r < heapLength && a[least].compareTo(a[r]) > 0) {
            least = r;
        }
        // 如果i处元素不是最大的,就把i处的元素与最大处的原交换,交换会使元素下降
        if (i != least) {
            T temp = a[i];
            a[i] = a[least];
            a[least] = temp;
            // 交换元素后,以a[i]为根的树就可能不在满足大根堆性质,于是递归调用该方法
            smallHeapify(a, least, heapLength);
        }
    }

    /**
     * 创建一个完成的堆序列
     *
     * @param a          数组
     * @param heapLength 数组长度
     */
    public void buildHeap(T[] a, int heapLength) {
        // 从后往前看,lengthParent-1 出的元素是第一个有孩子节点的节点
        int lengthParent = parent(heapLength - 1);
        // 最初,parent(length)之后的所有元素都是叶子结点;
        // 因为大于length/2处元素的孩子节点如果存在,那么
        // 它们的数组下标值必定大于length,这与事实不符;
        // 在数组中,孩子元素必定在父亲元素的后面,从后往前
        // 对元素调用bigHeapify,保证了元素的孩子都是大根堆
        for (int i = lengthParent; i >= 0; i--) {
            bigHeapify(a, i, heapLength);
        }
    }

    public static void main(String[] args) {
        // 初始化一个无序数组
        Integer[] ints = new Integer[]{9, 24, 2, 7, 26, 6, 3, 16, 8};
        HeapSort<Integer> heapSort = new HeapSort<>();
        // 调用堆创建方法,将无序数组生成为堆数组
        heapSort.buildHeap(ints, ints.length);
        // 打印输出堆数组
        System.out.println("堆数组序列:" + Arrays.asList(ints));
        int len = ints.length - 1;
        // 递归排序堆数组
        while (len > 0) {
            swap(ints, 0, len);
            len--;
            heapSort.bigHeapify(ints, 0, len);
        }
        // 打印输出排序数组
        System.out.println("排序数组序列:" + Arrays.asList(ints));
    }
}

3.算法总结

数组堆排序是一种非线性时间比较类选择型排序

平均时间复杂度为O(n\log n),最好的情况为O(n\log n),最坏的情况O(n\log n),空间复杂度为O(1),排序方式为In-place,是一种不稳定的排序算法

4.笔记总结

(1)初始化普通无序数组

(2)将无序数组转化成堆性质(大堆根,小堆根)数组

         无序数组需要转化成堆性质数组,需要进行堆节点交换操作:

         在进行堆节点交换时,从最后一个非叶子(一个树枝的最后一个节点成为叶子节点,没有叶子节点的节点称之为非叶子节点)节点a进行开始,与左b右c子节点进行比对大小,将三个节点中最大的节点交换到父节点a位置,如果b节点数据是三个节点中数据最大,则将a,b节点数据交换,然后再将交换之后的b节点数据进行与自己的左右子节点进行比对交换操作,依次循环,直到没有所处位置比左右子节点数据都大时结束;然后将原节点a的索引减一,进行循环比对交换。直到该堆符合相关性质为止。

(3)将堆性质数组进行(按大、小)进行排序

         获得堆性质数组之后,将数组最后一个位置数据与第一个进行交换,然后将原数组刚刚交换到最后一个位置的数据排除,将剩下的数组数据进行堆性质数据。堆序列化后,再进行数组第一个位置与倒数第二个进行交换,然后排序倒数第二个数组位置数据,依次循环往复。

(4)得到一个有序的数组

————————————————

对堆数据结构有疑惑的,点击原文链接
原文链接:https://blog.csdn.net/u013728021/article/details/84034420

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值