小顶堆排序(逆序)

1. 问题描述:

对于一个乱序的整型数组,使用小顶堆算法对数组进行排序,输出排序之后的数组

2. 小顶堆算法

堆是一种经过排序的完全二叉树,其中任一非叶子节点的数据值均不大于(或不小于)其左子节点和右子节点的值。

最大堆和最小堆是二叉堆的两种形式

最大堆:根结点的键值是所有堆结点键值中最大者

最小堆:根结点的键值是所有堆结点键值中最小者

我们可以把数组想象成一颗二叉树,比如当数组元素为7个的时候,元素值为9 7 8 2 6 7 9,那么我们可以将其想象成树的形式为

                             

先根据数组下标逐层画出每一层的元素,元素从左到右从上到下,我们可以根据一个公式来计算出非叶子节点的左子树或者右子树的下标

int left = 2 * n + 1;

int right = 2 * n + 2;

n为父节点的下标,left为左节点的下标,right为右节点的下标

小顶堆算法从数组下标为n  / 2 - 1的元素开始堆化,堆化的目的是为了维持每一个非叶子节点都是小于等于它的叶子节点的

像上面的7个元素的数组我们应该从下标为2即元素值为8的元素开始网上进行堆化,使每一个非叶子节点都维持一个小顶堆

在堆化的过程中我们根据非叶子节点与子节点进行比较,如果非叶子节点大于了子节点那么我们进行元素的交换,比如向上面的例子,从元素8开始堆化:

 

这样我们就保证了小范围的小顶堆,但是我们在往上堆化的过程中,我们在上一层可能在堆化的过程中有可能交换元素之后破坏了下面已经维持好的小顶堆,所以这个时候又要进行调整下层的小顶堆,这个时候我们采用的递归调用来调整被破坏的小顶堆

                     

像上面经过若干次的往上堆化我们得到了上面的小顶堆,这个时候我们需要把交换之后有影响的9再往下进行调整,所以使用递归的时候我们需要传入交换的被交换的元素的下标,继续递归向下进行调整,所以经过堆化之后那么我们就得到了小顶堆了

                         

较小的元素都被放在了非叶子节点上去了,所以这有利于我们从第一个元素开始往下堆化,然后我们需要通过循环依次交换第一个元素与最后一个元素,交换之后最后一个元素就是最小的,那么继续从下标为零的元素9开始向下堆化,把最小的元素放在下标为零的位置,形成了下面的二叉树,这样下一次交换的就是下标为零和下标n - 2的元素,这样便把第二小的元素放在了n - 2的位置

                 

通过循环向下堆化之后那么我们最终可以得到逆序排列的数组

3. 具体的代码如下:

import java.util.Random;
public class Main {
    public static void main(String[] args) {
        int arr[] = getRandom(10, 1, 20);
        print(arr);
        sort(arr);
        print(arr);
    }

    private static void print(int[] arr) {
        for(int i = 0; i < arr.length; i++){
            System.out.print(arr[i] + " ");
        }
        System.out.print("\n");
    }
    private static void sort(int[] arr) {
        int n = arr.length;
        //注意节点应该从n / 2 - 1开始
        for(int i = n / 2 - 1; i >= 0; i--){
            //传入两个参数是为了更好地交换元素
            //堆化之后那么较小的元素都在上面的几层
            makeMinHeap(arr, i, n);
        }
        
        for(int i = n - 1; i >= 0; i--){
            swap(arr, 0, i);
            makeMinHeap(arr, 0, i);
        }
    }

    private static void swap(int[] arr, int i, int j) {
        int t = arr[i];
        arr[i] = arr[j];
        arr[j] = t;
    }

    private static void makeMinHeap(int[] arr, int node, int n) {
        //两个叶子节点
        int left = 2 * node + 1;
        int right = 2 * node + 2;
        //当当前节点为叶子节点的时候那么直接返回就可以了
        if(left >= n){
            return;
        }
        int min = left;
        if(right < n && arr[min] > arr[right]){
            //右子树的节点比较小
            min = right;
        }
        //保持小顶堆比较父节点与最小的叶子节点的大小;
        if(arr[min] < arr[node]){
            swap(arr, min, node);
        }
        //继续递归调用
        makeMinHeap(arr, min, n);
    }

    private static int[] getRandom(int len, int begin, int end) {
        int arr[] = new int[len];
        Random rm = new Random();
        for(int i = 0; i < len; i++){
            arr[i] = rm.nextInt(end - begin) + begin;
        }
        return arr;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_39445165/article/details/84932335
今日推荐