數據結構與算法 -- 圖

1、圖的表示方式

997:找到小鎮的法官

題目描述:在一個小鎮裏,按從 1 到 N 標記了 N 我的。傳言稱,這些人中有一個是小鎮上的祕密法官。css

若是小鎮的法官真的存在,那麼:java

  1. 小鎮的法官不相信任何人。
  2. 每一個人(除了小鎮法官外)都信任小鎮的法官。
  3. 只有一我的同時知足屬性 1 和屬性 2 。

給定數組 trust,該數組由信任對 trust[i] = [a, b] 組成,表示標記爲 a 的人信任標記爲 b 的人。若是小鎮存在祕密法官而且能夠肯定他的身份,請返回該法官的標記。不然,返回 -1算法

 

示例 1:輸入:N = 2, trust = [[1,2]],輸出:2數組

示例 2:輸入:N = 3, trust = [[1,3],[2,3]],輸出:3spa

示例 3:輸入:N = 3, trust = [[1,3],[2,3],[3,1]],輸出:-1code

示例 4:輸入:N = 3, trust = [[1,2],[2,3]],輸出:-1blog

示例 5:輸入:N = 4, trust = [[1,3],[1,4],[2,3],[2,4],[4,3]],輸出:3遞歸

 提示:get

  1. 1 <= N <= 1000
  2. trust.length <= 10000
  3. trust[i] 是徹底不一樣的
  4. trust[i][0] != trust[i][1]
  5. 1 <= trust[i][0], trust[i][1] <= N

解題思路:這道題就是要找到一我的,他被全部人相信,可是不相信全部人。咱們能夠聯繫到圖,相信與被相信分別對應到圖的出度和入度,每一個人表明一個點。咱們要找的就是在這N個點裏面有沒有一個點符合:出度爲0,入度爲N-1。it

代碼實現(java):

class Solution {
    public int findJudge(int N, int[][] trust) {
        int in_degree[]=new int [N+1];//入度,即相信個人人數
        int out_degree[]=new int [N+1];//出度,即我相信的人數
//遍歷信任數組
int len = trust.length; for(int i=0;i<len;i++){
       //相信別人的人的出度+1 out_degree[trust[i][
0]]++;
//被人相信的人的入度+1 in_degree[trust[i][
1]]++; } for(int i=1;i<=N;i++){
       //符合要求的條件:出度爲0,入度爲N-1
if((out_degree[i]==0) & (in_degree[i]==N-1)){ return i; } } return -1; } }

104二、不鄰接植花

題目描述:有N個花園,按從1到N標記。在每一個花園中,你打算種下四種花之一。paths[i]=[x,y]描述了花園x到花園y的雙向路徑。另外,沒有花園有3條以上的路徑能夠進入或者離開。你須要爲每一個花園選擇一種花,使得經過路徑相連的任何兩個花園中的花的種類互不相同。

以數組形式返回選擇的方案做爲答案 answer,其中 answer[i] 爲在第 (i+1) 個花園中種植的花的種類。花的種類用 1, 2, 3, 4 表示。保證存在答案。

 

示例 1:輸入:N = 3, paths = [[1,2],[2,3],[3,1]],輸出:[1,2,3]
示例 2:輸入:N = 4, paths = [[1,2],[3,4]],輸出:[1,2,1,2]
示例 3:輸入:N = 4, paths = [[1,2],[2,3],[3,4],[4,1],[1,3],[2,4]],輸出:[1,2,3,4]

提示:
1 <= N <= 10000
0 <= paths.size <= 20000
不存在花園有 4 條或者更多路徑能夠進入或離開。
保證存在答案。

 

解題思路:遍歷鄰接數組,找出每一個節點的鄰節點,構造出圖,而後用鄰節點沒有用過的一個顏色將當前節點染色便可。

class Solution {

