Thursday, September 20, 2018

126. Word Ladder II

126Word Ladder II
Given two words (beginWord and endWord), and a dictionary's word list, find all shortest transformation sequence(s) from beginWord to endWord, such that:
  1. Only one letter can be changed at a time
  2. Each transformed word must exist in the word list. Note that beginWord is not a transformed word.
Note:
  • Return an empty list if there is no such transformation sequence.
  • All words have the same length.
  • All words contain only lowercase alphabetic characters.
  • You may assume no duplicates in the word list.
  • You may assume beginWord and endWord are non-empty and are not the same.
Example 1:
Input:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]

Output:
[
  ["hit","hot","dot","dog","cog"],
  ["hit","hot","lot","log","cog"]
]
Example 2:
Input:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]

Output: []

Explanation: The endWord "cog" is not in wordList, therefore no possible transformation.
-----------------
在I上做了一些修改,每个Pair都会记录当前走过的点
class Solution {
    public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
        int n = beginWord.length();
        Map<String, Integer> dic = getDic(wordList);
        dic.put(beginWord, 1);
        Queue<Pair> que = new LinkedList<>();
        List<String> ll = new ArrayList<>();
        ll.add(beginWord);
        que.add(new Pair(1, beginWord, ll));
        List<List<String>> rt = new ArrayList<>();
        
        while (!que.isEmpty()) {
            Pair p = que.poll();
            String cur = p.s;
            
            if (!rt.isEmpty() && p.level > rt.get(0).size()) {
                return rt;
            }
            if (cur.equals(endWord)) {
                rt.add(p.list);
                continue;
            }
            
            for (int i = 0; i < n; i++) {
                char c = cur.charAt(i);
                for (char nextC = 'a'; nextC <= 'z'; nextC++) {
                    String next = cur.substring(0, i) + nextC + cur.substring(i + 1);
                    
                    // Note: ">=", as we want to output all possible paths.
                    if (dic.containsKey(next) && dic.get(next) >= p.level + 1) { 
                        List<String> l = new ArrayList<>(p.list);
                        l.add(next);
                        que.add(new Pair(p.level + 1, next, l));
                        dic.put(next, p.level + 1);
                    }
                }
            }
        }
        
        return rt;
    }
    
    private Map<String, Integer> getDic(List<String> wordList) {
        Map<String, Integer> dic = new HashMap<>();
        
        for (String s : wordList) {
            dic.put(s, Integer.MAX_VALUE);
        }
        
        return dic;
    }
    
    class Pair{
        public int level;
        public String s;
        public List<String> list;
        public Pair(int level, String s, List<String> list) {
            this.level = level;
            this.s = s;
            this.list = list;
        }
    }
}

Wednesday, September 19, 2018

498. Diagonal Traverse

498Diagonal Traverse
Given a matrix of M x N elements (M rows, N columns), return all elements of the matrix in diagonal order as shown in the below image.
Example:
Input:
[
 [ 1, 2, 3 ],
 [ 4, 5, 6 ],
 [ 7, 8, 9 ]
]
Output:  [1,2,4,7,5,3,6,8,9]
Explanation:

Note:
  1. The total number of elements of the given matrix will not exceed 10,000.
--------------------------
分(上,右)和(下,左)2种情况,长方形的4个顶点可能需要特殊处理
class Solution {
    public int[] findDiagonalOrder(int[][] matrix) {
        if (matrix.length == 0) return new int[0];
        
        int m = matrix.length, n = matrix[0].length;
        int[] rt = new int[m * n];
        int row = 0, col = 0, i = 0;
        int up = 1;
        
        while (row != m - 1 || col != n - 1) {
            rt[i] = matrix[row][col];
            
            if ((row == 0 || col == n - 1) && up == 1) {
                if (col != n - 1 && row == 0) {
                    col++;
                }else {
                    row++;
                }
                
                up = -1;
            }else if ((row == m - 1 || col == 0) && up == -1) {
                if (row == m - 1) {
                    col++;
                }else {
                    row++;
                }
                
                up = 1;
            } else {
                row -= up;
                col += up;
            }
            i++;
        }
        
        rt[m * n - 1] = matrix[m - 1][n - 1];
        return rt;
    }
}

Monday, September 17, 2018

