算法与数据结构全阶班-左程云版(二)基础阶段之4.堆和比较器(中)

简介: 本文主要介绍了堆和比较器:堆包括大根堆和小根堆;比较器的实质就是重载比较运算符,可以用于普通方式的排序和自定义的排序。

现在进一步实现堆排序

package heap04;
/**
 * @author Corley
 * @date 2021/10/12 20:16
 * @description LeetCodeAlgorithmZuo-heap04
 */
public class HeapSort {
    /*
        新加进来的数,现在停在了index位置,请依次往上移动
         */
    private void heapInsert(int[] arr, int index) {
        while (arr[index] > arr[(index - 1) / 2]) {
            swap(arr, index, (index - 1) / 2);
        }
    }
    public void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
    /*
    从index位置,往下看,不断的下沉
    停:较大的子节点都不再比index位置的数大;已经没子节点
     */
    public void heapify(int[] arr, int index, int heapSize) {
        int left = 2 * index + 1;
        while (left < heapSize) {
            int largest = left + 1 < heapSize && arr[left] < arr[left + 1] ? left + 1 : left;
            largest = arr[largest] > arr[index] ? largest : index;
            if (largest == index) {
                break;
            }
            swap(arr, largest, index);
            index = largest;
            left = 2 * index + 1;
        }
    }
    /*
     * 堆排序
     * */
    public void heapSort(int[] arr) {
        if (null == arr || arr.length < 2) {
            return;
        }
        for (int i = 0; i < arr.length; i++) {
            heapInsert(arr, i);
        }
        int heapSize = arr.length;
        swap(arr, 0, --heapSize);
        while (heapSize > 0) {
            heapify(arr, 0, heapSize);
            swap(arr, 0, --heapSize);
        }
    }
}

现在分析时间复杂度

建堆时,调用heapInsert(arr, i)方法N次,时间复杂度为O(N*LogN),while循环也是O(N*LogN),整体时间复杂度也是O(N*LogN)

如果只是将数组变成大根堆,而不一定数组有序,可以优化为O(N)的时间复杂度,如下:

2345_image_file_copy_139.jpg

实现如下:

public void heapSort(int[] arr) {
    if (null == arr || arr.length < 2) {
        return;
    }
    /*for (int i = 0; i < arr.length; i++) {
        heapInsert(arr, i);
    }*/
    for (int i = arr.length - 1; i >= 0; i--) {
        heapify(arr, i, arr.length);
    }
    int heapSize = arr.length;
    swap(arr, 0, --heapSize);
    while (heapSize > 0) {
        heapify(arr, 0, heapSize);
        swap(arr, 0, --heapSize);
    }
}

现在进行证明,时间复杂度为O(N)

2345_image_file_copy_140.jpg

2345_image_file_copy_141.jpg

堆排序的优势:

堆排序实现了O(N*LogN)的时间复杂度,但是只使用了有限的几个变量,达到了空间复杂度O(1)。

综上,堆排序:

1,先让整个数组都变成大根堆结构,建立堆的过程:

1)从上到下的方法heapinsert,时间复杂度为O(N*logN);

2)从下到上的方法heapify,时间复杂度为O(N)。

直观说明如下:

2345_image_file_copy_143.jpg

2,把堆的最大值和堆末尾的值交换,然后减少堆的大小之后,再去调整堆。一直周而复始,时间复杂度为O(N*logN)

3,堆的大小减小成0之后,排序完成

现在实现使用系统的堆:

package heap04;
import java.util.PriorityQueue;
/**
 * @author Corley
 * @date 2021/10/13 14:56
 * @description LeetCodeAlgorithmZuo-heap04
 */
public class HeapUse {
    public static void main(String[] args) {
        PriorityQueue<Integer> heap = new PriorityQueue<>();
        heap.add(5);
        heap.add(7);
        heap.add(3);
        heap.add(0);
        heap.add(5);
        heap.add(2);
        while (!heap.isEmpty()) {
            System.out.println(heap.poll());
        }
    }
}

优先队列的底层就是堆,默认是小根堆。

