LintCode 433:岛屿的个数

https://www.lintcode.com/problem/number-of-islands

描述

给一个01矩阵,求不同的岛屿的个数
0代表海,1代表岛,如果两个1相邻,那么这两个1属于同一个岛。我们只考虑上下左右为相邻。

样例

  1. 输入:
  2. [
  3. [1,1,0,0,0],
  4. [0,1,0,0,1],
  5. [0,0,0,1,1],
  6. [0,0,0,0,0],
  7. [0,0,0,0,1]
  8. ]
  9. 输出:
  10. 3
  11. 输入:
  12. [
  13. [1,1]
  14. ]
  15. 输出:
  16. 1

解题思路

遍历矩阵,碰到1的节点对其进行宽搜寻找相邻为1的节点。遍历过的1节点将其置为0表示已经被查找过。

AC代码

  1. public class Solution {
  2. public int numIslands(boolean[][] grid) {
  3. // 边界条件
  4. if (grid == null) { return 0; }
  5. int n = grid.length;
  6. if (n < 1) { return 0; }
  7. int m = grid[0].length;
  8. int ans = 0;
  9. // 遍历矩阵
  10. for (int i = 0; i < n; i++) {
  11. for (int j = 0; j < m; j++) {
  12. if (grid[i][j]) {
  13. // 碰到1的节点岛屿数加 1
  14. ans++;
  15. // 对节点为1的节点进行宽搜
  16. dfs(grid, i, j);
  17. }
  18. }
  19. }
  20. return ans;
  21. }
  22. private void dfs(boolean[][] grid, int x, int y) {
  23. // 4个相邻节点的走法
  24. int[] a = {0, 0, 1, -1};
  25. int[] b = {1, -1, 0, 0};
  26. int n = grid.length;
  27. int m = grid[0].length;
  28. // 宽搜队列
  29. Deque<Location> deque = new ArrayDeque<>();
  30. Location now = new Location(x, y);
  31. grid[x][y] = false;
  32. deque.add(now);
  33. while (!deque.isEmpty()) {
  34. // 当前有多少个节点可以扩展
  35. int size = deque.size();
  36. // 扩展这size个节点
  37. for (int i = 0; i < size; i++) {
  38. Location next = deque.removeFirst();
  39. for (int j = 0; j < 4; j++) {
  40. int xx = next.x + a[j];
  41. int yy = next.y + b[j];
  42. if (xx >= 0 && xx < n && yy >= 0 && yy < m && grid[xx][yy]) {
  43. Location add = new Location(xx, yy);
  44. grid[xx][yy] = false;
  45. deque.add(add);
  46. }
  47. }
  48. }
  49. }
  50. }
  51. }
  52. // 定义一个坐标类
  53. class Location{
  54. int x;
  55. int y;
  56. Location(int x, int y) {
  57. this.x = x;
  58. this.y = y;
  59. }
  60. }

LintCode 611:骑士的最短路线

https://www.lintcode.com/problem/knight-shortest-path

描述

给定骑士在棋盘上的 初始 位置(一个2进制矩阵 0 表示空 1 表示有障碍物),找到到达 终点 的最短路线,返回路线的长度。如果骑士不能到达则返回 -1
起点跟终点必定为空.
骑士不能碰到障碍物.
路径长度指骑士走的步数

说明

如果骑士的位置为 (x,y),他下一步可以到达以下这些位置:

  1. (x + 1, y + 2)
  2. (x + 1, y - 2)
  3. (x - 1, y + 2)
  4. (x - 1, y - 2)
  5. (x + 2, y + 1)
  6. (x + 2, y - 1)
  7. (x - 2, y + 1)
  8. (x - 2, y - 1)

样例

  1. 输入:
  2. [[0,0,0],
  3. [0,0,0],
  4. [0,0,0]]
  5. source = [2, 0] destination = [2, 2]
  6. 输出: 2
  7. 解释:
  8. [2,0]->[0,1]->[2,2]
  9. 输入:
  10. [[0,1,0],
  11. [0,0,1],
  12. [0,0,0]]
  13. source = [2, 0] destination = [2, 2]
  14. 输出:-1

解题思路

简单宽搜,从起点开始按骑士的走法,将骑士下一步能到达的位置加入宽搜队列,一步一步拓展知道队列为空或走到终点

