图实践经典问题一览
图算是数据结构中比较难的问题,但是在实际中解决的问题也更多。
其中,在图结构中涉及的问题主要有:
图的存储:
- 邻接表(Adjacency list):为每个节点建立一个链表存放与之连接的点.
- 邻接矩阵(Adjacency matrix):n*n的矩阵,有边的是1,无边的是0.
最短路径:
- Dijkstra:记录起点能够到达的所有节点的最短路径,这样,我们要找的终点一定在其中啊。
- DIST(w) = min(DIST(w), DIST(u) + c(u, w))
- 代码实现示例:
-
package com.realise; import java.util.Arrays; public class Dijkstra { static int max = 10000; public static int[] dijkstra(int[][] edge) { int n = edge.length; int[] res = new int[n]; for(int i=0; i<n; i++) res[i] = edge[0][i]; for(int i=0; i<n; i++) { for(int j=0; j<n; j++) { if(res[j] + edge[j][i] < res[i]) res[i] = res[j] + edge[j][i]; } } return res; } public static void main(String[] args) { // TODO Auto-generated method stub int[][] edge = { {0,20,50,30,max, max, max}, {max,0,25,max,max,70,max}, {max,max,0,40,25,50,max}, {max,max,max,0,55,max,max}, {max,max,max,max,0,10,70}, {max,max,max,max,max,0,50}, {max,max,max,max,max,max,0} }; int[] res = dijkstra(edge); System.out.println(Arrays.toString(res)); } }
- Floyd:又称插点法,可以寻找任意两点间的最短路径,允许图中带负权值的边,但不允许包含负权值的边组成的回路。
- 代码实现示例:
-
package com.realise; /** * Floyd算法的实现 * @author * */ public class Floyd { static int Max = 1000; public static int[][] floydRealise(int[][] edge) { int n = edge.length; int[][] a = new int[n][n]; int[][] path = new int[n][n]; //Initialize for(int i=0; i<n; i++) { for(int j=0; j<n; j++) { a[i][j] = edge[i][j]; if(i != j && edge[i][j] < Max) path[i][j] = i; else path[i][j] = 0; } } //T(O) = O(n3) for(int i=0; i<n; i++) { for(int j=0; j<n; j++) { for(int k=0; k<n; k++) { if(a[j][i] + a[i][k] < a[j][k]) { a[j][k] = a[j][i] + a[i][k]; path[j][k] = path[i][k]; } } } } return a; } public static void main(String[] args) { // TODO Auto-generated method stub int[][] edge = { {0,1,Max,4}, {Max,0,9,2}, {3,5,0,8}, {Max, Max, 6,0} }; int[][] res = floydRealise(edge); for(int i=0; i<res.length; i++) { for(int j=0; j<res.length; j++) System.out.print(res[i][j] + " "); System.out.println(); } } }
最小生成树
- Prim算法:A为要求的生成树,将要计入到A中的下一条边(u, v),应是E种一条不在A中且最小成本的边。
- 代码示例:华为的一道笔试题,使用Prim解决的,作为本个算法的示例程序:
-
import java.util.Scanner; import java.util.Vector; /** * 沿海有N个岛屿,为方便岛与岛之间的沟通,现规划对这些岛屿进行建桥, * 建桥的消耗和岛屿之间的距离成正比,如何规划才能使每个桥梁都能连通,且消耗最少 * @author * */ public class Main { public static int prim(int[][] val) { if(val == null || val.length == 0 || val.length == 1) return 0; boolean[] visited = new boolean[val.length]; for(int i=0; i<visited.length; i++) visited[i] = false; visited[0] = true; int count = 1; int res = 0; Vector<Integer> v = new Vector<Integer>(); v.add(0); do { int weight = Integer.MAX_VALUE; int temp = -1; for(Integer i : v) { for(int j=0; j<val.length; j++) { if(j != i && visited[j] == false && val[i][j] < weight) { weight = val[i][j]; temp = j; } } } res += weight; v.add(temp); count++; visited[temp] = true; } while(count < val.length); return res; } public static void main(String[] args) { /*input example: 3 0 990 692 990 0 179 692 179 0 */ Scanner sc = new Scanner(System.in); int N = sc.nextInt(); sc.nextLine(); String[] arr = new String[N]; for(int i=0; i<N; i++) arr[i] = sc.nextLine(); int[][] val = new int[N][N]; for(int i=0; i<N; i++) { String[] temp = arr[i].split(" "); for(int j=0; j<temp.length; j++) val[i][j] = Integer.parseInt(temp[j]); } // int[][] val = {{0, 10, 1000, 30, 100},{10, 0, 50, 1000, 1000}, {1000, 50, 0, 20, 10}, // {30, 1000, 20, 0, 60},{100, 1000, 10, 60, 0}}; System.out.println(prim(val)); } }
- Kruskal算法:将图中所有边的权重按大小排序,然后在每次添加放入边中以不构成回路为前提,每次选择一条权重最小的边加入生成树中。
- 为了快速判断候选边e的加入是否会形成环,可以使用并查集。时间复杂度为:O(eloge).
图的搜索(遍历):其中要特别注意隐式图的搜索,一些问题乍一看可能不是图的问题,但是其实可以用图的方法解决!!!
- 广度优先搜索BFS:辅助数据结构为队列,与树的层次遍历类似
- 深度优先搜索DFS:辅助数据结构为栈,与树的先序/中序/后序遍历类似
- 隐式图:在实践中,往往一看不是关于图的问题,但如果给定一个起始状态和一些规则,求解决方案的问题:往往可以根据这些规则,将各个状态建立边,然后BFS/DFS搜索方法,在解空间中搜索。
几个经典的问题:
- 单词变换问题Word Ladder。http://www.cnblogs.com/little-YTMM/p/5502954.html
-
Given a
pattern
and a stringstr
, find ifstr
follows the same pattern.Here follow means a full match, such that there is a bijection between a letter in
pattern
and a non-empty word instr
.Examples:
- pattern =
"abba"
, str ="dog cat cat dog"
should return true. - pattern =
"abba"
, str ="dog cat cat fish"
should return false. - pattern =
"aaaa"
, str ="dog cat cat dog"
should return false. - pattern =
"abba"
, str ="dog dog dog dog"
should return false.
-
public class Solution { public boolean wordPattern(String pattern, String str) { Map<Character, String> map = new HashMap<Character, String> (); Set<String> set = new HashSet<String>(); char[] ch = pattern.toCharArray(); String[] strr = str.split(" "); if(ch.length != strr.length) return false; for(int i=0; i<ch.length; i++) { if(!map.containsKey(ch[i]) && !set.contains(strr[i])) { map.put(ch[i], strr[i]); set.add(strr[i]); } else { if(set.contains(strr[i]) && !map.containsKey(ch[i])) return false; if(!map.get(ch[i]).equals(strr[i])) return false; } } return true; } }
- pattern =
-
- 周围区域问题。http://www.cnblogs.com/little-YTMM/p/5480942.html
-
- 代码:
-
import java.util.LinkedList; import java.util.Queue; class Node { int x; int y; public Node(int x, int y) { this.x = x; this.y = y; } } class Regions { private int[] rowdi = {-1, 1, 0, 0}; private int[] coldi = {0, 0, -1, 1}; /** * BFS. * Search begin from the edge to the interior. * @param val * @return */ public int[][] check(int[][] val) { if(val == null || val.length == 0 || val[0].length == 0) return val; Queue<Node> q = new LinkedList<Node>(); int row = val.length; int col = val[0].length; for(int i=0; i<row; i++) { if(val[i][0] == 0) { Node n = new Node(i, 0); q.add(n); val[i][0] = 2; } if(val[i][col-1] == 0) { Node n = new Node(i, col-1); q.add(n); val[i][col-1] = 2; } } for(int j=1; j<col-1; j++) { if(val[0][j] == 0) { Node n = new Node(0, j); q.add(n); val[0][j] = 2; } if(val[row-1][j] == 0) { Node n = new Node(row-1, j); q.add(n); val[row-1][j] = 2; } } while(!q.isEmpty()) { Node n = q.remove(); for(int i=0; i<rowdi.length; i++) { if( n.x+rowdi[i]<row && n.y+coldi[i] < col && n.x+rowdi[i] >= 0 && n.y+coldi[i] >= 0) { if(val[n.x+rowdi[i]][n.y+coldi[i]] == 0) { Node t = new Node(n.x+rowdi[i], n.y+coldi[i]); q.add(t); val[n.x+rowdi[i]][n.y+coldi[i]] = 2; } } } } for(int i=0; i<row; i++) { for(int j=0; j<col; j++) { if(val[i][j] == 2) val[i][j] = 0; else if(val[i][j] == 0) val[i][j] = 1; } } return val; } } public class SurroundedRegions { public static void main(String[] args) { int[][] val = { {1,1,1,1,1}, {1,0,0,1,0}, {1,1,1,1,0}, {1,0,1,0,0}, {1,1,1,1,1}}; Regions region = new Regions(); int[][] res = region.check(val); for(int i=0; i<res.length; i++) { for(int j=0; j<res[0].length; j++) System.out.print(res[i][j] + " "); System.out.println(); } } }
-
- 八皇后问题。http://www.cnblogs.com/little-YTMM/p/5478140.html
-
- 代码:
-
package com.eight; import java.util.ArrayList; import java.util.Arrays; class EightQueen { private int N; //The number of queens. private boolean[] column; private boolean[] maind; private boolean[] secondaryd; private ArrayList<int[]> result = new ArrayList<int[]>(); /** * Initial the column, main diagonal, secondary diagonal to false, * which means there is no queen everywhere. * @param N */ public void initQ(int N) { this.N = N; column = new boolean[N]; maind = new boolean[2 * N - 1]; secondaryd = new boolean[2 * N - 1]; int i = 0; for(i=0; i<column.length; i++) column[i] = false; for(i=0; i<maind.length; i++) maind[i] = false; for(i=0; i<secondaryd.length; i++) secondaryd[i] = false; } /** * the main function of lay queens. */ public void queens() { int[] path = new int[N]; calcQueen(path, 0); } private void calcQueen(int[] path, int row) { // TODO Auto-generated method stub if(row == N) { result.add(path); System.out.println(Arrays.toString(path)); return; } for(int col=0; col<N; col++) { if(canLay(row, col)) { path[row] = col; column[col] = true; maind[row-col+N-1] = true; secondaryd[row+col] = true; calcQueen(path, row+1); //BackTracking, important!! column[col] = false; maind[row-col+N-1] = false; secondaryd[row+col] = false; } } } /** * Judge if the position can lay a queen. * @param row * @param col * @return */ private boolean canLay(int row, int col) { // TODO Auto-generated method stub return (!column[col] && !secondaryd[row+col] && !maind[row-col+N-1]); } public ArrayList<int[]> getResult() { return result; } } public class Queens { public static void main(String[] args) { EightQueen q = new EightQueen(); q.initQ(8); // It is a eight queens problem. q.queens(); } } //Some output instances: [0, 4, 7, 5, 2, 6, 1, 3] [0, 5, 7, 2, 6, 3, 1, 4] [0, 6, 3, 5, 7, 1, 4, 2] [0, 6, 4, 7, 1, 3, 5, 2] [1, 3, 5, 7, 2, 0, 6, 4] [1, 4, 6, 0, 2, 7, 5, 3] [1, 4, 6, 3, 0, 7, 5, 2] [1, 5, 0, 6, 3, 7, 2, 4] [1, 5, 7, 2, 0, 3, 6, 4] [1, 6, 2, 5, 7, 4, 0, 3] [1, 6, 4, 7, 0, 3, 5, 2] [1, 7, 5, 0, 2, 4, 6, 3] [2, 0, 6, 4, 7, 1, 3, 5]
-
- 数独问题。http://www.cnblogs.com/little-YTMM/p/5507841.html
-
- 代码:
-
import java.util.Stack; class Help { int row; int col; int val; } public class Sudoku { /** * Use stack store the roads. * @param chess * @return */ public static int[][] getSudoku(int[][] chess) { Stack<Help> stack = new Stack<Help>(); int val = -1; for(int i=0; i<9; i++) { for(int j=0; j<9; j++) { if(chess[i][j] != 0) continue; boolean flag = false; int k; if(val == -1) k = 0; else k = val+1; for(; k<10; k++) { if(isValid(k, i, j, chess)) { Help h = new Help(); h.row = i; h.col = j; h.val = k; stack.add(h); chess[i][j] = k; val = -1; flag = true; } if(flag == true) k = 10; } if(flag == false && !stack.isEmpty()) { //There is no road, backtracking Help h = stack.pop(); i = h.row; j = h.col-1; val = h.val; chess[i][j+1] = 0; } } } return chess; } /** * Judge if it is valid when chess[row][col] = k. * @param k * @param row * @param col * @param chess * @return */ private static boolean isValid(int k, int row, int col, int[][] chess) { for(int i=0; i<9; i++) if(chess[row][i] == k) return false; for(int i=0; i<9; i++) if(chess[i][col] == k) return false; int r = row/3, c = col/3; for(int i=r*3; i<r*3+3; i++) { for(int j=c*3; j<c*3+3; j++) { if(chess[i][j] == k) return false; } } return true; } public static void main(String[] args) { // TODO Auto-generated method stub int[][] a = { {0,4,2,0,6,3,0,0,9}, {6,0,0,0,1,0,0,0,5}, {3,0,0,0,2,0,4,8,0}, {1,0,0,5,0,2,6,0,8}, {4,0,0,0,0,7,0,0,1}, {9,0,5,6,0,0,0,0,7}, {0,3,6,0,5,0,0,0,2}, {2,0,0,0,7,0,0,0,4}, {7,0,0,2,9,0,8,5,0} }; int[][] res = getSudoku(a); for(int i=0; i<9; i++) { for(int j=0; j<9; j++) System.out.print(res[i][j] + " "); System.out.println(); } } }
-
-
所有括号的匹配问题。http://www.cnblogs.com/little-YTMM/p/5505522.html
-
- 代码:
-
/** * Given the number N, return all of the correct brackets. * @param n * @return */ @SuppressWarnings("unchecked") public static ArrayList<String> getBracketsOfN(int n) { @SuppressWarnings("rawtypes") ArrayList[] dp = new ArrayList[n+1]; for(int i=0; i<dp.length; i++) dp[i] = new ArrayList<String>(); dp[0].add(""); dp[1].add("()"); if(n == 0) return dp[0]; if(n == 1) return dp[1]; int count = 2; while(count < n+1) { ArrayList<String> lcount = dp[count]; for(int i=0; i<count; i++) { ArrayList<String> l1 = dp[i]; ArrayList<String> l2 = dp[count-i-1]; for(int j=0; j<l1.size(); j++) { for(int k=0; k<l2.size(); k++) { StringBuffer sb = new StringBuffer(); sb.append(l1.get(j)); sb.append("("); sb.append(l2.get(k)); sb.append(")"); lcount.add(sb.toString()); } } } dp[count++] = lcount; } return dp[n]; }
-