与堆有关的题目:

已知一个几乎有序的数组。几乎有序是指,如果把数组排好顺序的话,每个元素移动的距离一定不超过k,并且k相对于数组长度来说是比较小的。

请选择一个合适的排序策略,对这个数组进行排序。

分析:

方式是准备一个k+1大小的堆,放入

2345_image_file_copy_145.jpg

实现如下:

package heap04;
import java.util.PriorityQueue;
/**
 * @author Corley
 * @date 2021/10/13 15:12
 * @description LeetCodeAlgorithmZuo-heap04
 */
public class SortArrayDistanceLessK {
    public static void SortedArrayDistanceLessK(int[] arr, int k) {
        if (0 == k) {
            return;
        }
        // 小根堆
        PriorityQueue<Integer> heap = new PriorityQueue<>();
        int index = 0;
        for (; index <= k; index++) {
            heap.add(arr[index]);
        }
        int i = 0;
        for (; index < arr.length; i++, index++) {
            arr[i] = heap.poll();
            heap.add(arr[index]);
        }
        while (!heap.isEmpty()) {
            arr[i++] = heap.poll();
        }
    }
}

复杂度为:O(N*LogK)

相关文章
|
4天前
|
存储 Java
【数据结构】优先级队列(堆)从实现到应用详解
本文介绍了优先级队列的概念及其底层数据结构——堆。优先级队列根据元素的优先级而非插入顺序进行出队操作。JDK1.8中的`PriorityQueue`使用堆实现,堆分为大根堆和小根堆。大根堆中每个节点的值都不小于其子节点的值,小根堆则相反。文章详细讲解了如何通过数组模拟实现堆,并提供了创建、插入、删除以及获取堆顶元素的具体步骤。此外,还介绍了堆排序及解决Top K问题的应用,并展示了Java中`PriorityQueue`的基本用法和注意事项。
19 5
【数据结构】优先级队列(堆)从实现到应用详解
|
1月前
|
算法
【初阶数据结构】复杂度算法题篇
该方法基于如下的事实:当我们将数组的元素向右移动 k 次后,尾部 kmodn 个元素会移动至数组头部,其余元素向后移动 kmodn 个位置。
|
1月前
|
机器学习/深度学习 人工智能 算法
【人工智能】线性回归模型:数据结构、算法详解与人工智能应用,附代码实现
线性回归是一种预测性建模技术,它研究的是因变量(目标)和自变量(特征)之间的关系。这种关系可以表示为一个线性方程,其中因变量是自变量的线性组合。
40 2
|
20天前
|
存储 程序员 C语言
堆和栈之间有什么区别
【9月更文挑战第1天】堆和栈之间有什么区别
89 0
|
24天前
|
数据采集 算法
基于PSO粒子群算法的三角形采集堆轨道优化matlab仿真
该程序利用PSO算法优化5个4*20矩阵中的模块采集轨迹,确保采集的物品数量及元素含量符合要求。在MATLAB2022a上运行,通过迭代寻优,选择最佳模块组合并优化轨道,使采集效率、路径长度及时间等综合指标最优。具体算法实现了粒子状态更新、需求量差值评估及轨迹优化等功能,最终输出最优轨迹及其相关性能指标。
|
1月前
|
算法 机器人
【数据结构】什么是堆
【数据结构】什么是堆
31 0
|
1月前
|
算法
【初阶数据结构篇】二叉树算法题
二叉树是否对称,即左右子树是否对称.
|
1月前
|
算法
【初阶数据结构篇】堆的应用(堆排序与Top-K问题)
即求数据结合中前K个最⼤的元素或者最⼩的元素,⼀般情况下数据量都⽐较⼤。
|
1月前
|
存储 算法 测试技术
【初阶数据结构篇】实现顺序结构二叉树(堆的实现方法)
注意传过去的参数是插入的位置,即插入前的size,在调整完后再将size++
|
1月前
|
算法 索引
【初阶数据结构篇】单链表算法题进阶
深拷贝应该正好由 n 个全新节点组成,其中每个新节点的值都设为其对应的原节点的值。