Skip to content

Latest commit

 

History

History
1008 lines (894 loc) · 31 KB

每日手写.md

File metadata and controls

1008 lines (894 loc) · 31 KB

Daily Practice

Binary Search

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int start = firstGreaterOrEqual(nums, target);
        if (start == nums.length || nums[start] != target) {
            return new int[] {-1, -1};
        }
        return new int[]{start, firstGreaterOrEqual(nums, target+1)-1};
    }
    
    private int firstGreaterOrEqual(int[] nums, int target) {
        int low = 0, high = nums.length;
        while (low < high) {
            int mid = low + (high - low) / 2;
            if (nums[mid] < target) {
                low = mid + 1;
            } else {
                // A[mid] > target OR A[mid] == target
                //We want to capture A[mid] == target as well
                //So we should not do mid-1 here, and only do mid
                //Works for A[mid] > target case as well.
                high = mid;
            }
        }
        return low;
        
    }
}

Quick Select

class Solution {
    public int findKthLargest(int[] nums, int k) {
        return Quick.select(nums, nums.length - k);
    }
    
    public static class Quick {
        public static int select(int[] nums, int K) {
            knuthShuffle(nums);
            int low = 0, high = nums.length - 1;
            while (low < high) {
                int j = partition(nums, low, high);
                if (j < K) {
                    low = j + 1;
                } else if (j > K) {
                    high = j - 1;
                } else {
                    break;
                }
            }
            return nums[K];
        }
        
        private static void knuthShuffle(int[] nums) {
            for (int i =  1; i < nums.length; i++) {
                int j = new Random().nextInt(i+1); //[0, i], not [0, N], otherwise no t unifrom
                swap(nums, i, j);
            }
        }
        
        //low + 1 <= i < j <= high
        private static int partition(int[] nums, int low, int high) {
            int pivot = nums[low];
            int i = low, j = high + 1;
            while (true) {
                while (nums[++i] < pivot) {
                    if ( i == high) {
                        break;
                    }
                }
                while (nums[--j] > pivot) {
                    if (j == low) {
                        break;
                    }
                }
                if (i >= j) {
                    break;
                } 
                swap(nums, i, j);
            }
            //j is the first element not greater than pivot
            swap(nums, j, low);
            return j;
        }
        
        private static void swap(int[] nums, int i, int j) {
            int temp = nums[i];
            nums[i] = nums[j];
            nums[j] = temp;
        }
    }
}

Quick Sort

class Solution {
    public void sortColors(int[] nums) {
        Quick3Way.sort(nums);
    }
    
    //[low, lt): less than pivot
    //(gt, high]: greater than pivot
    // i examins [lt, gt]
    private static class Quick3Way {
        public static void sort(int[] nums) {
            sort(nums, 0, nums.length - 1);
        }
        
        public static void sort(int[] nums, int low, int high) {
            if (high <= low) {
                return;
            }
            int pivot = nums[low];
            int lt = low, gt = high;
            int i = low;
            while (i <= gt) {
                if (nums[i] < pivot) {
                    swap(nums, i++, lt++);
                } else if (nums[i] > pivot) {
                    swap(nums, i, gt--);
                } else {
                    i++; //do nothing
                }
                
            }
            sort(nums, low, lt-1);
            sort(nums, gt+1, high);
        }
        
        private static void swap(int[] nums, int i, int j) {
            int temp = nums[i];
            nums[i] = nums[j];
            nums[j] = temp;
        }
    }
}

BST

class BSTIterator {

    Stack<TreeNode> stack;

    public BSTIterator(TreeNode root) {
        
        // Stack for the recursion simulation
        this.stack = new Stack<TreeNode>();
        
        // Remember that the algorithm starts with a call to the helper function
        // with the root node as the input
        this._leftmostInorder(root);
    }

    private void _leftmostInorder(TreeNode root) {
      
        // For a given node, add all the elements in the leftmost branch of the tree
        // under it to the stack.
        while (root != null) {
            this.stack.push(root);
            root = root.left;
        }
    }

