LeetCode 373. Find K Pairs with Smallest Sums 【优先队列】

优先队列

    的一个常见应用是作为高效的优先队列。优先队列是一种用来维护维护由一组元素构成的集合 S S S 的数据结构,其中的每一个元素都有一个相关值,称为关键字。一个最大优先队列支持以下操作:

  1. I N S E R T ( S , x ) INSERT(S,x) INSERT(S,x): 把元素 x x x 插入集合 S S S 中。
  2. M A X I M U M ( S ) MAXIMUM(S) MAXIMUM(S): 返回 S S S 中具有最大键字的元素。
  3. E X T R A C T − M A X ( S ) EXTRACT-MAX(S) EXTRACTMAX(S): 去掉并返回 S S S 中具有最大键字的元素。
  4. I N C R E A S E − K E Y ( S , x , k ) INCREASE-KEY(S,x,k) INCREASEKEY(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) EXTRACTMAX(S)

    将堆的最后一个元素设为根,同时堆的大小规模减一。因为原来的数组是满足堆的条件的。现在改变的根可能不满足堆的性质,调用MAX-HEAPIFY维护堆的性质。

I N C R E A S E − K E Y ( S , x , k ) INCREASE-KEY(S,x,k) INCREASEKEY(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;
	}
};

参考

算法导论
高级数据结构

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值