图论part4|827. 最大人工岛、127. 单词接龙、463. 岛屿的周长

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缩短遍历的流程

      1. 除了visited(所有访问过的节点)之外,采用startvisited和endvisited存储前后向遍历的节点(非全部节点,而是要处理的新一层的节点)
    • 交替处理startvisited和endvisited中需要处理的数据,每次处理节点数小的那个list
    • 如果startvisited的下一层(nextvisited)中,包含endvisited中的元素,则表示找到了最短的路径
    • 图示:(来源weiwei)
  • 代码2:
    *

    java 复制代码
    public 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模式,用的是广度优先的搜索方式,用深度是一样的

  • 代码:

    java 复制代码
    import 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. 岛屿的周长

相关推荐
hacker_LeeFei6 小时前
图论的基础知识:平凡图、简单图、连通图、平面图、完全图、对偶图、同构图
图论
一只码代码的章鱼7 小时前
数据结构与算法-图论-欧拉路径和欧拉回路(有向图和无向图,骑马修栅栏,单词游戏 play on words)详细代码注解
算法·图论
一只码代码的章鱼13 小时前
数据结构与算法-图论-二分图
算法·图论
HIT最菜电控14 小时前
代码随想录二刷|图论11
图论
HIT最菜电控14 小时前
代码随想录二刷|图论7
图论
HIT最菜电控1 天前
代码随想录二刷|图论9
图论
m0_625725672 天前
图论part3|101.孤岛的总面积、沉没孤岛、417. 太平洋大西洋水流问题
算法·图论
m0_625725673 天前
图论part2|200. 岛屿数量、695. 岛屿的最大面积
算法·深度优先·图论
小竹子144 天前
L3-1 夺宝大赛
数据结构·算法·图论