    /**
     * @return the next smallest number
     */
    public int next() {
        // Node at the top of the stack is the next smallest element
        TreeNode topmostNode = this.stack.pop();

        // Need to maintain the invariant. If the node has a right child, call the 
        // helper function for the right child
        if (topmostNode.right != null) {
            this._leftmostInorder(topmostNode.right);
        }

        return topmostNode.val;
    }

    /**
     * @return whether we have a next smallest number
     */
    public boolean hasNext() {
        return this.stack.size() > 0;
    }
}
class BSTIterator {
    ArrayList<Integer> nodeSorted;
    int index;

    public BSTIterator(TreeNode root) {
        this.nodeSorted = new ArrayList<Integer>();
        this.index = -1;
        this.inorder(root);
    }
    
    
    private void inorder(TreeNode root) {
        if (root == null) {
            return;
        }
        inorder(root.left);
        nodeSorted.add(root.val);
        inorder(root.right);
    }
    /** @return the next smallest number */
    public int next() {
        index++;
        return nodeSorted.get(index);
    }
    
    /** @return whether we have a next smallest number */
    public boolean hasNext() {
        return index+1 < nodeSorted.size();
    }
}

DFS:

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> solutions = new ArrayList<>();
        backtrack(nums, solutions, new ArrayList<>(), 0);
        return solutions;
    }
    
    private void backtrack(int[] nums, List<List<Integer>> solutions, List<Integer> solution, int start) {
        solutions.add(new ArrayList<>(solution));
        
        for (int i = start; i < nums.length; i++) {
            solution.add(nums[i]);
            backtrack(nums, solutions, solution, i+1);
            solution.remove(solution.size() - 1);
        }
    }
}
class Solution {
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        Arrays.sort(nums);
        List<List<Integer>> result = new ArrayList<>();
        backtrack(nums, result, new ArrayList<>(), 0);
        return result;
    }
    
    private void backtrack(int[] nums,  List<List<Integer>> result, List<Integer> subset, int start) {
        result.add(new ArrayList<>(subset));
        
        for (int i = start; i < nums.length; i++) {
            if ( i > start && nums[i] == nums[i-1]) {
                continue;
            }
            subset.add(nums[i]);
            backtrack(nums, result, subset, i+1);
            subset.remove(subset.size() - 1);
        }
    }
}
class Solution {
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        backtrack(result, nums, new ArrayList<>(), new boolean[nums.length]);
        return result;
    }
    
    public void backtrack(List<List<Integer>> result, int[] nums, List<Integer> prefix, boolean[] visited) {
        if (prefix.size() == nums.length) {
            result.add(new ArrayList<>(prefix));
            return;
        }
        
        for (int i = 0; i < nums.length; i++) {
            if (visited[i]) {
                continue;
            }
            prefix.add(nums[i]); visited[i] = true;
            backtrack(result, nums, prefix, visited);
            prefix.remove(prefix.size() - 1); visited[i] = false;
        }
    }
}
class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(nums);
        backtrack(result, nums, new ArrayList<>(), new boolean[nums.length]);
        return result;
    }
    
    private void backtrack(List<List<Integer>> result, int[] nums, List<Integer> prefix, boolean[] visited) {
        if (prefix.size() == nums.length) {
            result.add(new ArrayList<>(prefix));
            return;
        }
        
        for (int i = 0; i < nums.length; i++) {
            if (visited[i]) {
                continue;
            }
            if (i > 0 && nums[i] == nums[i-1]  && visited[i-1]) {
                continue;
            }
            prefix.add(nums[i]); visited[i] = true;
            backtrack(result, nums, prefix, visited);
            prefix.remove(prefix.size() - 1); visited[i] = false;
        }
    }
}
class Solution {
    //string s => solution of s
    HashMap<String, List<String>> memo = new HashMap<>();
    
    public List<String> wordBreak(String s, List<String> wordDict) {
        return dfs(s, wordDict);
    }
    
    public List<String> dfs(String s, List<String> wordDict) {
        if (memo.containsKey(s)) {
            return memo.get(s);
        }
        
        List<String> result = new ArrayList<>();
        if (s.length() == 0) {
            result.add("");
            return result;
        }
        
        for (String word: wordDict) {
            if (s.startsWith(word)) {
                List<String> sublist = dfs(s.substring(word.length()), wordDict);
                for (String sub: sublist) {
                    result.add(word + (sub.isEmpty() ? "" : " ") + sub);
                }
            }
        }
        memo.put(s, result);
        return result;
    }
}

