首先我们要明确一下堆排序的概念,堆排序就是利用大根堆和小根堆来实现的一种排序方法。

那么什么是堆呢?堆(heap)也被称为优先队列(priority queue)。队列中允许的操作是先进先出(FIFO),在队尾插入元素,在队头取出元素。而堆也是一样,在堆底插入元素,在堆顶取出元素,但是堆中元素的排列不是按照到来的先后顺序,而是按照一定的优先顺序排列的。这个优先顺序可以是元素的大小或者其他规则。如图一所示就是一个堆,堆优先顺序就是大的元素排在前面,小的元素排在后面,这样得到的堆称为大根堆。大根堆中堆顶的元素是整个堆中最大的,并且每一个分支也可以看成一个大根堆。同样的,我们可以定义小根堆,如图二所示。

java中存在堆的是什么变量_堆排序


而我们可以在数组中实现这种结构,我们这样定义数在数组中的位置:(其中i是数的索引)

父节点的位置:(i-1)/2

左子节点的位置:2*i+1

右子节点的位置:2*i+2

于是上面的大根堆我们可以用数组表示为:{12,8,10,6,4,7,9}

那么在明白了堆的概念之后我们可以构建一个大根堆,按照概念:用插入的方式构建一个大根堆,把当前要插入的节点与之的父节点进行比较,如果比父节点大的话就和父节点进行交换,直到父节点大于当前节点或者当前节点就是根节点

private void insertMaxHeap(int[] nums) {
        for (int i = 0;i < nums.length;i++) {
            //把当前要插入的节点与之的父节点进行比较,如果比父节点大的话就与之进行交换,直到父节点大于当前节点或者到达顶点
            int parent = (i-1) >> 1;
            while (parent >= 0 && nums[i] > nums[parent]) {
                swap(nums,i,parent);
                i = parent;
                parent = (i-1) >> 1;
            }
        }
    }

构建完成之后,假设我们得到:{12,8,10,6,4,7,9}这样的一个大根堆,这个时候我们如何完成排序呢,现在我们知道大根堆中的最大值一定是位于堆顶的元素,也就是"12",我们此时把12和9交换位置,让12变成数组中的最后一位,并且永远不动它的位置,那么我们就完成了第一次“排序”,之后我们再利用{9,8,10,6,4,7}重新构建一个“大根堆”,那么又可以找出位于堆顶的“10",再把10放到数组中12的前一位并保持它的位置不变即可。

 

1.构建一个大根堆

2.把堆顶的元素放到最后,并保持其永远不变位置

3.用剩下的元素继续构建大根堆

4.重复上面的操作直到数组完成排序

附上java代码实现如下:

package com.company;

public class SortClass {
    //堆排序
    public void heapSort(int[] nums) {
        //首先要建立一个大根堆或者小根堆
        int length = nums.length;
        insertMaxHeap(nums);
        //每次将最大或者最小的值固定,然后将剩下的元素重新构建堆,再去选出最大或者最小的元素
        while (length > 1) {
            //把现在位于堆顶的元素固定到尾部
            swap(nums,0,length-1);
            length--;
            //继续构建
            buildHeap(nums,0,length);
        }
    }
    private void swap(int[] nums,int x,int y) {
        int temp = nums[x];
        nums[x] = nums[y];
        nums[y] = temp;
    }
    private void buildHeap(int[] nums,int i,int length) {
        如果目前的结构已经基本上是一个大根堆或者小根堆了,只有刚替换的这个堆顶元素的位置不正确,则没必要重新构建整个结构了,直接每次和两个子节点中较大的交换位置即可
        int l = (i << 1) + 1;
        int r = (i << 1) + 2;
        while (l < length) {
            int maxIndex = i;
            if (nums[maxIndex] < nums[l]) {
                maxIndex = l;
            }
            if (nums[maxIndex] < nums[r] && r < length) {//注意确保右节点也在置换的范围内
                maxIndex = r;
            }
            //如果父节点已经是最大的了,直接返回即可
            if (maxIndex == i) {
                return;
            }
            swap(nums,maxIndex,i);
            i = maxIndex;
            l = (i << 1) + 1;
            r = (i << 1) + 2;
        }
    }
    private void insertMaxHeap(int[] nums) {
        for (int i = 0;i < nums.length;i++) {
            //把当前要插入的节点与之的父节点进行比较,如果比父节点大的话就与之进行交换,直到父节点大于当前节点或者到达顶点
            int parent = (i-1) >> 1;
            while (parent >= 0 && nums[i] > nums[parent]) {
                swap(nums,i,parent);
                i = parent;
                parent = (i-1) >> 1;
            }
        }
    }
}