Algorithm Notes
  • Introduction
  • Search & Backtracking 搜索与回溯
    • Tree 与 BackTracking 的比较
    • Subsets, Combination 与 Permutation
    • Subsets & Combinations & Combination Sum
    • 枚举法
    • N 皇后 + 矩阵 Index Trick
    • Sudoku 数独 + 矩阵 Index Trick
    • Word Ladder I & II
    • Number of ways 类
    • DFS flood filling
    • Strobogrammatic 数生成
    • String 构造式 DFS + Backtracking
    • Word Pattern I & II
    • (G) Binary Watch
    • (FB) Phone Letter Combination
    • 常见搜索问题的迭代解法
  • String,字符串类
    • 多步翻转法
    • Substring 结构和遍历
    • Palindrome 问题
    • Palindrome Continued
    • String / LinkedList 大数运算
    • 序列化与压缩
    • 5/24 String 杂题
    • Knuth–Morris–Pratt 字符串匹配
    • Lempel–Ziv–Welch 字符串压缩算法
    • (G) Decode String
    • (G) UTF-8 Validation
  • Binary Tree,二叉树
    • 各种 Binary Tree 定义
    • LCA 类问题
    • 三序遍历,vertical order
    • Post order traversal 的应用
    • Min/Max/Balanced Depth
    • BST
    • 子树结构
    • Level Order traversal
    • Morris 遍历
    • 修改结构
    • 创建 / 序列化
    • 子树组合,BST query
    • 路径与路径和
    • NestedInteger 类
    • (FB) 从 Binary Tree Path 看如何递归转迭代
    • (FB) Binary Tree Path 比较路径大小
    • 比较好玩的 Binary Tree 概率题
  • Segment & Fenwick Tree,区间树
    • Segment Tree 基础操作
    • Segment Tree 的应用
    • Fenwick Tree (Binary Indexed Tree)
    • Range Sum Query 2D - Immutable
  • Union-Find,并查集
    • Union-Find,并查集基础
    • Union-Find, 并查集应用
  • Dynamic Programming, 动态规划
    • 6/20, 入门 House Robber
    • 7/12, Paint Fence / House
    • 6/24, 滚动数组
    • 6/24, 记忆化搜索
    • 6/24, 博弈类 DP
    • 博弈类DP, Flip Game
    • 6/25, 区间类DP
    • 6/27, subarray 划分类,股票
    • 7/2, 字符串类
    • Bomb Enemies
    • 8/2,背包问题
    • (G) Max Vacation
    • (11/4新增) AST 子树结构 DP
  • LinkedList,链表
    • 6/9, LinkedList,反转与删除
    • 6/11, LinkedList 杂题
    • (FB) 链表的递归与倒序打印
  • LinkedIn 面经,算法题
    • 6/17, LinkedIn 面经题
    • 6/28, LinkedIn 面经题
    • 7/6, LinkedIn 面经
    • Shortest Word Distance 类
    • DFA Parse Integer
  • Two Pointers,双指针
    • 3 Sum, 3 Sum Closest / Smaller, 4 Sum
    • 对撞型,灌水类
    • 对撞型,partition类
    • Wiggle Sort I & II
    • 双指针,窗口类
    • 双指针,窗口类
    • Heap,排序 matrix 中的 two pointers
  • Bit & Math,位运算与数学
    • Bit Manipulation,对于 '1' 位的操作
    • Math & Bit Manipulation, Power of X
    • 坐标系 & 数值计算类
    • Add Digits
    • 用 int 做字符串 signature
  • Interval 与 扫描线
    • Range Addition & LCS
    • 7/5, Interval 类,扫描线
  • Trie,字典树
    • 6/9, Trie, 字典树
  • 单调栈,LIS
    • 4/13 LIS
    • 栈, 单调栈
    • Largest Divisible Subset
  • Binary Search 类
    • Matrix Binary Search
    • Array Binary Search
    • Find Peak Element I & II
    • **Median of Two Sorted Arrays
  • Graph & Topological Sort,图 & 拓扑排序
    • 有向 / 无向 图的基本性质和操作
    • 拓扑排序, DFS 做法
    • 拓扑排序, BFS 做法
    • Course Schedule I & II
    • Alien Dictionary
    • Undirected Graph, BFS
    • Undirected Graph, DFS
    • 矩阵,BFS 最短距离探索
    • 欧拉回路,Hierholzer算法
    • AI, 迷宫生成
    • AI, 迷宫寻路算法
    • (G) Deep Copy 无向图成有向图
  • 括号与数学表达式的计算
  • Iterator 类
  • Majority Element,Moore's Voting
  • Matrix Inplace Operations
  • 常见数据结构设计
  • (G) Design / OOD 类算法题
  • 随机算法 & 数据结构
  • (FB) I/O Buffer
  • (FB) Simplify Path, H-Index I & II
  • (FB) Excel Sheet, Remove Duplicates
  • Integer 的构造,操作,序列化
  • Frequency 类问题
  • Missing Number 类,元素交换,数组环形跳转
  • 8/10, Google Tag
  • (FB) Rearrange String k Distance Apart
  • Abstract Algebra
    • Chap1 -- Why Abstract Algebra ?
    • Chap2 -- Operations
    • Chap3 -- The Definition of Groups
