《剑指offer》中面试题4:
问题描述:
在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
1 2 8 9
2 4 9 12
4 7 10 13
6 8 11 15
分析:
首先我们选取数组右上角的数字9.由于9大于7,并且9还是第4列的第一个(也是最小的)数字,因此7不可能出现在数字9所在的列,于是只需要分析剩下的3列,位于右上角的数字是8.同样8大于7,因此8所在的列也可以剔除。接下来只需分析剩下的两列,由于2小于7,那么要查找的7可能在2的右边和下边。由于2的右边都已经被剔除,所以只可能出现在2的下边,将2所在的行剔除,只剩下三行两列,再和前面同样的方法查找,当找到7时就结束了。
总结规律如下:首先选取矩阵右上角的数字。如果等于要查找的数字,查找过程结束;如果大于要查找的数字,则剔除这个数字所在的列,如果小于要查找的数字,则剔除这个数字所在的行。这样每一步都可以缩减查找范围,直到找到要查找的数字,或者查找失败。
书上主代码如下:
bool Find(int* matrix, int rows, int columns, int number)
{
bool found = false;
if(matrix != nullptr && rows > 0 && columns > 0)
{
int row = 0;
int column = columns-1;
while (row < rows && column >= 0)
{
if(matrix[row*columns + column] == number)
{
found = true;
break;
}
else if(matrix[row*columns + column] > number){
--column;
}
else {
++row;
}
}
}
return found;
}
现在要为其编写测试代码,代码中需要传入二维数组,二维数组在内存中占据连续的空间,实际上可以当成是连续的一维数组,二维数组的首地址做参数时会弱化成int*,这样在Find函数体内要按二维数组的行列思路读取值时,要通过row*colums+column的方式读取。
那么如果我想在函数体内直接按p[i][j]的方式直观读取呢?! 我想到传入int**,(这种方式并不推荐,仅做测试,搞明白二维数组与二级指针间的区别),原以为直接将int matrix[][4]首地址强制转换成int**就行,结果是错误的,只能强制转换成int*,原因上面已经说了。int**是一个二级指针,指向int*的指针,内存中分配的地址是不连续的。若非用这种方式转换,只能如下赋值:
void Test3()
{
int p[][4] = {{1, 2, 8, 9}, {2, 4, 9, 12}, {4, 7, 10, 13}, {6, 8, 11, 15}};
//二维数组实际只是一个指向int的指针,而int**是二级指针,完全不等价。不能强制转换
// int **matrix;
// matrix = new int*[4]; //row, 或者如下写法
int* matrix[4]; //row,指向指针int*的数组,分配的内存不连续,
//下面TestTwoDimention中做参数时自动弱化成int**指针
for (int i=0; i<4; i++) {
matrix[i] = new int[4]; //column
}
for (int i=0; i<4; i++) {
for (int j=0; j<4; j++) {
matrix[i][j] = p[i][j];
}
}
TestTwoDimention("Test3", matrix, 4, 4, 1);
}
这样就能传入int**参数,在函数体内直接matrix[i][j]读取。
//若要函数体内能直观p[i][j]取值,参数为int**,传的是二级指针,并非二维数组,见Test3
bool FindTwoDimention(int** matrix, int rows, int columns, int number)
{
if(matrix != nullptr && rows > 0 && columns > 0)
{
int row = 0;
int column = columns-1;
while (row < rows && column >= 0) {
if(matrix[row][column] == number)
return true;
else if (matrix[row][column] > number) {
--column;
}
else {
++row;
}
}
}
return false;
}
Newcomers to C are sometimes confused about the difference between a two-dimensional array and an array of pointers, such as name in the example above. Given the definitions
int a[10][20];
int *b[10];
then a[3][4] and b[3][4] are both syntactically legal references to a single int. But a is a true two-dimensional array: 200 int-sized locations have been set aside, and the conventional rectangular subscript calculation 20 * row +col is used to find the element a[row,col]. For b, however, the definition only allocates 10 pointers and does not initialize them; initialization must be done explicitly, either statically or with code. Assuming that each element of b does point to a twenty-element array, then there will be 200 ints set aside, plus ten cells for the pointers. The important advantage of the pointer array is that the rows of the array may be of different lengths. That is, each element of b need not point to a twenty-element vector; some may point to two elements, some to fifty, and some to none at all.
Although we have phrased this discussion in terms of integers, by far the most frequent use of arrays of pointers is to store character strings of diverse lengths, as in the function month_name. Compare the declaration and picture for an array of pointers:
char *name[] = { "Illegal month", "Jan", "Feb", "Mar" };
with those for a two-dimensional array:
char aname[][15] = { "Illegal month", "Jan", "Feb", "Mar" };
大致翻译为:c初学者通常会对二维数组和指针数组感到困惑,譬如给定如下定义 int a[10][20]; int *b[10];
那么a[3][4]和b[3][4]都是有效的语法,都指向单个int数值。但是a是一个真正的二维数组,并且分配了200个int单元的内存空间,通常用传统的矩形下标计算方式20*row+col来查找元素a[row,col] 。 然而b的定义仅仅分配了10个指针,并且并未初始化它们;必须要明确初始化。 假设每个b的元素确实都指向一个含20个元素的数组,那么加上10个这样的指针单元,就共分配了200个int单元。
指针数组的最大优势是数组每一行的长度可以不同。因此,b的每个元素并不需一定指向含20个元素的矢量,它也可以指向两个元素,50个元素,或值甚至为空。
尽管我们用int来讨论了这个问题,至今指针数组却最常用在存储不同长度的字符串,比如month_name函数。
(翻译若有误,请多指教,谢谢!)
其它不多说,上完整源代码,更能清晰地表达两者用法的不同:
#include
//二维数组首地址弱化为指针后,函数体内读取数组值时,要通过row*colums+column的方式读取
bool Find(int* matrix, int rows, int columns, int number)
{
bool found = false;
if(matrix != nullptr && rows > 0 && columns > 0)
{
int row = 0;
int column = columns-1;
while (row < rows && column >= 0)
{
if(matrix[row*columns + column] == number)
{
found = true;
break;
}
else if(matrix[row*columns + column] > number){
--column;
}
else {
++row;
}
}
}
return found;
}
//若要函数体内能直观p[i][j]取值,参数为int**,传的是二级指针,并非二维数组,见Test3
bool FindTwoDimention(int** matrix, int rows, int columns, int number)
{
if(matrix != nullptr && rows > 0 && columns > 0)
{
int row = 0;
int column = columns-1;
while (row < rows && column >= 0) {
if(matrix[row][column] == number)
return true;
else if (matrix[row][column] > number) {
--column;
}
else {
++row;
}
}
}
return false;
}
void Test(const char* testname, int* matrix, int rows, int columns, int number)
{
bool b = Find(matrix,rows,columns,number);
if(b)
{
printf("%s passed.\n",testname);
}
else {
printf("%s failed.\n", testname);
}
}
void TestTwoDimention(const char* testname, int** matrix, int rows, int columns, int number)
{
bool b = FindTwoDimention(matrix,rows,columns,number);
if(b)
{
printf("%s passed.\n",testname);
}
else {
printf("%s failed.\n", testname);
}
}
// 1 2 8 9
// 2 4 9 12
// 4 7 10 13
// 6 8 11 15
// 查找的数字介于数组中的最大值和最小值之间
void Test1()
{
// int matrix[][4] = {{1,2,8,9},{2,4,9,12},{4,7,10,13},{6,8,11,15}};
int matrix[] = {1,2,8,9,2,4,9,12,4,7,10,13,6,8,11,15};
Test("Test1",matrix,4,4,7);
}
// 1 2 8 9
// 2 4 9 12
// 4 7 10 13
// 6 8 11 15
// 查找的值在最大值和最小值之间,但数组中没有
void Test2()
{
int matrix[][4] = {{1, 2, 8, 9}, {2, 4, 9, 12}, {4, 7, 10, 13}, {6, 8, 11, 15}};
int *p = &matrix[0][0];
Test("Test2", p, 4, 4, 5);
}
// 1 2 8 9
// 2 4 9 12
// 4 7 10 13
// 6 8 11 15
// 查找值为数组最小值,于左上角. 将二维数组赋值给二级指针int**
void Test3()
{
int p[][4] = {{1, 2, 8, 9}, {2, 4, 9, 12}, {4, 7, 10, 13}, {6, 8, 11, 15}};
//二维数组实际只是一个指向int的指针,而int**是二级指针,完全不等价。不能强制转换
// int **matrix;
// matrix = new int*[4]; //row, 或者如下写法
int* matrix[4]; //row,指向指针int*的数组,分配的内存不连续,
//下面TestTwoDimention中做参数时自动弱化成int**指针
for (int i=0; i<4; i++) {
matrix[i] = new int[4]; //column
}
for (int i=0; i<4; i++) {
for (int j=0; j<4; j++) {
matrix[i][j] = p[i][j];
}
}
TestTwoDimention("Test3", matrix, 4, 4, 1);
}
// 1 2 8 9
// 2 4 9 12
// 4 7 10 13
// 6 8 11 15
// 查找值为数组最大值,于右下角
void Test4()
{
int matrix[][4] = {{1, 2, 8, 9}, {2, 4, 9, 12}, {4, 7, 10, 13}, {6, 8, 11, 15}};
Test("Test4", (int*)matrix, 4, 4, 15);
}
// 1 2 8 9
// 2 4 9 12
// 4 7 10 13
// 6 8 11 15
//查找值小于最小值,不存在
void Test5()
{
int matrix[][4] = {{1, 2, 8, 9}, {2, 4, 9, 12}, {4, 7, 10, 13}, {6, 8, 11, 15}};
Test("Test5", (int*)matrix, 4, 4, 0);
}
// 1 2 8 9
// 2 4 9 12
// 4 7 10 13
// 6 8 11 15
// 查找值大于最大值,不存在
void Test6()
{
int matrix[][4] = {{1, 2, 8, 9}, {2, 4, 9, 12}, {4, 7, 10, 13}, {6, 8, 11, 15}};
Test("Test6", (int*)matrix, 4, 4, 16);
}
//传空指针
void Test7()
{
Test("Test7", nullptr, 0, 0, 16);
}
int main()
{
Test1();
Test2();
Test3();
Test4();
Test5();
Test6();
Test7();
return 0;
}
测试结果如下:
Test1 passed.
Test2 failed.
Test3 passed.
Test4 passed.
Test5 failed.
Test6 failed.
Test7 failed.
Press
如上, Test1直接以一维数组传入二维数组的值,做参数时自动弱化成int*。Test2定义的二维数组,但是手动将二维数组的首地址赋给了int*。Test4,5,6,7都是传参时强制将二维数组首地址转换成int*,c++新标准是会报警的,正确方式当Test2。 Test3定义了二级指针作为参数,只能在调用前将二维数组的值一一赋值给int**。
参考文章:https://www.cnblogs.com/weiweisuhe/p/5691737.html
https://blog.csdn.net/nice__xixi/article/details/82081595