编程马拉松 Day05 堆、二叉堆、堆排序
堆 堆排序需要用到二叉堆,在开始之前,我们先来了解一下什么是二叉堆。 当二叉树满足满足如下条件时,我们说这个二叉树是堆有序的: 每一个父结点的值都比它的子结点大(称为大顶堆)或小(称为小顶堆) 子结点的大小与其左右位置无关 堆有序的二叉树,也可称为二叉堆。二叉堆是最常见的堆结构,因此也常将二叉堆直接称为堆,可以采用如下两种方式来表示二叉堆 使用指针,二叉树的每个结点需存储三个指针,分别指向其父结点和两个子结点 使用数组,对二叉树做层序遍历,按层级顺序放入数组中,根结点在数组索引0的位置存放,其子结点分别在索引1和2的位置,1和2个子结点分别在位置3、4和5、6中存放,以此类推 就排序来讲,其所需处理的数据较为连续,没有空隙,可用完全二叉树来表示。对于完全二叉树,采用数组的表示方法也更方便些,下图展示了采用数组实现的两个二叉堆。 对于数组实现的二叉堆,索引为k的结点的父结点的索引为(k-1)/2,它的子结点的索引分别为2k+1和2k+2。 堆有序化 以大顶堆为例,有序化的过程中我们会遇到两种情况 在堆底加入一个较大元素时,我们需要由下至上恢复堆的顺序 当将根结点替换为一个较小元素时,我们需要由上到下恢复堆的顺序 由下至上的堆有序化(上浮) 如果堆的有序状态因为某个结点变的比它的父结点更大而被打破,就需要通过将它与它的父结点交换来恢复堆有序。交换后,这个结点比它的两个子结点都大,但这个结点仍然可能比它现在的父结点更大。我们可以一遍遍的用同样的方式来将其向上移动,直到遇到一个比它更大的父结点或到达了堆的根结点,如下图所示。 上浮操作对应的代码如下 1 2 3 4 5 6 private void swim(Integer arr[], int k) { while(k > 0 && arr[(k - 1) / 2] < arr[k]) { //若k>0且索引为k的结点大于其父结点时,将该结点与其父结点交换 swap(arr, k, (k - 1) / 2); k = (k - 1) / 2; } } 由上至下的堆有序化(下沉) 如果堆的有序状态因为某个结点变的比它的某个子结点更小而被打破,就需要通过将它和它的子结点中较大者交换位置来恢复堆有序。交换可能会在子结点处继续打破堆的有序状态,此时可以采用相同的方式,将结点向下移动直到它的子结点都比它小或是到达了堆的底部,如下图所示。 下沉操作对应的代码如下...