参考:http://wolf5x.cc/blog/algorithm/young-tableau-smallest-kth
杨氏矩阵:一个N*N的矩阵,它的每行每列都单调递增(或者宽松一些,单调不减),即a[i][j]<=a[i+1][j], a[i][j]<=a[i][j+1]。
0. 怎样在杨氏矩阵中查找某个元素X?
1. 输出杨氏矩阵中最小的N个数。
2. 两个升序数组A和B,长度都是N。从两个数组中分别取出一个数,相加得到一个和。求这N*N个和的前N小。
(0) 问题0求解
【方案一】
<二分查找>
对于杨氏矩阵,由于每行每列均是有序的,则可以于矩阵采用二分查找。具体方法是:
对于当前子矩阵a[i][j]~a[s][t],中间元素为a[(i+s)/2][(j+t)/2],如果a[(i+s)/2][(j+t)/2]==x,则找出该元素;如果a[(i+s)/2][(j+t)/2] > x,则在子矩阵a[i][j]~a[(i+s)/2][(j+t)/2]中查找;如果a[(i+s)/2][(j+t)/2] < x,则在三个子矩阵:a[i][(j+t)/2]~ a[(i+s)/2][t],a[(i+s)/2][(j+t)/2]~ a[s][t]和a[(i+s)/2][j]~ a[s][ (j+t)/2]中查找。该算法的递归式为f(mn)=3f(mn/4)+O(1),根据主定理知,时间复杂度为:O((mn)^(log4(3)))。
****这个分析不对,如果如果a[(i+s)/2][(j+t)/2] > x,同样有三块区域。
【方案二】
<类堆查找法>
杨氏矩阵具有明显的堆特征:从矩阵的右上角出发(从左下角出发思路类似),对于元素a[i][j],如果a[i][j]==x,则找到元素x,直接返回;如果a[i][j] > x,则向下移动,即继续比较a[i+1][j]与x;如果a[i][j]<x,则向左移动,即继续比较a[i][j-1]与x。该算法的时间复杂度是O(m+n),代码如下:
<span style="font-family:Microsoft YaHei;font-size:14px;">bool FindInYongMatrix(int ** array, int M, int N, int s)
{
if(a!=NUll && M>0 && N>0)
{
int row = 0, col = N-1;
while(row<M && col>=0)
{
if(s == array[row][col])
return true;
else if(s < array[row][col])
col--;
else
row++;
}
}
return false;
}</span><span style="font-family:Microsoft YaHei;font-size:18px;">
</span>
(1) 问题1求解
【方案一】
<小顶堆法>
首先将a[0][0]加入小顶堆,然后每次从小顶堆中取出最小元素,并加入比该元素大且与之相邻的两个元素(对于a[0][0],则需要加入a[0][1]和a[1][0]),直到取出第k个元素,需要注意的是,需要采用额外空间记录某个元素是否已经加入到小顶堆以防止重复加入。
需要维持bool类型的二维数组。
<N路归并法>
N路归并,用一个大小为N的堆,可以O(NlgN)得到解。
(2) 问题2求解
问题二转换成问题一求解。把A[0]+B[k]的结果填入矩阵第一行,A[1]+B[k]的结果填入第二行……就得到一个杨氏矩阵。所以就只考虑第1题。