堆排序易错点
1.建堆和调整堆(插入和删除)
建堆和调整堆的过程是不一样的:
建堆
从非终端节点编号的结点开始依次建立大根堆,例如:
拿第2个图说,首先比较-1,7,从中选一个小的,即“-1”,再与父结点“7”比较。要比较两次。所以,某非终端节点有两个孩子要比较两次,有一个孩子只用比较一次。
插入
直接看一题:
可以很直观的看到和"建立堆"的区别,在第3个图中,18并不需要与13进行对比,直接与父结点对比即可。因为这个堆本来就是一个大根堆,也就是说:在插入之前,13本来就是确定比25小的了,所以直接将插入节点与父结点进行对比(比父结点大,肯定比13大了)。所以在插入数据时,只需要和父结点比较。
补充:
比较次数最多等于树的高度减1,因为树的高度为
(或者
),所以堆的向上调整的比较次数最多等于
。
所以插入一个新元素的时间复杂度为O(
) ,建堆的时间复杂度为O(n)。
删除
删除的过程通过一个题体现:
看到第1个图,一些人可能会想,将大根堆的堆尾放到堆顶,那么22肯定比53小,只用比较53和其兄弟节点,如果其兄弟节点大于53,那么肯定大于22,为什么还要将53与34的较大者再与22进行比较?
这只是一种特殊情况,来看另一种情况:
如果这个图按上面的做法,直接将9,8对比,换父结点时,而不与父结点10对比,显然就错了。
所以删除操作,堆顶数据从上至下对比,如果其有两个孩子对比两次,如果其有一个孩子对比一次。对比次数还是主要看树高,时间复杂度为O(log2n)
2.不同的建堆方式
上面提到的建堆方式是给定序列直接调整成堆,再看一遍过程:
而某些题是给定序列依次插入空堆,那么过程就完全不一样了:
补充:二叉排序树与堆的差别
以小根堆为例,堆的特点是双亲结点的关键字必然小于或等于该孩子结点的关键字,而两个孩子结点的关键字没有次序规定。在二叉排序树中,每个双亲结点的关键字均大于左子树结点的关键字,均小于右子树结点的关键字,也就是说,每个双亲结点的左、右孩子的关键字有次序关系。这样,当对两种树执行中序遍历后,二叉排序树会得到一个有序的序列,而堆则不一定能得到一个有序的序列。
既满足大根堆要求又满足二叉排序树的要求的二叉树(除了单个结点):