思考题6-3(Young氏矩阵)在一个 m × n m\times n m×n的Young氏矩阵(Young tableau)中,每一行的数据都是从左到右排序的,每一列的数据都是从上到下排列的。Young氏矩阵中也会存在一些为 ∞ \infin ∞的数据项,表示那些不存在的数据。因此,Young氏矩阵可以用来村出 r ≤ m n r \leq mn r≤mn个有限的数。
a. 画出一个包含元素 { 9 , 16 , 3 , 2 , 4 , 8 , 5 , 14 , 12 } \{9,16,3,2,4,8,5,14,12\} {9,16,3,2,4,8,5,14,12}的 4 × 4 4 \times 4 4×4Young氏矩阵。
第一题比较简单,只要满足插入和每行每列都是非递减就行。
下面示例:
[
2
5
8
∞
3
9
14
∞
4
12
16
∞
∞
∞
∞
∞
]
\begin{bmatrix} 2 & 5 & 8 & \infin \\ 3 & 9 & 14 & \infin \\ 4 & 12 & 16 & \infin \\ \infin & \infin & \infin & \infin \\ \end{bmatrix}
⎣⎢⎢⎡234∞5912∞81416∞∞∞∞∞⎦⎥⎥⎤
采用不同规则最终效果不同,只要满足其定义即可。
在后面的实现中我们为了简化边界检查,我们实际的矩阵实现行数和列数要比实际大2,Y的存储位置为
[
1
,
1
]
[1,1]
[1,1]到
[
M
,
N
]
[M,N]
[M,N]。
4
×
4
4 \times 4
4×4的Young氏矩阵的实际存储空间。
[
−
∞
−
∞
−
∞
−
∞
−
∞
−
∞
−
∞
2
5
8
∞
∞
−
∞
3
9
14
∞
∞
−
∞
4
12
16
∞
∞
−
∞
∞
∞
∞
∞
∞
]
\begin{bmatrix} -\infin& -\infin & -\infin & -\infin & -\infin &-\infin \\ -\infin& 2 & 5 & 8 & \infin & \infin\\ -\infin & 3 & 9 & 14 & \infin & \infin \\ -\infin & 4 & 12 & 16 & \infin & \infin \\ -\infin & \infin & \infin & \infin & \infin & \infin\\ \end{bmatrix}
⎣⎢⎢⎢⎢⎡−∞−∞−∞−∞−∞−∞234∞−∞5912∞−∞81416∞−∞∞∞∞∞−∞∞∞∞∞⎦⎥⎥⎥⎥⎤
采取这种方案,当达到边界时,不会越界。
b. 对于一个 m × n m \times n m×n的Young氏矩阵来说,请证明:如果 Y [ 1 , 1 ] = ∞ Y[1,1]=\infin Y[1,1]=∞,则 Y Y Y为空;如果 Y [ m , n ] < ∞ Y[m,n]< \infin Y[m,n]<∞,则 Y Y Y为满(即包含 m n mn mn个元素)。
这道题比较简单,根据定义就可以快速得出。
证明:根据Young氏矩阵的定义,
Y
[
i
,
j
]
≤
Y
[
i
+
k
1
,
j
+
k
2
]
,
∀
i
,
i
+
k
1
≤
m
Y[i,j] \leq Y[i+k1,j+k2],\forall i,i+k1 \leq m
Y[i,j]≤Y[i+k1,j+k2],∀i,i+k1≤m 且
j
,
j
+
k
2
≤
n
j,j+k2 \leq n
j,j+k2≤n。即
Y
[
1
,
1
]
Y[1,1]
Y[1,1]为矩阵的最小值,
Y
[
m
,
n
]
Y[m,n]
Y[m,n]为最大值。
如果 Y [ 1 , 1 ] = ∞ Y[1,1]=\infin Y[1,1]=∞,则矩阵其他元素均为 ∞ \infin ∞,即为空。若 Y [ m , n ] < ∞ Y[m,n]< \infin Y[m,n]<∞,则其他元素均小于 ∞ \infin ∞,故 Y Y Y为满。
c. 请给出一个在 m × n m \times n m×n Young氏矩阵上时间复杂度为 O ( m + n ) O(m+n) O(m+n)的EXTRACT-MIN的算法实现。你的算法可以考虑使用一个递归过程,它把一个规模为 m × n m \times n m×n的问题分解为规模为 ( m − 1 ) × n (m-1) \times n (m−1)×n或 m × ( n − 1 ) m \times (n-1) m×(n−1)的子问题(提示:考虑使用MAX-HEAPFY)。这里,定义T§用来表示在任意 m × n m \times n m×n的Young氏矩阵上的时间复杂度,其中 p = m + n p = m+n p=m+n。给出并求解 T ( p ) T(p) T(p)的递归表达式,其结果为 O ( m + n ) O(m+n) O(m+n)。
这个实现上也比较简单。如下:
- 在 Y Y Y非空时,将 Y [ 1 , 1 ] Y[1,1] Y[1,1]用 ∞ \infin ∞替换
然后重新调整 ∞ \infin ∞的位置,记当前位置在 Y [ i , j ] Y[i,j] Y[i,j],让其满足定义。
- 取其下面元素 Y [ i + 1 , j ] Y[i+1,j] Y[i+1,j]和右边元素 Y [ i , j + 1 ] Y[i,j+1] Y[i,j+1]的最小值 m i x mix mix,若 m i n ≠ ∞ min \neq \infin min̸=∞交换它和 ∞ \infin ∞的位置。
- 当达到矩阵边界,或 m i n = ∞ min = \infin min=∞,此时结束。
下面是伪代码:
extractMin(Y)
if(!Y.empty())//非空
Y[1][1] = INF;//第一个元素置为INF
MinFixup(Y,Loc{1,1});//调整
MinFixup(Young,loc)
t = Y[loc.x][loc.y]
do
if(Y[loc.x][loc.y+1] < Y[loc.x+1][loc.y])//获取下方和右边小的数的位置
small = {loc.x,loc.y+1}
else
small = {loc.x+1,loc.y}
if(Y[small.x][small.y] < t)
Y[loc.x][loc.y] = Y[small.x][small.y]
loc = small
else
break
while(true)
Y[loc.x][loc.y] = t
该过程相当于一个棋子从左上角向右下方行走,每次交换后 i = i + 1 i=i+1 i=i+1或 j = j + 1 j=j+1 j=j+1,最多交换 m + n m+n m+n次,总是时间复杂度为 O ( m + m ) O(m+m) O(m+m)。
d. 是说明如何在 O ( m + n ) O(m+n) O(m+n)的时间内,将一个元素插入到一个未满的 m × n m \times n m×n的Young氏矩阵中。
类似二叉堆的插入过程,我们将新的元素放置在矩阵的最后一个位置 Y [ m , n ] Y[m,n] Y[m,n],类似题目c相反,让它与上面的元素 Y [ m − 1 , n ] Y[m-1,n] Y[m−1,n]或左边的元素 Y [ m , n − 1 ] Y[m,n-1] Y[m,n−1]比较,取它们的最大值 m a x max max,若 m a x ≤ Y [ m , n ] max\leq Y[m,n] max≤Y[m,n],那么此时满足,否则,取用 m a x max max与它交换。直到达到达到边界或满足Young氏矩阵的定义。
下面是伪代码
insert(Y,x)
if(!Y.full())
Y[M][N] = x
MaxFixup(Loc{M,N});
MaxFixup(Y,loc)
t = Y[loc.x][loc.y]
do
if(Y[loc.x][loc.y-1] < Y[loc.x-1][loc.y])
lager = {loc.x,loc.y-1}
else
small = {loc.x-1,loc.y}
if(Y[lager.x][lager.y] > t)
Y[loc.x][loc.y] = Y[lager.x][lager.y]
loc = lager
else
break
while(true)
Y[loc.x][loc.y] = t
e. 在不用其他排序算法的情况下,是说明如何利用一个 n × n n \times n n×n的Young氏矩阵在 O ( n 3 ) O(n^3) O(n3)时间内将 n 2 n^2 n2个数进行排列。
将 n × n n \times n n×n个元素插入Young氏矩阵中,花费 O ( n 3 ) O(n^3) O(n3),然后将他们意义取出即可,也是花费 O ( n 3 ) O(n^3) O(n3)。故总的时间复杂度为 O ( n 3 ) O(n^3) O(n3)。
f. 设计一个时间复杂度为 O ( m + n ) O(m+n) O(m+n)的算法,它可以用来判断一个给定的数是否存储在 m × n m \times n m×n的Young氏矩阵内。
查找一个数 x x x,我们可以从左下角或右上角开始搜索。不能从左上角或右下角进行,例如左上角来说,当我们查找点元素大于 Y [ 1 , 1 ] Y[1,1] Y[1,1]时,若往右或下均是大于 x x x时,我们不知道往那个方向,有可能选择方向错误后,后面需要回过头来找尝试另一方向,花费额外时间。
但是如果我们从左下角开始查找,如果 x < Y [ m , 1 ] x < Y[m,1] x<Y[m,1],那么我们往上继续查找,若 X > T [ m , 1 ] X > T[m,1] X>T[m,1],那么我们往右边查找。这样可以在 O ( m + n ) O(m+n) O(m+n)时间内完成。当然,从右上角开始也是一样的效果。
下面从左下角查找的伪代码:
search(Y,t)//查找t
x = M,y=1
while(Y[x][y] != INF && Y[x][y] != -INF)//未到达边界
if(Y[x][y] == t)
return true
else if(t < Y[x][y])
x = x + 1
else
y = y - 1
return false
代码实现
#include<iostream>
#include<cassert>
#include<array>
#include<ctime>
#include<random>
using namespace std;
const int INF=0x3f3f3f3f;
struct Loc//位置定义
{
unsigned x,y;
};
template<typename T,unsigned M,unsigned N=M>
class Youngtableau{
public:
Youngtableau();//构造函数
void insert(const T&x);//插入
void extractMin();//删除
bool empty() {return matrix[1][1]==INF;}//判空
bool full() {return matrix[M][N]!=INF;}//判满
T top() {return matrix[1][1];}//返回首元素
bool search(const T&t);//查找
void print();//输出
private:
void MinFixup(Loc loc);//调整函数
void MaxFixup(Loc loc);
private:
//底层存储
//为了方便边界处理,行列多开2个空间,方便处理边界
array<array<T,N+2>,M+2> matrix;
Loc ins;//插入位置[M,N]
};
/*
Functions Definition
*/
template<typename T,unsigned M,unsigned N>
Youngtableau<T,M,N>::Youngtableau():ins({M,N})
{
matrix[0].fill(-INF);
matrix[M+1].fill(INF);
for(int i=1;i<matrix.size();++i)
{
matrix[i][0]=-INF;
for(int j=1;j<matrix[i].size();++j)
matrix[i][j]=INF;
}
}
template<typename T,unsigned M,unsigned N>
void Youngtableau<T,M,N>::print(){
for(int i=1;i<=M;++i)
{
for(int j=1;j<=N;++j)
{
if(matrix[i][j]!=INF)
cout << matrix[i][j] << " ";
else
cout << "INF ";
}
cout << endl;
}
}
template<typename T,unsigned M,unsigned N>
void Youngtableau<T,M,N>::insert(const T&x)
{
if(!full())
{
matrix[ins.x][ins.y]=x;
MaxFixup(ins);
}
}
template<typename T,unsigned M,unsigned N>
void Youngtableau<T,M,N>::extractMin()
{
if(!empty())
{
matrix[1][1]=INF;
MinFixup(Loc{1,1});
}
}
template<typename T,unsigned M,unsigned N>
bool Youngtableau<T,M,N>::search(const T&t)
{
int x=M,y=1;
while(matrix[x][y]!=INF&&matrix[x][y]!=-INF)//边界检查
{
if(t==matrix[x][y])
return true;
else if(t<matrix[x][y])
--x;
else
++y;
}
return false;
}
template<typename T,unsigned M,unsigned N>
void Youngtableau<T,M,N>::MaxFixup(Loc loc){
T t=matrix[loc.x][loc.y];
Loc lager;
do{
if(matrix[loc.x][loc.y-1]>matrix[loc.x-1][loc.y])
lager={loc.x,loc.y-1};
else
lager={loc.x-1,loc.y};
if(matrix[lager.x][lager.y]>t){
matrix[loc.x][loc.y]=matrix[lager.x][lager.y];
loc=lager;
}
else
break;
}while(true);
matrix[loc.x][loc.y]=t;
}
template<typename T,unsigned M,unsigned N>
void Youngtableau<T,M,N>::MinFixup(Loc loc){
T t=matrix[loc.x][loc.y];
Loc small;
do{
if(matrix[loc.x][loc.y+1]<matrix[loc.x+1][loc.y])
small={loc.x,loc.y+1};
else
small={loc.x+1,loc.y};
if(matrix[small.x][small.y]<t){
matrix[loc.x][loc.y]=matrix[small.x][small.y];
loc=small;
}
else
break;
}while(true);
matrix[loc.x][loc.y]=t;
}
template<unsigned M,unsigned N=M>
void Youngtableau_test(Youngtableau<int,M,N>&matrix)
{
static default_random_engine e(time(nullptr));
static uniform_int_distribution<int> d(100,999);//等概率生成3位的随机整数
cout << "Before insert" << endl;
matrix.print();
int i=0;
while(i<M*N)
{
matrix.insert(d(e));
i++;
}
cout << "After insert" << endl;
matrix.print();
cout << "\nInput the number to check it if in the Young tableau,(-1 to stop):\n";
while(cin >> i && i!=-1)
{
cout << boolalpha << matrix.search(i) << endl;
}
int pre=-INF;
while(!matrix.empty())
{
int t=matrix.top();
assert(pre<=t);//不满足非递减终止
pre=t;
matrix.extractMin();
}
cout << "After delete" << endl;
matrix.print();
}
int main()
{
Youngtableau<int,5,10> matrix;
Youngtableau_test(matrix);
return 0;
}