杨氏矩阵:
有一个二维数组. 数组的每行从左到右是递增的,每列从上到下是递增的.
在这样的数组中查找一个数字是否存在。 要求时间复杂度小于O(N);
思路:
是个矩阵,涉及到每行每列,那么应该用二维数组来解决最适合;要求时间复杂度小于O(n),那说明不能 直接粗暴的用遍历的方法来实现。我们用 要查找的数 与 矩阵的右上角的数 来进行比较,从而进行查找。
可以发现,每一个杨氏矩阵的右上角的数是一个特殊的数,他是所在这一行的最大值,又是所在这一列的最小值。所以我们拿要查找的数(k)和这个右上角数(arr[x][y])进行比较,如果k大于arr[x][y],说明,k不在当前arr[x][y]所在的这一行,因为k比这一行最大的值都大,所以这时候,这一行我们就排除掉了;然后将行数x的值增大1,就是转移到下一行去比较,如果当前的右上角数还是比k小,那么行数继续加一直到右上角数比k大;当右上角数比k大时,说明k就在这一行里,然后将列数y的值减小1,因为一直以来y就是最后一列,将y值一一减小,才能找完这一行的值。
如下图:
目录
方法一
代码及详情如下:
#define ROW 3 //列
#define CRO 3 //横
//查找函数
void Find(int arr[CRO][ROW], int k) {
//定义一个ok来表示有没有找到数,找到了将ok变为1
int ok = 0;
//定义x,y为右上角的数的下标,x为行数,y为列数
int x = 0;
int y = ROW-1;
//x行数是从0到CRO-1的,y列数是从ROW-1到0的
while (x<CRO&&y>=0) {
if (arr[x][y] > k) {
y--;
}
else if (arr[x][y] < k) {
x++;
}
else {
printf("找到了,%d在第%d行%d列的位置。\n",k,x+1,y+1);
//找到了该数后,如果后面还有其他和该数相同的数,
//那么要将行数列数都改变,因为该数已经是这一行最大、这一列最小的数了
x++;
y--;
ok = 1;
}
}
//如果没有找到,ok就会是0
if (ok == 0) {
printf("找不到\n");
}
}
//主函数
int main() {
//先给二维数组赋值,当为矩阵
int arr[CRO][ROW] = {
{1,2,3}, //1,2,3
{4,5,6}, //2,3,4
{7,8,9} //3,4,5
};
//输入要查找的数
int k;
scanf("%d", &k);
Find(arr, k);
return 0;
}
效果如图:
方法二
上面这种写法可以说是看着很不干净,还有一种写法,就是将Find函数里的逻辑和IO分离,也就是将Find函数里的printf语句分离出来,这样这个函数看着就会干净很多,不仅这个 程序如此,每次写的程序都应该要慢慢养成这个好习惯。
但是我们要输出坐标位置,而返回值又不能同时返回两个值过来,所以我们用指针来完成,用按址传参。
代码如下:
//查找函数
//用指针接收传过来的地址
int FindK(int arr[3][3], int k, int* cro, int* row) {
int x = 0;//行
int y = *row - 1;//列
while (x < *cro && y >= 0) {
if (arr[x][y] > k)
y--;
else if (arr[x][y] < k)
x++;
else {
//再找到了的时候,将传过来的行列的值进行修改
*cro = x+1;
*row = y+1;
return 1;
}
}
return 0;
}
//主函数
int main() {
int arr[3][3] = {
{1,2,3},
{2,3,4},
{3,4,5} };
int cro = 3;//行
int row = 3;//列
int k;//要查找的数
scanf("%d", &k);
//如果找到了,就返回1
//没有找到就返回0
//将cro和row的地址传过去,那么在函数里面对这两个的值进行了修改就是永久性修改
if (FindK(arr, k, &cro, &row) == 1)
printf("%d 的坐标是(%d,%d)", k, cro, row);
else
printf("找不到\n");
return 0;
}
但是这个函数目前不能像上面那个程序那样,能把所有的k都找出来 ,因为这个程序一旦找到一个k就直接返回了。
如下图: