劍指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;
  }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章