首先我们要明确堆并非一种数据结构,所谓堆排序其实是对数组进行排序。首先,我们可以将数组看作一个完全二叉树,数组第一个元素a[0]即为二叉树根节点,而后我们需要了解一些完全二叉树的基础概念:完全二叉树最后一个非叶子节点索引为:元素个数/2-1。记住这个以后我们就可以进行堆排序了,堆排序的思路比较简单,如果我们想将输入的数据由小到大排列并输出,我们可以这样做:(1)首先将待排序的数组构建成一个大顶堆,什么是大顶堆?大顶堆即是一颗完全二叉树,这颗二叉树的根必须为所有元素中的最大值,并且这颗完全二叉树中的所有父亲节点都比起孩子节点大。那要怎么构建大顶堆呢?构建大顶堆就要用到我们之前所说的二叉树的最后一个非叶子节点索引,我们要从最后一个非叶子节点索引开始,将该节点与其孩子节点做比较,如果有孩子节点比它大,则将这个孩子节点与它(父节点)交换位置,重复这个比较、交换操作直到根节点。这样一个过程后我们得到的堆(完全二叉树)不就是一个大顶堆了吗。得到大顶堆只是第一步操作,接下来我们将这个最大值即树根的值与二叉树的最后一个节点交换位置,这样最大的值就位于二叉树末尾了(也可以说将最大的数放到数组末尾了)。为什么我们要将其放到末尾?因为我们想将数组中元素由小到大排列输出啊,那最大的数是不是应该最后一个输出,最大的数是不是应该放到数组末尾?同理我们得到的次大的数也应该放到数组倒数第二个位置、得到的第三大的数应该放到数组倒数第三个位置..........,注意!确认了输出位次的数就不能再动了,比如前面已经确认是最后一个输出的最大数!因此我们后续的所有操作都与其无关。回到正题,刚刚我们把最大的数与处于末尾的元素交换了位置,现在的堆还是大顶堆吗?那就不敢保证了,因此我们还得继续建立大顶堆,因为我们还要找到第二大的元素,此时就不用再从最后一个非叶子节点的索引开始排序了,因为经过第一次的排序,这已经不再是一颗杂乱无章的二叉树了,而是一个除了刚刚交换上去的根节点外,所有父节点都比其孩子节点大的树。我们现在就可以直接对根节点进行操作:其孩子节点是否有比它大的,有的话交换,交换后再看有无比它大的孩子节点,有的话再交换............大功告成,现在找到了第二大的元素,并且也处在了树根的位置,接下来呢,与树末尾的元素交换啊,注意!现在末尾的元素已经不再是之前换下来的最大的那个元素了!接下来重复对根的操作(与孩子节点比较、交换,最后与末尾元素交换位置)。
贴上代码:
主函数:
构建大顶堆的函数:
交换树根与树末尾元素的函数:
输入输出: