问题描述:
有一个数字矩阵,矩阵的每行从左到右是递增的,矩阵从上到下是递增的,请编写程序在这样的矩阵中查找某个数字是否存在,且程序的时间复杂度要小于O(N);
问题分析:
经过初步分析后我们可以发现,这道题可以通过遍历数组的办法解决。
问题解决:
方法一:
由于代码过于简单我们直接上代码就行:
int find(int (*p)[4], int row, int col, int put)
{
bool flag = false;
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
if (*(*(p + i) + j) == put)
{
printf("找到了,它位于矩阵的第%d行,第%d列\n", i + 1, j + 1);
flag = true;
break;
}
}
}
if (flag == false)
{
printf("该数不在矩阵中,请重新输入\n");
}
}
int main()
{
int i = 0;
int arr[3][4] = { {1,2,3,4} ,{5,6,7,8},{9,10,11,12} };
printf("请输入要查找的数字:");
while ((scanf_s("%d", &i)) != EOF)
{
find(arr, 3, 4, i);
}
return 0;
}
方法二:
方法一虽然可以得到正确结果,但是问题也很明显,就是我们每次查找都需要重新遍历一次数组,如果数组元素较少还可以接收,但是如果数组元素过多就会降低查找效率不能满足时间复杂度小于O(N)的题目要求,所以我们想到了方法二。
我们仔细分析,不难发现,对于杨氏矩阵来说,右上角和左下角的元素是有特点的:右上角的元素是一行中最大的,一列中最小的。左下角的元素是一行中最小的,是一列中最大的。所以我们可以从右上角或者左下角开始查找。
比如:从右上角开始查找的时候,右上角的元素比我们要查找元素小,我们就可以去掉右上角元素所在的这一行;右上角的元素比我们要查找的元素大,我们就可以去掉右上角元素所在的这一列。然后依然找右上角的元素继续和要查找的元素与比较。这样每一次比较去掉一行或者去掉一列。这个查找效率是高于遍历数组元素的,所以时间复杂度是小于O(N),也满足题目要求。
步骤一:在主函数中创建一个二维数组
int arr[][3] = {{1,3,5},{3,5,7},{6,7,9}}
步骤二:创建查找函数find
void find(int (*a)[3], int row ,int col ,int findnum)
{
int i = 0;
int j = row - 1;
while(j >=0 && i < row) //col>=0 确保数组列数不为0,i<row方便下面的排查不会越界
{
if(*(a+i)[j] < findnum) //如果查找位置小于findnum
{
i++; //行数加一,向下一行查找
}
else if(*(a+i)[j] > findnum) //如果查找位置大于findnum
{
j--; //列数减一,向上一列查找
}
else
{
return 1;
}
}
}
通过画图,我们不难发现右上角元素在二维数组中元素下标总是数组行数减一:
步骤三:完善主函数
int main()
{
int a[][3] = { {1, 3, 5},
{3, 5, 7},
{5, 7, 9} };
if (find(a, 3, 3, 2)) //只有当找到时返回值等于1是真,显示找到,否则显示未找到
{
printf("It has been found!\n");
}
else
{
printf("It hasn't been found!\n");
}
return 0;
}
整体代码
int find(int (*a)[3], int row, int col, int findnum)
{
int i = 0, j = col - 1;
while (j >= 0 && i < row)
{
if ((*a+i)[j] < findnum)
{
i++;
}
else if (*(a+i)[j] > findnum)
{
j--;
}
else
{
return 1;
}
}
return 0;
}
int main()
{
int a[][3] = { {1, 3, 5},
{3, 5, 7},
{5, 7, 9} };
if (find(a, 3, 3, 2))
{
printf("It has been found!\n");
}
else
{
printf("It hasn't been found!\n");
}
return 0;
}
~over~