(纯白话算法系列)堆排序,时间复杂度分析、代码演示,堆是什么?堆的数据结构底层

堆排序(英语:Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

想要了解堆排序,那么必须要明白堆这种数据结构,先介绍一下堆结构,如果已经明白了可以直接跳过。

什么是堆

堆是一种非常重要的数据结构,其本身是由完全二叉树构成的,二叉树想必大家都明白是什么,如果不明白请百度…堆需要分两种,一种叫大根堆,一种叫小根堆,先介绍这几个名词:

名词介绍

满二叉树:除了叶子节点,每个根节点都有完整的两个孩子:
完全二叉树:除了叶子节点,其他根节点必须都是满的,叶子节点可以不满,但是必须都在左边。如图:
满二叉树和完全二叉树的区别
大根堆:大根堆是以完全二叉树为基础的,每个根节点一定比它的左右两个孩子的值要大,看图:
在这里插入图片描述
小根堆:跟大根堆相反,每个根节点一定比左右两个孩子的值要小,如图:在这里插入图片描述
了解了这几个名词和定义之后,就可以看堆排序了!!

堆排序怎么实现

为什么堆排序这么重要呢??因为其复杂度是O(logN)的,要知道O(logN)比O(n²)要好太多太多了,比如要排序10亿个数据,log10亿只有28.9,而十亿的平方是多少就不用说了吧,性能差距太多了!!!所以面试堆排序必问,一定要完全地弄熟练这个排序!

堆排序其实是一种抽象的数据结构,它存储在一维数组中,画成树的样子只是便于理解,可是树怎么能存在一位数组当中呢?

堆有一种存放的原则,根节点的左孩子一定是(n * 2)+ 1,右孩子一定是(n * 2)+ 2,一个孩子的父节点一定是(n - 1) / 2.有一个特殊的根节点是0这个根节点,0的根节点套用公式是(0 - 1)/ 2,-1÷2其实还是0,所以大家不必担心。

所以首先需要将数组中无序的元素变成堆的这种结构,举个栗子:
比如1,2,3,4,5,6这六个元素已经依次排列在数组中了,这个例子是最不好的情况,也就是已经排好序的数组,看看他的时间复杂度会不会很高。
在这里插入图片描述
很显然如果用堆的结构排它是这样的
在这里插入图片描述
这很明显是一个小根堆,但是堆排序需要用大根堆,我们怎么把这个小根堆变成大根堆呢?

首先定义一个index,这个index从第1个元素开始,因为是从第0个元素默认它已经是一个大根堆结构了,走到2这个数字,进行判断,如果这个元素大于它的根节点(怎么找到它的父节点呢?(index - 1)/2),那么就进行交换,也就是1和2交换,堆中现在是2,1,数组中是(2,1),3,4,5,6
在这里插入图片描述
index++,往堆中新增一个3,然后判断它跟父节点的关系,很明显3>2,所以就交换位置,堆中元素如下:数组中元素为(3,1,2),4,5,6
在这里插入图片描述
index++,往堆中新增一个4在这里插入图片描述
很明显4比它的父节点1要大,那么就跟1交换位置
在这里插入图片描述
再进行判断,4比父节点3还要大,继续换,堆中位置如下,数组中:(4,3,2,1),5,6
在这里插入图片描述
同理5,6也是这样排,最后堆中位置如下:
在这里插入图片描述
数组中为:6,4,5,1,3,2,那么怎么进行排序呢??
先把第0位置的元素和第5位置的元素进行交换,也就是(2,4,5,1,3),6,size = 6,size-1,也就是堆中有效位置变成了从第0位置到第4位置,6就被排除了,堆中现在是:
在这里插入图片描述
所以需要一步操作叫heapify,就是把不是堆的结构变成堆结构,把index变量指向2,先找他的两个孩子哪个最大,然后让index和两个孩子的最大值进行比较,如果相等,则不需要交换位置,如果index小于最大值,则和这个最大值进行交换,也就是先找2的两个孩子哪个最大,显然是5,然后2和5比较,显然5大,所以交换位置,由于交换位置后2没有子节点了,所以停止比较,堆中位置如下:
在这里插入图片描述
在数组中的位置:5,4,2,1,3,6,虽然6还在,可是在堆的范围内它已经被排除在外了,所以是一个越界的元素,取到它也没用,实际上堆中的元素是5,4,2,1,3,然后还是弹出第0个元素,和–size进行交换,也就是5和3交换,变成(3,4,2,1),5,6,堆中元素只剩下了3,4,2,1,再执行heapify方法,把其变成大根堆结构,也就是3和两个孩子节点中较大的那个元素比较,如果根节点小于孩子节点较大的那个元素,则和较大的元素进行交换,也就是3和4进行交换,堆变成了4,3,2,1,再执行交换,4,1交换,数组中变成(1,3,2),4,5,6,堆中元素只有1,3,2,1小于3,所以变成3,1,2,再执行交换,2,1,数组中元素变成(2,1),3,4,5,6,然后堆中元素只剩下2,1了,所以2>1,然后进行交换,变成1,2,3,4,5,6,排序到此也就结束了。

看一下代码实现:

package com.bean.com.bean.sortAlg;

import java.util.Arrays;

public class HeapSort {
    public static void HeapSort(int[] arr) {
        if(arr == null || arr.length < 2) {
            return;
        }
        for(int i = 0; i < arr.length; i++) {
            HeapInsert(arr, i);//把数组变成堆结构
        }
        int size = arr.length;
        swap(arr, 0, --size);0位置元素和第--size元素进行交换
        while(size > 0) {//只要堆空间还有元素
            heapify(arr, 0, size);//把堆空间heap化
            swap(arr, 0, --size);//再交换第0个元素和堆空间的最后一个元素
        }
    }

    //把index之前的元素全部变成大根堆
    public static void HeapInsert(int[] arr, int index) {
        while(arr[index] > arr[(index - 1) / 2]) {//和父节点比较
            swap(arr, index, (index - 1) / 2);
            index = (index - 1) / 2;
        }
    }

    //从index到size变成大根堆
    public static void heapify(int[] arr, int index, int size) {
        int left = index*2 + 1;//左孩子
        while(left < size) {
            int largest = arr[left + 1] > arr[left] && left + 1 < size ? left + 1 : left;
            largest = arr[largest] > arr[index] ? largest : index;
            if(largest == index) break;
            swap(arr, largest, index);
            index = largest;
            left = index * 2 + 1;
        }
    }

    public static void swap(int[] arr, int left, int right) {
        int temp = arr[left];
        arr[left] = arr[right];
        arr[right] = temp;
    }

    public static int[] generateRandomArray(int maxSize, int maxNum) {
        if(maxSize == 0) {
            return null;
        }
        int[] randomArray = new int[(int) (Math.random() * (maxSize+1) )];
        for(int i = 0; i < randomArray.length; i++) {
            randomArray[i] = (int)(Math.random() * (maxNum+1) - (int)(Math.random() * (maxNum)));
        }
        return randomArray;
    }

    public static int[] arrayCopy(int[] arr) {
        if(arr == null) {
            return null;
        }
        int[] copyArray = new int[arr.length];
        for(int i = 0; i < arr.length; i++){
            copyArray[i] = arr[i];
        }
        return copyArray;
    }

    public static void printArray(int[] arr) {
        if(arr == null) {
            return;
        }
        for(int i = 0; i < arr.length; i++) {
            System.out.print(arr[i]+" ");
        }
        System.out.println();
    }

    public static void ArrSort(int[] arr) {
        Arrays.sort(arr);
    }

    public static boolean isEqual(int[] arr1, int[] arr2) {
        if(arr1 == null && arr2 != null || arr1 != null && arr2 == null) {
            return false;
        }else if(arr1 == null || arr2 == null) {
            return true;
        }else if(arr1.length != arr2.length) {
            return false;
        }
        for(int i = 0; i < arr1.length; i++) {
            if(arr1[i] != arr2[i])
                return false;
        }
        return true;
    }

    public static void main(String[] args) {
        int maxSize = 20;
        int maxNum = 50;
        int testTimes = 50000;
        boolean flag = true;
        for(int i = 0; i < testTimes; i++) {
            int[] arr1 = generateRandomArray(maxSize, maxNum);
            int[] arr2 = arrayCopy(arr1);
            ArrSort(arr1);
            HeapSort(arr2);
            flag = isEqual(arr1, arr2);
            if (!flag) {
                break;
            }
        }
        System.out.println(flag ? "Congraduation! Everything is ok!" : "Damn.. the wrong " +
                "method has been found..");
        int[] arr1 = generateRandomArray(maxSize, maxNum);
        printArray(arr1);
        HeapSort(arr1);
        printArray(arr1);
    }
}

发布了54 篇原创文章 · 获赞 82 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/u011679785/article/details/97559588