百度百科: 杨氏矩阵,是对组合表示理论和舒伯特演算很有用的工具。它提供了一种方便的方式来描述对称和一般线性群的群表示,并研究它们的性质。有一个二维数组. 数组的每行从左到右是递增的,每列从上到下是递增的,在这样的数组中查找一个数字是否存在。 时间复杂度小于O(N);
如上所述,一个二维数组,每行从左到右是递增的,每列从上到下是递增的,这样的一个数组就是杨氏矩阵,如下图所示
这是一个3*3的杨氏矩阵,也可以看作一个3行3列的二维数组
在一个N个元素的二维数组中查找一个元素,最坏的情况是查找N次,时间复杂度为O(N)
对于杨氏矩阵,可以发现它右上角的元素具有特殊性,这个元素是一行中最大的元素,同时也是一列中最小的元素
arr[3][3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
假设k = 7是我们需要查找的元素,我们可以先把要查找的元素与右上角的元素比较,如果k>arr[0][2],那k就一定不会是第一行的元素,此时我们需要向下查找;如果k<arr[0][2],那k就一定不会是第三列的元素,此时我们需要向左查找;如下图所示
第一次查找:k=7>arr[0][2],所以我们排除第一行,向下查找
第二次查找我们采取同样的方法,k与右上角数字6比较,k>6,所以排除第一行,向下继续查找
k与右上角数字9比较,k<9,排除9这一列,向左查找
k与右上角数字8比较,k<8,排除8这一列,继续向左查找
k与右上角数字7比较,k==7,找到要查到的元素,输出其下标
总结:在一个杨氏矩阵中查找元素7,我们总共进行了5次比较,找到了元素,这样的查找方式明显比遍历二维数组的效率高
#include<stdio.h>
int find_num(int arr[3][3], int col, int row, int k)
{
int i = 0;
//从右上角数字查找
while (k != arr[i][row - 1] && i < col && row >= 0)
{
if (k < arr[i][row - 1])
{
row--;
}
else
{
i++;
}
}
if (k == arr[i][row-1])
return 1;//找到了,返回1
else
return 0;//没找到,返回0
}
int main()
{
int arr[3][3] = { 1,2,3,4,5,6,7,8,9 };
int k = 0;
scanf("%d", &k);
int ret = find_num(arr, 3, 3, k);
if (ret == 1)
{
printf("找到了\n");
}
else
{
printf("找不到\n");
}
return 0;
}
可以发现这种方法只能告诉我们这个元素是否存在,不能告知我们具体的位置,因为定位一个二维数组的位置需要行列俩个值,但是函数调用一次只能返回一个值
以下我们通过使用结构体可以实现返回坐标的效果
#include<stdio.h>
struct Point
{
int x;
int y;
};
//函数返回一个结构体类型的数据
struct Point find_num(int arr[3][3], int col, int row, int k)
{
int x = 0;
int y = row - 1;
struct Point p = { 0 };
//从右上角数字查找
while (k != arr[x][y] && x < col && y >= 0)
{
if (k < arr[x][y])
{
y--;
}
else
{
x++;
}
}
if (k == arr[x][y])
{
p.x = x;
p.y = y;
return p;
}
//没找到,返回坐标(-1,-1)
p.x = -1;
p.y = -1;
return p;
}
int main()
{
int arr[3][3] = { 1,2,3,4,5,6,7,8,9 };
int k = 0;
scanf("%d", &k);
struct Point ret = find_num(arr, 3, 3, k);
printf("下标是%d %d\n", ret.x, ret.y);
return 0;
}
除了使用结构体,我们还可以利用函数的传址调用实现
#include<stdio.h>
int find_num(int arr[3][3], int* px, int* py, int k)
{
int x = 0;
int y = *py - 1;
while (x < *px && y >= 0)
{
//向下查找
if (k > arr[x][y])
{
x++;
}
//向左查找
else if (k < arr[x][y])
{
y--;
}
//找到了
else
{
*px = x;
*py = y;
return 1;
}
}
return 0;
}
int main()
{
int arr[3][3] = { 1,2,3,4,5,6,7,8,9 };
int k = 0;
scanf("%d", &k);
int x = 3;
int y = 3;
int ret = find_num(arr, &x, &y, k);
if (ret == 1)
{
printf("找到了,下标是%d %d\n", x, y);
}
else
{
printf("找不到\n");
}
return 0;
}
注:使用传址调用,会修改原变量x和y的值,利用这一特性,我们可以直接在函数中对x和y进行操作;但是在下一次调用时,x和y需要重新赋值