432. All O`one Data Structure

432All O`one Data Structure
Implement a data structure supporting the following operations:
  1. Inc(Key) - Inserts a new key with value 1. Or increments an existing key by 1. Key is guaranteed to be a non-empty string.
  2. Dec(Key) - If Key's value is 1, remove it from the data structure. Otherwise decrements an existing key by 1. If the key does not exist, this function does nothing. Key is guaranteed to be a non-empty string.
  3. GetMaxKey() - Returns one of the keys with maximal value. If no element exists, return an empty string "".
  4. GetMinKey() - Returns one of the keys with minimal value. If no element exists, return an empty string "".
Challenge: Perform all these in O(1) time complexity.
----------------------------
用doubly linked list, value相同的string都放在一个node里面

class AllOne {

    class Node{
        public Node pre;
        public Node next;
        public int val;
        public boolean isDummy;
        public Set<String> set;
        public Node(int val) {
            this.val = val;
            set = new HashSet<>();
            isDummy = false;
        }
    }
    
    private Node min;
    private Node max;
    private Map<String, Integer> map;
    private Map<Integer, Node> iToN;
    
    /** Initialize your data structure here. */
    public AllOne() {
        min = new Node(0);
        max = new Node(0);
        min.next = max;
        max.pre = min;
        min.isDummy = true;
        max.isDummy = true;
        
        map = new HashMap<>();
        iToN = new HashMap<>();
    }
    
    /** Inserts a new key <Key> with value 1. Or increments an existing key by 1. */
    public void inc(String key) {
        if (!map.containsKey(key)) {
            addOne(key);
        }else {
            int val = map.get(key);
            map.put(key, val + 1);
            
            addToNewNode(key, val + 1);
            removeKey(key, val);
        }
    }
    
    private void addToNewNode(String key, int val) {
        if (iToN.containsKey(val)) {
            iToN.get(val).set.add(key);
        }else {
            Node node = new Node(val);
            node.set.add(key);
            iToN.put(val, node);
            
            Node pre = iToN.get(val - 1);
            insertAfter(pre, node);
        }
    }
    
    private void insertAfter(Node cur, Node node) {
        Node next = cur.next;
        cur.next = node;
        node.pre = cur;
        node.next = next;
        next.pre = node;
    }
    
    private void removeKey(String key, int val) {
        Node node = iToN.get(val);
        node.set.remove(key);
        if (node.set.isEmpty()) {
            Node pre = node.pre;
            Node next = node.next;
            pre.next = next;
            next.pre = pre;
            
            iToN.remove(val);
        }
    }
    
    private void addOne(String key) {
        map.put(key, 1);
        
        if (iToN.containsKey(1)) {
            iToN.get(1).set.add(key);
        }else {
            Node node = new Node(1);
            node.set.add(key);
            iToN.put(1, node);

            insertAfter(min, node);
        }
    }
    
    /** Decrements an existing key by 1. If Key's value is 1, remove it from the data structure. */
    public void dec(String key) {
        if (!map.containsKey(key)) return;
        
        int val = map.get(key);
        if (val == 1) {
            map.remove(key);
            removeKey(key, 1);
        }else {
            if (iToN.containsKey(val - 1)) {
                iToN.get(val - 1).set.add(key);
            }else {
                Node node = new Node(val - 1);
                node.set.add(key);
                iToN.put(val - 1, node);
                Node pre = iToN.get(val).pre;
                
                insertAfter(pre, node);
            }
            
            removeKey(key, val);
            map.put(key, val - 1);
        }
    }
    
    /** Returns one of the keys with maximal value. */
    public String getMaxKey() {
        if (max.pre.isDummy) return "";
        Iterator<String> itr = max.pre.set.iterator();
        return itr.next();
    }
    
    /** Returns one of the keys with Minimal value. */
    public String getMinKey() {
        if (min.next.isDummy) return "";
        Iterator<String> itr = min.next.set.iterator();
        return itr.next();
    }
}

/**
 * Your AllOne object will be instantiated and called as such:
 * AllOne obj = new AllOne();
 * obj.inc(key);
 * obj.dec(key);
 * String param_3 = obj.getMaxKey();
 * String param_4 = obj.getMinKey();
 */

Friday, September 14, 2018

505. The Maze II