BFS

public class Solution {
    //A copy of dictionary, INPUT
    private Set<String> wordSet;
    //Adjacency Map of SymbolGraph, INPUT by dict, OUTPUT to bfs, dfs
    private Map<String, List<String>> graph = new HashMap<>();
    //Path Length from begin word to ALL word in dict, INPUT by bfs, output to dfs
    private Map<String, Integer> distance = new HashMap<>();
    
    public List<List<String>> findLadders(String start, String end, List<String> wordList) {
        //Step 1: Init all necessary auxiliary information
        wordSet = new HashSet<String>(wordList);
        wordSet.add(start);  
        buildGraph();
        
        //Step 2: BFS to Build node adjacency list AND distance lookup        
        bfs(start, end);  
        
        //Step 3: Backtrack on all possible path
        List<List<String>> result = new ArrayList<List<String>>();
        List<String> pathPrefix = new ArrayList<String>();
        dfs(start, end, pathPrefix, result);
        
        return result;
    }
    
    private void buildGraph() {        
        for (String word : wordSet) {
            graph.put(word, getNeighbors(word));
        }
    }
    
    // BFS: Trace every node's distance from the start node (level by level).
    private void bfs(String start, String end) {
        Queue<String> queue = new LinkedList<String>();
        int level = 0;
        queue.offer(start);
        distance.put(start, 0); //Serve as the marked[] to see if nodes are already visited

        
        while (!queue.isEmpty()) {
            int size = queue.size(); //NOTE: Used to explore node level by level
            
            //NOTE: MUST explore all nodes of the level where end word is in.
            for (int i = 0; i < size; i++) { 
                //Get a word
                String currentWord = queue.poll();

                //Update relevant information: distance & neighbor
                int currentDistance = distance.get(currentWord);

                //Enqueue unvisited neighbor
                for (String neighbor: graph.get(currentWord)) {
                    if (!distance.containsKey(neighbor)) { 
                        distance.put(neighbor, level+1);
                        queue.offer(neighbor);
                    }
                }
            } 
            level++;
        }
    }
    
    // Find all next level nodes.    
    private List<String> getNeighbors(String node) {
        List<String> result = new ArrayList<String>();
        char chs[] = node.toCharArray();

        //Compute all different variations of the word, and see if it's in the dictionary
        for (int i = 0; i < chs.length; i++) {
            for (char ch ='a'; ch <= 'z'; ch++) {
                if (chs[i] == ch) {
                    continue;
                }
            
                char origCh = chs[i];
                chs[i] = ch;
                String word = String.valueOf(chs);
                if (wordSet.contains(word)) {
                    result.add(word);
                }
                chs[i] = origCh;
            }
        }
        return result;
    }
    
    // DFS: output all paths with the shortest distance using backtracking
    private void dfs(String currentWord, String endWord, List<String> pathPrefix, List<List<String>> result) {
        //Step 1: Take a step with currentWord
        pathPrefix.add(currentWord);

        //Step 2: Explore on all possible next steps
        if (endWord.equals(currentWord)) {
            //Step 2.1: If already reached end, no need to explore next step
            result.add(new ArrayList<String>(pathPrefix));
            //NOTE: Do not return early here, you need to be able to backtrack since some other path might lead to endWord
        } else {
            //Step 2.2: Otherwise, the next step should be the neighbor AND one step larger from current node to end
            for (String nextWord : graph.get(currentWord)) {            
                if (distance.get(nextWord) == distance.get(currentWord) + 1) {
                    dfs(nextWord, endWord, pathPrefix, result);
                }
            }
        }

        //Step 3: Take the step back
        pathPrefix.remove(pathPrefix.size() - 1);
    }
}

Union-Find:

class Solution {
    class UnionFind {
        int[] parent;
        int count; 
        
        public UnionFind(int N) {
            count = 0;
            parent = new int[N];
            for (int i = 0; i < N; i++) {
                parent[i] = -1;
            }
        }
        
        int find(int x) {
            if (parent[x] != x) {
                parent[x] = find(parent[x]);
            }
            return parent[x];
        }
        
