剑指offer——面试题4:二维数组中的查找(Java版)

题目4:二维数组中的查找

在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

例子 :
      1   2   8   9
      2   4   9   12
      4   7   10  13
      6   8   11  15
查找数字7则返回true,查找数字5则返回false

思路:

看完题目,我们可以先用一个5*5的矩形来表示5*5的数组。

根据题意,有三种情况:

1、若选中的数字a比目标数字x大,则a可能存在的有效区域分为两块(A1-E2)和(A1-B5),如图1所示(深绿色为重叠的有效区域)。

2、若选中的数字a比目标数字x小,则a可能存在的有效区域也分为两块(D1-E5)和(A4-E5),如图2所示。

3、选中的数字a等于目标数字x,则直接返回true。

                                               

这么一看,似乎问题挺复杂的:1、下一个数字改怎么选?从哪个位置选?2、重复的区域该怎么避免重复比较?

稍微再分析一下,重复区域都是在数字a的对角线上,那么如果我们为了简化问题,尝试选择一个只有一边对角线的数字的话会怎样呢?(选择位于矩形中四个角位置的数字)

这时会有四个选取点:

选取点A:左上角

                                               

图3:若a小于目标数字x,则有效区域为绿色部分,这种情况下若想找出是否有目标数字只能通过遍历数组,这样的话花费的时间会相当多。

图4:若a大于目标数字x,则可以直接返回false

结论:该选取点只适用于目标数字小于数组中最小值的情况,不合格。

选取点B:右上角

                                               

当a小于目标数字x的时候,因为a为其所在行中最大的数,故x比该行中所有的数都要大,可以排除a所在行;而当a大于目标数字x的时候,因为a为其所在列中最小的数,故x比改行中所有的数都要小,可以排除a所在列。

有趣的情况出现了,无论a是小于还是大于目标数字x,都可以排除掉当前的行/列,进而缩小有效区域。

这时候,问题就简单了,若每次都取有效区域中右上角的数字进行比较,那么每次都能排除掉一行或一列的数字,最终找到目标数字x或者没有有效区域了也还没有找到目标数字x。

结论:可行!

选取点C:左下角

                                              

当选取点在左下角的时候,和选取点在右上角的时候情况有点类似,当选取数字a小于目标数字x的时候,由于a为同一列中最大的数字,故同一列中的数字均小于目标数字x,可排除a的所在列。而当a大于目标数字x时,可排除a所在行。

结论:无论a大于还是小于目标数字x,每次比较都能缩小有效区域,该选取点可行!

选取点D:右下角

                                               

当选取点为右下角时,和选取点为左上角类似,该选取点只适用于目标数字x大于数组中所有数字的情况,不可行。

实战演练:

1、使用选取有效区域右上角数字的方法对例子进行查找数字5来验证下结果。

                           

      

最后一步,由于有效区域的行和列都为0了,故数组中没有数字5,返回false。

2、使用选取有效区域右上角数字的方法对例子进行查找数字7来验证下结果。

                           

关键代码实现:

//RightTopFind:从有效范围的右上角开始查找
//如果当前整数比目标整数小,因为同一行的整数中右边的最大,所以可以排除当前行,继续取有效范围中右上角的整数;
//如果当前整数比目标整数大,因为同一列的整数中最上面的最大,所以可以排除当前列,继续取有效范围中右上角的整数
  public static boolean rtFind(int[][] a, int rows, int columns, int number) {
	  boolean flag = false;
      if (a != null && rows > 0 && columns > 0) {
    	  int row = 0;
    	  int column = columns - 1;
    	  while(row < rows && column >= 0 && column < columns) {
    		  if(a[row][column] > number) {
    			  column--;
    		  } else if(a[row][column] < number) {
    			  row++;
    		  } else {
    			  flag = true;
    			  break;
    		  }
    	  }
      }
      return flag;  
  }

  //LeftBottomFind从有效范围的左下角开始查找
  //如果当前整数比目标整数小,因为同一列的整数中最下面的最大,所以可以排除当前列,继续取有效范围中左下角的整数
  //如果当前整数比目标整数大,因为同一行的整数中最左边的最小,所以可以排除当前行,继续取有效范围中左小角的整数
  public static boolean lbFind(int[][] a, int rows, int columns, int number) {
      boolean flag = false;
      if (a != null && rows > 0 && columns > 0) {
    	  int row = rows - 1;
    	  int column = 0;
    	  while(column < columns && row >= 0 && row < rows) {
    		  if(a[row][column] > number) {
    			  row--;
    		  } else if(a[row][column] < number) {
    			  column++;
    		  } else {
    			  flag = true;
    			  break;
    		  }
    	  }
      }
      return flag;
  }

测试用例及完整代码:

//面试题4:二维数组中的查找
//在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,
//输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
//例子 : 1   2   8   9
//     2   4   9   12
//     4   7   10  13
//     6   8   11  15
//查找数字7则返回true,查找数字5则返回false
//说说为什么不能取左上角和右下角的整数判断?