Powered by GitBook
On this page
  • Find Leaves of Binary Tree
  • 在 dfs 中直接 root = null 是无法删除 root 的.
  • 因为对于 object, java 是 pass by value, 传递过去的是 object reference.
  • 在这个帖子中举例:
  • 换句话讲,dfs 中作为参数的 TreeNode root 是一个新建的 reference,默认指向的是原参数 root 的同一个位置。因此我们可以对它指向的 object 进行各种操作.
  • 但是当我们更改这个 reference 指向时,我们只是改了函数自己的那个 copy 所指向的 object,而对原始参数指向的东西没有任何改变。
  • 于是这题如果试图在 dfs 中用 root = null 删除节点,实际上我们不会删除任何节点,只会更改在自己这层 call 中, root 指向的节点而已。因此我们之前可以用其他 method call 在 dfs 中对各种 list, set, map 和 collection 里面的值进行修改,但是改 method call 里面的 reference 并不会对原 object 造成任何影响。
  • Find the Celebrity
  • Product of Array Except Self
  • Factor Combinations
  • 简单说就是,这题的 subproblem 之间,不具有 optimal substructure.
  • 这题的 dfs 里 base case 还不太一样,我们做的是试图直接把 n 加进去作为一个解,同时要注意不能直接 return ,否则后面的解就都看不到了。
  • 真正的 return ,是在对于 n 来讲从大到小连 i = 2 都尝试过作为 factor 之后自然结束搜索。
  • Repeated DNA Sequences
  • 去地里看了一圈,这题更有意思的解法是利用 DNA 只有四种字母的性质去做 encode / decode.
  • Evaluate Reverse Polish Notation

Was this helpful?

  1. LinkedIn 面经,算法题

7/6, LinkedIn 面经

Previous6/28, LinkedIn 面经题NextShortest Word Distance 类

Last updated 4 years ago

Was this helpful?

这题本身不难,但是有一个常见错误:

在 dfs 中直接 root = null 是无法删除 root 的.

因为对于 object, java 是 pass by value, 传递过去的是 object reference.

在中举例:

换句话讲,dfs 中作为参数的 TreeNode root 是一个新建的 reference,默认指向的是原参数 root 的同一个位置。因此我们可以对它指向的 object 进行各种操作.

但是当我们更改这个 reference 指向时,我们只是改了函数自己的那个 copy 所指向的 object,而对原始参数指向的东西没有任何改变。

于是这题如果试图在 dfs 中用 root = null 删除节点,实际上我们不会删除任何节点,只会更改在自己这层 call 中, root 指向的节点而已。因此我们之前可以用其他 method call 在 dfs 中对各种 list, set, map 和 collection 里面的值进行修改,但是改 method call 里面的 reference 并不会对原 object 造成任何影响。

public class Solution {
    public List<List<Integer>> findLeaves(TreeNode root) {
        List<List<Integer>> rst = new ArrayList<List<Integer>>();

        while(root != null) {
            List<Integer> list = new ArrayList<Integer>();
            if(dfs(list, root)) root = null;
            rst.add(list);
        }

        return rst;
    }

