小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
前言
每天一小步,成功一大步。大家好,我是程序猿小白 gw_GW,很高兴能和大家一起学习每天小知识。
以下内容部分来自于网络,如有侵权,请联系我删除,本文仅用于学习交流,不用作任何商业用途。
摘要
本文主要讲诉堆排序。内容有堆排序的应用,以及堆排序的插入,删除,修改操作。
1. 堆排序简介
堆排序是利用堆这种数据结构实现的一种排序算法。都是近似完全二叉树的结构。堆排序的平均时间复杂度为 Ο(nlogn)。分为大顶堆和小顶堆。
- 大顶堆。每个节点的值都大于等于其孩子节点的值。
- 小顶堆。每个节点的值都小于等于其孩子节点的值。
2. 堆排序的应用
在N个元素中取TOPK个小(大)的元素,例如:有一万个数据,但我们只需要前100个数据。那我们就可以使用堆排序。建立一个一位数组,大小为K+1,从一万个元素中逐个取出元素,如果比key小,就放到数组中。(根据具体需求而定),建立堆。
3. 堆排序的操作
堆排序的主要操作就是插入,删除,更新元素。以下就以小顶堆为例讲诉三个主要操作。
因为二叉树的特点,第i个节点其左孩子节点为2i,右孩子节点为2i+1,父亲节点为2*i。为了方便计算我们就把数组从下标1开始存取,并把下标为0的位置放置元素的个数。
3.1 插入元素
主要思想:
- 把该元素放到数组末尾
- 把元素逐渐上移,即如果小于父节点就和父节点进行交换。直到上移不动为止,上移不动有两种情况:已经到了顶 或者 碰到了更小的父元素。
- 插入元素后,把长度+1
void insert( int x, int heap[]){
int child = heap[0] + 1;
int father = (heap[0] + 1) / 2;
heap[child] = x;//插入新元素
while(father && x < heap[father]){//如果没超过顶,并且x一直比父元素小,就让x一直上移
swap(&heap[father],&heap[child]);
child = father;
father /= 2;
}
heap[0]++;
}
3.2 删除元素
删除元素就是删除堆顶元素。主要思想是:
- 把堆顶元素保存下来,方便后面返回。
- 把最后一个元素覆盖掉堆顶元素,并把长度-1
- 重新调整堆为最小堆。即把替换后的堆顶元素元素逐渐下移。直到移不动为止。下移不动有两种情况:已经移动到了叶子节点 或者 孩子节点比该节点要大。
int del( int heap[]){
int key = heap[1];
heap[1] = heap[heap[0]];
heap[0]--;
int i = 1;
int leftChild = 2*i;
int rightChild = 2*i + 1;
if(heap[0]){//如果堆不为空
while(1){//一共有两种情况可以退出循环,一种是当前节点已经没有孩子了,即碰到了叶子节点
//一种是还当前节点有孩子,但是当前节点的值已经小于了孩子节点的值
int min;
if((2*i + 1) <= heap[0]){//如果有两个孩子 ,找两个孩子的最小值
min = heap[2*i] < heap[2*i + 1]?2*i:(2*i + 1);
}
else if(2*i <= heap[0]){//如果只有一个孩子,最小值就是左孩子
min = 2*i;
}
else{//如果没有孩子,就不需要往下比较
break;
}
if(heap[i] > heap[min]){//如果父节点的值比孩子节点的值大就交换父节点和孩子节点。
swap(&heap[i],&heap[min]);
i = min;
}
else{
break;
}
}
}
return key;
}
3.3 更新元素
更新元素的主要思想是:
-
更新元素
-
调整跟新后的堆为最小堆。即把更新后的元素上移或下移。
这里需要注意的是,应该先判断元素是否上移,然后再判断元素是否下移。因为要判断元素是否下移需要判断该节点是否含有孩子节点。
void update( int x, int pos, int heap[]){
heap[pos] = x;
int father = pos/2;
while(father && pos*2 <= heap[0]){//终止条件是上移到了顶,或者到达了叶子节点
//先判断能不能上移
if(heap[father] > heap[pos]){
swap(&heap[father],&heap[pos]);
pos = father;
father /= 2;
}
else{//判断能不能下移
int min;
if( pos*2+1 <= heap[0]){//如果有两个孩子,找两个孩子的最小值 ,判断能不能下移
min = heap[pos*2] < heap[pos*2+1]?pos*2:(pos*2+1);
}
else if(pos*2 <= heap[0]){//如果只有一个孩子,最小值就是左孩子
min = pos*2;
}
else{//如果没有孩子
break;
}
if(heap[pos] > heap[min]){//如果父节点的值比孩子节点的值大就交换父节点和孩子节点。
swap(&heap[pos],&heap[min]);
pos = min;
}
else{
break;
}
}
}
}
结语
以上就是我对推排序的一些浅见,如有不正之处,欢迎掘友们批评指正。