        void union(int a, int b) {
            int rootA = find(a);
            int rootB = find(b);
            if (rootA != rootB) {
                parent[rootA] = rootB;
                count--;
            }
        }
        
        void addToUF(int i) {
            parent[i] = i;
            count++;
        }
        
        public boolean contains(int i) {
            return parent[i] != -1;
        }
        
        int getCount() {
            return count;
        }
    }
    
    public List<Integer> numIslands2(int m, int n, int[][] positions) {
        List<Integer> result = new ArrayList<>();
        UnionFind uf = new UnionFind(m * n);
        int[][] DIRS = new int[][] {
            {-1,0}, {1,0}, {0,1}, {0,-1}};
        
        for (int[] pos: positions) {
            int r = pos[0], c = pos[1];
            int current = r * n + c;
            if (uf.contains(current)) {
                result.add(uf.getCount());
                continue;
            }
            
            List<Integer> validNeighbors = new ArrayList<>();
            for(int[] dir: DIRS) {
                int x = r + dir[0], y = c + dir[1];
                if (x >= 0 && x < m && y >= 0 && y < n ) {
                    int neighbor = x * n + y;
                    if (uf.contains(neighbor)) {
                        validNeighbors.add(neighbor);
                    }
                }
            }
            

            uf.addToUF(current);
            for (int neighbor: validNeighbors) {
                uf.union(neighbor, current);
            }
            result.add(uf.getCount());
        }
        return result;
    }
}

Trie:

class Solution {
    class TrieNode {
        TrieNode[] child = new TrieNode[26];
        String word;
    }
    
    TrieNode buildTrie(String[] words) {
        TrieNode root = new TrieNode();
        for (String w: words) {
            TrieNode p = root;
            for (char c: w.toCharArray()) {
                if (p.child[c-'a'] == null) {
                    p.child[c-'a'] = new TrieNode();
                }
                p = p.child[c-'a'];
            }
            p.word = w;
        }
        return root;
    }
    
    char VISITED = '#';
    
    public List<String> findWords(char[][] board, String[] words) {
        List<String> result = new ArrayList<>();
        TrieNode root = buildTrie(words);
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[0].length; j++) {
                dfs(board, i, j, root, result);
            }
        }
        return result;
    }
    
    private void dfs(char[][] board, int i, int j, TrieNode p, List<String> result) {
        char c = board[i][j];
        if (c == VISITED || p.child[c-'a'] == null) {
            return;
        }
        p = p.child[c-'a'];
        if (p.word != null) {
            result.add(p.word);
            p.word = null; //Mark this word as invalid to avoid duplication
        }
        
        board[i][j] = VISITED;
        //Visit four valid neighbor cells
        if (i > 0) {
            dfs(board, i-1, j, p, result);
        }
        if (j > 0) {
            dfs(board, i, j-1, p, result);
        }
        if (i < board.length - 1) {
            dfs(board, i+1, j, p, result);
        }
         if (j < board[0].length - 1) {
            dfs(board, i, j+1, p, result);
        }
        board[i][j] = c;
    }
}
class Solution {    
    class TrieNode {
        TrieNode[] child = new TrieNode[26];
        //Set of words that have current prefix
        Set<String> wordSet = new HashSet<String>();
    }
    
    TrieNode buildTrie(String[] words) {
        TrieNode root = new TrieNode();
        for (String word: words) {
            TrieNode node = root;
            for (char c: word.toCharArray()) {
                if (node.child[c-'a'] == null) {
                    node.child[c-'a'] = new TrieNode();
                }
                node = node.child[c-'a'];
                node.wordSet.add(word);
            }
        }
        return root;
    }
    
    Set<String> getWordsWithPrefix(TrieNode root, String prefix) {
        TrieNode node = root;
        for (char c: prefix.toCharArray()) {
            if (node.child[c-'a'] == null) {
                return new HashSet<>();
            }
            node = node.child[c-'a'];
        }
        return node.wordSet;
    }
    
    
    public List<List<String>> wordSquares(String[] words) {
        TrieNode root = buildTrie(words);
        List<List<String>> result = new ArrayList<>();
        for (String word: words) {
            List<String> candidate = new ArrayList<>();
            candidate.add(word);
            backtrack(candidate, result, 1, words[0].length(), root);
        }
        return result;
    }
    