    private boolean dfs(List<Integer> list, TreeNode root){
        if(root == null) return false;
        if(root.left == null && root.right == null){
            list.add(root.val);
            return true;
        }

        if(dfs(list, root.left)) root.left = null;
        if(dfs(list, root.right)) root.right = null;

        return false;
    }
}

对于两个人 A 与 B, 有如下可能:

knows(A, B)

  • A --> B ( A knows B )

    • A 一定不是明星

  • A <-- B ( B knows A)

    • B 一定不是明星

  • A ... B ( 互相不认识 )

    • B 一定不是明星

因为给定条件“所有人都认识明星,而明星不认识任何人”,对于任意一对 A 和 B,我们仅仅做一次 knows 比较就可以排除掉一个人,我们就可以存一个 ptr 依次扫描整个数组。

当最后只剩下一个可能性的时候,我们依然有必要检查一下,因为我们不确定这个 cur 是不是认识数组里的其他人,或者是不是其他人都认识 cur. 因此再一次循环进行判断 + 记录认识 cur 的人数就可以了。

public class Solution extends Relation {
    public int findCelebrity(int n) {
        int cur = 0;
        for(int i = 1; i < n; i++){
            if(knows(cur, i)) cur = i;
        }

        int knowCount = 0;
        for(int i = 0; i < n; i++){
            if(i == cur) continue;

            if(knows(cur, i)) return -1;
            else if(knows(i, cur)) knowCount ++;
        }

        return knowCount == n - 1 ? cur: -1;
    }
}

老题了,前缀积数组的应用。

public class Solution {
    public int[] productExceptSelf(int[] nums) {
        int N = nums.length;

        int[] leftCumProd = new int[N + 1];
        int[] rightCumProd = new int[N + 1];

        leftCumProd[0] = 1;
        rightCumProd[N] = 1;

        for(int i = 0; i < N; i++){
            leftCumProd[i + 1] = leftCumProd[i] * nums[i];
        }
        for(int i = N - 1; i >= 0; i--){
            rightCumProd[i] = rightCumProd[i + 1] * nums[i];
        }

        int[] rst = new int[N];
        for(int i = 0; i < N; i++){
            rst[i] = leftCumProd[i] * rightCumProd[i + 1];
        }

        return rst;
    }
}

Word Search II 之后留下了后遗症,看到这种问题也想用记忆化搜索加快速度。

不过这题的 subproblem 不太一样。 因为当最开始的 n 真的等于质数时,我们返回 empty list; 但是如果这是个 dfs 嵌套的 method call,我们却要把这个质数加到 list 里。 换句话说,同样 size / interval 的 subproblem, 返回的结果却要依情况而定,这是违反了记忆化搜索和动态规划性质的。

简单说就是,这题的 subproblem 之间,不具有 optimal substructure.

dfs(12) 并不等于 2 + dfs(6) 和 3 + dfs(4) 与 4 + dfs(3) ..

于是我们仔细观察 sample output 中,n = 32 的正解,重新排列顺序之后得到:

n = 32

  • [16,2]

  • [8, 4]

  • [8, 2, 2]

  • [4, 4, 2]

  • [4, 2, 2, 2]

  • [2, 2, 2, 2, 2]

我们可以发现把解按照递减顺序排列之后,这是一个类似 combination 的问题,因为:

  • 每一个解是若干个元素的组合,解与解之间大小不等

  • 同一组解之间,元素的选取有单调性 (去重)

具体实现上,为了保证不盲目把初始 n 加到结果中,第一层 dfs 里传进去的 prev 是 n - 1.

这题的 dfs 里 base case 还不太一样,我们做的是试图直接把 n 加进去作为一个解,同时要注意不能直接 return ,否则后面的解就都看不到了。

真正的 return ,是在对于 n 来讲从大到小连 i = 2 都尝试过作为 factor 之后自然结束搜索。

public class Solution {
    public List<List<Integer>> getFactors(int n) {
        List<List<Integer>> rst = new ArrayList<List<Integer>>();

        dfs(rst, new ArrayList<Integer>(), n, n - 1);

        return rst;
    }

