記錄算法博客主要是記錄學習過程當中,目前階段仍是不能主動地去寫出算法。java
1 題目來源 leetcode算法
2 leetcode官方方法一學習
System.out代碼是爲了從控制檯打印出算法執行的數據路線,從而達到能很好理解算法。
import java.util.*; class Solution { public int swimInWater(int[][] grid) { int N = grid.length; // 記錄走過的點 Set<Integer> seen = new HashSet(); // 記錄對於當前點來講,將要和以前由於某個選擇未走的點,須要在這個數組中獲取最小的值,而後從新走 // (k1, k2) -> grid[k1 / N][k1 % N] - grid[k2 / N][k2 % N]是lambda表達式,是int compare(T o1, T o2);的方法實現 // 換言之若是想比較列隊中k1,k2的大小,則實際比較的是grid[k1 / N][k1 % N],grid[k2 / N][k2 % N]的大小 // 由於行座標*N+列座標的值能肯定數組中的一點,因此列隊中存的是行座標*N+列座標值,從而根據列隊中的值也能推導出來數組中的點 PriorityQueue<Integer> pq = new PriorityQueue<Integer>((k1, k2) -> grid[k1 / N][k1 % N] - grid[k2 / N][k2 % N]); pq.offer(0); // 記錄水位 int ans = 0; // 控制當前點能夠走的下一個點的位置,即上下左右 int[] dr = new int[]{1, -1, 0, 0}; int[] dc = new int[]{0, 0, 1, -1}; while (!pq.isEmpty()) { // 獲取最小值,爲當前須要走的點 int k = pq.poll(); // r爲行下標,c爲列下標 int r = k / N, c = k % N; // ans記錄的是已經走的點的最大值 ans = Math.max(ans, grid[r][c]); if (r == N-1 && c == N-1) return ans;//達到終點結束遍歷 // 取當前點的上下左右四個點存到待走的點裏面 System.out.println("開始搜索:"); for (int i = 0; i < 4; ++i) { // cr爲數組行下標,cc爲數組列下標 int cr = r + dr[i], cc = c + dc[i]; // ck記錄該點 cr * N + cc的值能肯定一個點 int ck = cr * N + cc; if (0 <= cr && cr < N && 0 <= cc && cc < N && !seen.contains(ck)) { // 將要走的點放入列隊中 pq.offer(ck); // 走的點記錄下來 seen.add(ck); System.out.print("當前所在點:["+r+","+c+"]"+"搜索方向:["+dr[i]+","+dc[i]+"]"+"搜索結果:["+cr+","+cc+"]"+"存入列隊的點:"+ck+","+"存入列隊的點對應的數組值:"+grid[cr][cc]+","); System.out.print("目前列隊存儲的所有點對應的值:["); for(Integer a:pq){ System.out.print(grid[a/N][a%N]+","); } System.out.print("]"); System.out.println(); } } } throw null; } }
思考:測試
1) 列隊中存儲的是 行下標*N+列下標的值?ui
2) 若是根據列隊中的存儲的數據來比較對應的數組中對應的點的數據。spa
3) 算法思路:選擇走的點是全部待走點的最小值,最後的結果就是在全部已經走過的點中取最大值。code
4) PriorityQueue 優先隊列的使用,比較器的使用,拿出來的值必定是最小值。blog
測試數據 int[][] a={{0,4,1},{2,8,7},{3,6,5}};隊列
輸出結果 最後一行的6就是答案,前面的控制檯打印出來的都是數據路線
開始搜索: 當前所在點:[0,0]搜索方向:[1,0]搜索結果:[1,0]存入列隊的點:3,存入列隊的點對應的數組值:2,目前列隊存儲的所有點對應的值:[2,] 當前所在點:[0,0]搜索方向:[0,1]搜索結果:[0,1]存入列隊的點:1,存入列隊的點對應的數組值:4,目前列隊存儲的所有點對應的值:[2,4,] 開始搜索: 當前所在點:[1,0]搜索方向:[1,0]搜索結果:[2,0]存入列隊的點:6,存入列隊的點對應的數組值:3,目前列隊存儲的所有點對應的值:[3,4,] 當前所在點:[1,0]搜索方向:[-1,0]搜索結果:[0,0]存入列隊的點:0,存入列隊的點對應的數組值:0,目前列隊存儲的所有點對應的值:[0,4,3,] 當前所在點:[1,0]搜索方向:[0,1]搜索結果:[1,1]存入列隊的點:4,存入列隊的點對應的數組值:8,目前列隊存儲的所有點對應的值:[0,4,3,8,] 開始搜索: 開始搜索: 當前所在點:[2,0]搜索方向:[0,1]搜索結果:[2,1]存入列隊的點:7,存入列隊的點對應的數組值:6,目前列隊存儲的所有點對應的值:[4,8,6,] 開始搜索: 當前所在點:[0,1]搜索方向:[0,1]搜索結果:[0,2]存入列隊的點:2,存入列隊的點對應的數組值:1,目前列隊存儲的所有點對應的值:[1,8,6,] 開始搜索: 當前所在點:[0,2]搜索方向:[1,0]搜索結果:[1,2]存入列隊的點:5,存入列隊的點對應的數組值:7,目前列隊存儲的所有點對應的值:[6,8,7,] 開始搜索: 當前所在點:[2,1]搜索方向:[0,1]搜索結果:[2,2]存入列隊的點:8,存入列隊的點對應的數組值:5,目前列隊存儲的所有點對應的值:[5,8,7,] 開始搜索: 當前所在點:[0,0]搜索方向:[1,0]搜索結果:[1,0]存入列隊的點:3,存入列隊的點對應的數組值:2,目前列隊存儲的所有點對應的值:[2,] 當前所在點:[0,0]搜索方向:[0,1]搜索結果:[0,1]存入列隊的點:1,存入列隊的點對應的數組值:4,目前列隊存儲的所有點對應的值:[2,4,] 開始搜索: 當前所在點:[1,0]搜索方向:[1,0]搜索結果:[2,0]存入列隊的點:6,存入列隊的點對應的數組值:3,目前列隊存儲的所有點對應的值:[3,4,] 當前所在點:[1,0]搜索方向:[-1,0]搜索結果:[0,0]存入列隊的點:0,存入列隊的點對應的數組值:0,目前列隊存儲的所有點對應的值:[0,4,3,] 當前所在點:[1,0]搜索方向:[0,1]搜索結果:[1,1]存入列隊的點:4,存入列隊的點對應的數組值:8,目前列隊存儲的所有點對應的值:[0,4,3,8,] 開始搜索: 開始搜索: 當前所在點:[2,0]搜索方向:[0,1]搜索結果:[2,1]存入列隊的點:7,存入列隊的點對應的數組值:6,目前列隊存儲的所有點對應的值:[4,8,6,] 開始搜索: 當前所在點:[0,1]搜索方向:[0,1]搜索結果:[0,2]存入列隊的點:2,存入列隊的點對應的數組值:1,目前列隊存儲的所有點對應的值:[1,8,6,] 開始搜索: 當前所在點:[0,2]搜索方向:[1,0]搜索結果:[1,2]存入列隊的點:5,存入列隊的點對應的數組值:7,目前列隊存儲的所有點對應的值:[6,8,7,] 開始搜索: 當前所在點:[2,1]搜索方向:[0,1]搜索結果:[2,2]存入列隊的點:8,存入列隊的點對應的數組值:5,目前列隊存儲的所有點對應的值:[5,8,7,] 6
3 leetcode官方方法二
System.out代碼是爲了從控制檯打印出算法執行的數據路線,從而達到能很好理解算法。
本身的理解:算法是二分搜索,由於題目中已經明確說出 grid[i][j] 位於區間 [0, ..., N*N - 1] 內,即咱們知道數組中的值最小值爲0,最大值爲N*N - 1,能夠經過試錯的方法,而且這個試錯方法是二分算法,
第一次深度遍歷時,發現有一條路線比(n*n-1)/2小,則調整比較的數字範圍,作第二次深度遍歷,發現有一條路線比(n*n-1)/2/2小,則繼續調整比較的範圍,如此進行。遍歷時,若是發現沒有比猜想值小的,則將比較值作相反的調整。
import java.util.*; class Solution { public int swimInWater(int[][] grid) { int N = grid.length; int lo = grid[0][0], hi = N * N; while (lo < hi) {// 判斷猜想的結束 System.out.println("猜想開始||||||||||||||||||||||||||||||||||||||"); // 定義猜想值 int mi = lo + (hi - lo) / 2; System.out.println("當次遍歷猜想值爲:"+mi); if (!possible(mi, grid)) { lo = mi + 1;// 沒有比猜想值小的,猜想值須要往大猜想 } else { hi = mi;// 有比猜想值小的,猜想值須要往小猜想 } System.out.println("猜想結束||||||||||||||||||||||||||||||||||||||"); System.out.println(); System.out.println(); System.out.println(); } return lo; } public boolean possible(int T, int[][] grid) { int N = grid.length; Set<Integer> seen = new HashSet(); seen.add(0);// 存儲訪問過的點 int[] dr = new int[]{1, -1, 0, 0};// 定義行走的方向 int[] dc = new int[]{0, 0, 1, -1};// 定義行走的方向 Stack<Integer> stack = new Stack(); stack.add(0);// 深度遍歷協助的棧結構,達到深度遍歷的效果 System.out.println("開始遍歷========="); // 深度遍歷 while (!stack.empty()) { // 棧找那個存儲的是cr * N + cc值,返回來也能夠推測出是哪一個點 int k = stack.pop(); int r = k / N, c = k % N; System.out.println("當前行:"+r+","+"當前列:"+c+"當前值:"+grid[r][c]); if (r == N-1 && c == N-1) { System.out.println("有一條路徑的值都比預測值小"); return true;// 若是返回的是true說明至少有一條路線,值都比猜想值小 } for (int i = 0; i < 4; ++i) { int cr = r + dr[i], cc = c + dc[i]; int ck = cr * N + cc; // grid[cr][cc] <= T 決定一條路徑是否能走到頭,與 if (r == N-1 && c == N-1)配合完成程序 if (0 <= cr && cr < N && 0 <= cc && cc < N && !seen.contains(ck) && grid[cr][cc] <= T) { stack.add(ck); seen.add(ck); } } } System.out.println("結束遍歷========="); // 從程序能夠看出若是一條路徑中的一個點比T大,那麼這條路徑是沒法走到頭的,能走到頭的即知足r == N-1 && c == N-1條件的就是至少有一條路線值都比T小 // 若是返回false的話那就是全部的路徑都至少有一個點比T大,那麼猜想的值確定是猜想小了的,須要往大猜想。 System.out.println("在全部路徑中,對於一個路徑至少有一個點比T值大"); return false;// 若是返回值爲false說明沒有一條路徑,最大值比猜想值小 } }
測試數據 int[][] a={{0,4,1},{2,8,7},{3,6,5}};
輸出結果 最後一行的6就是答案,前面的控制檯打印出來的都是數據路線
第一個猜想解析: 遍歷0->4->1 ,想遍歷7時發現比預測值大,不進行遍歷,即不存入棧中,也就不可能作下面的遍歷;遍歷0->4時,準備走8,也不符合;遍歷0->2>3,準備走6時,發現又不合符,此時棧內爲空,沒法走下去,沒有走到終點,程序返回false.
其它的猜想都很好理解,都能走點終點.當lo >= hi時,此時就沒必要猜想,由於會陷入無限的相同輸出結果中,本身能夠試驗一下,本身設置循環次數,固然這個循環次數要比找到正確結果的循環次數大。
猜想開始|||||||||||||||||||||||||||||||||||||| 當次遍歷猜想值爲:4 開始遍歷========= 當前行:0,當前列:0當前值:0 當前行:0,當前列:1當前值:4 當前行:0,當前列:2當前值:1 當前行:1,當前列:0當前值:2 當前行:2,當前列:0當前值:3 結束遍歷========= 在全部路徑中,對於一個路徑至少有一個點比T值大 猜想結束|||||||||||||||||||||||||||||||||||||| 猜想開始|||||||||||||||||||||||||||||||||||||| 當次遍歷猜想值爲:7 開始遍歷========= 當前行:0,當前列:0當前值:0 當前行:0,當前列:1當前值:4 當前行:0,當前列:2當前值:1 當前行:1,當前列:2當前值:7 當前行:2,當前列:2當前值:5 有一條路徑的值都比預測值小 猜想結束|||||||||||||||||||||||||||||||||||||| 猜想開始|||||||||||||||||||||||||||||||||||||| 當次遍歷猜想值爲:6 開始遍歷========= 當前行:0,當前列:0當前值:0 當前行:0,當前列:1當前值:4 當前行:0,當前列:2當前值:1 當前行:1,當前列:0當前值:2 當前行:2,當前列:0當前值:3 當前行:2,當前列:1當前值:6 當前行:2,當前列:2當前值:5 有一條路徑的值都比預測值小 猜想結束|||||||||||||||||||||||||||||||||||||| 猜想開始|||||||||||||||||||||||||||||||||||||| 當次遍歷猜想值爲:5 開始遍歷========= 當前行:0,當前列:0當前值:0 當前行:0,當前列:1當前值:4 當前行:0,當前列:2當前值:1 當前行:1,當前列:0當前值:2 當前行:2,當前列:0當前值:3 結束遍歷========= 在全部路徑中,對於一個路徑至少有一個點比T值大 猜想結束|||||||||||||||||||||||||||||||||||||| 6
思考:
1) 二分查找概念。
2) 程序如何實現二分查找的。