问题描述
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
解题思路及知识点
在C/C++中对于查找最常用的方法为顺序查找(即线性查找)和折半查找(即二分法)。
-
顺序查找的思路是:将待查找的量与数组中的每一个元素进行比较,若有一个元素与之相等则找到;若没有一个元素与之相等则找不到。顺序查找的效率较低,当数据很多时,用二分法查找可以提高效率。使用二分法查找的前提是数列必须有序。
-
二分法查找的思路是:要查找的关键值同数组的中间一个元素比较,若相同则查找成功,结束;否则判别关键值落在数组的哪半部分,就在这半部分中按上述方法继续比较,直到找到或数组中没有这样的元素值为止。
思路一: 采用顺序查找,暴力求解。
通过遍历二维数组所有元素判断数组中是否含有该整数,此方法简单,但不推荐使用。下附思路一代码:
#include<stdio.h>
int cz(int a[4][4], int x) //查找函数,查找x是否二维数组中
{
int i, j;
if (a==NULL) //判断二维数组是否为空,为空则直接返回 -1
return -1;
for (i = 0; i < 4; i++) //遍历二维数组所有元素
{
for (j = 0; j < 4; j++)
{
if (x == a[i][j]) //判断a[i][j]的值是否为查找值,存在即返回 1
return 1;
}
}
return -1; //二维数组不为空且查找值不存在返回 -1
}
int main()
{
int a[4][4] = { {0,1,2,4},{ 2,4,5,6},{3,6,7,8},{8,9,10,11} };
printf("%d\n",cz(a,-1)); //测试比最小值还小值
printf("%d\n",cz(a,0)); //测试最小值
printf("%d\n",cz(a,9)); //测试例值
printf("%d\n",cz(a,11)); //测试最大值
printf("%d\n",cz(a,12)); //测试最大值还大值
printf("%d\n",cz(NULL,9)); //测试数组为空
return 0;
}
思路二:采用二分查找法
由题意可知此数组的特点是从左到右递增排序,从上到下递增排序。初步满足二分查找法的条件,而且在处理二维数组问题时我们可以将其转换为一维数组解决,但包含多种情况,
例a[4][4]={ {0,1,2,3},{ 4,5,6,7},{8,9,10,11},{12,13,14,15} };该二维数组在转换为一维数组时满足二分查找的方法;
例a[4][4]={ {0,1,2,4},{ 2,4,5,6},{3,6,7,9},{8,9,10,11} };该二维数组在转换为一维数组时并不满足二分查找的方法;
下面进行分类解决:
- 当二维数组在转换为一维数组时满足二分查找的方法时,按照二分查找的方法进行查找。(下面附例图 查找数值9)
#include<stdio.h>
int cz(int a[4][4], int x) //查找函数,查找x是否二维数组中
{
int i, j,low,high, z;
if (a == NULL) //判断二维数组是否为空,为空则直接返回 -1
return -1;
low = 0;
high = 15; //注意此时的low,high代表的是一维数组的最小和最大位置
z = (low + high) / 2; //计算一维数组中间位置下标
i = z / 4; //将一维数组中间位置下标转换为二维数组下标
j = z % 4;
while (x != a[i][j] && low < high)
{
if (x < a[i][j])
{
high = z - 1; //判断x是否小于a[i][j],是则将high赋值为z-1,改变上界
}
else
{
low = z + 1; //判断x是否大于a[i][j],是则将low赋值为z+1,改变下界
}
z = (low + high) / 2; //计算一维数组中间位置下标
i = z / 4; //将一维数组中间位置下标转换为二维数组下标
j = z % 4;
}
if (x == a[i][j])
return 1; //判断a[i][j]是否为要查找的x,是则返回 1
else
return -1; //二维数组不为空且查找值不存在返回 -1
}
int main()
{
int a[4][4] = { {0,1,2,3},{ 4,5,6,7},{8,9,10,11},{12,13,14,15} };
printf("%d\n", cz(a, -1)); //测试比最小值还小值
printf("%d\n", cz(a, 0)); //测试最小值
printf("%d\n", cz(a, 8)); //测试验证值
printf("%d\n", cz(a, 15)); //测试最大值
printf("%d\n", cz(a, 16)); //测试最大值还大值
printf("%d\n", cz(NULL, 8)); //测试数组为空
return 0;
}
- 当二维数组在转换为一维数组时不满足二分查找的方法时,将其二维数组的每一行或者每一列看做一位数组按照二分查找的方法进行查找。(此处采用将二维数组的每一行看做一维数组进行查找,下面附例图 查找数值8,附源码)
#include<stdio.h>
int cz(int a[4][4], int x) //查找函数,查找x是否二维数组中
{
int i , j,h,z;
if (a == NULL) //判断二维数组是否为空,为空则直接返回 -1
return -1;
for (h=0;h<4;h++)
{
i = 0;
j = 3; //注意此时的i,j代表的是每一行的最小和最大位置即最大最小列数
z = (i + j) / 2; //计算中间位置下标
while (x != a[h][z]&&i<j)
{
if (x < a[h][z])
{
j=z-1; //判断x是否小于a[h][z],是则将j赋值为z-1,改变上界
}
else
{
i=z+1; //判断x是否大于a[h][z],是则将i赋值为z+1,改变下界
}
z = (i + j) / 2; //计算中间位置下标
}
if (x == a[h][z])
return 1; //判断a[h][z]是否为要查找的x,是则返回 1
}
return -1; //二维数组不为空且查找值不存在返回 -1
}
int main()
{
int a[4][4] = { {0,1,2,4},{ 2,4,5,6},{3,6,7,9},{8,9,10,11} };
printf("%d\n", cz(a, -1)); //测试比最小值还小值
printf("%d\n", cz(a, 0)); //测试最小值
printf("%d\n", cz(a, 8)); //测试验证值
printf("%d\n", cz(a, 11)); //测试最大值
printf("%d\n", cz(a, 12)); //测试最大值还大值
printf("%d\n", cz(NULL, 8)); //测试数组为空
return 0;
}
思路三:找特殊点(建议采用)
由题意可知此数组的特点是从左到右递增排序,从上到下递增排序;故有几个特殊的位置需要注意:左上角,右上角,左下角,右下角;且左上角和右下角分别是数组中最小和最大的整数,右上角元素为当前列最小,所在行最大,左下角则相反。我们可以取右上角开始查找:右上角为当前行(i)最小,当前列(j)最大元素(a[i][j]),如果待查找元素(x)大于a[i][j], 去 i+1 行查找,如果待查找元素(x)小于 a[i][j],去j-1 列查找(下面附例图 查找数值9,附源码)
源码1:C语言
#include<stdio.h>
int cz(int a[4][4], int x) //查找函数,查找x是否二维数组中
{
int i=0,j=3;
if (a == NULL) //判断二维数组是否为空,为空则直接返回 -1
return -1;
while (i >= 0 && j < 4)
{
if (x== a[i][j])
return 1; //判断a[i][j]是否为要查找的x,是则返回 1
else if (x < a[i][j])
{
--j; //判断x是否小于a[i][j],是则将j-1转入上一列
}
else
{
++i; //判断x是否大于a[i][j],是则将i+1转入下一行
}
}
return -1; //二维数组不为空且查找值不存在返回 -1
}
int main()
{
int a[4][4] = { {0,1,2,4},{ 2,4,5,6},{3,6,7,8},{8,9,10,11} };
printf("%d\n", cz(a, -1)); //测试比最小值还小值
printf("%d\n", cz(a, 0)); //测试最小值
printf("%d\n", cz(a, 9)); //测试验证值
printf("%d\n", cz(a, 11)); //测试最大值
printf("%d\n", cz(a, 12)); //测试最大值还大值
printf("%d\n", cz(NULL,9)); //测试数组为空
return 0;
}
源码2:C++语言(牛客网在线编程剑指offer测试通过)
-
在一个类中有一个Find(int target, vector<vector<int> > array)函数,其中的形参vector<vector<int> >array表达的是array是一个二维的int型的vector(实际上相当于一个二维数组)
#include<iostream>
#include<vector>
using namespace std;
class Solution {
public:
bool Find(int target, vector<vector<int> >array) {
int i,j,h,z;
h = array.size();
z = array[0].size();
if (h== 0) //判断二维数组是否为空,为空则直接返回 false
return false;
if (z== 0)
return false;
i = 0;j=array[0].size()-1; //将二维数组右上角元素下标赋给i,j
while(i>=0&&j>= 0 &&j<z&&i<h)
{
if (target == array[i][j]) //判断a[i][j]是否为要查找的target,是则返回 true
{
return true;
}
else if (target <array[i][j]) //判断target是否小于a[i][j],是则将j-1转入上一列
{
--j;
}
else //判断target是否大于a[i][j],是则将i+1转入下一行
{
++i;
}
}
return false; //二维数组不为空且查找值不存在返回 flase
}
};
int main()
{
vector<vector<int> >array(4, vector<int>(4)); //定义一个4*4的二维数组
array[0][0] = 0; //给二维数组元素赋值
array[0][1] = 1;
array[0][2] = 2;
array[0][3] = 4;
array[1][0] = 2;
array[1][1] = 4 ;
array[1][2] = 5;
array[1][3] = 6;
array[2][0] = 3;
array[2][1] = 6;
array[2][2] = 7;
array[2][3] = 8;
array[3][0] = 8;
array[3][1] = 9;
array[3][2] = 10;
array[3][3] = 11;
Solution a;
cout << a.Find(-1, array)<<endl; //测试比最小值还小值
cout << a.Find(0, array) << endl; //测试最小值
cout << a.Find(9, array) << endl; //测试验证值
cout << a.Find(11, array) << endl; //测试最大值
cout << a.Find(12, array) << endl; //测试最大值还大值
return 0;
}
注意问题:
- 在查找时要注意数组为空的情况;
- 在测试时要注意考虑各种情况,不能遗漏;查找时,最小值,最大值,不在数组中的值以及数组是空数组;
- vector<vector<int> >array表达的是array是一个二维的int型的vector(实际上相当于一个二维数组),给数组赋值时需注意方法,此处采用一一赋值;
- 布尔型变量的值只有 真 (true) 和假 (false),此处不能再定义返回值为1和-1,否则结果如下,最后返回值全为1.
初次见面,请多多指教!如发现有错误,请及时告知。谢谢!
有疑问可评论或者私信,在能力范围内有问必答!