Lexographically smallest path in a N*M grid
我在最近的一次采访中遇到了这个问题。
给定一个由数字组成的 N*M 网格,网格中的路径是您遍历的节点。给定一个约束,我们只能在网格中向右或向下移动。因此,给定这个网格,我们需要找到排序后的字典最小路径,从网格的左上角到右下角点
例如。如果网格是 2*2
4 3
5 1
那么根据问题的字典最小路径是"1 3 4"。
遇到这样的问题怎么办?代码表示赞赏。提前致谢。
您可以使用动态编程来解决这个问题。令
1 | f(i, j) = sort( a(i, j) + smallest(f(i + 1, j), f(i, j + 1))) |
其中
重复的基本情况是:
1 | f(N, M) = a(N, M) |
考虑以下用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | //-- the 200 is just the array size. It can be modified string a[200][200]; //-- represent the input grid string f[200][200]; //-- represent the array used for memoization bool calculated[200][200]; //-- false if we have not calculate the value before, and true if we have int N = 199, M = 199; //-- Number of rows, Number of columns //-- sort the string str and return it string srt(string &str){ sort(str.begin(), str.end()); return str; } //-- return the smallest of x and y string smallest(string & x, string &y){ for (int i = 0; i < x.size(); i++){ if (x[i] < y[i]) return x; if (x[i] > y[i]) return y; } return x; } string solve(int i, int j){ if (i == N && j == M) return a[i][j]; //-- if we have reached the buttom right cell (I assumed the array is 1-indexed if (calculated[i][j]) return f[i][j]; //-- if we have calculated this before string ans; if (i == N) ans = srt(a[i][j] + solve(i, j + 1)); //-- if we are at the buttom boundary else if (j == M) ans = srt(a[i][j] + solve(i + 1, j)); //-- if we are at the right boundary else ans = srt(a[i][j] + smallest(solve(i, j + 1), solve(i + 1, j))); calculated[i][j] = true; //-- to fetch the calculated result in future calls f[i][j] = ans; return ans; } string calculateSmallestPath(){ return solve(1, 1); } |
如果没有数字重复,也可以在
直觉:
假设我将左上角
所以,首先我们找到
方法:
每次在
现在,我们将维护一个有效网格列表,我们将在其中找到路径的候选者。第一个有效的网格将是
对键进行排序并从排序后的键集
维护一个有效网格树
现在的算法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | for each val in sorted key set S do (i,j) <- Dict(val) Grid G <- Root(T) do while (i,j) in G if G has no child do G.left <- G(a,b,i,j) G.right <- G(i,j,c,d) else if (i,j) in G.left G <- G.left else if (i,j) in G.right G <- G.right else dict(val) <- null end do end if-else end do end for for each val in G(1,1,m,n) if dict(val) not null solution.append(val) end if end for return solution |
Java 代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | class Grid{ int a, b, c, d; Grid left, right; Grid(int a, int b, int c, int d){ this.a = a; this.b = b; this.c = c; this.d = d; left = right = null; } public boolean isInGrid(int e, int f){ return (e >= a && e <= c && f >= b && f <= d); } public boolean hasNoChild(){ return (left == null && right == null); } } public static int[] findPath(int[][] arr){ int row = arr.length; int col = arr[0].length; int[][] index = new int[row*col+1][2]; HashMap<Integer,Point> map = new HashMap<Integer,Point>(); for(int i = 0; i < row; i++){ for(int j = 0; j < col; j++){ map.put(arr[i][j], new Point(i,j)); } } Grid root = new Grid(0,0,row-1,col-1); SortedSet<Integer> keys = new TreeSet<Integer>(map.keySet()); for(Integer entry : keys){ Grid temp = root; int x = map.get(entry).x, y = map.get(entry).y; while(temp.isInGrid(x, y)){ if(temp.hasNoChild()){ temp.left = new Grid(temp.a,temp.b,x, y); temp.right = new Grid(x, y,temp.c,temp.d); break; } if(temp.left.isInGrid(x, y)){ temp = temp.left; } else if(temp.right.isInGrid(x, y)){ temp = temp.right; } else{ map.get(entry).x = -1; break; } } } int[] solution = new int[row+col-1]; int count = 0; for(int i = 0 ; i < row; i++){ for(int j = 0; j < col; j++){ if(map.get(arr[i][j]).x >= 0){ solution[count++] = arr[i][j]; } } } return solution; } |
空间复杂度由维护字典 -
填充然后排序字典的时间复杂度 -
当然,如果值重复,这将不起作用,因为那时我们将有多个
其他仅供参考:与我之前听到的类似问题有一个额外的网格属性 - 没有重复的值,数字来自
您可以应用动态规划方法以
下面我会考虑,
让每个单元格以排序顺序存储在此单元格处结束的字典最小路径。具有
算法是:
1 2 3 4 | path[0][0] <- a[0][0] path[i][0] <- insert(a[i][0], path[i - 1][0]) path[0][j] <- insert(a[0][j], path[0][j - 1]) path[i][j] <- insert(a[i][j], min(path[i - 1][j], path[i][j - 1]) |