【八大排序】-- 堆排序(动图演示)

首先堆是一种以顺序结构存储的完全二叉树,本质上还是数组

为什么是完全二叉树?由于堆是以顺序结构存储的,如果是非完全二叉树,必然会造成空间的浪费。

堆分为大根堆小根堆

大根堆是指每个根节点的值都大于其左右子树的值;

小根堆是指每个根节点的值都小于其左右子树的值。

已知双亲结点的下标为i,则其左右孩子的下标分别为 2*i+1 , 2*i+2

已知左右孩子的下标,则其双亲结点的下标统一为 ((左孩子下标/右孩子下标-1)/  2) 。

并且优先级队列默认就是小根堆

堆排序的步骤

1.首先将待排序元素建立成大根堆(对应的是升序), 或 小根堆(对应的是降序)。

2.交换堆顶元素和堆尾元素,此时堆尾的元素默认已经排好序,以后就不用动了。

3.排除交换后的堆尾元素,即在剩下的元素中进行建立大根堆。

4.再次再剩下的元素中交换堆顶元素和堆尾元素。此时堆尾的元素默认已经排好序,以后就不用动了。

5.重复3,4操作。直到排好序。

动画演示

算法代码

public static void HeapSort(int[] elem){  //堆排序
        //首先将待排序元素建立成大根堆 或 小根堆
        //以大根堆为例
        createBigHeap(elem,elem.length);  //创建大根堆
        int size = elem.length-1;

        while(size>0) {
            //交换堆顶元素和堆尾元素   交换完后代表此时的堆尾已经排好序 后面就不用在参与到创建大根堆中了
            int tmp = elem[0];
            elem[0] = elem[size];
            elem[size] = tmp;

            //再在剩下的元素中创建大根堆
            createBigHeap(elem,size);
            size--;

        }
    }
    
    public static void createBigHeap(int[] elem,int end) {  //向下调整  时间复杂度O(n)
        int size = end;
        for (int parent = (size-1-1)/2; parent >= 0; parent--) {
            int child = 2*parent+1;

            while(child < size) {
                if (child < size - 1 && elem[child] < elem[child + 1]) {
                    child++;
                }

                //已经得到左右孩子中最大的值了
                if (elem[parent] < elem[child]) {
                    int tmp = elem[parent];
                    elem[parent] = elem[child];
                    elem[child] = tmp;
                    parent = child;
                    child = parent * 2 + 1;
                } else {
                    break;
                }
            }
        }
    }

测试并打印

 public static void main(String[] args) {
        int[] elem = {56,89,21,32,20,10,2,5,8,645,87,2,6,3,56,94,122,30};
        HeapSort(elem);
        for (int x:elem) {
            System.out.print(x+" ");
        }
    }

 
要实现降序排序的话需要将创建大根堆改为创建小根堆。

创建大根堆代码及时间复杂度推导

创建大小根堆过程分为向下调整和向上调整,向下的时间复杂度O(n)。要优于向上。

1.创建大根堆代码 以向下调整为例

public static void createBigHeap(int[] elem,int end) { //大根堆 向下调整  时间复杂度O(n)
        int size = end;
        for (int parent = (size-1-1)/2; parent >= 0; parent--) {
            shiftDown(parent,size,elem); //向下调整
        }
    }

private static void shiftDown(int parent,int size,int[] elem) { //向下调整
        int child = 2*parent+1;

        while(child < size) {
            if (child < size - 1 && elem[child] < elem[child + 1]) {
                child++;
            }

            //已经得到左右孩子中最大的值了
            if (elem[parent] < elem[child]) {
                int tmp = elem[parent];
                elem[parent] = elem[child];
                elem[child] = tmp;
                parent = child;
                child = parent * 2 + 1;
            } else {
                break;
            }
        }

2.向下调整建堆的时间复杂度推导

创建小根堆代码

public static void createSmallHeap(int[] elem,int end) {//创建小根堆 向下调整  时间复杂O(n)
        int size = end;
        for (int parent = (size-1-1)/2; parent >= 0; parent--) {
            shiftDown(parent,size,elem); //向下调整
        }
    }
    
private static void shiftDown(int parent,int size,int[] elem) {
        int child = 2*parent+1;

        while(child < size) {
            if (child < size - 1 && elem[child] > elem[child + 1]) {
                child++;
            }

            //已经得到左右孩子中最小的值了
            if (elem[parent] > elem[child]) {
                int tmp = elem[parent];
                elem[parent] = elem[child];
                elem[child] = tmp;
                parent = child;
                child = parent * 2 + 1;
            } else {
                break;
            }
        }
}

猜你喜欢

转载自blog.csdn.net/m0_73381672/article/details/132297352