堆
理解:以数组为结构,更方便查找,数据上下级之间的联系用下标的数学关系表示,增删也只需要组内交换数据即可,排序看似有两条路线,还包括一个上级,但实际上,同级、上级和自己并无关系,只有上级对下级单方面存在大小关系,所以数据改变之后只需要对往下的数据进行修改,往下的数据也只需要对符合条件的下级修改,另一个下级不用管,所以每次排序实际上只是对当前位置往下的一条线进行了交换排序。
概念:一种数据关系,一个上级与两个下级之间的联系,每个下级又有两个下级,以此获得一种数据顺序,分为大顶堆和小顶堆,大小的区别是,每个堆的上级比下级大还是小
实现:用数组按级储存,0是第一级,1,2是第二级,3,4,5,6是第三级......
上级与下级之间的关系,(上级下标 * 2 + 1) 为第一个下级,+2为第二个下级
(下级下标 - 1 / 2) 为它的上级
操作:
压入:把数据加入数组尾部,然后往上遍历,直到遇到比自己大的上级
向下排序:以大顶或小顶为条件,把堆进行上级比下级大/小的排序
技巧:以堆尾的上级为起点,意思就是以每个小堆为单位进行排序,第一个小堆就是堆尾的小堆,然后不断向前排序,也就是排序每一个小堆,每一个上级也是它上级的下级
看代码:
//大顶堆
//单个堆排序
void get_greatheap(int *heap, int idx, int len){
int tmp = idx; //上级
int l = idx * 2 + 1; //左下级
int r = l + 1; //右下级
while(l < len){ //没有左下级代表没有下级,结束排序
if(heap[tmp] < heap[l]) //上级先和左下级比较
tmp = l;
if(r < len && heap[tmp] < heap[r]) //如果存在右下级,再进行比较
tmp = r;
if(tmp == idx) //如果以上比较后,上级都没有变化,说明上级最大,无需遍历,退出循环
break;
swap(heap[tmp], heap[idx]); //将比较后的结果,最大的值和上级交换
idx = tmp; //以交换后的下级为下一个上级继续遍历排序
l = tmp * 2 + 1; //更新左下级和右下级
r = l + 1;
}
return ;
}
//以每个小堆为单位,以堆尾的小堆为起点,向堆首遍历排序
int i;
for(i = (LEN - 2) / 2; i >= 0; --i){ //找到堆尾的上级,以堆尾的小堆为起点进行遍历排序,LEN - 1表示堆尾坐标,上级=(下级 - 1) / 2
get_greatheap(heap, i, LEN);
}
弹出:把任一位置数据弹出后,将堆尾和弹出位置swap,以弹出数据的位置往下为一个堆,开始遍历,不断把下级数据往上提,重新排好当前的堆,下级数据被删除对上级数据没有影响,对分支更没有影响
优先队列
概念:利用堆头数据一定是全堆最大数据的特点,形成一种全队最大的数据优先从队头出队,从队尾进队的队结构
实现:在堆的基础上,不断将堆头和堆尾调换位置,然后减小堆长,也就是让堆尾的空间依次存储最大的数据,然后将新堆从堆头开始重新排序
看代码
for(int i = LEN - 1; i > 0; i--){
swap(heap[0], heap[i]); //交换堆尾和堆头
get_greatheap(heap, 0, i);//从头开始对新堆排序,因为长度减小了,减小的那部分空间作为队尾依次存放每次堆的最大数据,也就是从大到小入队
}