28 mai 2024

50 DSA-Questions: Graph #43, #44

#43. Number of Islands

Given an m x n 2D binary grid which represents a map of '1's (land) and '0's (water), return the number of islands. An island (1's) is surrounded by water (0's) and is formed by connecting adjacent lands horizontally or vertically.

class Solution {
    public int numIslands(char[][] grid) {
        if (grid.length == 0) {
            return 0;
        }

        int[][] markedGrid = new int[grid.length][grid[0].length];
        for (int i=0; i<markedGrid.length; i++) {
            for (int j=0; j<markedGrid[i].length; j++) {
                markedGrid[i][j] = 0;
            }
        }

        int tag = 1;
        for (int i=0; i<grid.length; i++) {
            for (int j=0; j<grid[i].length; j++) {
                if (markedGrid[i][j] == 0) {
                    AtomicBoolean modified = new AtomicBoolean(false);
                    markedGrid = markGrid(i, j, grid, markedGrid, tag, modified);
                    if (modified.get()) {
                        tag++;
                    }
                }
            }
        }
       
        return tag - 1;
    }

    private int[][] markGrid(int i, int j, char[][] grid, int[][] markedGrid, int tag, AtomicBoolean modified) {
        if (i < 0 || i >= grid.length || j < 0 || j >= grid[0].length || grid[i][j] == '0' || markedGrid[i][j] > 0) {
            return markedGrid;
        }

        markedGrid[i][j] = tag;
        markedGrid = markGrid(i-1, j, grid, markedGrid, tag, modified);
        markedGrid = markGrid(i+1, j, grid, markedGrid, tag, modified);
        markedGrid = markGrid(i, j-1, grid, markedGrid, tag, modified);
        markedGrid = markGrid(i, j+1, grid, markedGrid, tag, modified);
        modified.set(true);

        return markedGrid;
    }
}

#44. Pacific Atlantic Water Flow

There is an m x n rectangular island that borders both the Pacific Ocean and the Atlantic Ocean. The Pacific Ocean touches the island's left and top edges, and the Atlantic Ocean touches the island's right and bottom edges. 

The island is partitioned into a grid of square cells. You are given an m x n integer matrix height where heights[r][c] represent the height above sea level of the cell at coordinate (r, c).

The island receives a lot of rain, and the rainwater can flow to neighboring cells directly north, south, east, and west if the neighboring cell's height is less than or equal to the current cell's height. Water can flow from any cell adjacent to an ocean into the ocean.

Return a 2D list of grid coordinates results where result[i] = [ri, ci] denotes that rainwater can flow from cell (ri, ci) to both the Pacific and Atlantic oceans.

// solutie cu timp ridicat de executie

class Solution {
    public List<List<Integer>> pacificAtlantic(int[][] heights) {
        Map<Integer,Set<Integer>> edges = new HashMap<>();
        for (int i=0; i<heights.length; i++) {
            for (int j=0; j<heights[0].length; j++) {
                int label = getNodeLabel(i, j, heights[0].length);
                edges.put(label, getNeighbors(i,j,heights)); // edge from label to its neighbors (water flows to)
            }
        }

        findAllEdges(edges, heights[0].length, heights.length);

        List<List<Integer>> results = new ArrayList<>();
        for (int node : edges.keySet()) {
            boolean pacific = nearPacific(node, heights[0].length);
            boolean atlantic = nearAtlantic(node, heights[0].length, heights.length);
            if (!pacific || !atlantic) {
                for (int conn : edges.get(node)) {
                    pacific = pacific || nearPacific(conn, heights[0].length);
                    atlantic = atlantic || nearAtlantic(conn, heights[0].length, heights.length);
                    if (pacific && atlantic) {
                        break;
                    }
                }
            }
            if (pacific && atlantic) {
                addResult(node, heights[0].length, results);
            }
        }

        return results;
    }

    private void addResult(int node, int M, List<List<Integer>> results) {
        int i = getI(node, M), j = getJ(node, M);
        List<Integer> res = new ArrayList<>();
        res.add(i);
        res.add(j);
        results.add(res);
    }

    private void findAllEdges(Map<Integer, Set<Integer>> edges, int M, int N) {
        Map<Integer, Set<Integer>> allEdges = new HashMap(edges);
        boolean doContinue = true;
        while (doContinue) {
            doContinue = false;
            Set<Integer> toRemove = new HashSet<>();
           
            for (int a : edges.keySet()) {
                Set<Integer> toAdd = new HashSet<>();
                for (int b : edges.get(a)) {
                    if (edges.get(b) != null) {
                        toAdd.addAll(edges.get(b));
                        toAdd.remove(a);
                    }
                }
               
                int size = edges.get(a).size();
                edges.get(a).addAll(toAdd);
                boolean modified = size < edges.get(a).size();
                if (!modified) {
                    toRemove.add(a); // if no new connections added to a, it will never add more
                }
               
                allEdges.get(a).addAll(toAdd);
                doContinue = doContinue || modified;
            }

            for (int a : toRemove) {
                edges.remove(a);
            }
        }
        edges = allEdges;
    }

    private boolean nearPacific(int nodeLabel, int M) {
        int i = getI(nodeLabel, M), j = getJ(nodeLabel, M);
        return i == 0 || j == 0;
    }

    private boolean nearAtlantic(int nodeLabel, int M, int N) {
        int i = getI(nodeLabel, M), j = getJ(nodeLabel, M);
        return i == N - 1 || j == M - 1;
    }

