[Classic Sorting Algorithm] 07. Heap Sorting

Heap sorting is sorting using the properties of the largest heap and the operation of maintaining the largest heap.

Remember some properties of the heap:

The index of the heap has a specific corresponding relationship with the index of the array

If the heap is indexed with 1 as the starting node, the array index from left to right is 0, 1, 2..., corresponding to the index order of each node in the heap, which is the order of traversing each node in the binary heap sequence . For a node index of a binary heap iii coming,iii 's parent node index is i/2,iiThe index of the left child node of i is i*2, theniiThe index of the right child node of i is i*2+1 (the index of the left child node +1).
Insert picture description here

But if the heap uses 0 as the starting node index, the array index from left to right 0, 1, 2..., for a certain node index of the binary heap iii coming,iiThe index of the parent node of i is i/2 (left child node) i/2-1 (right child node),iiThe index of the left child node of i is i*2 + 1, theniiThe index of the right child node of i is i*2+2 (the index of the left child node +1).

This also explains why it is customary to use 1 as the starting node index of the heap, because for the left and right child nodes, the calculation of the parent node index can be unified to i/2, which is more convenient for calculation.

Insert picture description here

code show as below:

public class Main {

    public static void main(String[] args) {
        int[] arr = {3, 5, 6, 2, 1, 7, 4};
        System.out.print("排序前:");
        arrPrint(arr);
        HeapSort(arr);
        System.out.print("排序后:");
        arrPrint(arr);
    }

    // 堆排序
    //
    // 第一个for循环,目的是将数组转化为最大堆数组(在本实例中转为[7, 5, 6, 2, 1, 3, 4])。
    // 从最后一个非叶结点索引i=len/2 - 1开始遍历,倒叙遍历,直到堆顶结点0。
    // heapify函数是一种不完全的堆化,可以将遍历结点i及其部分子二叉堆转化为最大二叉堆,
    // 我们用for循环从最后的非叶结点开始倒序遍历,可以覆盖到整个二叉堆。
    // (不知道为什么是len/2-1,可以假想最后一个结点len-1的后面还有一个虚拟结点len,
    // 当结点len是左子结点时,len的父结点len/2的上一个结点len/2 - 1 一定是最后一个非叶结点。
    // 当结点len是右子结点时,len的父结点len/2-1同样一定是最后一个非叶结点。
    // 严格证明可以看看 https://blog.csdn.net/qq_34629352/article/details/105591415 )
    //
    // 第二个for循环,从尾到头倒序遍历二叉堆所有结点,遍历索引为j,
    // 循环中,交换0和j元素,即把最大值交换到堆(数组)末端的j位置,
    // 之后调用heapify(以0为函数遍历结点,长度边界为j),可将0到j-1的子二叉堆进行堆维护,
    // 即0到j-1中的最大值上升到堆顶0的位置处。如此循环,j在倒序遍历的过程中,子二叉堆堆顶
    // 的最大值会不断地交换到j遍历位置,交换完,子二叉堆进行维护,新的最大值重新上升到堆顶,
    // 待循环结束,排序就完成了。
    private static void HeapSort(int[] arr) {
        int len = arr.length;
        for (int i = len / 2 - 1; i >= 0; i--) {
            heapify(arr, i, len);
        }
        for (int j = len - 1; j > 0; j--) {
            swap(arr, 0, j);
            heapify(arr, 0, j);
        }
    }

    // 堆化函数/堆维护函数
    // 输入为:最大堆数组arr,遍历的结点索引i,长度边界len,堆维护的操作范围是i到len-1
    // heapify实际是将遍历结点i,及遍历结点i的左右子结点,及左右子结点中较大结点的左右子结点
    // (以此顺延下去),这些结点构成的子二叉堆进行堆化操作。
    //
    // 将当前遍历结点值arr[i]记为temp。
    // for循环:起始为遍历结点i的左子节点k=i*2+1,在k超过len-1时终止,
    // 变化条件为k继续取k的左子节点,即k=k*2+1。
    //      第一个if:保证i的右子结点k+1不越界,如果i的右子结点大于左子结点值,
    //      则k指向右结点。
    //      第二个if,如果k的结点值arr[k]比父结点i的结点值arr[i]=temp要大,
    //      此时不满足堆的性质,则将子结点k赋给父结点i,arr[i]值赋为arr[k]
    //      i修改为k。if不满足,循环直接结束。
    // 最后将temp给i所在的结点(此时i是原来的k),完成i和k结点的交换。
    private static void heapify(int[] arr, int i, int len) {
        int temp = arr[i];
        for (int k = i*2 + 1; k < len; k = k*2 + 1) {
            if (k + 1 < len && arr[k] < arr[k + 1]) {
                k++;
            }
            if (arr[k] > temp) {
                arr[i] = arr[k];
                i = k;
            }
            else {
                break;
            }
        }
        arr[i] = temp;
    }

    // 交换位置函数
    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    // 辅助函数:将int[] 打印出来
    private static void arrPrint(int[] arr) {
        StringBuilder str = new StringBuilder();
        str.append("[");
        for (int v : arr) {
            str.append(v + ", ");
        }
        str.delete(str.length() - 2, str.length());
        str.append("]");
        System.out.println(str.toString());
    }
}

The animation of our instance of heap sorting is as follows:

Insert picture description here

The demonstration of the heapify function is as follows:

Insert picture description here

As you can see, the entire array cannot be heaped up using the heapify function only once.

Guess you like

Origin blog.csdn.net/fisherish/article/details/114239178