505The Maze II
There is a ball in a maze with empty spaces and walls. The ball can go through empty spaces by rolling updownleft or right, but it won't stop rolling until hitting a wall. When the ball stops, it could choose the next direction.
Given the ball's start position, the destination and the maze, find the shortest distance for the ball to stop at the destination. The distance is defined by the number of empty spaces traveled by the ball from the start position (excluded) to the destination (included). If the ball cannot stop at the destination, return -1.
The maze is represented by a binary 2D array. 1 means the wall and 0 means the empty space. You may assume that the borders of the maze are all walls. The start and destination coordinates are represented by row and column indexes.
Example 1
Input 1: a maze represented by a 2D array

0 0 1 0 0
0 0 0 0 0
0 0 0 1 0
1 1 0 1 1
0 0 0 0 0

Input 2: start coordinate (rowStart, colStart) = (0, 4)
Input 3: destination coordinate (rowDest, colDest) = (4, 4)

Output: 12
Explanation: One shortest way is : left -> down -> left -> down -> right -> down -> right.
             The total distance is 1 + 1 + 3 + 1 + 2 + 2 + 2 = 12.

Example 2
Input 1: a maze represented by a 2D array

0 0 1 0 0
0 0 0 0 0
0 0 0 1 0
1 1 0 1 1
0 0 0 0 0

Input 2: start coordinate (rowStart, colStart) = (0, 4)
Input 3: destination coordinate (rowDest, colDest) = (3, 2)

Output: -1
Explanation: There is no way for the ball to stop at the destination.

Note:
  1. There is only one ball and one destination in the maze.
  2. Both the ball and the destination exist on an empty space, and they will not be at the same position initially.
  3. The given maze does not contain border (like the red rectangle in the example pictures), but you could assume the border of the maze are all walls.
  4. The maze contains at least 2 empty spaces, and both the width and height of the maze won't exceed 100.
------------------
这题其实是一个weighted grahp,vertex是每一个靠墙的点,edge weight是每个vertex之间的距离。所以搜索的方法有dfs,bfs跟dijkstra。复杂度为 (|E| + |V|) * K,K为在每个vertex上的花费

Solution #1, 在Maze I的基础上进行的修改。
1. 因为每一步走的距离都是不等的,所以得保持另一个2d array来记录当前坐标的距离
2. 可以不用预先把记录距离的2d array填满,但是那种算法oj内存溢出,很奇怪
Worst complexity是O((m * n)^2),

class Solution {
    public int shortestDistance(int[][] maze, int[] start, int[] destination) {
        Queue<int[]> que = new LinkedList<>();
        que.add(start);
        int[][] dirs = {{1,0},{-1,0},{0,1},{0,-1}};
        int[][] dis = new int[maze.length][maze[0].length];
        for (int[] row: dis)
            Arrays.fill(row, Integer.MAX_VALUE);
        dis[start[0]][start[1]] = 0;
        
        while (!que.isEmpty()) {
            int[] pos = que.poll();
            
            for (int[] dir : dirs) {
                int i = 0;
                while (pos[0] + dir[0] * i >= 0 && pos[0] + dir[0] * i < maze.length
                      && pos[1] + dir[1] * i >= 0 && pos[1] + dir[1] * i < maze[0].length
                      && maze[pos[0] + dir[0] * i][pos[1] + dir[1] * i] == 0) {
                    i++;
                }
                i--;
                
                if (dis[pos[0] + dir[0] * i][pos[1] + dir[1] * i] > dis[pos[0]][pos[1]] + i) {
                    dis[pos[0] + dir[0] * i][pos[1] + dir[1] * i] = dis[pos[0]][pos[1]] + i;
                    int[] next = new int[]{pos[0] + dir[0] * i, pos[1] + dir[1] * i};
                    que.add(next);
                }
            }
        }
        
        return dis[destination[0]][destination[1]] == Integer.MAX_VALUE ? -1 : dis[destination[0]][destination[1]];
    }
}

Solution #2, Dijkastra
O(m * n * log(m * n)), queue的大小为 m * n, 每次搜索为lg

