堆结构与堆排序详解-左程云

堆结构与堆排序

堆结构

逻辑结构:完全二叉树的形式(从左到右,一层层覆盖,且每个父节点最多有两个子节点)
物理结构:采用数组进行存储
如何保证完全二叉树与数组前缀范围的映射?

  • 下标为i的元素,父亲结点为(i-1)/2,左孩子为2i+1,右孩子为2i+2(结点0的父亲结点仍然是自己,根节点)

大小根堆的区别:

  • 大根堆父节点大于所有子节点,小根堆父节点小于所有子节点

堆的调整:

  • 向上调整 heapInsert:从当前i结点开始调整,如果当前节点大于他的父节点,则交换当前结点与父节点元素,并让i等于父节点下标,i= (i-1)/2,直到不再大于其父亲结点或者到达下标为0结点为止(0的父节点也是0,显然不会大于)
// i位置的数,向上调整大根堆

    // arr[i] = x,x是新来的!往上看,直到不比父亲大,或者来到0位置(顶)

    public static void heapInsert(int[] arr, int i) {

        while (arr[i] > arr[(i - 1) / 2]) {

            swap(arr, i, (i - 1) / 2);

            i = (i - 1) / 2;

        }

    }
  • 向下调整heapify:
    • 第一步让i等于其左孩子
    • 只要i小于size就不断迭代循环
    • 若右孩子结点存在并且左孩子小于右孩子,则最大索引best等于右孩子索引,否则左孩子索引
    • 若best元素大于i元素交换best与i元素
    • 若未交换则直接break,说明不用再向下调整了,子树都比你小
    • 让i等于best
    • 让i等于其左孩子继续迭代
// i位置的数,变小了,又想维持大根堆结构

    // 向下调整大根堆

    // 当前堆的大小为size

    public static void heapify(int[] arr, int i, int size) {

        int l = i * 2 + 1;

        while (l < size) {

            // 有左孩子,l

            // 右孩子,l+1

            // 评选,最强的孩子,是哪个下标的孩子

            int best = l + 1 < size && arr[l + 1] > arr[l] ? l + 1 : l;

            // 上面已经评选了最强的孩子,接下来,当前的数和最强的孩子之前,最强下标是谁

            best = arr[best] > arr[i] ? best : i;

            if (best == i) {

                break;

            }

            swap(arr, best, i);

            i = best;

            l = i * 2 + 1;

        }

    }
  • 向上调整与向下调整时间复杂度分析:
    树层高为logn,向上调整与向下调整的时间复杂度均为logN。

堆排序

  • 代码
    从顶到底建堆,heapInsert方法,每次插入一个结点,开始向上调整直到他的位置,可以分析出复杂度为时间复杂度O(n(ogn),log1+|og2+log3+.+logn->O(n(ogn)
    或者使用倍增分析法分析时间复杂度:一个复杂度既是N个数据量为时为下线,2N个数据量时为上限,那么他的复杂度就是这个复杂度,因为常量不会影响复杂度**
    这里可以进行如下分析:
    如果数据量为N,log1+|og2+log3+.+logn其上限为nlogn,如果数据量为2n,其时间复杂度的下限为nlogn,但数据量不会影响时间复杂度,故可以得出时间复杂度为nlogn
// 从顶到底建立大根堆,O(n * logn)

    // 依次弹出堆内最大值并排好序,O(n * logn)

    // 整体时间复杂度O(n * logn)

    public static void heapSort1(int[] arr) {

        int n = arr.length;

        for (int i = 0; i < n; i++) {

            heapInsert(arr, i);

        }

        int size = n;

        while (size > 1) {

            swap(arr, 0, --size);

            heapify(arr, 0, size);

        }

    }
  • 自底向上建堆
    建堆过程优化到了logN,具体过程不赘述了,只需要直到大部分的结点调整层数少,少部分的结点调整层数多,这一点与自顶向上建堆相反
// 从底到顶建立大根堆,O(n)

    // 依次弹出堆内最大值并排好序,O(n * logn)

    // 整体时间复杂度O(n * logn)

    public static void heapSort2(int[] arr) {

        int n = arr.length;

        for (int i = n - 1; i >= 0; i--) {

            heapify(arr, i, n);

        }

        int size = n;

        while (size > 1) {

            swap(arr, 0, --size);

            heapify(arr, 0, size);

        }

    }

附上全部代码以及leetcode,牛客测试链接

// 堆结构和堆排序,填函数练习风格

// 测试链接 : https://leetcode.cn/problems/sort-an-array/

public class Code02_HeapSort {

  

    public static int[] sortArray(int[] nums) {

        if (nums.length > 1) {

            // heapSort1为从顶到底建堆然后排序

            // heapSort2为从底到顶建堆然后排序

            // 用哪个都可以

            // heapSort1(nums);

            heapSort2(nums);

        }

        return nums;

    }

  

    // i位置的数,向上调整大根堆

    // arr[i] = x,x是新来的!往上看,直到不比父亲大,或者来到0位置(顶)

    public static void heapInsert(int[] arr, int i) {

        while (arr[i] > arr[(i - 1) / 2]) {

            swap(arr, i, (i - 1) / 2);

            i = (i - 1) / 2;

        }

    }

  

    // i位置的数,变小了,又想维持大根堆结构

    // 向下调整大根堆

    // 当前堆的大小为size

    public static void heapify(int[] arr, int i, int size) {

        int l = i * 2 + 1;

        while (l < size) {

            // 有左孩子,l

            // 右孩子,l+1

            // 评选,最强的孩子,是哪个下标的孩子

            int best = l + 1 < size && arr[l + 1] > arr[l] ? l + 1 : l;

            // 上面已经评选了最强的孩子,接下来,当前的数和最强的孩子之前,最强下标是谁

            best = arr[best] > arr[i] ? best : i;

            if (best == i) {

                break;

            }

            swap(arr, best, i);

            i = best;

            l = i * 2 + 1;

        }

    }

  

    public static void swap(int[] arr, int i, int j) {

        int tmp = arr[i];

        arr[i] = arr[j];

        arr[j] = tmp;

    }

  

    // 从顶到底建立大根堆,O(n * logn)

    // 依次弹出堆内最大值并排好序,O(n * logn)

    // 整体时间复杂度O(n * logn)

    public static void heapSort1(int[] arr) {

        int n = arr.length;

        for (int i = 0; i < n; i++) {

            heapInsert(arr, i);

        }

        int size = n;

        while (size > 1) {

            swap(arr, 0, --size);

            heapify(arr, 0, size);

        }

    }

  

    // 从底到顶建立大根堆,O(n)

    // 依次弹出堆内最大值并排好序,O(n * logn)

    // 整体时间复杂度O(n * logn)

    public static void heapSort2(int[] arr) {

        int n = arr.length;

        for (int i = n - 1; i >= 0; i--) {

            heapify(arr, i, n);

        }

        int size = n;

        while (size > 1) {

            swap(arr, 0, --size);

            heapify(arr, 0, size);

        }

    }

  

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值