本文包含知识点
- 杨氏矩阵极其解法
- 函数return多个值的四种方法
题目:
杨氏矩阵
有一个数字矩阵,矩阵的每行从左到右是递增的,矩阵从上到下是递增的,请编写程序在这样的矩阵中查找某个数字是否存在。
要求:时间复杂度小于O(N);
一、解题思路
首先我们要清楚什么是杨氏矩阵,题目中也解释得很清楚了,我们看样例
这样的数组有重复元素是允许的,因为题目文的是某个值是否存在,我们只要能找到存在即可,存在多个值没有影响
再看要求,时间复杂度要小于O(N),这是什么意思,我相信学过算法的朋友都是一眼便知的。如果你还没有学过,那我就用通俗的话解释一下啊,就是该题不允许我们用二层循环遍历的去寻找某个值,我们要用一种效率比二层循环更高的解法来完成
那这里我们该如何解决呢?
emmmm
我们先联想一下以前学过的二分法查找某个数,这个方法的时间复杂度就是小于O(N)。只不过它针对的是有序的一维数组,这里的杨氏数组是每行每列有序的二维数组
(ps:为什么是小于O(N),就是因为二分法每次判断都会减少一半不满足条件的元素,而不是从小到大一个个的循环求解)
思考片刻之后是不是有点思路了!
接下来我就详细讲讲思路:
如果我们寻找4是否存在在这个数组中
首先,我们拿右上角的3和4相比,我们发现3比4小,那么我们把这一整行去掉,为什么?因为每行是递增的,如果这一行最右边的3都比4小了,那么这一行都是比3小的元素,怎么可能存在4
此时矩阵只有第二行和第三行了,然后我们再看矩阵右上角的6,6比4大,那么我们可以把这一整列去掉,为什么呢,因为列从上往下递增,此时的6是该列最小的值了,最小的6都比4大,那么这一列是不可能存在4了
就这样一直走下去,直到矩阵的最左下角位置,如果还没有发现4,就说明不存在,反之,则存在
好,接下来看看代码
二、代码
2.1第一版——一股脑写在main函数内
#include<stdio.h>
int main()
{
int arr[3][3] = { 1,2,3,4,5,6,5,7,8 };
int key = 0;//要寻找的值
scanf("%d", &key);
int x = 0;
int y = 2;//(x,y)是初始矩阵右上角元素的下标
int flag = 0;
while (x <= 2 && y >= 0)
{
if (arr[x][y] > key)
{
y--;//去掉一列
}
else if(arr[x][y] < key)
{
x++;//去掉一行
}
else
{
printf("找到了,下标是:(%d,%d)\n", x, y);
flag = 1;
break;
}
}
if (0 == flag)
{
printf("找不到\n");
}
return 0;
}
第一版只能说完成了题目的要求,但我们作为新时代程序猿,对自己的要求要逐步提高,接下来看第二版
2.2第二版——初步封装成函数
这一步其实很简单,把核心功能代码放入函数即可,但是不要心急,这一版是在为第三版做铺垫
#include<stdio.h>
void Young_matrix(int arr[3][3], int key, int row, int col)
{
int x = 0;
int y = col-1;//(x,y)是初始矩阵右上角元素的下标
int flag = 0;
while (x <= row-1 && y >= 0)
{
if (arr[x][y] > key)
{
y--;//去掉一列
}
else if (arr[x][y] < key)
{
x++;//去掉一行
}
else
{
printf("找到了,下标是:(%d,%d)\n", x, y);
flag = 1;
break;
}
}
if (0 == flag)
{
printf("找不到\n");
}
}
int main()
{
int arr[3][3] = { 1,2,3,4,5,6,5,7,8 };
int key = 0;//要寻找的值
scanf("%d", &key);
Young_matrix(arr, key, 3, 3);
return 0;
}
这样简单粗暴的封装成函数其实是违背函数高内聚低耦合的原则的,这个打印的需求应该交还给用户,函数内最好只负责找到该数的下标,或者返回特定值告诉用户不存在
但是问题又来了,就是如何在函数里返回两个值呢?思考片刻,我们第三版见
2.3第三版——返回型参数(函数内做到return多个值)
首先,我之前写的文章里以及介绍过三种方式能在函数中return多个值,感兴趣的同学可以戳下方链接去看看
一个函数如何实现return好几个返回值
接下来我们介绍第四种方法,就是返回型参数
这个方法的思想就是,在函数参数中多传入两个变量的地址,然后在函数中解引用访问这两个变量,可以把需要返回的值放入这两个变量,因为是传址,因此可以成功接收到。
但是,如果这两个参数是毫无意义的,那我觉得不如就用我之前介绍的三种方式。
但是但是,在杨氏矩阵这道题里面,我们可以让这两个变量具有意义,这就是为什么我会在这篇文章介绍这个方法的原因了
废话不多说,先看代码,然后再介绍这个方法的妙处
#include<stdio.h>
void Young_matrix(int arr[3][3], int key, int *px, int *py)
{
int x = 0;
int y = *py-1;//(x,y)是初始矩阵右上角元素的下标
while (x <= *px-1 && y >= 0)
{
if (arr[x][y] > key)
{
y--;//去掉一列
}
else if (arr[x][y] < key)
{
x++;//去掉一行
}
else
{
*px = x;
*py = y;
return;
}
}
*px = -1;
*py = -1;
}
int main()
{
int arr[3][3] = { 1,2,3,4,5,6,5,7,8 };
int key = 0;//要寻找的值
scanf("%d", &key);
int row = 3;
int col = 3;
Young_matrix(arr, key, &row, &col);
if(-1 == row && -1 == col)
{
printf("找不到\n");
}
else
{
printf("找到了,下标是:(%d,%d)\n", row, col);
}
return 0;
}
这个方法,首先就是改进了版本中函数没有满足高内聚低耦合的缺点
其次,我们可以看到函数能够把找到的下标成功返回main函数中
最后,参入的参数是极其有意义的。一、row、col代表矩阵的行列长度,这正是矩阵需要的。二、row、col都有代表下标的属性(意思是行列和下标都是代表位置的变量,用来接收目标下标也是情理之中)