下面的实现方式有点问题。检查destination应该在刚刚从queue里出来的时候
class Solution {
    public int shortestDistance(int[][] maze, int[] start, int[] destination) {
        int[][] dirs = {{1,0},{-1,0},{0,1},{0,-1}};
        int[][] dis = new int[maze.length][maze[0].length];
        
        PriorityQueue<int[]> que = new PriorityQueue<>((a, b) -> a[2] - b[2]);
        que.add(new int[]{start[0], start[1], 0});
        for (int[] row: dis)
            Arrays.fill(row, Integer.MAX_VALUE);
        dis[start[0]][start[1]] = 0;

        while (!que.isEmpty()) {
            int[] pos = que.poll();
            
            for (int[] dir : dirs) {
                int i = 0;
                    
                while (pos[0] + dir[0] * i >= 0 && pos[0] + dir[0] * i < maze.length
                      && pos[1] + dir[1] * i >= 0 && pos[1] + dir[1] * i < maze[0].length
                      && maze[pos[0] + dir[0] * i][pos[1] + dir[1] * i] == 0) {
                    i++;
                }
                i--;
                
                int row = pos[0] + dir[0] * i;
                int col = pos[1] + dir[1] * i;
                
                if (destination[0] == row && destination[1] == col) return pos[2] + i;
                if (dis[row][col] > dis[pos[0]][pos[1]] + i) {
                    
                    dis[row][col] = dis[pos[0]][pos[1]] + i;
                    int[] next = new int[]{ row, col, pos[2] + i};    
                    que.add(next);
                }
            }
        }
        
        return -1;
    }
}

Thursday, September 13, 2018

490. The Maze

490The Maze
There is a ball in a maze with empty spaces and walls. The ball can go through empty spaces by rolling updownleft or right, but it won't stop rolling until hitting a wall. When the ball stops, it could choose the next direction.
Given the ball's start position, the destination and the maze, determine whether the ball could stop at the destination.
The maze is represented by a binary 2D array. 1 means the wall and 0 means the empty space. You may assume that the borders of the maze are all walls. The start and destination coordinates are represented by row and column indexes.
Example 1
Input 1: a maze represented by a 2D array

0 0 1 0 0
0 0 0 0 0
0 0 0 1 0
1 1 0 1 1
0 0 0 0 0

Input 2: start coordinate (rowStart, colStart) = (0, 4)
Input 3: destination coordinate (rowDest, colDest) = (4, 4)

Output: true
Explanation: One possible way is : left -> down -> left -> down -> right -> down -> right.

Example 2
Input 1: a maze represented by a 2D array

0 0 1 0 0
0 0 0 0 0
0 0 0 1 0
1 1 0 1 1
0 0 0 0 0

Input 2: start coordinate (rowStart, colStart) = (0, 4)
Input 3: destination coordinate (rowDest, colDest) = (3, 2)

Output: false
Explanation: There is no way for the ball to stop at the destination.

Note:
  1. There is only one ball and one destination in the maze.
  2. Both the ball and the destination exist on an empty space, and they will not be at the same position initially.
  3. The given maze does not contain border (like the red rectangle in the example pictures), but you could assume the border of the maze are all walls.
  4. The maze contains at least 2 empty spaces, and both the width and height of the maze won't exceed 100.
---------------------
Solution #1, DFS. 这个其实也不用backtracking
比正常的DFS要多一些步骤:
1. 记录走的方向,
2. 每个方向都需要visited信息
3. 检查撞墙
4. 如果不是墙,继续当前方向

O(m * n), 最坏情况每一个格子都走一次

class Solution {
    public boolean hasPath(int[][] maze, int[] start, int[] destination) {
        int m = maze.length, n = maze[0].length;
        boolean[][][] visited = new boolean[m][n][5];
        
        return dfs(maze, destination, visited, start[0] - 1, start[1], 1) ||
            dfs(maze, destination, visited, start[0], start[1] + 1, 2) ||
            dfs(maze, destination, visited, start[0] + 1, start[1], 3) ||
            dfs(maze, destination, visited, start[0], start[1] - 1, 4);
    }
    
