二維數組中的查找雜談

這幾天在Iteye看到有個有獎問答的題目,二維數組的二分查找。想想最近也沒什麼事情,就做了一下。自己的算法基礎很是薄弱,所以也參考了一些網上的東東。那個有獎什麼就不參加了。哈哈。。。。

原題如下(估計很多人知道了):
在一個二維數組中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。
例如下面的二維數組就是每行、每列都遞增排序。如果在這個數組中查找數字7,則返回true;如果查找數字5,由於數組不含有該數字,則返回false。
[img]http://dl.iteye.com/upload/attachment/609162/324ea7f7-da0d-3d80-aa33-866ce144a07e.jpg[/img]

我這裏主要講下我當時是怎麼做的 :oops: 。
我的第一印象如果想快速的查找的話應該要利用到對角線。因爲對角線元素在他所在的矩陣中最大的,可以在一定程度縮小查找範圍。但當時沒有具體想清楚該怎麼做。後來又想到題目中沒有指定數組是N*N的,所以先放棄了這個想法。然後繼續想想,如果面試官問我這個題目的話該怎麼做。

首先當然是保證不錯,二重遍歷是肯定不會錯的。我都一個一個比較了還會有什麼問題呢。當然這個就跟別人問你用什麼方法排序的,你回答冒泡排序一樣,沒什麼技術含量。不過至少說明你瞭解了題目的內容。
再深入一下,二維數組是數組的數組。一般對於有序數組可以採用二分法對數組進行一個lgn的查找。那這樣可以對二維數組的每個數組進行一次二分查找。這樣就比較不錯了,知道了二分查找。估計可以打個及格的分數了。
繼續深入,可能就會像我一樣想到了利用對角線。但是這個要考慮到不是N*N的情況。利用對角線對矩陣分塊,然後就可以排除一些數據了。類如上圖中,查找元素 7.跟對角線元素比較之後, 4 < 7 < 10, 那就說明,4左上角,10右下角的數據不用處理。就減少了一半的數據。這篇文章很清楚的說明了這種方法應該怎麼做。[url]http://justjavac.iteye.com/blog/1310178[/url]

做到這一步以後,我是沒什麼辦法了。如果面試官問我,還有更好的辦法沒。我就只能拜拜了。不過,好的方法總是有的,只要好好的尋找。我在網上搜索的時候發現了楊氏矩陣查找的方法。[url]http://blog.csdn.net/michealmeng555/article/details/2489923[/url]
楊氏矩陣查找跟對角線的方法差不多,但是不是用的主對角線\,而是用的副對角線/. 在副對角線中的元素有個性質。在以他爲左右兩個頂點的矩陣中,他是一個數組中的最大值,是另外一個數組中的最小值。那我們可以利用這個性質,假設我們選擇最右上角的元素 a。那麼對於需要查找的元素b,如 a > b 向左走,如果 a < b向下走。直到到達最左下角結束。真是太精妙了。

其實這些算法都不是很難,難的是能夠想到。如果沒想到,那就儘快的學起來吧。。。

public class Search2Array {

private static int[][] arrays = {{1,2,8,9},{2,4,9,12},{4,7,10,13},{6,8,11,15}};

//最容易的辦法,使用兩重循環
//複雜度O(n2)
public static boolean search1(int target){
for(int[] arr : arrays){
for(int a : arr){
if (a == target){
return true;
}
}
}
return false;
}

//對每個子一維數組用二分查找
//複雜度爲O(nlgn)
public static boolean search2(int target){
boolean flag = false;
for(int [] arr : arrays){
flag = binarySearch(arr, 0, arr.length -1, target);
if(flag){
return true;
}
}
return false;
}

//二分查找
private static boolean binarySearch(int[] array, int start, int end, int target){
int middle = start + (end - start)/2;
while(start <= end){
if(array[middle] == target){
return true;
}else if(array[middle] > target){
end = middle - 1;
}else {
start = middle + 1;
}
middle = start + (end - start)/2;
}
return false;
}

//使用最右上角或者最左下角的元素作爲起始元素
//這兩個位置的元素有個性質:是一個數組中最大的,也是一個數組中最小的
//縱向也看做一個數組
//複雜度爲O(m+n)
public static boolean youngTableau(int target){
int raw = 0;
int col = arrays[0].length - 1;
while(col >= 0 && raw <= arrays.length -1){
int tmp = arrays[raw][col];
if( tmp == target){
return true;
}else if(tmp > target) {
col--;
}else{
raw++;
}
}
return false;
}

public static void main(String[] args){
System.out.println(search1(7));
System.out.println(search1(5));

System.out.println(search2(7));
System.out.println(search2(5));

System.out.println(youngTableau(7));
System.out.println(youngTableau(5));
}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章