    private void backtrack(List<String> candidate, List<List<String>> result, int wordCount, int squareSize, TrieNode root) {
        if (wordCount == squareSize) {
            result.add(new ArrayList<>(candidate));
            return;
        }
        
        StringBuilder prefix = new StringBuilder();
        for (String word: candidate) {
            prefix.append(word.charAt(wordCount));
        }
        
        for (String word: getWordsWithPrefix(root, prefix.toString())) {
            candidate.add(word);
            backtrack(candidate, result, wordCount+1, squareSize, root);
            candidate.remove(candidate.size()-1);
        }
    }
}

Sweep Line:

LC218 The Skyline Problem 扫描线经典入门题目

class Solution {
    public List<List<Integer>> getSkyline(int[][] buildings) {
        int LEFT = 0, RIGHT = 1, HEIGHT = 2;
        //Ordered by critical points: left -> building, right -> building
        Map<Integer, List<int[]>> cps = new TreeMap<>(); 
        for (int[] building: buildings) {
            int left = building[LEFT], right = building[RIGHT];
            if (!cps.containsKey(left)) {
                cps.put(left, new ArrayList<>());
            }
            if (!cps.containsKey(right)) {
                cps.put(right, new ArrayList<>());
            }
            cps.get(left).add(building);
            cps.get(right).add(building);
        }
        
        
        List<List<Integer>> result = new ArrayList<>();
        int prevHeight= -1;
        //Heap of building by height 
        PriorityQueue<int[]> heap = new PriorityQueue<>(
            (b1, b2) -> Integer.compare(b2[HEIGHT], b1[HEIGHT]));
        
        //Sweep line
        for (int cp: cps.keySet()) {
            List<int[]> activeBuilding = cps.get(cp);
            for (int[] building: activeBuilding) {
                if (cp == building[LEFT]) {
                    heap.offer(building);
                } else if (cp == building[RIGHT]) {
                    heap.remove(building);
                }
            }
            
            if (heap.isEmpty()) {
                result.add(Arrays.asList(cp, 0));   
            }else {
                int height = heap.peek()[HEIGHT];
                if (height != prevHeight) {
                    result.add(Arrays.asList(cp, height));
                    prevHeight = height;
                }
            }
        }
        return result;
    }
}

Deque:

public class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums == null || k <= 0) {
            return new int[0];
        }
		
		int[] results = new int[nums.length - k + 1];
		int ri = 0;
		// store index
		Deque<Integer> q = new ArrayDeque<>();
		for (int i = 0; i < nums.length; i++) {
		    //remove numbers out of range k
		    while (!q.isEmpty() && q.peek() < i - k + 1) {
		        q.poll();
		        //inspect(i, q, "index out of range, POLL", nums);
		    }
		    
		    //remove smaller numbers in k range as they are useless
		    while(!q.isEmpty() && nums[q.peekLast()] < nums[i]) {
		        q.pollLast();
		        //inspect(i, q, "last less than current, POLL_LAST", nums);
		    }
		    
		    //q contains index where as r contins content
		    q.offer(i);
		    //inspect(i, q, "add new i,OFFER", nums);
		    if (i >= k - 1) {
		        //inspect(i, q, "window size reached, PEEK", nums);
		        results[ri] = nums[q.peek()];
		        ri++;
		    }
		}
		return results;
    }
}

Monotonic Stack:

class Solution {
    public int largestRectangleArea(int[] heights) {
        //What's the first element smaller than me on my left
        Stack<Integer> stack = new Stack<>();
        int maxArea = 0;
        stack.push(-1); //Mark end of stack
        for (int i = 0; i< heights.length; i++) {
            //Only the first element smaller than me can pop me out of stack, so we update area only on pop
            while (stack.peek() != -1 && heights[stack.peek()] >= heights[i]) {
                maxArea = Math.max(maxArea, heights[stack.pop()] * (i - stack.peek() - 1) );
            }
            stack.push(i);
        }
        
        while (stack.peek() != -1) {
            maxArea = Math.max(maxArea, heights[stack.pop()] * (heights.length - stack.peek() - 1));
        }
        return maxArea;
    }
}