    private Set<Integer> getNeighbors(int i, int j, int[][] heights) {
        Set<Integer> neighbors = new HashSet<>(); // to whom water flows from (i,j)
        if (i>0 && heights[i][j] >= heights[i-1][j]) {
            neighbors.add(getNodeLabel(i-1, j, heights[0].length));
        }
        if (j>0 && heights[i][j] >= heights[i][j-1]) {
            neighbors.add(getNodeLabel(i, j-1, heights[0].length));
        }
        if (i<heights.length-1 && heights[i][j] >= heights[i+1][j]) {
            neighbors.add(getNodeLabel(i+1, j, heights[0].length));
        }
        if (j<heights[0].length-1 && heights[i][j] >= heights[i][j+1]) {
            neighbors.add(getNodeLabel(i, j+1, heights[0].length));
        }
        return neighbors;
    }

    private int getNodeLabel(int i, int j, int M) {
        return i * M + j;
    }

    private int getI(int nodeLabel, int M) {
        return nodeLabel / M;
    }

    private int getJ(int nodeLabel, int M) {
        return nodeLabel % M;
    }
}

// solutie acceptabila ca timp

class Solution {
    private enum Tag {UNK, PAC, ATL, BOTH};

    public List<List<Integer>> pacificAtlantic(int[][] heights) {
        // init tags
        Tag[][] tags = new Tag[heights.length][heights[0].length];
        for (int i=0; i<heights.length; i++) {
            for (int j=0; j<heights[0].length; j++) {
                tags[i][j] = Tag.UNK;
            }
        }
        for (int j=0; j<heights[0].length; j++) {
            tags[0][j] = Tag.PAC;
            tags[heights.length-1][j] = updateTag(tags[heights.length-1][j], Tag.ATL);
        }
        for (int i=0; i<heights.length; i++) {
            tags[i][0] = updateTag(tags[i][0], Tag.PAC);
            tags[i][heights[0].length-1] = updateTag(tags[i][heights[0].length-1], Tag.ATL);
        }

        // tag all
        for (int i=0; i<heights.length; i++) {
            for (int j=heights[0].length-1; j>=0; j--) {
                tags = tagNode(i, j, heights, tags, new ArrayList<>()); // top-right to bottom-left
            }
        }
        for (int i=heights.length-1; i>=0; i--) {
            for (int j=0; j<heights[0].length; j++) {
                tags = tagNode(i, j, heights, tags, new ArrayList<>()); // bottom-left to top-right
            }
        }
        for (int i=0; i<heights.length; i++) {
            for (int j=0; j<heights[0].length; j++) {
                tags = tagNode(i, j, heights, tags, new ArrayList<>()); // top-left to bottom-right
            }
        }
        for (int i=heights.length-1; i>=0; i--) {
            for (int j=heights[0].length-1; j>=0; j--) {
                tags = tagNode(i, j, heights, tags, new ArrayList<>()); // bottom-right to top-left
            }
        }

        List<List<Integer>> results = new ArrayList<>();
        for (int i=0; i<heights.length; i++) {
            for (int j=0; j<heights[0].length; j++) {
                if (tags[i][j] == Tag.BOTH) {
                    List<Integer> res = new ArrayList<>();
                    res.add(i);
                    res.add(j);
                    results.add(res);
                }
            }
        }

        return results;
    }

    private Tag[][] tagNode(int i, int j, int[][] heights, Tag[][] tags, List<Integer> trace) {
        int currentNode = getNodeLabel(i, j, heights[0].length);
        trace.add(currentNode);

        if (i>0 && heights[i][j] >= heights[i-1][j]) {
            int next = getNodeLabel(i-1, j, heights[0].length);
            if (tags[i-1][j] == Tag.UNK && !trace.contains(next)) {
                tagNode(i-1, j, heights, tags, trace);
            }
            tags[i][j] = updateTag(tags[i][j], tags[i-1][j]);
        }
        if (j>0 && heights[i][j] >= heights[i][j-1]) {
            int next = getNodeLabel(i, j-1, heights[0].length);
            if (tags[i][j-1] == Tag.UNK && !trace.contains(next)) {
                tagNode(i, j-1, heights, tags, trace);
            }
            tags[i][j] = updateTag(tags[i][j], tags[i][j-1]);
        }
        if (i<heights.length-1 && heights[i][j] >= heights[i+1][j]) {
            int next = getNodeLabel(i+1, j, heights[0].length);
            if (tags[i+1][j] == Tag.UNK && !trace.contains(next)) {
                tagNode(i+1, j, heights, tags, trace);
            }
            tags[i][j] = updateTag(tags[i][j], tags[i+1][j]);
        }
        if (j<heights[0].length-1 && heights[i][j] >= heights[i][j+1]) {
            int next = getNodeLabel(i, j+1, heights[0].length);
            if (tags[i][j+1] == Tag.UNK && !trace.contains(next)) {
                tagNode(i, j+1, heights, tags, trace);
            }
            tags[i][j] = updateTag(tags[i][j], tags[i][j+1]);
        }

        return tags;
    }

    private Tag updateTag(Tag initialTag, Tag newTag) {
        if (initialTag == Tag.UNK || initialTag == null) {
            return newTag;
        }
        if (newTag == Tag.UNK) {
            return initialTag;
        }
        if (initialTag != newTag) {
            return Tag.BOTH;
        }
        return initialTag; // PAC sau ATL sau BOTH
    }

    private int getNodeLabel(int i, int j, int M) {
        return i * M + j;
    }
}

Niciun comentariu: