二叉堆/堆排序概念理解以及Java语言实现(学习笔记)

允我心安 提交于 2020-02-04 09:07:40

二叉堆/堆排序概念理解以及Java语言实现(学习笔记)

1.基础概念介绍

(1)满二叉树:每一层都达到了最大节点数,高度为h的满二叉树节点数为pow(2,h)-1 ( pow(x,y)代表x^y )

如图所示:
在这里插入图片描述

(2)完全二叉树:高度为h的二叉树每一个节点都和高度为h的满二叉树节点编号一一对应,那么它就是满二叉树。

如图所示:

在这里插入图片描述

	[1] 高度为h的完全二叉树节点数取值范围[pow(2,h-1),pow(2,h)-1],可见完全二叉树的高度是O( log(n) )
	
	[2] 满二叉树是特殊的完全二叉树

	[3]根节点编号记为1,对于节点i,它的左孩子编号为2i,右孩子编号为2i+1,对于节点i,它的父节点为floor(i/2) (floor表示向下取整)。完全二叉树适合用顺序存储结构来表示

(4)二叉堆概念:

[1] 首先是一颗完全二叉树

[2] 小根堆:任何一个节点小于等于它的左孩子且小于等于他的右孩子

[3] 大根堆:任何一个节点大于等于它的左孩子且大于等于他的右孩子

举个例子(大根堆):
在这里插入图片描述

2.堆排序

[1] 堆排序属于选择排序,第i趟在n-i+1个元素中选择最大(值),作为子序列的第i个元素。

[2]和AVL树、红黑树、B树和B+树不同,他们是用于查找的,但堆是用于排序的。

2.1 建立大根堆(在已有序列基础上建立)

设序列长度为n,则最后一个父节点编号为floor(n/2) (floor表示向下取整)

	[1]  i=floor(n/2),查看节点i和它的孩子,如果节点i小于两个(也可能是一个)孩子的最大值,则把节点i和最大孩子交换
	
	[2] 令i减少1,查看节点i和它的孩子,如果节点i小于两个(也可能是一个)孩子的最大值,则把节点i和最大孩子交换,以交换后的孩子为根节点的树可能不再满足堆的定义,如果不满足需要对它进行调整(下调)(多级的调整)
	
	[3] 重复[2],直到i=1

如图:

在这里插入图片描述

空间复杂度:O(1)

时间复杂度:O(n)

Java实现:

实际上Java可以比较任何对象的大小,我的上篇博客有讲到(https://blog.csdn.net/weixin_42111859/article/details/104132520),这里用int数组(ArrayList更合适,可以动态添加删除,更加灵活)举个例子:

    // 从0编号,i左孩子为2i+1,右孩子为2i+2,父节点为(i+1)/2-1
    private static void buildMaxHeap(int[] array) {
        for (int i = array.length / 2 - 1; i >= 0; i--) {
            adjustDown(i, array);
        }
    }

    // 使得i为顶点的子树成为堆
    private static void adjustDown(int i, int[] array) {
        for (int j = i; j < array.length; ) {
            // k为较大的孩子
            int k;
            // 有两个孩子
            if (array.length >= 2 * j + 3) {
                k = array[2 * j + 1] > array[2 * j + 2] ? 2 * j + 1 : 2 * j + 2;
            }
            // 只有左孩子
            else if (array.length >= 2 * j + 2) {
                k = 2 * j + 1;

            }
            // 没有孩子
            else {
                break;
            }
            // 父节点更大,不必交换
            if (array[j] >= array[k]) {
                break;
            }
            // 交换
            int temp = array[j];
            array[j] = array[k];
            array[k] = temp;
            j = k;
        }
    }

    @Test
    public void testBuildMaxHeap() {
        int[] a = {1, 2, 3, 4, 5, 6, 7, 8, 9};
        System.out.println(Arrays.toString(a));
        buildMaxHeap(a);
        System.out.println(Arrays.toString(a));
    }

控制台输出结果:

在这里插入图片描述
代码和上面的例子是对应的

2.2 堆排序

第i趟,先取堆顶元素(堆顶一定是最大值),然后把堆底赋给堆顶,对n-i个元素下调(建立大根堆)

这也是不断删除最大元素的过程

时间复杂度:O( n*log(n) )

空间复杂度:O(1) 下面代码中用一个数组存储结果了,实际上可以输出或者用迭代器返回结果,只需要常数个辅助单元

如果只求前m大的值,时间复杂度为 O( m*log(n) )

java代码:

public static int[] heapSort(int[] array) {
        buildMaxHeap(array);
        int[] rlt = new int[array.length];
        int index = 0;
        for (int i = array.length - 1; i >= 0; i--) {
            // 保存堆顶元素
            rlt[index++] = array[0];
            // 堆底给堆顶
            array[0] = array[i];
            // 构建大根堆
            adjustDown(0, array, i);
        }
        return rlt;

    }

    @Test
    public void testHeapSort() {
        int[] a = {1, 2, 3, 4, 5, 6, 7, 8, 9};
        System.out.println(Arrays.toString(a));
        System.out.println(Arrays.toString(heapSort(a)));
    }

运行结果为:

在这里插入图片描述
2.3 堆的插入
堆是可以插入元素的,尾接新元素,如果比父元素更大就交换,然后重复这个步骤(上调)

举一个例子(不写代码了):
在这里插入图片描述

转载请注明出处,谢谢合作

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!