Compiler:

/*
expr -> term ((PLUS|MINUS) term)*
term -> factor ((MUL|DIV) factor)*
factor -> INTEGER | (PLUS|MINUS) factor | LPAREN expr RPAREN
*/

class Solution {
    enum Type {
        PLUS, MINUS, MUL, DIV, LPAREN, RPAREN, EOF, INTEGER
    }
    
    class Token {
        Type type;
        int value;
        
        public Token(Type type) {
            this.type = type;
            this.value = -1;
        }
        
        public Token(Type type, int value) {
            this.type = type;
            this.value = value;
        }
    }
    
    class Lexer {
        char NONE = '#';
        int pos;
        char currentChar;
        String text;
        public Lexer(String text) {
            this.text = text;
            this.pos = 0;
            currentChar = text.length() == 0 ? NONE: text.charAt(pos);
        }
        
        private void advance() {
            currentChar = (pos >= text.length() - 1 ? NONE : text.charAt(++pos));
        }
        
        private int integer() {
            int value = 0;
            while (currentChar != NONE && Character.isDigit(currentChar)) {
                value = value * 10 + currentChar - '0';
                advance();
            }
            return value;
        }
        
        public Token getNextToken() {
            while (currentChar != NONE) {
                if (Character.isSpace(currentChar)) {
                    advance();
                    continue;
                } else if (Character.isDigit(currentChar)) {
                    return new Token(Type.INTEGER, integer());
                } else if (currentChar == '+') {
                    advance();
                    return new Token(Type.PLUS);
                } else if (currentChar == '-') {
                    advance();
                    return new Token(Type.MINUS);
                } else if (currentChar == '*') {
                    advance();
                    return new Token(Type.MUL);
                } else if (currentChar == '/') {
                    advance();
                    return new Token(Type.DIV);
                } else if (currentChar == '(') {
                    advance();
                    return new Token(Type.LPAREN);
                } else if (currentChar == ')') {
                    advance();
                    return new Token(Type.RPAREN);
                } 
            }
            return new Token(Type.EOF);
        }
    }
    
    class Parser {
        Lexer lex;
        Token currentToken;
        
        public Parser(Lexer lex) {
            this.lex = lex;
            currentToken = lex.getNextToken();
        }
        
        private void eat(Type type) {
            if (currentToken.type != type) {
                System.out.println("Parser Error: invalid type");
            }
            currentToken = lex.getNextToken();
        }
        
        // expr -> term ((PLUS|MINUS) term)*
        int expr() {
            int result = term();  
            while (currentToken.type == Type.PLUS || currentToken.type == Type.MINUS) {
                Token token = currentToken;
                if (token.type == Type.PLUS) {
                    eat(Type.PLUS);
                    result += term();
                } else if (token.type == Type.MINUS) {
                    eat(Type.MINUS);
                    result -= term();
                }
            }
            return result;
        }
        // term -> factor ((MUL|DIV) factor)*
        int term() {
            int result = factor();  
            while (currentToken.type == Type.MUL || currentToken.type == Type.DIV) {
                Token token = currentToken;
                if (token.type == Type.MUL) {
                    eat(Type.MUL);
                    result *= factor();
                } else if (token.type == Type.DIV) {
                    eat(Type.DIV);
                    result /= factor();
                }
            }
            return result;
        }
        // factor -> INTEGER | (PLUS|MINUS) factor | LPAREN expr RPAREN
        int factor() {
            Token token = currentToken;
            if (token.type == Type.INTEGER) {
                eat(Type.INTEGER);
                return token.value;
            } else if (token.type == Type.LPAREN) {
                eat(Type.LPAREN);
                int result = expr();
                eat(Type.RPAREN);
                return result;
            } else if  (token.type == Type.MINUS) {
                eat(Type.MINUS);
                return -1*factor();
            } else if  (token.type == Type.PLUS) {
                eat(Type.PLUS);
                return factor();
            }
            return -1;
        }
    }
    
    public int calculate(String s) {
        Lexer lex = new Lexer(s);
        Parser parser = new Parser(lex);
        return parser.expr();
    }
}