題目描述
題目代碼
import java.util.Scanner; /** * 題目描述 * 在一個二維數組中(每個一維數組的長度相同), * 每一行都按照從左到右遞增的順序排序, * 每一列都按照從上到下遞增的順序排序。 * 請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。 * Created by YuKai Fan on 2018/8/13. */ public class Solution { public static void main(String[] args) { Scanner scan = new Scanner(System.in); System.out.println("請輸入查詢的整數:"); int m = scan.nextInt(); int n = 4; int[][] array = new int[n][n]; System.out.println("請輸入數組:"); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { array[i][j] = scan.nextInt(); } } boolean find = find(m, array); System.out.println(find); } //自己寫的方式:循環遍歷二維數組,比較每個值 /* public static boolean Find (int target, int[][] array) { for (int i = 0; i < array.length; i++) { for (int j = 0; j <array[i].length; j++) { if (target == array[i][j]) { return true; } } } return false; }*/ /*別人的方式 把每一行看成有序遞增的數組,利用二分查找,通過遍歷每一行得到答案,時間復雜度是nlogn */ public static boolean Find (int target, int[][] array) { for (int i = 0; i < array.length; i++) { int low = 0; int high = array[i].length - 1; while (low <= high) { int mid = (low + high)/2; if (target > array[i][mid]) { low = mid + 1; } else if (target < array[i][mid]) { low = mid - 1; } else { return true; } } } return false; } /* 別人的方式2: 利用二維數組由上到下,由左到右遞增的規律, 那么選取右上角或者左下角的元素a[row][col]與target進行比較, 當target小於元素a[row][col]時,那么target必定在元素a所在行的左邊, 即col--; 當target大於元素a[row][col]時,那么target必定在元素a所在列的下邊, 即row++; */ public static boolean find(int target, int[][] array) { int row = 0; int col = array[0].length - 1; while (row <= array.length - 1 && col >= 0) { if (target == array[row][col]) { return true; } else if (target > array[row][col]) { row++; } else { col--; } } return false; } }
題目延伸
在上面代碼中用到了二分查找,所以這里想回顧一下二分查找的算法
1.二分查找又稱折半查找,它是一種效率較高的查找方法。
2.二分查找要求:(1)必須采用順序存儲結構 (2).必須按關鍵字大小有序排列
3.原理:將數組分為三部分,依次是中值(所謂的中值就是數組中間位置的那個值)前,中值,中值后;將要查找的值和數組的中值進行比較,若小於中值則在中值前 面找,若大於中值則在中值后面找,等於中值時直接返回。然后依次是一個遞歸過程,將前半部分或者后半部分繼續分解為三部分。
4.實現:二分查找的實現用遞歸和循環兩種方式
22 //遞歸實現二分查找 23 public static int binarySearch(int[] dataset,int data,int beginIndex,int endIndex){ 24 int midIndex = (beginIndex+endIndex)/2; 25 if(data <dataset[beginIndex]||data>dataset[endIndex]||beginIndex>endIndex){ 26 return -1; 27 } 28 if(data <dataset[midIndex]){ 29 return binarySearch(dataset,data,beginIndex,midIndex-1); 30 }else if(data>dataset[midIndex]){ 31 return binarySearch(dataset,data,midIndex+1,endIndex); 32 }else { 33 return midIndex; 34 } 35 } 36 37 public static void main(String[] args) { 38 int[] arr = { 6, 12, 33, 87, 90, 97, 108, 561 }; 39 System.out.println("循環查找:" + (binarySearch(arr, 87) + 1)); 40 System.out.println("遞歸查找"+binarySearch(arr,3,87,arr.length-1)); 41 } 42 }
因為二分查找需要方便地定位查找區域,所以適合二分查找的存儲結構必須具有隨機存儲的特性。因此,該查找方法僅適合於線性表的順序存儲結構,不適合鏈式存儲結構,且要求元素按關鍵字有序排列。
判定樹:
二分查找的過程可以用下圖表示,稱為判定樹。樹中每個圓形節點表示一個紀錄,節點中的值表示為該記錄的關鍵字值:樹中最下面葉節點都是方形的,它表示查找不成功的情況。從判定樹中可以看出,查找成功時查找的查找長度為從根節點到目的節點的路徑上的節點數,而查找不成功時的查找長度為從根節點到對應失敗節點的父節點的父節點路徑上的節點數;每個節點值均大於其左子節點值,且均小於右子節點值。若有序序列有n個元素,這對應的判定樹有n個圓形的非葉節點和n+1個方形的葉節點。
上圖中,n個圓形節點(代表有序序列有n個元素)構成的樹的深度與n個節點完全二叉樹的深度(高度)相等,均為⌊log2n⌋+1或⌈log2(n+1)⌉
二分查找的時間復雜度為O(log2N),比順序查找的效率高。
由上述分析可知,用二分查找到給定值或查找失敗的比較次數最多不會超過樹的高度。查找成功與不成功,最壞的情況下,都需要比較⌊log2n⌋+1次。
二分查找的優點和缺點
雖然二分查找的效率高,但是要將表按關鍵字排序。而排序本身是一種很費時的運算。既使采用高效率的排序方法也要花費O(nlgn)的時間。
二分查找只適用順序存儲結構。為保持表的有序性,在順序結構里插入和刪除都必須移動大量的結點。因此,二分查找特別適用於那種一經建立就很少改動、而又經常需要查找的線性表。
對那些查找少而又經常需要改動的線性表,可采用鏈表作存儲結構,進行順序查找。鏈表上無法實現二分查找。