杨氏矩阵——逻辑分析和代码设计详解

C语言学习之杨氏矩阵


前言

查找杨氏矩阵中是否存在某个数值。鉴于杨氏矩阵的特点,我们虽然可以采用暴力求解的方式来遍历整个矩阵,以此来查找是否存在某个值,但效率较低。这里介绍如何使用杨氏矩阵本身的特点进行代码设计。


一、知识储备

杨氏矩阵的行和列均是从小到大进行变化,也就是有规律变化的。那么每一行末尾的元素一定比同行的其它元素都大,每一列的第一个元素都比同列的其它元素要小。如给出二维数组:
int arr[3][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };
为了便于分析,将数组写为如下形式:
数组
假设给出一个数值7,要求在杨氏矩阵中查找有无这个值。

二、方法解析

  1. 为了在进行判断的同时,缩小比较的范围,我们先选择一个最初用来和给出的数值 7 进行判断的元素,这里使用最右上角的元素 arr[0][2],让 arr[0][2] 先与 7 进行比较,发现 3 < 7 ,而 3 已经是数组 0 行最大的元素了,那么数组 0 行肯定不会有所要查找的值 7 ,因此将最初选择的数组元素 arr[0][2] 的下标的行标加一,将选择的元素更新为 arr[1][2] ,来到数组 1 行继续查找。此时列标未改变。

  2. 此时需要进行比较的元素是 arr[1][2] 和 7 ,可以看到 6 < 7,同理,数组行数加一。更新后的数组元素为 arr[2][2] 。此时列标未改变。

  3. 来到数组 2 行继续查找。此时需要进行比较的元素是 arr[2][2] 和 7, 可以看到 9 > 7 。9 已经是数组 2 列(截止到数组 2 行)的最大值,说明数组 2 列 不会存在数值 7 ,因此列数减一,选择的数组元素更新为 arr[2][1] 。此时行标未改变。
    这里为什么选择改变列标而不是行标呢?因为此时的 arr[2][2] 值为 9 ,9 是数组 2 行所在元素的最大值,说明在其所在行存在小于 9 的元素,说明我们此时定位到了所要查找值 7 在矩阵中所在的行,这就是我们要找的目标之一。而 9 所在的列,也就是数组 2 列在截止到数组 2 行的整个 2 列中,9 就是最大的那个元素。换个说法,9 比同列的其它元素都大(只限在 2 行及之前的列中进行比较。比如数组 arr 中,元素 6 比 同列的 3 大,我们其实是把比较范围放在了数组 1 行及 1 行之前,肯定不是说 6 是数组 arr 2列中最大的元素)。既然 9 是“目前”同列元素的最大值,
    9 > 7 ,那么其所在列肯定不存在所要查找的值 7 。因此,我们可以在之后不断改变列标来精准锁定某个元素了,而且前边已经锁定到了行标,要找到某个值,肯定也是要开始查找列标了呀。

    到此时为止,我们已经将范围缩小到只剩 arr[2][0] 和 arr[2][1] 两个元素的范围,当然这是我们的“上帝视角”,此方法实现的对应代码在执行时是不会有这个先知视角的,它只会照着既定的逻辑去执行。

  4. 此时选择要进行判断的元素是 arr[2][1] 和 7 ,8 > 7 ,把列标减一,更新后的数组元素为 arr[2][0] 。此时行标未改变。在操作 3. 中,所要查找值在矩阵中所在的行标已经确认,我们接下来都是在不断改变列标,将更新后的元素和所要查找值 7 进行比较,只要得到相等的判断,查找就算结束。

  5. 接下来进行判断的元素是 arr[2][0] 和 7 ,7 == 7 ,此时,我们已经在矩阵中找到这个给出的查找值了,查找结束。

上述方法中,每次选择改变行标或者列标都是在判断所选元素是否和给出的查找值相等时,要重复进行的操作,所以代码 while 循环中放的是每一次判断后所要执行的操作。因为示例代码采用数组最左上角的元素作为初始和查找值要比较的对象,所以循环操作结束的标志就是找到了元素或者行标达到最大值并且列标变为零。整体方法是先确定了行标,然后确定列标。