    private boolean dfs(int[][] maze, int[] destination, boolean[][][] visited, int row, int col, int dir) {
        if (row < 0 || row >= maze.length || col < 0 || col >= maze[0].length 
            || visited[row][col][dir] || maze[row][col] == 1) return false;
        
        visited[row][col][dir] = true;
        if ((dir == 1 && (row == 0 || maze[row - 1][col] == 1)) || 
           (dir == 3 && (row == maze.length - 1 || maze[row + 1][col] == 1))) {
            
            if (destination[0] == row && destination[1] == col) return true;
            return dfs(maze, destination, visited, row, col - 1, 4) || dfs(maze, destination, visited, row, col + 1, 2); 
        } 
        
        if ((dir == 2 && (col == maze[0].length - 1 || maze[row][col + 1] == 1)) || 
           (dir == 4 && (col == 0 || maze[row][col - 1] == 1))) {
            
            if (destination[0] == row && destination[1] == col) return true;
            return dfs(maze, destination, visited, row - 1, col, 1) || dfs(maze, destination, visited, row + 1, col, 3); 
        }
        
        boolean flag = false;
        if (dir == 1) flag = dfs(maze, destination, visited, row - 1, col, 1);
        if (dir == 2) flag = dfs(maze, destination, visited, row, col + 1, 2);
        if (dir == 3) flag = dfs(maze, destination, visited, row + 1, col, 3); 
        if (dir == 4) flag = dfs(maze, destination, visited, row, col - 1, 4); 
        
        if (flag) return true;
        visited[row][col][dir] = false;
        return false;
    }
}

DFS,在每一层递归里一直走,直到碰到墙。这是对Solution #1的简化
这里不用backtracking,因为每次遇到墙之后情况都会往4个方向尝试走。

class Solution {

    public boolean hasPath(int[][] maze, int[] start, int[] destination) {
        int m = maze.length, n = maze[0].length;
        boolean[][] visited = new boolean[m][n];
        int[][] dirs = {{-1,0}, {1,0}, {0,1},{0,-1}};
        
        return dfs(maze, destination, visited, start[0], start[1], dirs);
    }
    
    private boolean dfs(int[][] maze, int[] destination, boolean[][] visited, int row, int col, int[][] dirs) {
        if (visited[row][col]) return false;
        if (destination[0] == row && destination[1] == col) return true;
        visited[row][col] = true;
        
        for (int[] dir : dirs) {
            int i = 1;
            while (row + dir[0] * i >= 0 && row + dir[0] * i < maze.length 
                   && col + dir[1] * i >= 0 && col + dir[1] * i < maze[0].length 
                   && maze[row + dir[0] * i][col + dir[1] * i] != 1) {
                
                i++;
            }
            i--;
            if (dfs(maze, destination, visited, row + dir[0] * i, col + dir[1] * i, dirs)) return true;
        }
                
        // visited[row][col] = true;
        return false;
    }
}

Solution #3, BFS
class Solution {
    
    public boolean hasPath(int[][] maze, int[] start, int[] destination) {
        Queue<int[]> que = new LinkedList<>();
        que.add(start);
        int[][] dirs = {{1,0},{-1,0},{0,1},{0,-1}};
        boolean[][] visited = new boolean[maze.length][maze[0].length];
        
        while (!que.isEmpty()) {
            int[] pos = que.poll();
            if (pos[0] == destination[0] && pos[1] == destination[1]) return true;
            if (maze[pos[0]][pos[1]] == 1 || visited[pos[0]][pos[1]]) continue;
            visited[pos[0]][pos[1]] = true;
            
            for (int[] dir : dirs) {
                int i = 0;
                while (pos[0] + dir[0] * i >= 0 && pos[0] + dir[0] * i < maze.length
                      && pos[1] + dir[1] * i >= 0 && pos[1] + dir[1] * i < maze[0].length
                      && maze[pos[0] + dir[0] * i][pos[1] + dir[1] * i] == 0) {
                    i++;
                }
                i--;
                int[] next = {pos[0] + dir[0] * i, pos[1] + dir[1] * i};
                que.add(next);
            }
        }
        
        return false;
    }
}

Sunday, September 9, 2018

399. Evaluate Division