AC代码

  1. public int shortestPath(boolean[][] grid, Point source, Point destination) {
  2. //
  3. if (source.x == destination.x && destination.y == source.y) {
  4. return 0;
  5. }
  6. int n = grid.length, m = grid[0].length;
  7. int ans = 0;
  8. int[] a = {1, 1, -1, -1, 2, 2, -2, -2};
  9. int[] b = {2, -2, 2, -2, 1, -1, 1, -1};
  10. Deque<Point> deque = new ArrayDeque<>();
  11. deque.add(source);
  12. while (!deque.isEmpty()) {
  13. Deque<Point> nDeque = new ArrayDeque<>();
  14. ans++;
  15. for (Point now : deque) {
  16. for (int i = 0; i < 8; i++) {
  17. int x = now.x + a[i];
  18. int y = now.y + b[i];
  19. if (x >= 0 && x < n && y >= 0 && y < m && !grid[x][y]) {
  20. if (x == destination.x && y == destination.y) {
  21. return ans;
  22. }
  23. Point next = new Point(x, y);
  24. nDeque.add(next);
  25. grid[x][y] = true;
  26. }
  27. }
  28. }
  29. deque = nDeque;
  30. }
  31. return -1;
  32. }

LintCode 137:克隆图

https://www.lintcode.com/problem/clone-graph

描述

克隆一张无向图. 无向图的每个节点包含一个 label 和一个列表 neighbors. 保证每个节点的 label 互不相同.
你的程序需要返回一个经过深度拷贝的新图. 新图和原图具有同样的结构, 并且对新图的任何改动不会对原图造成任何影响.
你需要返回与给定节点具有相同 label 的那个节点.