其实也可以使用最左下角的元素,作为最初和查找值进行比较的元素,然后就是行标减一或者列标加一的操作,开始循环。至于最左上角和右下角的元素能否使用呢?我先卖个关子,代码示例后会解释的。

三、代码和注意事项

先上代码:

#include <stdio.h>

//01 不能返回下标值
//int FindNum(int arr[3][3], int k, int row, int col)
//{
//	int x = 0;
//	int y = col - 1;
//	 
//	while (x <= row -1 && y >= 0)
//	{
//		if (arr[x][y] > k)//所要查找的值在本行,需要改变列进一步查找
//		{
//			y--;
//		}
//		else if (arr[x][y] < k)//所要查找的值不在本行,需要换行继续查找
//		{
//			x++;
//		}
//		else
//		{
//			return 1;//找到了
//		}
//	}
//}
//
//int main()
//{
//	int arr[3][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };
//	int k = 7;
//
//	int ret = FindNum(arr, k, 3, 3);//返回1,找到   返回0,找不到
//	if (ret == 1)
//	{
//		printf("找到了\n");
//	}
//	else
//	{
//		printf("找不到\n");
//	}
//
//	return 0;
//}

//02可以带回返回值参数
int FindNum(int arr[3][3], int k, int* prow, int* pcol)
{
	int x = 0;
	int y = *pcol - 1;
	 
	while (x <= *prow -1 && y >= 0)
	{
		if (arr[x][y] > k)//所要查找的值在本行,需要改变列进一步查找
		{
			y--;
		}
		else if (arr[x][y] < k)//所要查找的值不在本行,需要换行继续查找
		{
			x++;
		}
		else
		{
			*prow = x;
			*pcol = y;
			return 1;//找到了
			//*prow = x; //不能放在return之后,代码在执行完return后会直接结束该程序,不再执行后续代码
			//*pcol = y; //因此指向x,y地址的指针并未改变其内容就结束了该子程序,因此打印下标时都是3 3
		}
	}
}

int main()
{
	int arr[3][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };
	int k = 7;
	int x = 3;
	int y = 3;

	//通过传递地址,使用指针直接改变变量内的内容  返回型参数
	int ret = FindNum(arr, k, &x, &y);//返回1,找到   返回0,找不到
	if (ret == 1)
	{
		printf("找到了\n");
		printf("下标是%d %d\n", x, y);
	}
	else
	{
		printf("找不到\n");
	}

	return 0;
}

在使用地址(指针)改变 x, y 变量的内容时,要把代码行放在 return 之前,否则会无法执行,那么改变变量内容的目的也就无法达到,输出 x 和 y 值时,只会得到 3 和 3 。我已经试过了,直接跳过,不会执行 return 之后的代码,┭┮﹏┭┮ 如下:

*prow = x;
*pcol = y;
return 1;//找到了
//*prow = x; //不能放在return之后,代码在执行完return后会直接结束该程序,不再执行后续代码
//*pcol = y; //因此指向x,y地址的指针并未改变其内容就结束了该子程序,因此打印下标时都是3 3

代码示例中给出了01 和 02 两种实例,大家可以根据需要自行更改。在杨氏矩阵中查找是否存在某个元素时,可以先用矩阵左上角(杨氏矩阵的元素最小值)和右下角(杨氏矩阵的元素最大值)的数值和需要查找的数值进行比较,如果需要查找的数值在这个最小值和最大值之间,才有查找的意义,如果不再这个范围内,那就可以直接判断不在该矩阵内。本次的代码示例中并未给出这个过程,大家可以自行增改去完善代码。
所以使用本文所述方法,选择和查找值比较的原始元素时,最左上角和右下角的元素只具有判断该查找值是否在矩阵内的作用,但不能帮我们缩小查找范围,因此不具有细致判断和缩小查找范围的意义,不适合使用其作为原始的比较元素。感谢阅读。


  • 14
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值