827. 最大人工岛
java
class Solution {
/**
思路:
1. 先把已经存在的岛找出来,并且计算他们的面积(dfs)
2. 遍历为0的点,计算加上附近的岛的面积之后的最大面积
3. 去重:避免把一个相邻的岛的面积计算两遍
*/
private final int[][] dirs = {{-1,0},{1,0},{0,1},{0,-1}};
public int largestIsland(int[][] grid) {
int n = grid.length;
// 计算每一个存在的岛的面积
List<Integer> area = new ArrayList<>();
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
if(grid[i][j] == 1)//找到小岛
area.add(dfs(grid, i, j, area.size()+2));
}
}
//
int maxSquare = 0;
Set<Integer> s = new HashSet<>();
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
//遍历为0的点,计算加上附近的岛的面积之后的最大面积
if(grid[i][j] == 0){
int square = 1;
s.clear();
for(int[] dir: dirs){
int newi = dir[0] + i;
int newj = dir[1] + j;
if(newi>=0 && newj>=0 && newi<grid.length && newj < grid.length && grid[newi][newj]!=0 && s.add(grid[newi][newj])){
square += area.get(grid[newi][newj]-2);
}
}
maxSquare = Math.max(square, maxSquare);
}
}
}
// 考虑小岛不需要添加任何1的情况
return maxSquare == 0 ? n*n : maxSquare;
}
private int dfs(int[][] grid, int row, int col, int island){
// 标记小岛的编号
grid[row][col] = island;
int square = 1;
for(int[] dir: dirs){
int newr = row + dir[0];
int newc = col + dir[1];
if(newc>=0 && newr>=0 && newc<grid.length && newr < grid.length && grid[newr][newc]==1){
// 计算岛屿的大小
square += dfs(grid, newr, newc, island);
}
}
return square;
}
}
127. 单词接龙
java
class Solution {
/**
beginword: --> --> --> --> endword
hit hot dot dog cog
用图论的思想去看待这道题,begin--》end的最短路径
*/
// to judge whether there is an edge
private boolean isEdge(String cur, String next){
if(cur.length()!=next.length()) return false;
int diff = 0;
for(int i=0; i<cur.length(); i++){
if(diff > 1) return false;
if(cur.charAt(i) != next.charAt(i)){
diff++;
}
}
return diff == 1;
}
//
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
Set<String> visited = new HashSet<>();
if(!wordList.contains(endWord)) return 0;
Queue<String> neighbours = new LinkedList<>();
neighbours.add(beginWord);
visited.add(beginWord);
int length = 1;
while(!neighbours.isEmpty()){
int size = neighbours.size();
for(int i=0; i<size; i++){
String currentword = neighbours.poll();
if(isEdge(currentword,endWord)) return length+1;
for(String word: new HashSet<>(wordList)){
if(visited.contains(word)) continue;
if(isEdge(currentword,word)){
neighbours.add(word);
visited.add(word);
}
}
}
// 现在的数值和endword间有边
length++;
}
return 0;
}
}
-
思路2: 优化方式--用双向bfs缩短遍历的流程
-
- 除了visited(所有访问过的节点)之外,采用startvisited和endvisited存储前后向遍历的节点(非全部节点,而是要处理的新一层的节点)
- 交替处理startvisited和endvisited中需要处理的数据,每次处理节点数小的那个list
- 如果startvisited的下一层(nextvisited)中,包含endvisited中的元素,则表示找到了最短的路径
- 图示:(来源weiwei)
-
-
代码2:
*javapublic class Solution { public int ladderLength(String beginWord, String endWord, List<String> wordList) { // 第 1 步:先将 wordList 放到哈希表里,便于判断某个单词是否在 wordList 里 Set<String> wordSet = new HashSet<>(wordList); if (wordSet.size() == 0 || !wordSet.contains(endWord)) { return 0; } // 第 2 步:已经访问过的 word 添加到 visited 哈希表里 Set<String> visited = new HashSet<>(); // 分别用左边和右边扩散的哈希表代替单向 BFS 里的队列,它们在双向 BFS 的过程中交替使用 Set<String> beginVisited = new HashSet<>(); beginVisited.add(beginWord); Set<String> endVisited = new HashSet<>(); endVisited.add(endWord); // 第 3 步:执行双向 BFS,左右交替扩散的步数之和为所求 int step = 1; while (!beginVisited.isEmpty() && !endVisited.isEmpty()) { // 优先选择小的哈希表进行扩散,考虑到的情况更少 if (beginVisited.size() > endVisited.size()) { Set<String> temp = beginVisited; beginVisited = endVisited; endVisited = temp; } // 逻辑到这里,保证 beginVisited 是相对较小的集合,nextLevelVisited 在扩散完成以后,会成为新的 beginVisited Set<String> nextLevelVisited = new HashSet<>(); for (String word : beginVisited) { if (changeWordEveryOneLetter(word, endVisited, visited, wordSet, nextLevelVisited)) { return step + 1; } } // 原来的 beginVisited 废弃,从 nextLevelVisited 开始新的双向 BFS beginVisited = nextLevelVisited; step++; } return 0; } /** * 尝试对 word 修改每一个字符,看看是不是能落在 endVisited 中,扩展得到的新的 word 添加到 nextLevelVisited 里 * * @param word * @param endVisited * @param visited * @param wordSet * @param nextLevelVisited * @return */ private boolean changeWordEveryOneLetter(String word, Set<String> endVisited, Set<String> visited, Set<String> wordSet, Set<String> nextLevelVisited) { char[] charArray = word.toCharArray(); for (int i = 0; i < word.length(); i++) { char originChar = charArray[i]; for (char c = 'a'; c <= 'z'; c++) { if (originChar == c) { continue; } charArray[i] = c; String nextWord = String.valueOf(charArray); if (wordSet.contains(nextWord)) { if (endVisited.contains(nextWord)) { return true; } if (!visited.contains(nextWord)) { nextLevelVisited.add(nextWord); visited.add(nextWord); } } } // 恢复,下次再用 charArray[i] = originChar; } return false; } }
105. 有向图的完全可达性
-
🔗:105. 有向图的完全可达性
https://kamacoder.com/problempage.php?pid=1177
-
思路:不难做,主要练习一下acm模式,用的是广度优先的搜索方式,用深度是一样的
-
代码:
javaimport java.util.*; class Main{ private static Set<Integer> visited = new HashSet<>(); public static void main(String[] args){ Scanner scanner = new Scanner(System.in); int num_node = scanner.nextInt(); int num_edge = scanner.nextInt(); // if there is only 1 node //if(num_node==1) return 1; List<int[]> edges = new ArrayList<>(); for(int i=0 ;i<num_edge; i++){ int[] edge = new int[2]; edge[0] = scanner.nextInt(); edge[1] = scanner.nextInt(); edges.add(edge); } int currentNode = 1; Queue<Integer> que = new LinkedList<>(); que.add(currentNode); visited.add(currentNode); while(!que.isEmpty()){ int size = que.size(); for(int j=0; j<size; j++){ currentNode = que.poll(); for(int[] edge1: edges){ // // System.out.println(edge1[0]+" "+edge1[1]); if(edge1[0] == currentNode){ // 如果加不进去代表重复visited了 if(visited.add(edge1[1])){ que.add(edge1[1]); } } } } } if(visited.size()==num_node) System.out.println(1); else System.out.println(-1); } }
463. 岛屿的周长
-
思路:深度优先搜索
- dfs遍历的方式可扩展至统计多个岛屿各自的周长。
-
代码:
javaclass Solution { int[][] dirs = {{-1,0},{1,0},{0,1},{0,-1}}; public int islandPerimeter(int[][] grid) { int circle = 0; int m = grid.length; int n = grid[0].length; for(int i=0; i<m; i++){ for(int j=0; j<n; j++){ if(grid[i][j]==1){ circle = dfs(grid, i, j); return circle; } } } return circle; } private int dfs(int[][] grid, int row, int column){ if(grid[row][column]==2) return 0; int circle = 0; grid[row][column] = 2; for(int[] dir: dirs){ int x = row + dir[0]; int y = column + dir[1]; if(x<0 || y<0 || x >= grid.length || y >= grid[0].length || grid[x][y] == 0){ circle += 1; } else if(grid[x][y] == 1){ circle += dfs(grid, x, y); } } return circle; } }