优先级队列: 基于大根堆和小根堆实现
设初始序列为:(49,38,65,97,76)①大根堆:父节点的值大于或等于子节点的值(令数值越大优先级越高)
此时堆顶的元素为所有元素的最大值(97)
②小根堆:父节点的值小于或等于子节点的值(令数值越小优先级越高)
此时堆顶的元素为所有元素的最小值(38)
- 将队列中的所有元素按从大到小的数值输出:
①分析: 因为队列是先进先出,后进后出,所以每次出队的为队头元素即堆顶元素queue【0】==>基于大根堆实现;
②思路如下:
1>将入队的元素进行上浮操作,将最大值放置到堆顶(剩下的数没有顺序)
public void siftUp(int i,T val) { //队尾插入元素
while (i > 0) { //当到堆顶时跳出循环
int j=(i-1)/2; //此时j指向的是i的父节点
if (queue[j].compareTo(val) < 0) {
queue[i] = queue[j]; //如果插入元素大于父节点元素,并将父节点的值放置到i处
i=j; //将i指向当前父节点,在往上进行比较
} else {
break;
}
}
queue[i]=val; //此时找到val的合适插入位置
}
2>此时出队的元素(堆顶元素)则为所有值中的最大值,将queue【0】出队,接下来需要出第二小的元素,由于剩下的数据是没有顺序的,所以利用下沉函数将剩下数据中的最大值放至堆顶
3>将队尾元素放置堆顶,并与其子节点进行比较,找出最大值放至父节点,并从最大值的位置继续往下找非叶子节点
for (int j=2*i+1;j<index;j=2*j+1){
//右孩子大于左孩子
if (j+1<index&&queue[j+1].compareTo(queue[j])>0) {
j++; //如果右孩子值大于左孩子
}
if (queue[j].compareTo(val)>0){ //将两者中的较大值与队尾val(此时val为堆顶元素)比较
queue[i]=queue[j];
i=j;
}
else{
break;
}
}
queue[i]=val; //将val放置合适的位置
此时已经将最大值放置堆顶,出队元素为值第二大的数
- 将队列中的所有元素按从小到大的数值输出:
与1相似,只需要调整优先级比较结果即可。(数值越小优先级越大)
用优先级队列解决:
1.求一亿个整数里面,花费O(n)时间,找出前十小的数据?1. 问题分析:
1. 先拿出前十个数入队,假设为一亿个整数中前十小的数据(A为其中的最大值)
2. 因为要找出最小的数,所以需要淘汰大的数(有较小值入队时将最大值出队)。
基于大根堆实现
3. 然后把剩下的数依次入队。
在入队前和 A 做比较
如果入队的数 >= A,则肯定不为前十小,直接入下一个数
若 < A ,则舍弃 A ,对新数进行入队操作
4. 直至将最后一个数入队,最终剩下为前十小的数
相比较基于大根堆的优先级队列来说,解决此问题只需要在push()方法当放置元素下标越界时调用judge()函数进行上述操作 3
public void judge(T val) {
if (val.compareTo(queue[0]) >= 0) { //如果新入队的数比前十小的树中最大的数都大(等于),则舍弃
return;
}
// 若小于,则舍弃最大值,将新数值入队
else {
pop();
push(val);
}
}
当一亿个整数都完成入队操作后,最终剩下的十个数为数组中前十小的数。
2.求一亿个整数里面,花费O(n)时间,找出前十大的数据?
2. 问题分析: 1. 先拿出前十个数入队,假设为一亿个整数中前十大的数据(A为其中的最大值)
2. 因为要找出最大的数,所以需要淘汰小的数(有较大值入队时将最小值出队)。
基于小根堆实现
3. 然后把剩下的数依次入队。
在入队前和 A 做比较
如果入队的数 <= A,则肯定不为前十小,直接入下一个数
若 >A ,则舍弃 A ,对新数进行入队操作
4. 直至将最后一个数入队,最终剩下为前十大的数