399Evaluate Division
Equations are given in the format A / B = k, where A and B are variables represented as strings, and k is a real number (floating point number). Given some queries, return the answers. If the answer does not exist, return -1.0.
Example:
Given a / b = 2.0, b / c = 3.0.
queries are: a / c = ?, b / a = ?, a / e = ?, a / a = ?, x / x = ? .
return [6.0, 0.5, -1.0, 1.0, -1.0 ].
The input is: vector<pair<string, string>> equations, vector<double>& values, vector<pair<string, string>> queries, where equations.size() == values.size(), and the values are positive. This represents the equations. Return vector<double>.
According to the example above:
equations = [ ["a", "b"], ["b", "c"] ],
values = [2.0, 3.0],
queries = [ ["a", "c"], ["b", "a"], ["a", "e"], ["a", "a"], ["x", "x"] ]. 
The input is always valid. You may assume that evaluating the queries will result in no division by zero and there is no contradiction.
----------------------
Solution #1, Graph dfs
O(n * m), n是graph大小,m是query的数量,应该可以用union-find来优化
class Solution {
    class Node {
        public String key;
        public Map<Node, Double> neighbors;
        public Node(String key) {
            this.key = key;
            neighbors = new HashMap<>();
        }
    }
    
    public double[] calcEquation(String[][] equations, double[] values, String[][] queries) {
        Map<String, Node> graph = buildGraph(equations, values);
        
        int n = queries.length;
        double[] rt = new double[n];
        
        for (int i = 0; i < queries.length; i++) {
            rt[i] = dfs(graph, queries[i][0], queries[i][1], 1.0, new HashSet<String>());
        }
        
        return rt;
    }
    
    private double dfs(Map<String, Node> graph, String start, String end, double value, Set<String> visited) {
        if (!graph.containsKey(start) || !graph.containsKey(end) || visited.contains(start)) return -1.0;
        if (start.equals(end)) return value;
        
        visited.add(start);
        for (Map.Entry<Node, Double> entry : graph.get(start).neighbors.entrySet()) {
            double rt = dfs(graph, entry.getKey().key, end, value * entry.getValue(), visited);
            if (rt != -1.0) return rt;    
            
        }
        
        return -1.0;
    }
    
    private Map<String, Node> buildGraph(String[][] equations, double[] values) {
        Map<String, Node> graph = new HashMap<>();
        
        for (int i = 0; i < equations.length; i++) {
            String[] pair = equations[i];
            if (!graph.containsKey(pair[0])) {
                Node node = new Node(pair[0]);
                graph.put(pair[0], node);
            }
            
            if (!graph.containsKey(pair[1])) {
                Node node = new Node(pair[1]);
                graph.put(pair[1], node);
            }
            
            graph.get(pair[0]).neighbors.put(graph.get(pair[1]), values[i]);
            graph.get(pair[1]).neighbors.put(graph.get(pair[0]), 1 / values[i]);
        }
        
        return graph;
    }
}

Solution #2, Union Find,用被除数当作parent
ToDo: 加入UF本身的优化:size based

class Solution {
    
    class Node {
        public String key;
        public double val;
        public Node(String key) {
            this.key = key;
            val = 1;
        }
        
        public Node(String key, double val) {
            this.key = key;
            this.val = val;
        }
    }
    
    public double[] calcEquation(String[][] equations, double[] values, String[][] queries) {
        
        Map<String, Node> map = new HashMap<>();
        Map<String, String> uf = new HashMap<>();
        for (int i = 0; i < equations.length; i++) {
            String[] pair = equations[i];
            if (!map.containsKey(pair[0])) {
                map.put(pair[0], new Node(pair[0]));
                uf.put(pair[0], pair[0]);
            }

            if (!map.containsKey(pair[1])) {
                map.put(pair[1], new Node(pair[1]));
                uf.put(pair[1], pair[1]);
            }

            Node parentOf1 = find(uf, map, pair[0]);
            Node parentOf2 = find(uf, map, pair[1]);

            if (!parentOf1.key.equals(parentOf2.key)) {
                uf.put(parentOf2.key, parentOf1.key);
                map.get(parentOf2.key).val = values[i] * parentOf1.val / parentOf2.val;
            }
        }

        double[] rt = new double[queries.length];
        for (int i = 0; i < queries.length; i++) {
            if (!map.containsKey(queries[i][0]) || !map.containsKey(queries[i][1])) {
                rt[i] = -1.0;
                continue;
            }

            Node p1 = find(uf, map, queries[i][0]);
            Node p2 = find(uf, map, queries[i][1]);
            if (p1.key.equals(p2.key)) {
                rt[i] = p2.val / p1.val;
            }else {
                rt[i] = -1.0;
            }
        }

        return rt;
    }
        
