优先队列
堆的一个常见应用是作为高效的优先队列。优先队列是一种用来维护维护由一组元素构成的集合 S S S 的数据结构,其中的每一个元素都有一个相关值,称为关键字。一个最大优先队列支持以下操作:
- I N S E R T ( S , x ) INSERT(S,x) INSERT(S,x): 把元素 x x x 插入集合 S S S 中。
- M A X I M U M ( S ) MAXIMUM(S) MAXIMUM(S): 返回 S S S 中具有最大键字的元素。
- E X T R A C T − M A X ( S ) EXTRACT-MAX(S) EXTRACT−MAX(S): 去掉并返回 S S S 中具有最大键字的元素。
- I N C R E A S E − K E Y ( S , x , k ) INCREASE-KEY(S,x,k) INCREASE−KEY(S,x,k): 将元素 x x x 的关键字值增加到 k k k,这里假设k的值不小于 x x x 的原关键字值。
M A X I M U M ( S ) MAXIMUM(S) MAXIMUM(S)
即返回根结点。
E X T R A C T − M A X ( S ) EXTRACT-MAX(S) EXTRACT−MAX(S)
将堆的最后一个元素设为根,同时堆的大小规模减一。因为原来的数组是满足堆的条件的。现在改变的根可能不满足堆的性质,调用MAX-HEAPIFY维护堆的性质。
I N C R E A S E − K E Y ( S , x , k ) INCREASE-KEY(S,x,k) INCREASE−KEY(S,x,k)
增大的建值会违反堆的性质,向上依次对比父节点,不满足性质的话就交换两结点,由于之前满足堆的性质,所以交换下来的父节点肯定也满足堆的性质(父节点建值大于子树中任意的结点的建值)。
I N S E R T ( S , x ) INSERT(S,x) INSERT(S,x)
我们可以把插入操作当成先向堆最后插入-∞(肯定满足堆的性质),再增加次元素的值,调用INCREASE-KEY(S,x,k)。
LeetCode 373
设第一个有序数组为A,其长度为m;第二个有序数组为B,其长度为n。则所有的对数是m*n对。由于A、B数组都有序,我们可知以下关系:
总共有m行n列,相邻的元素左列小于右列,上行小于下行。但是我们不能判别一些不相邻元素的大小,比如 A [ 0 ] + B [ 0 ] ≤ A [ 0 ] + B [ 1 ] , A [ 0 ] + B [ 0 ] ≤ A [ 1 ] + B [ 0 ] A[0]+B[0]\le A[0]+B[1],A[0]+B[0] \le A[1]+B[0] A[0]+B[0]≤A[0]+B[1],A[0]+B[0]≤A[1]+B[0],但是不能直接知道 A [ 0 ] + B [ 1 ] A[0]+B[1] A[0]+B[1] 和 A [ 1 ] + B [ 0 ] A[1]+B[0] A[1]+B[0] 的大小。我们把m*n组分成m行,由于每行的元素都从小到大,并且第一列都是每组最小的,我们先把第一列放入堆中,一定能确保得到整体的最小值。一旦得到了一个最小值,我们把这一行次小的元素插入堆中,再进行比较此时最小的。就相当于每行有一个指针指向当前堆中待比较的元素,一旦指向的内容为最小,则指向下一次小的元素(因为每行的元素都从小到大),接着继续比较指针指向内容中的最小值,可得算法正确。
代码如下:
class Solution {
public:
#define LEFT(i) (((i) << 1) + 1)
#define RIGHT(i) (((i) << 1) + 2)
#define PARENT(i) (((i)-1) >> 1)
struct HEAPNODE
{
int sum;
int bIndex;
};
HEAPNODE A[10000];
int HEAPSIZE;
void MIN_HEAPIFY(int i)
{
int lf, rg;
int min = -1;
HEAPNODE tmp;
while (min != i)
{
lf = LEFT(i);
rg = RIGHT(i);
min = i;
if (lf < HEAPSIZE && A[lf].sum < A[min].sum)
min = lf;
if (rg < HEAPSIZE && A[rg].sum < A[min].sum)
min = rg;
if (min != i)
{
tmp = A[min];
A[min] = A[i];
A[i] = tmp;
i = min;
min = -1;
}
}
}
void DECREASE_KEY(int i)
{
HEAPNODE tmp;
int p = PARENT(i);
while (i > 0 && A[i].sum < A[p].sum)
{
tmp = A[i];
A[i] = A[p];
A[p] = tmp;
i = p;
p = PARENT(i);
}
}
vector<pair<int, int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k)
{
vector<pair<int, int>> res;
int i;
int m = nums1.size(), n = nums2.size();
if (m == 0 || n == 0)
return res;
//初始小根堆
HEAPSIZE = m;
for (i = 0; i < m; i++)
{
A[i].sum = nums1[i] + nums2[0];
A[i].bIndex = 0;
}
HEAPNODE min;
for (i = 1; i <= m * n && i <= k; i++)
{
//EXTRACT
min = A[0];
A[0] = A[HEAPSIZE - 1];
HEAPSIZE--;
MIN_HEAPIFY(0);
pair<int, int> pairNode(min.sum - nums2[min.bIndex], nums2[min.bIndex]);
res.push_back(pairNode);
//INSERT
if (min.bIndex < n - 1)
{
A[HEAPSIZE].bIndex = min.bIndex + 1;
A[HEAPSIZE].sum = min.sum - nums2[min.bIndex] + nums2[min.bIndex + 1];
HEAPSIZE++;
DECREASE_KEY(HEAPSIZE - 1);
}
}
return res;
}
};
参考
算法导论
高级数据结构