public class test4 {
  public static void main(String[] args) {
	  int[][] arr1 = {{1,2,8,9}, {2,4,9,12}, {4,7,10,13}, {6,8,11,15}};
	  int[][] arr2 = {{1,2,5,8,10}, {2,5,7,10,12}, {4,6,9,13,18}, {8,10,13,15,19}};
	  System.out.println("rtFind:");
	  System.out.println("    arr1:");
	  Test(arr1, 4, 4, 7, "rtFind");//数组中包含目标数字
	  Test(arr1, 4, 4, 5, "rtFind");//数组中不包含目标数字
	  Test(arr1, 4, 4, 1, "rtFind");//目标数字为数组中最小值
	  Test(arr1, 4, 4, 15, "rtFind");//目标数字为数组中最大值
	  Test(arr1, 4, 4, 0, "rtFind");//目标数字比数组中最小值还小
	  Test(arr1, 4, 4, 18, "rtFind");//目标数字比数组中最大值还大
	  
	  System.out.println("    arr2:");
	  Test(arr2, 4, 5, 8, "rtFind");//数组中包含目标数字
	  Test(arr2, 4, 5, 3, "rtFind");//数组中不包含目标数字
	  Test(arr2, 4, 5, 1, "rtFind");//目标数字为数组中最小值
	  Test(arr2, 4, 5, 19, "rtFind");//目标数字为数组中最大值
	  Test(arr2, 4, 5, 0, "rtFind");//目标数字比数组中最小值还小
	  Test(arr2, 4, 5, 23, "rtFind");//目标数字比数组中最大值还大
	  
	  System.out.println("");
	  System.out.println("lbFind:");
	  System.out.println("    arr1:");
	  Test(arr1, 4, 4, 7, "lbFind");//数组中包含目标数字
	  Test(arr1, 4, 4, 5, "lbFind");//数组中不包含目标数字
	  Test(arr1, 4, 4, 1, "lbFind");//目标数字为数组中最小值
	  Test(arr1, 4, 4, 15, "lbFind");//目标数字为数组中最大值
	  Test(arr1, 4, 4, 0, "lbFind");//目标数字比数组中最小值还小
	  Test(arr1, 4, 4, 18, "lbFind");//目标数字比数组中最大值还大
	  
	  System.out.println("    arr2:");
	  Test(arr2, 4, 5, 8, "lbFind");//数组中包含目标数字
	  Test(arr2, 4, 5, 3, "lbFind");//数组中不包含目标数字
	  Test(arr2, 4, 5, 1, "lbFind");//目标数字为数组中最小值
	  Test(arr2, 4, 5, 19, "lbFind");//目标数字为数组中最大值
	  Test(arr2, 4, 5, 0, "lbFind");//目标数字比数组中最小值还小
	  Test(arr2, 4, 5, 23, "lbFind");//目标数字比数组中最大值还大
  }
  
  public static void Test(int[][] a, int rows, int columns, int number, String method){
	  if (a == null) {
		  System.out.println("array is null!");
	  } else {
			  	if (method == "rtFind") {
			  		System.out.println("rtFind:" + number + "  " + rtFind(a, rows, columns, number));
			  	} else if (method == "lbFind") {
				  System.out.println("lbFind:" + number + "  " + lbFind(a, rows, columns, number));
			  	} else {
			  		System.out.println("error");
			  	}
	  }
  }

  //RightTopFind:从有效范围的右上角开始查找
  //如果当前整数比目标整数小,因为同一行的整数中右边的最大,所以可以排除当前行,继续取有效范围中右上角的整数;
  //如果当前整数比目标整数大,因为同一列的整数中最上面的最大,所以可以排除当前列,继续取有效范围中右上角的整数
  public static boolean rtFind(int[][] a, int rows, int columns, int number) {
	  boolean flag = false;
      if (a != null && rows > 0 && columns > 0) {
    	  int row = 0;
    	  int column = columns - 1;
    	  while(row < rows && column >= 0 && column < columns) {
    		  if(a[row][column] > number) {
    			  column--;
    		  } else if(a[row][column] < number) {
    			  row++;
    		  } else {
    			  flag = true;
    			  break;
    		  }
    	  }
      }
      return flag;  
  }

  //LeftBottomFind从有效范围的左下角开始查找
  //如果当前整数比目标整数小,因为同一列的整数中最下面的最大,所以可以排除当前列,继续取有效范围中左下角的整数
  //如果当前整数比目标整数大,因为同一行的整数中最左边的最小,所以可以排除当前行,继续取有效范围中左小角的整数
  public static boolean lbFind(int[][] a, int rows, int columns, int number) {
      boolean flag = false;
      if (a != null && rows > 0 && columns > 0) {
    	  int row = rows - 1;
    	  int column = 0;
    	  while(column < columns && row >= 0 && row < rows) {
    		  if(a[row][column] > number) {
    			  row--;
    		  } else if(a[row][column] < number) {
    			  column++;
    		  } else {
    			  flag = true;
    			  break;
    		  }
    	  }
      }
      return flag;
  }
}
发布了12 篇原创文章 · 获赞 0 · 访问量 290

猜你喜欢

转载自blog.csdn.net/qq_39790633/article/details/103909637