    private void dfs(List<List<Integer>> rst, 
                     List<Integer> list, int n, int prev){
        if(n <= prev){
            list.add(n);
            rst.add(new ArrayList<Integer>(list));
            list.remove(list.size() - 1);
            //return ;
        }

        for(int i = n / 2; i >= 2; i--){
            if(n % i == 0 && i <= prev){
                list.add(i);
                dfs(rst, list, n / i, i);
                list.remove(list.size() - 1);
            }
        }
    }
}

我想了半天怎么用 KMP 做这题复杂度比较低,看到 tag 里有 hashtable 就直接用 hashmap 套用了一下,然后不但 AC 了还在运行速度上超过了 76% 的人。。。这题的用意到底是什么。。。

唯一需要注意的是对于出现超过 2 次的 str 不要重复添加,因此 hashmap 里留一个 count ,根据 count 行事。

public class Solution {
    public List<String> findRepeatedDnaSequences(String s) {
        HashMap<String, Integer> map = new HashMap<>();
        List<String> list = new ArrayList<>();
        int N = s.length();
        for(int i = 0; i <= N - 10; i++){
            String str = s.substring(i, i + 10);
            if(map.containsKey(str)){
                int count = map.get(str);
                if(count == 1) list.add(str);
                map.put(str, count + 1);
            } else {
                map.put(str, 1);
            }
        }

        return list;
    }
}

去地里看了一圈,这题更有意思的解法是利用 DNA 只有四种字母的性质去做 encode / decode.

因为字母只有四种可能,我们可以用 2 bits 表示任意字母;同时因为 sequence 长度为 10,所以我们只需要 20 bits 就可以 Uniquely represent 一个 sequence,即自己实现无 collision 的 hashing. 简便起见,一个 32-bit int 就够了。

encode 时,对于每一个给定的 substring,遍历每个字母,然后根据字母决定其数字,给最后两位赋值之后 << 2.

decode 时,每次把当前数字除以 4 看余数,根据余数决定字母,而后 >> 2.

这样对于每一个 string / int ,其 encode / decode 都是唯一的。

空间占用为 2^20 = 1048576 个 int = 4.194304 MB ,代表可能的 hash 值数量。

public class Solution {
    public List<String> findRepeatedDnaSequences(String s) {
        List<String> list = new ArrayList<>();
        int N = s.length();
        int[] hash = new int[1048576];
        for(int i = 0; i <= N - 10; i++){
            String str = s.substring(i, i + 10);
            int index = encode(str);
            hash[index] ++;
            if(hash[index] == 2) list.add(str);
        }

        return list;
    }

    private int encode(String s){
        int num = 0;
        for(int i = 0; i < s.length(); i++){
            char c = s.charAt(i);
            num = num << 2;
            if(c == 'A') num += 0; 
            if(c == 'C') num += 1;
            if(c == 'G') num += 2;
            if(c == 'T') num += 3;
        }
        return num;
    }

    private String decode(int num){
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < 10; i++){
            if(num % 4 == 0) sb.append('A');
            if(num % 4 == 1) sb.append('C');
            if(num % 4 == 2) sb.append('G');
            if(num % 4 == 3) sb.append('T');

            num = num >> 2;
        }
        return sb.reverse().toString();
    }
}

轻松愉快,一次 AC.

public class Solution {
    public int evalRPN(String[] tokens) {
        Stack<Integer> stack = new Stack<>();
        for(String str : tokens){
            if(!isOperator(str)){
                stack.push(Integer.parseInt(str));
            } else {
                int num2 = stack.pop();
                int num1 = stack.pop();

                if(str.equals("+")) stack.push(num1 + num2);
                if(str.equals("-")) stack.push(num1 - num2);
                if(str.equals("*")) stack.push(num1 * num2);
                if(str.equals("/")) stack.push(num1 / num2);
            }
        }

        return stack.peek();
    }

    private boolean isOperator(String str){
        if(str.equals("+") || str.equals("-")) return true;
        if(str.equals("/") || str.equals("*")) return true;
        return false;
    }
}

Find the Celebrity
Product of Array Except Self
Factor Combinations
Repeated DNA Sequences
Evaluate Reverse Polish Notation
Find Leaves of Binary Tree
这个帖子