    private Node find(Map<String, String> uf, Map<String, Node> map, String key) {
        String ori = key;
        double val = map.get(ori).val;

        while (!uf.get(key).equals(key)) {
            val *= map.get(uf.get(key)).val;
            key = uf.get(key);
        }

        uf.put(ori, key);
        map.get(ori).val = val;
        return new Node(key, val); // Use Node as Pair: return the key of parent, but the value of the original passed in key
    }
}

Friday, September 7, 2018

493. Reverse Pairs

493Reverse Pairs
Given an array nums, we call (i, j) an important reverse pair if i < j and nums[i] > 2*nums[j].
You need to return the number of important reverse pairs in the given array.
Example1:
Input: [1,3,2,3,1]
Output: 2
Example2:
Input: [2,4,3,5,1]
Output: 3
Note:
  1. The length of the given array will not exceed 50,000.
  2. All the numbers in the input array are in the range of 32-bit integer.
------------------------------
ref: https://leetcode.com/problems/reverse-pairs/solution/
BIT: https://www.topcoder.com/community/data-science/data-science-tutorials/binary-indexed-trees/
关键是需要一个数据结构可以提供实时排序并计数。binary search tree可以满足,但是最坏情况是O(n^2),得用AVL或Red-Black来实现。而BIT(binary indexed tree) 实现更简单

Solution #1 Binary Indexed Tree,遍历以[i] 为pair里第2位的所有pair,在[0, i - 1]内找所有符合 j < i && nums[j] >= 2 * num[i] + 1的个数并返回。根据算法要返回排序后的[j, i]数量,而普通的BIT实现是[0, i]和,所有这里要修改下read和update方法,使BIT存的是suffix sum。

想验证的该算法的话画一个[1, 8]的BIT就可以了

class Solution {
    public int reversePairs(int[] nums) {
        int n = nums.length;
        int[] bit = new int[n + 1];
        int[] sorted = Arrays.copyOf(nums, nums.length);
        Arrays.sort(sorted);
        
        int rt = 0;
        
        for (int i = 0; i < n; i++) {
            int index = getIndex(sorted, nums[i] * 2L + 1);
            rt += read(bit, index);
            int index2 = getIndex(sorted, nums[i]); 
            update(bit, index2);
        }
                        
        return rt;
    }
    
    private int getIndex(int[] sorted, long val) {
        int l = 0, r = sorted.length - 1;
        
        while (l <= r) {
            int m = (l + r) / 2;
            if (sorted[m] >= val) {
                r = m - 1;
            }else {
                l = m + 1;
            }
        }

        return l + 1;
    }
    
    private void update(int[] bit, int idx) {
        
        while (idx > 0) {
            bit[idx] += 1;
            idx -= idx & -idx;
        }
    }
    
    private int read(int[] bit, int idx) {
        int sum = 0;
        while (idx < bit.length) {
            sum += bit[idx];
            idx += idx & -idx;
        }
        
        return sum;
    }
}

Solution #2, 用merge sort的思路。分成2堆,pair里的第一个在第一堆,第二个在第二堆。这样能保证i < j, 然后再过滤 nums[i] > nums[j] * 2
class Solution {
    public int reversePairs(int[] nums) {
        return sort(nums, 0, nums.length - 1);
    }
    
    private int sort(int[] nums, int l, int r) {
        
        if (l >= r) {
            return 0;
        }
        
        int m = (l + r) / 2;
        int rt = sort(nums, l, m) + sort(nums, m + 1, r);
        
        int x = l, z = m + 1;
        while (x <= m && z <= r) {
            
            if (nums[x] > nums[z] * 2L) {
                rt += m - x + 1;            
                z++;
            }else {
                x++;    
            }
        }

        int i = l, j = m + 1;
        int[] backup = new int[r - l + 1];
        int p = 0;     
        
        while (i <= m || j <= r) {
            if (i > m || (j <= r && nums[i] >= nums[j])) {
                backup[p++] = nums[j++];
            }else {
                backup[p++] = nums[i++];
            }
        }
        
        for (int k = l; k <= r; k++) {
            nums[k] = backup[k - l];
        }
        
        return rt;
    }
}