    public int[] gardenNoAdj(int N, int[][] paths) {
        //一、構造圖:建立描述節點與鄰節點關係圖
        Map<Integer, Set<Integer>> graph = new HashMap<>();
        for(int i=0; i<N; i++) {
            graph.put(i, new HashSet<Integer>());
        }
        for(int[] path : paths) {
            //互相鄰接
            graph.get(path[0]-1).add(path[1]-1);
            graph.get(path[1]-1).add(path[0]-1);
        }
        
        //着色結果數組
        int[] answer = new int[N];
        //二、遍歷着色
        for(int i=0; i<N; i++) {
            //必須知道當前節點的鄰節點都着了哪些顏色,所以須要一個顏色數組,標記當前顏色是否已經使用過了
            boolean[] colorUsed = new boolean[5];//因爲顏色是從1開始,因此此處使用5而不是4,由於第0個元素僅佔位而沒有任何做用
            for(int nextNode : graph.get(i)) {
                colorUsed[answer[nextNode]] = true;
            }
            //當前節點的鄰節點使用的顏色肯定了,遍歷已使用的顏色數組,便可肯定當前節點的顏色
            for(int j=1; j<colorUsed.length; j++) {
                //若是當前顏色沒有使用,則當前節點使用該顏色
                if(!colorUsed[j]) {
                    answer[i] = j;
                    break;
                }
            }
        }
        
        return answer;
    }
}

 2、廣度優先搜索算法(BFS)

99四、腐爛的橘子

題目描述:每分鐘,任何與腐爛的橘子(在 4 個正方向上)相鄰的新鮮橘子都會腐爛。返回直到單元格中沒有新鮮橘子爲止所必須通過的最小分鐘數。若是不可能,返回 -1

示例 1:輸入:[[2,1,1],[1,1,0],[0,1,1]],輸出:4

 

示例 2:輸入:[[2,1,1],[0,1,1],[1,0,1]],輸出:-1;解釋:左下角的橘子(第 2 行, 第 0 列)永遠不會腐爛,由於腐爛只會發生在 4 個正向上。

示例 3:輸入:[[0,2]],輸出:0;解釋:由於 0 分鐘時已經沒有新鮮橘子了,因此答案就是 0 。

提示:

1 <= grid.length <= 10
1 <= grid[0].length <= 10
grid[i][j] 僅爲 0、1 或 2

解題思路:腐爛的橘子會把靠近他的新鮮的橘子腐蝕,那麼就是隻要使用BFS從全部腐爛的橘子的位置一層一層往外遍歷就能夠了。

public class Solution {
    public int orangesRotting(int[][] grid) {
        int row = grid.length, col = grid[0].length, time = 0;
        //保存腐爛的橘子
        Queue<int[]> queue = new LinkedList<>();
        for(int i=0; i<row; i++) {
            for(int j=0; j<col; j++) {
                if(grid[i][j] == 2) {
                    int[] p = {0, i, j};
                    queue.add(p);
                }
            }
        }
        while(!queue.isEmpty()) {
            int[] p = queue.poll();
            time = time>p[0] ? time : p[0];
            //每一分鐘都腐爛旁邊的橘子
            if(p[1]+1 < row && grid[p[1]+1][p[2]] == 1) {//向下
                grid[p[1]+1][p[2]] = 2;
                int[] po = {p[0]+1, p[1]+1, p[2]};
                queue.add(po);
            }
            if(p[1]-1 >= 0 && grid[p[1]-1][p[2]] == 1) {//向上
                grid[p[1]-1][p[2]] = 2;
                int[] po = {p[0]+1, p[1]-1, p[2]};
                queue.add(po);
            }
            if(p[2]+1 < col && grid[p[1]][p[2]+1] == 1) {//向右
                grid[p[1]][p[2]+1] = 2;
                int[] po = {p[0]+1, p[1], p[2]+1};
                queue.add(po);
            }
            if(p[2]-1 >= 0 && grid[p[1]][p[2]-1] == 1) {//向左
                grid[p[1]][p[2]-1] = 2;
                int[] po = {p[0]+1, p[1], p[2]-1};
                queue.add(po);
            }
        }
        
        //判斷全部橘子是否都已經腐爛
        for(int i=0; i<row; i++) {
            for(int j=0; j<col; j++) {
                if(grid[i][j] == 1) {
                    return -1;
                }
            }
        }
        return time;
    }
    
