堆排序
堆排序是将序列人为的类比做堆来处理,分为大堆和小堆,大堆就是双亲结点大于孩子结点,这里的孩子自身可能也是“双亲”,那么这个“双亲”还要大于它的孩子,小堆于此相反。
当所有结点满足这个大堆的条件后,最上面的根节点肯定是这个序列中最大的数,然后我们将这个数与最后一个结点交换,再对除了最后一个结点外的所有结点进行大堆处理,依次反复,最终会将这个序列排成从小到大的顺序。
下面,让我们结合图来详细的说明:
以序列a[] = {29, 17, 35, 6, 12, 31, 28, 15}为例;
整个遍历的过程是从最后一个非叶子节点开始,依次将所有非叶子结点遍历完成;
首先将序列按照顺序排成二叉树的模样:
这里我们需要知道几个知识点:
1.最后一个非叶子结点的下标为:length / 2 - 1 ;
2.假设根节点的下标为i,其左孩子的下标为2 * i + 1,右孩子的下标为2 * i + 2;
第一次遍历:
6是最后一个非叶子节点,比较其与孩子结点的大小,发现6<15,于是交换两者的内容,并将15作为双亲,继续与它的孩子进行比较,但15并没有孩子,于是第一次遍历结束,结果如下图;
第二次遍历:
35是倒数第二个非叶子结点,比较其与孩子结点的大小,35大于它所有的孩子结点,所以不进行交换,本次遍历结束;
第三次遍历:
17是倒数第三个非叶子结点,比较其与孩子结点的大小,17大于它所有的孩子结点,所以不进行交换,本次遍历结束;
第四次遍历:
29是倒数第四个非叶子结点,比较其与孩子结点的大小,发现29<35,于是交换二者内容(此时35在下标为0的位置,29在下标为2的位置)
之后,再将29作为双亲与它的孩子结点比较大小,发现29 < 31,于是交换二者内容(此时31在下标为2的位置,29在下标为5的位置)
最后,将29与其孩子结点进行比较,并没有孩子结点,本次遍历结束。
通过这四次遍历,这个二叉树已经满足大堆的要求,并且最大数35在最上面,对应于下标0的位置。于是将35与最后一个结点交换内容,(此时35的下标为7)并对除去35以外的所有结点再次执行大堆操作。
新一轮遍历
第一次遍历:
31作为最后一非叶子结点,与其孩子进行比较,并没有比它大的孩子,于是本次遍历结束;
第二次遍历:
17是倒数第二个非叶子结点,比较其与孩子结点,并没有比它大的孩子结点,于是本次遍历结束;
第三次遍历:
将6与其孩子结点比较大小,并且最大的孩子结点是31,所以将31与6进行交换(此时,31在下标为0的位置,6在下标为2的位置)
将6再与孩子结点进行比较,将最大孩子结点与其交换内容(此时,29在下标为2的位置,6在下标为5的位置)
再次将6与其孩子结点进行比较,并不存在孩子结点,所以本次遍历结束;
通过这三次遍历,这个二叉树已经满足大堆的要求,并且最大数31在最上面,对应于下标0的位置。于是将31与最后一个结点交换内容,(此时31的下标为6)并对除去31以外的所有结点再次执行大堆操作。
新一轮遍历:
第一次遍历:
29是最后一个非叶子结点,比较其与孩子结点的大小,并没有比它大的孩子结点,此次遍历结束;
第二次遍历:
17是倒数第二个非叶子结点,比较其与孩子结点的大小,并没有比它大的孩子结点,此次遍历结束;
第三次遍历:
28与其叶子结点比较大小,发现29>28,于是交换二者内容(此时29在下标为0的位置,28在下标为2的位置)
将28与其孩子结点比较大小,并没有大于它的孩子结点,于是本次遍历结束;
通过这三次遍历,将最大数29移到下标为0的位置,于是将 29与最后一个结点交换内容,即6在下标为0的位置,29在下标为5的位置,再对除去29以外的所有数进行大堆操作;
之后的过程以前面相同。
以下是代码:
void heapadjust(Arr arr,int n,int m)//对堆进行调整
{
Elementype temp = arr[n];//用temp来保存双亲节点的数值
int i;
for(i = 2*n+1;i <= m;i = 2*i+1)
{
if(i < m && arr[i] < arr[i+1])//比较左右子叶的大小
i++;
if(arr[i] < temp)//左右子叶中较大的与双亲节点比较
break;
arr[n] = arr[i];//如果子叶的数值大于双亲节点,将这个数值赋值给双亲节点
n = i;
}
arr[n] = temp;//将原来双亲节点的数值赋值给子叶
}
void heap(Arr arr)//堆排序
{
int i;
for(i = (MAX-2)/2;i >= 0;i--)//初始化堆
heapadjust(arr,i,MAX-1);//进行堆调整
for(i = MAX - 1;i > 0;i--)
{
swap(arr,0,i);//将首节点(数值最大)调到最后一位
heapadjust(arr,0,i-1);//将剩下的数在进行堆排,将最大数依次排列
print(arr);
}
}