【算法題目解析】楊氏矩陣數字查找

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"一 背景"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"遇到的一道算法題:已知矩陣內的元素,每行 從左到右遞增;每列 從上到下遞增; 給定一個數字t,要求判斷矩陣中是否存在這個元素。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要求:時間複雜度儘可能低"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"二 概念"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這樣的矩陣也叫做楊氏矩陣,通常可以用二維數組來表示。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"楊氏矩陣示例(1):"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ab/ab013df5e72cc370b81a32b2854dd896.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏有一個需要注意的地方,每行的遞增和每列的遞增,並不能保證跨行情況下的右邊數字一定大於左邊數字。我們只能知道 左上一定小於右下。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"之所以描述這麼多,是因爲這道查找題目的解答一定要建立在對楊氏矩陣的理解之上。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"三 解法和思考"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"3.1 數組遍歷"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"m行n列數組,逐個數字遍歷,最差的時間複雜度爲 O(mxn);"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"3.2 遍歷優化-1"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3.1的解法沒有利用任何已知信息。考慮到一行數字,從左到右遞增,那麼我們可以在3.1的基礎上,把每行內的查找改爲使用二分查找的方式,時間複雜度爲O(m logn)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果m!=n,那麼還可以降爲O(min(mxlogn,nlogm))"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"3.3 遍歷查找優化-2"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"楊氏矩陣查值的優化:由於楊氏矩陣從左到右從上到下都是逐漸遞增的,假如找11這個數,先從第一行從左到右,如果找到大於11的第一個值,此時表明這一行沒有值,這時向下找,看下面的值如果大於11向左找,如果找到小於11的第一個值,此時說明這一行也沒有要找的值,這時向下繼續找,如果下面的值小於要找的值就向右找,如此反覆就可以找到目標值,相比於遍歷查找少了很多的比較,但是實現過程也比較複雜"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"3.4 遞歸解法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所有元素都掃描一遍用遞歸解法。由楊氏矩陣的特點我們可以每次查找矩陣中當前元素的下邊和右邊直到要查找的數key小於當前元素那就說明沒有這個數不存在返回false,就這樣每次改變要查找元素的座標並遞歸調用該方法,直到元素的座標大於這個二維數組的長度時返回false即可。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"3.5 分治法查找"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在元素中取第一個元素的對角線,由於其特點對角線上的元素也是遞增的,如果有就在對角線上,如果沒有就找和這個目標值相鄰的兩個數再通過這兩個數找到兩個可能存在的子矩陣。之後繼續每個矩陣取第一個元素這樣就能找到了。這個相鄰的子矩陣具體找法是:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於小的那個值取其右邊和下邊構成的矩陣。這個矩陣中的值大於它。對於大的那個值取其左邊和上邊構成的矩陣,該矩陣中的值小於它。這樣反覆的找對角線,找矩形。就可以找到這個值了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"3.6 定位查找法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從右邊開始比較元素,如果比目標元素大就往左查找比較,如果比目標元素小就往下然後繼續往左找,這個方法相比3.3,好在不用向右查找,因爲右邊的上面一定大於要查找的值那麼它的右邊也一定大於要查找的值,這是由楊氏矩陣的特性決定的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了簡化步驟,最好是從矩陣的右上角(即 第一行 第n-1列) 或 左下角(第m行第0列)開始查找,這樣是爲了最好地利用矩陣屬性。以右上角開始查找爲例,這裏使用示例矩陣舉例,待查找元素爲10:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/16/16d93d2b19843251e01fe36b7ffb54bb.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1、右上角元素爲8,小於10;而8是本行最大數值,所以只能向下查找,8所在的第一行元素都被排除;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a0/a0bf4af33bf98e7f57181f84adda98f8.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2、9依然小於10,所以繼續向下,查到11>10,因此在本行向左查找,(11所在的這列元素都可以排除,因爲上面的8、9前兩輪已排除,而11以下的元素都大於11,所以自然也都大於10)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/15/152f71dabbc7907559f4a80564c16a74.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3、9<10,因爲右側元素已經都排除,所以只剩下了同列下一行(元素10)這唯一一個選擇"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/98/98ce7f7cb103dd4291c08c4841859e72.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4、10正好是要查找的元素,所以返回成功。由此也容易推斷,最差的情況是繼續在最後一行,向左遍歷完剩餘的兩個元素。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那麼這種方法的時間複雜度最差情況爲O(m+n)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/96/96746f4c8eaaba66fdc0ea37ace24683.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基於上述的分析和示例所示的推導過程,可以寫出如下代碼【java版本】:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"public class YoungSearch {\n \n public static int findNum(int[][] arr, int row, int col, int target){\n int i=0;\n int j=col-1;\n while(i=0){\n if(arr[i][j] < target){\n ++i;\n }\n else if(arr[i][j]>target){\n --j;\n }else{\n return 1;\n }\n }\n return 0;\n }\n \n \n public static void main(String[] args){\n int a[][] = {{ 1, 3, 5 }, { 3, 5, 7 }, { 5, 7, 9 }};\n \n int result = findNum(a, 3,3, 3);\n System.out.println(result);\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章