样例

  1. 输入:
  2. {1,2,4#2,1,4#4,1,2}
  3. 输出:
  4. {1,2,4#2,1,4#4,1,2}
  5. 解释:
  6. 1------2
  7. \ |
  8. \ |
  9. \ |
  10. \ |
  11. 4

解题思路

因为要克隆每个节点所包含的邻居节点,所以用一个map存储label对应的节点。
从 node 节点开始宽搜,碰到一个新节点(没在map中出现过),就复制一个存入map中。
每次从宽搜队列中取出一个节点,然后将其邻居也复制到克隆图中。
直到克隆完成。

AC代码

  1. public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) {
  2. // 空直接返回
  3. if (node == null) { return null; }
  4. // 宽搜队列
  5. Deque<UndirectedGraphNode> deque = new ArrayDeque<>();
  6. // map存储label对应的节点
  7. Map<Integer, UndirectedGraphNode> map = new HashMap<>();
  8. // 克隆node
  9. map.put(node.label, new UndirectedGraphNode(node.label));
  10. deque.add(node);
  11. while (!deque.isEmpty()) {
  12. // 取出队列的首节点
  13. UndirectedGraphNode newNode = deque.removeFirst();
  14. // 取出克隆节点
  15. UndirectedGraphNode copy = map.get(newNode.label);
  16. // 复制neighbors
  17. for (UndirectedGraphNode tmp : newNode.neighbors) {
  18. // 克隆新节点
  19. if (!map.containsKey(tmp.label)) {
  20. UndirectedGraphNode newTmp = new UndirectedGraphNode(tmp.label);
  21. map.put(tmp.label, newTmp);
  22. deque.add(tmp);
  23. }
  24. copy.neighbors.add(map.get(tmp.label));
  25. }
  26. }
  27. return map.get(node.label);
  28. }

LintCode 127:拓扑排序

https://www.lintcode.com/problem/topological-sorting

描述

给定一个有向图,图节点的拓扑排序定义如下:

  • 对于图中的每一条有向边 A -> B , 在拓扑排序中A一定在B之前.
  • 拓扑排序中的第一个节点可以是图中的任何一个没有其他节点指向它的节点.

针对给定的有向图找到任意一种拓扑排序的顺序.
你可以假设图中至少存在一种拓扑排序

样例

例如下图:
91cf07d2-b7ea-11e9-bb77-0242ac110002.jpg
拓扑排序可以为:

  1. [0, 1, 2, 3, 4, 5]
  2. [0, 2, 3, 1, 5, 4]

解题思路

题目只给了一个有向图,没有给出起点,所以要先做预处理,找出起点。统计所有点的入度(有多少条有向边指向它),当一个点的入度不为0时,代表着它前面还有节点,它还不能加入拓扑排序中;当入度为0时,便可加入拓扑排序中,此时它指向的节点的入度都减1,如果减到了0,那么该节点是新的可以加入拓扑排序的节点。正好用宽搜可以解决。

AC代码

  1. public ArrayList<DirectedGraphNode> topSort(ArrayList<DirectedGraphNode> graph) {
  2. // 预处理,统计每个节点的入度
  3. // 存储label对应的节点map
  4. Map<Integer, DirectedGraphNode> map = new HashMap<>();
  5. // 存储节点对应的入度
  6. Map<Integer, Integer> cmap = new HashMap<>();
  7. for (DirectedGraphNode tmp : graph) {
  8. int label = tmp.label;
  9. map.put(label, tmp);
  10. if (!cmap.containsKey(label)) {
  11. cmap.put(label, 0);
  12. }
  13. for (DirectedGraphNode neighber : tmp.neighbors) {
  14. int lab = neighber.label;
  15. if (!cmap.containsKey(lab)) {
  16. cmap.put(lab, 1);
  17. }
  18. else {
  19. cmap.put(lab, cmap.get(lab) + 1);
  20. }
  21. }
  22. }
  23. // 存储拓扑顺序
  24. ArrayList<DirectedGraphNode> ans = new ArrayList<>();
  25. // 宽搜队列
  26. Deque<DirectedGraphNode> deque = new ArrayDeque<>();
  27. // 入度为0的节点作为宽搜的起点
  28. for (Integer count : cmap.keySet()) {
  29. if (cmap.get(count) == 0) {
  30. DirectedGraphNode node = map.get(count);
  31. deque.add(node);
  32. ans.add(node);
  33. }
  34. }
  35. while (!deque.isEmpty()) {
  36. int size = deque.size();
  37. for (int i = 0; i < size; i++) {
  38. DirectedGraphNode now = deque.removeFirst();
  39. // 每个走出队列节点指向的节点入度都要减一,入度为0了加入宽搜队列
  40. for (DirectedGraphNode next : now.neighbors) {
  41. int count = cmap.get(next.label);
  42. count--;
  43. if (count == 0) {
  44. deque.add(next);
  45. ans.add(next);
  46. }
  47. cmap.put(next.label, count);
  48. }
  49. }
  50. }
  51. return ans;
  52. }

LintCode 120:单词接龙

https://www.lintcode.com/problem/word-ladder

描述

给出两个单词(startend)和一个字典,找出从startend的最短转换序列,输出最短序列的长度。
变换规则如下:

  1. 每次只能改变一个字母。
  2. 变换过程中的中间单词必须在字典中出现。(起始单词和结束单词不需要出现在字典中)
  • 如果不存在这样的转换序列,返回 0。
  • 所有单词具有相同的长度。
  • 所有单词只由小写字母组成。
  • 字典中不存在重复的单词。
  • 你可以假设 beginWord 和 endWord 是非空的,且二者不相同。

样例

  1. 输入:start = "a"end = "c"dict =["a","b","c"]
  2. 输出:2
  3. 解释:
  4. "a"->"c"
  5. 输入:start ="hit"end = "cog"dict =["hot","dot","dog","lot","log"]
  6. 输出:5
  7. 解释:
  8. "hit"->"hot"->"dot"->"dog"->"cog"

解题思路

BFS,从start单词开始搜索,每个单词一次都有25length种变换,都是一个中间状态,相当于一个单词是一个节点,它可以指向25length个节点,end是最后要到达的节点,这样就变成了了前面遇到过的最短路线问题,用bfs可以解决。

AC代码

  1. public int ladderLength(String start, String end, Set<String> dict) {
  2. // 将end加入dict中。
  3. dict.add(end);
  4. // 存储已经搜索过的单词
  5. Set<String> set = new HashSet<>();
  6. // 宽搜队列
  7. Deque<String> deque = new ArrayDeque<>();
  8. deque.add(start);
  9. // length 代表做了几下转换
  10. int length = 1;
  11. // 宽搜过程
  12. while (!deque.isEmpty()) {
  13. length++;
  14. int size = deque.size();
  15. for (int i = 0; i < size; i++) {
  16. String word = deque.removeFirst();
  17. for (String nextWord : getNextWords(word, dict)) {
  18. if (set.contains(nextWord)) {
  19. continue;
  20. }
  21. if (nextWord.equals(end)) {
  22. return length;
  23. }
  24. set.add(nextWord);
  25. deque.add(nextWord);
  26. }
  27. }
  28. }
  29. return 0;
  30. }
  31. // 获取单词能转换到的在dict中的单词
  32. private List<String> getNextWords(String word, Set<String> dict) {
  33. List<String> list = new ArrayList<>();
  34. for (char c = 'a'; c <= 'z'; c++) {
  35. for (int i = 0; i < word.length(); i++) {
  36. if (c == word.charAt(i)) {
  37. continue;
  38. }
  39. String nextWord = replaceWord(word, i, c);
  40. if (dict.contains(nextWord)) {
  41. list.add(nextWord);
  42. }
  43. }
  44. }
  45. return list;
  46. }
  47. private String replaceWord(String word, int i, char c) {
  48. char[] chars = word.toCharArray();
  49. chars[i] = c;
  50. return new String(chars);
  51. }