    public static void main(String[] args) {
        Solution solution = new Solution();
        int[][] grid1 = {{2,1,1},{1,1,0},{0,1,1}};
        System.out.println(solution.orangesRotting(grid1));
        int[][] grid2 = {{2,1,1},{0,1,1},{1,0,1}};
        System.out.println(solution.orangesRotting(grid2));
        int[][] grid3 = {{0,2}};
        System.out.println(solution.orangesRotting(grid3));
    }
}

 

 3、深度優先搜索算法(DFS)

73三、圖像渲染

題目描述:有一幅以二維整數數組表示的圖畫,每個整數表示該圖畫的像素值大小,數值在 0 到 65535 之間。給你一個座標 (sr, sc) 表示圖像渲染開始的像素值(行 ,列)和一個新的顏色值 newColor,讓你從新上色這幅圖像。爲了完成上色工做,從初始座標開始,記錄初始座標的上下左右四個方向上像素值與初始座標相同的相連像素點,接着再記錄這四個方向上符合條件的像素點與他們對應四個方向上像素值與初始座標相同的相連像素點,……,重複該過程。將全部有記錄的像素點的顏色值改成新的顏色值。最後返回通過上色渲染後的圖像。

示例 1:輸入: image = [[1,1,1],[1,1,0],[1,0,1]],sr = 1, sc = 1, newColor = 2,輸出: [[2,2,2],[2,2,0],[2,0,1]]

解析: 在圖像的正中間,(座標(sr,sc)=(1,1)),在路徑上全部符合條件的像素點的顏色都被更改爲2。注意,右下角的像素沒有更改成2,由於它不是在上下左右四個方向上與初始點相連的像素點。

注意:

一、image 和 image[0] 的長度在範圍 [1, 50] 內。
二、給出的初始點將知足 0 <= sr < image.length 和 0 <= sc < image[0].length。
三、image[i][j] 和 newColor 表示的顏色值在範圍 [0, 65535]內。

public class Solution {
    public static void main(String[] args) {
        Solution solution = new Solution();
        if(solution.visited == null) {
            System.out.println("null");
        }
//        int[][] image = {{1,1,1}, {1,1,0}, {1,0,1}};
//        int[][] result = solution.floodFill(image, 1, 1, 2);
        int[][] image = {{0,0,0}, {0,0,0}};
        int[][] result = solution.floodFill(image, 0, 0, 2);
        System.out.println("");
    }
    //深度優先搜索
    boolean[][] visited;
    public int[][] floodFill(int[][] image, int sr, int sc, int newColor) {
        int row = image.length, col = image[0].length;
        Queue<int[]> queue = new LinkedList<>();
        if(visited == null) {
            visited = new boolean[row][col];
        }
        visited[sr][sc] = true;
        if(sr+1 < row && !visited[sr+1][sc] && image[sr+1][sc] == image[sr][sc]) {
            int[] nextPos = {sr+1, sc};
            visited[sr+1][sc] = true;
            queue.add(nextPos);
        }
        if(sr-1 >= 0 && !visited[sr-1][sc] && image[sr-1][sc] == image[sr][sc]) {
            int[] nextPos = {sr-1, sc};
            visited[sr-1][sc] = true;
            queue.add(nextPos);
        }
        if(sc+1 < col && !visited[sr][sc+1] && image[sr][sc+1] == image[sr][sc]) {
            int[] nextPos = {sr, sc+1};
            visited[sr][sc+1] = true;
            queue.add(nextPos);
        }
        if(sc-1 >= 0 && !visited[sr][sc-1] && image[sr][sc-1] == image[sr][sc]) {
            int[] nextPos = {sr, sc-1};
            visited[sr][sc-1] = true;
            queue.add(nextPos);
        }
        image[sr][sc] = newColor;
        
        while(!queue.isEmpty()) {
            int[] next = queue.poll();
            floodFill(image, next[0], next[1], newColor);
        }
        return image;
    }
    //廣度優先搜索
    public int[][] floodFill_BFS(int[][] image, int sr, int sc, int newColor) {
        int row = image.length, col = image[0].length;
        Queue<int[]> queue = new LinkedList<>();
        int[] src = {sr, sc};
        queue.add(src);
        boolean[][] visited = new boolean[row][col];
        while(!queue.isEmpty()) {
            int[] pos = queue.poll();
            if(pos[0]+1 < row && !visited[pos[0]+1][pos[1]] && image[pos[0]+1][pos[1]] == image[pos[0]][pos[1]]) {
                int[] nextPos = {pos[0]+1, pos[1]};
                queue.add(nextPos);
            }
            if(pos[0]-1 >= 0 && !visited[pos[0]-1][pos[1]] && image[pos[0]-1][pos[1]] == image[pos[0]][pos[1]]) {
                int[] nextPos = {pos[0]-1, pos[1]};
                queue.add(nextPos);
            }
            if(pos[1]+1 < col && !visited[pos[0]][pos[1]+1] && image[pos[0]][pos[1]+1] == image[pos[0]][pos[1]]) {
                int[] nextPos = {pos[0], pos[1]+1};
                queue.add(nextPos);
            }
            if(pos[1]-1 >= 0 && !visited[pos[0]][pos[1]-1] && image[pos[0]][pos[1]-1] == image[pos[0]][pos[1]]) {
                int[] nextPos = {pos[0], pos[1]-1};
                queue.add(nextPos);
            }
            image[pos[0]][pos[1]] = newColor;
            visited[pos[0]][pos[1]] = true;
        }
        return image;
    }
}

200、島嶼數量

題目描述:給定一個由 '1'(陸地)和 '0'(水)組成的的二維網格,計算島嶼的數量。一個島被水包圍,而且它是經過水平方向或垂直方向上相鄰的陸地鏈接而成的。你能夠假設網格的四個邊均被水包圍。

示例 1:

輸入:
11110
11010
11000
00000

輸出: 1
示例 2:

輸入:
11000
11000
00100
00011

輸出: 3

解題思路:採用深度優先搜索遍歷給定的二維網格,每遇到'1'就把島嶼數量加1,並把訪問過的改成‘0’,繼續遍歷

public class Solution {
    public static void main(String[] args) {
        Solution solution = new Solution();
        char[][] grid1 = {{'1','1','1','1','0'}, {'1','1','0','1','0'}, {'1','1','0','0','0'}, {'0','0','0','0','0'}};
        int result1 = solution.numIslands(grid1);
        System.out.println(result1);
        char[][] grid2 = {{'1','1','0','0','0'}, {'1','1','0','0','0'}, {'0','0','1','0','0'}, {'0','0','0','1','1'}};
        int result2 = solution.numIslands(grid2);
        System.out.println(result2);
    }
    public int numIslands(char[][] grid) {
        if(grid == null || grid.length == 0
                || grid[0].length == 0) {
            return 0;
        }
        int row = grid.length, col = grid[0].length, count = 0;
        /**
         * 每遇到'1',島嶼數量就加1,並開始向四個方向進行深度優先搜索,
         * 並把搜索過的位置變爲'0',由於相鄰的都屬於一個island,而後
         * 開始繼續找下一個'1'
         */
        for(int i=0; i<row; i++) {
            for(int j=0; j<col; j++) {
                if(grid[i][j] == '1') {
                    count++;
                    dfsSearch(grid, i, j, row, col);
                }
            }
        }
        return count;
    }
    //深度優先搜索
    private void dfsSearch(char[][] grid, int i, int j, int row, int col) {
        //深度搜索終止條件
        if(i<0 || i>=row || j<0 || j>=col || grid[i][j] != '1') {
            return;
        }
        grid[i][j] = '0';
        //方法1:分別向四個方向遞歸
        dfsSearch(grid, i+1, j, row, col);//向下深度搜索
        dfsSearch(grid, i-1, j, row, col);//向上深度搜索
        dfsSearch(grid, i, j+1, row, col);//向右深度搜索
        dfsSearch(grid, i, j-1, row, col);//向左深度搜索
        //方法2:用一個visited數組,標記遍歷過的島嶼
    } 
}
相關文章
相關標籤/搜索