易贝

迎难而上,祝我好运。面朝大海,春暖花开。

k way merge

  • LC23。给K个排好序的链表头节点数组,将他们merge到一个list中。
  • 方法一:利用PriorityQueue存储,先一波流将所有链表头加入pq,然后每次poll掉最小的同时将它后续的节点也放进pq中。pq的offer, poll, remove复杂度都是log(N),因此在这里维护规模为K的就需要log(K),而总共有KN个节点,时间复杂度是O(KNlogK)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public ListNode mergeKLists(ListNode[] lists) {
    if (lists == null || lists.length == 0) {
    return null;
    }
    PriorityQueue<ListNode> pq = new PriorityQueue<>((a, b) -> {
    return a.val - b.val;
    });
    for (ListNode l: lists) {
    if (l != null)
    pq.add(l);
    }
    ListNode fakeHead = new ListNode(0), curr = fakeHead;
    while (!pq.isEmpty()) {
    curr.next = pq.poll();
    curr = curr.next;
    if (curr.next != null)
    pq.add(curr.next);
    }
    return fakeHead.next;
    }
  • 方法二:递归+分治法,归结为merge两个List的问题。时间复杂度是O(KNlogK),因为一开始K个数组,需要遍历每个元素合并之后形成K/2个数组,以此类推,每次需要遍历需要O(KN),总共有logK次,因此是O(KNlogK)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    // divide and conquer
    public ListNode mergeKLists(ListNode[] lists) {
    if (lists == null || lists.length == 0) {
    return null;
    }
    // last two params are used to divide into sub problems
    return mergeKLists(lists, 0, lists.length);
    }
    private ListNode mergeKLists(ListNode[] lists, int start, int end) {
    if (end - start == 1) { // only one list
    return lists[start];
    } else if (end - start == 2) { // merge two lists
    return mergeTwoLists(lists[start], lists[start + 1]);
    } else {
    int mid = start + (end - start) / 2;
    // cut into first and second halves
    return mergeTwoLists(mergeKLists(lists, start, mid),
    mergeKLists(lists, mid, end)); // warning not mid + 1
    }
    }
    private ListNode mergeTwoLists(ListNode l1, ListNode l2) {
    if (l1 == null) {
    return l2;
    }
    if (l2 == null) {
    return l1;
    }
    if (l1.val <= l2.val) {
    l1.next = mergeTwoLists(l1.next, l2);
    return l1;
    } else {
    l2.next = mergeTwoLists(l1, l2.next);
    return l2;
    }
    }

longest common substring

  • 给两个String,求最长的连续的共同的子串。例如s1 = 12358和s2 = 1242358,最长的子序列就是2358.
  • DP.用二维int数组维护dp[i][j]表示s1以i结尾处、s2以j结尾处的substring情况。当s1.charAt(i) == s2.charAt(j)时,就更新为dp[i - 1][j - 1] + 1
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public static String maxCommonSubstring(String s1, String s2) {
    if (s1 == null || s1.length() == 0 || s2 == null || s2.length() == 0) {
    return "";
    }
    int len1 = s1.length(), len2 = s2.length();
    int[][] dp = new int[len1][len2];
    int maxLen = 0, index = -1;
    for (int i = 0; i < len1; i++) {
    for (int j = 0; j < len2; j++) {
    if (s1.charAt(i) == s2.charAt(j)) {
    dp[i][j] = i > 0 && j > 0 ? dp[i - 1][j - 1] + 1 : 1;
    if (maxLen < dp[i][j]) {
    maxLen = dp[i][j];
    index = i;
    }
    }
    }
    }
    return index < 0 ? "" : s1.substring(index + 1 - maxLen, index + 1);
    }

reverse word in string

  • 给一个字符串和一个分隔符,将每个单词翻转,同时大小写翻转。注意这个Zillow的那个翻转字符串不一样。skip.

decimal to hex

  • 将一个十进制数转换成十六进制。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    final private char[] map = {'0', '1', '2', '3','4','5','6','7','8','9','a','b','c','d','e','f'};
    public String toHex(int num) {
    if (num == 0) {
    return "0";
    }
    long longNum = num & 0x00000000ffffffffL; // 不能直接强制转换,不然会保留符号
    String hexStr = "";
    while (longNum != 0) {
    // System.out.println();
    hexStr = map[(int)(longNum % 16)] + hexStr;
    longNum /= 16;
    }
    return hexStr;
    }

最大正方形

  • 类似LC 221,给一个0/1 grid,求其中最大的正方形面积。
  • DP。当前位置为1,则需要看看左、左上、上三个方向的最小值+1作为边长,最后返回最大的即可。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public int maximalSquare(char[][] matrix) {
    if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
    return 0;
    }
    int rowTotal = matrix.length, colTotal = matrix[0].length;
    int max = 0;
    int[][] dp = new int[rowTotal][colTotal];
    for (int i = 0; i < rowTotal; i++) {
    for (int j = 0; j < colTotal; j++) {
    if (matrix[i][j] == '1') {
    dp[i][j] = getMin(dp, i, j) + 1;
    max = Math.max(max, dp[i][j]);
    }
    }
    }
    return max * max;
    }
    private int getMin(int[][] dp, int i, int j) {
    int up = i > 0 ? dp[i - 1][j] : 0;
    int left = j > 0 ? dp[i][j - 1] : 0;
    int upLeft = i > 0 && j > 0 ? dp[i - 1][j - 1] : 0;
    return Math.min(Math.min(up, left), upLeft);
    }

第N个BLAH数

  • 如果一个数字可以被5,7,11整除 那么这个数称之为BLAH数 (5,7,10,11,14,15…). 求第n个BLAH数。
  • 用三个变量存下一个5、7、11整除的blah数,从1到n每次取最小的,并更新到下一个。注意出现重复的时候就需要跳到下一个。

移动0到数组末尾

  • 给一个数组,将所有的0移动到数组末尾,其余数组需要维持相对次序。
  • 先走一波循环讲非0的项往前覆盖,走完之后剩余的数直接覆盖为0即可。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    public void moveZeroes(int[] nums) {
    int k = 0;
    for (int i = 0; i < nums.length; ++i){
    if (nums[i] != 0) nums[k++] = nums[i];
    }
    for(; k < nums.length; ++k){
    nums[k] = 0;
    }
    }

反转链表

  • 给一个链表头,反转之。LC 206
  • 方法一:Iterative就直接前后两个节点直接把next指向对调一下就好了。
  • 方法二:Recursive就先将当前节点后面的节点都reverse一波,当前节点的next原本指的是后续第一个节点、现在就变成了后续最末尾的节点,那么调整一下next指向即可。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    public ListNode reverseList(ListNode head) {
    if (head == null || head.next == null) {
    return head;
    }
    ListNode newHead = reverseList(head.next);
    head.next.next = head;
    head.next = null;
    return newHead;
    }

找第一次、最后一次出现位置

  • LC 34给一个有序数组,给一个target,找第一个和最后一个出现的位置。复习一波二分查找了。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    class Solution {
    public int[] searchRange(int[] nums, int target) {
    if (nums == null || nums.length == 0) {
    return new int [] {-1, -1};
    }
    int first = searchFirst(nums, target);
    if (first == -1) {
    return new int [] {-1, -1};
    }
    int last = searchLast(nums, target);
    return new int[] {first, last};
    }
    private int searchFirst(int[] nums, int target) {
    // hope to biase to the front
    // invariant relation: A[start] < target <= A[end]
    int start = -1, end = nums.length;
    while (end - start > 1) {
    int mid = start + (end - start) / 2;
    if (nums[mid] >= target) { // end need to be more aggresive
    end = mid; // shrink down
    } else {
    start = mid; // step up
    }
    }
    return end == nums.length || nums[end] != target? -1: end; // end will proceed at front
    }
    private int searchLast(int[] nums, int target) {
    // biase to the back
    // invariant relation: A[start] <= target < A[end]
    int start = -1, end = nums.length;
    while (end - start > 1) {
    int mid = start + (end - start) / 2;
    if (nums[mid] > target) {
    end = mid; // shrink down
    } else { // start need to be more aggressive
    start = mid; // step up
    }
    }
    return start;
    }
    }

二维矩阵二分查找

  • LC 240。矩阵中每一行、每一列都是有序的,给一个target,判断是否存在。
  • 方法一:仍然是二分查找的思想,取行和列的中间元素,若target小了则需要到左侧细长竖矩形和上方的肥扁矩形查找,否则是到右侧的细长竖矩形和下方的肥扁矩形中查找。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
    if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
    return false;
    }
    return binarySearch(matrix, target, -1, -1, matrix.length, matrix[0].length);
    }
    private boolean binarySearch(int[][] matrix, int target,
    int lowRow, int lowCol, int hiRow, int hiCol) {
    if (hiCol - lowCol <= 2 || hiRow - lowRow <= 2) {
    return false;
    }
    int midCol = lowCol + (hiCol - lowCol) / 2;
    int midRow = lowRow + (hiRow - lowRow) / 2;
    if (target == matrix[midRow][midCol]) {
    return true;
    }
    if (target < matrix[midRow][midCol]) {
    if (binarySearch(matrix, target, lowRow, lowCol, midRow + 1, hiCol)) {
    // (lowRow, lowCol) to (midRow + 1, hiCol), exclusive
    return true;
    } else if (binarySearch(matrix, target, lowRow, lowCol, hiRow, midCol)) {
    // (lowRow, lowCol) to (hiRow, midCol)
    return true;
    } else {
    return false;
    }
    } else {
    if (binarySearch(matrix, target, midRow - 1, lowCol, hiRow, hiCol)) {
    // (midRow - 1, lowCol) to (hiRow, hiCol), exclusive
    return true;
    } else if (binarySearch(matrix, target, lowRow, midCol, hiRow, hiCol)) {
    // (lowRow, lowCol) to (hiRow, midCol)
    return true;
    } else {
    return false;
    }
    }
    }
    }
  • 方法二:直接扫,首先从右上角开始,如果小了说明当前这一行都不可能了,如果大了说明当前这列都不可能了,因此每次都会排除掉一整行/一整列,时间复杂度是O(M + N),相当于线性查找。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public boolean searchMatrix(int[][] matrix, int target) {
    if (matrix == null || matrix.length ==0 || matrix[0].length == 0) {
    return false;
    }
    int col = matrix[0].length - 1;
    int row = 0;
    while (col >= 0 && row <= matrix.length - 1) {
    if (target == matrix[row][col]) {
    return true;
    } else if (target < matrix[row][col]) {
    col--;
    } else if (target > matrix[row][col]) {
    row++;
    }
    }
    return false;
    }

实现LRU

  • LC 146. 实现Cache就是用Map,但是least recent特性就需要额外的数据结构来提升效率:双向链表。Map中存放key - Node映射,所有的cache都放在一个双向链表上,每次get的时候就将get到的node挪到第一位。put时若capacity超了就将链表最后一个节点删除即可。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    class LRUCache {
    class Node {
    int key;
    int val;
    Node prev;
    Node next;
    public Node(int key, int val) {
    this.key = key;
    this.val = val;
    prev = null;
    next = null;
    }
    }
    Map<Integer, Node> map;
    Node fakeHead, fakeTail;
    int capacity;
    public LRUCache(int capacity) {
    this.capacity = capacity;
    map = new HashMap<>();
    fakeHead = new Node(-1, -1);
    fakeTail = new Node(-1, -1);
    fakeHead.next = fakeTail;
    fakeTail.prev = fakeHead;
    }
    public int get(int key) {
    if (map.containsKey(key)) {
    Node ret = map.get(key);
    moveToFirst(ret);
    return ret.val;
    } else {
    return -1;
    }
    }
    public void put(int key, int value) {
    if (capacity == 0) {
    return;
    }
    if (map.containsKey(key)) {
    Node oldNode = map.get(key);
    oldNode.val = value;
    moveToFirst(oldNode);
    return;
    }
    if (map.size() == capacity) {
    map.remove(fakeTail.prev.key);
    remove(fakeTail.prev);
    }
    Node newNode = new Node(key, value);
    insertToFirst(newNode);
    map.put(key, newNode);
    }
    private void moveToFirst(Node node) {
    remove(node);
    insertToFirst(node);
    }
    private void insertToFirst(Node node) {
    node.next = fakeHead.next;
    fakeHead.next = node;
    node.prev = fakeHead;
    node.next.prev = node;
    }
    private void remove(Node node) {
    if (node.prev == null) {
    return;
    }
    node.prev.next = node.next;
    node.next.prev = node.prev;
    }
    }

2018.3.23 Onsite

15. 3Sum
  • 给一个int数组,列出三个数使得它们的和为0.
  • 先排序,然后固定当前数,设置target为0 - curr,然后用双指针在后方找,当left + right == target就是一组数了。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
    List<List<Integer>> ans = new ArrayList<>();
    if (nums == null || nums.length == 0) {
    return ans;
    }
    Arrays.sort(nums); // sort from least to largest
    for (int i = 0; i < nums.length; i++) {
    if (i > 0 && nums[i] == nums[i - 1]) {
    continue;
    }
    // fix nums[i] and let two pointers get target's indices
    int left = i + 1, right = nums.length - 1, target = 0 - nums[i];
    while (left < right) {
    int sum = nums[left] + nums[right];
    if (sum == target) {
    List<Integer> currList = new ArrayList<>();
    currList.add(nums[i]);
    currList.add(nums[left]);
    currList.add(nums[right]);
    ans.add(currList);
    do {
    left++; // for duplicate
    } while (left < right && nums[left] == nums[left - 1]);
    do {
    right--;
    } while (right > left && nums[right] == nums[right + 1]);
    } else if (sum < target) {
    left++;
    } else {
    right--;
    }
    }
    }
    return ans;
    }
    }
216. combination sum
  • 在1~9中选k个数之和等于n,求所有可能的组合。DFS/backtracking搞定。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    class Solution {
    public List<List<Integer>> combinationSum3(int k, int n) {
    List<List<Integer>> ans = new ArrayList<>();
    dfs(k, n, 1, ans, new ArrayList<>());
    return ans;
    }
    private void dfs(int k, int n, int start, List<List<Integer>> ans, List<Integer> curr) {
    if (n == 0 && curr.size() == k) {
    ans.add(new ArrayList<Integer>(curr));
    return;
    }
    for (int i = start; i <= 9; i++) {
    curr.add(i);
    dfs(k, n - i, i + 1, ans, curr);
    curr.remove(curr.size() - 1);
    }
    }
    }
535. tinyURL
  • 实现长URL和短URL的互相转换。
  • 在生成短链接时,先利用随机数生成index逐个字符地拼接成短链接,然后判断是否存在。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    public class Codec {
    private static final String HOST = "http://tinyurl.com/";
    private static final String CHAR = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static final int LIMIT = CHAR.length();
    Map<String, String> url2tiny = new HashMap<>();
    Map<String, String> tiny2url = new HashMap<>();
    // Encodes a URL to a shortened URL.
    public String encode(String longUrl) {
    if (url2tiny.containsKey(longUrl)) {
    return HOST + url2tiny.get(longUrl);
    }
    String ret = null;
    do {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < 6; i++) {
    int index = (int) (Math.random() * LIMIT);
    sb.append(CHAR.charAt(index));
    }
    ret = sb.toString();
    } while (tiny2url.containsKey(ret));
    url2tiny.put(longUrl, ret);
    tiny2url.put(ret, longUrl);
    return HOST + ret;
    }
    // Decodes a shortened URL to its original URL.
    public String decode(String shortUrl) {
    return tiny2url.get(shortUrl.replace(HOST, ""));
    }
    }
4. Median of 2 sorted arrays
  • 给两个int数组,返回二者合并后的中位数,要求在O(log(m+n))的时间复杂度以内。
  • 上来可以先讲讲mergesort的那种线性扫到中位数的O(m + n)做法。二分查找找的是能将两个数组恰好分成两个部分的index,而且确定了第一个数组的index之后,第二个数组的index也就确定了(因为每个部分取多少数是已知的)。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
    if (nums1 == null || nums2 == null) {
    return 0;
    }
    int m = nums1.length, n = nums2.length;
    if (nums1.length > nums2.length) { // ensure the len of 1 <= 2
    return findMedianSortedArrays(nums2, nums1);
    }
    // to ensure equality of the two parts after merged, i + j = m - i + n - j
    int iLo = 0, iHi = m, allMid = (n + m + 1) / 2; // but why (n + m + 1)/2?
    // i stands for "how many num taken from nums1 as front part" 0 ~ i-1 | i ~ m-1
    // j stands for "how many num taken from nums2 as front part" 0 ~ j-1 | j ~ n-1
    while (iLo <= iHi) {
    int i = (iLo + iHi) / 2, j = allMid - i;
    // nums1[i-1], nums2[j-1] are the largest element of front part of nums1, nums2
    // nums1[i], nums2[j] are the smallest of lag part of nums1, nums2
    if (i < m && nums2[j - 1] > nums1[i]) { // i not big enough
    iLo = i + 1;
    } else if (i > 0 && nums1[i - 1] > nums2[j]) {
    iHi = i - 1;
    } else {
    int maxLeft = 0, minRight = 0;
    if (i == 0) {
    maxLeft = nums2[j - 1];
    } else if (j == 0) {
    maxLeft = nums1[i - 1];
    } else {
    maxLeft = Math.max(nums1[i - 1], nums2[j - 1]);
    }
    if ((m + n) % 2 == 1) { // I think thats why to make (allMid = (n + m + 1)/2)
    return maxLeft; // -- to make left part always at least no fewer than right
    }
    if (i == m) {
    minRight = nums2[j];
    } else if (j == n) {
    minRight = nums1[i];
    } else {
    minRight = Math.min(nums1[i], nums2[j]);
    }
    return (maxLeft + minRight) / 2.0;
    }
    }
    return 0;
    }
    }
572. Subtree of Another Tree
  • 给两个二叉树,判断其中一棵是否说另一个的子树(存在val、结构完全相同的子树)。
  • naive的方法是无脑recursive,但是这样每个节点可能会被重复访问。另一种做法是用preorder将两个树encode成字符串,然后用contains判断是否是substring即可。但注意这个contains本身并不是O(N)的,可以提一句KMP算法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public class Solution {
    public boolean isSubtree(TreeNode s, TreeNode t) {
    StringBuilder spre = new StringBuilder();
    StringBuilder tpre = new StringBuilder();
    preOrder(s, spre.append(","));
    preOrder(t, tpre.append(","));
    return spre.toString().contains(tpre.toString());
    }
    public void preOrder(TreeNode root, StringBuilder str){
    if(root == null){
    str.append("#,");
    return;
    }
    str.append(root.val).append(",");
    preOrder(root.left, str);
    preOrder(root.right, str);
    }
    }
130. surrounded-regions
  • 给一个只含有XXOO的char二维数组,要求把所有被X完美包围的O替换成X,而绵延到边界的O就不替换。
  • 先将四个边边遍历一波,将所有的O以及连绵的O替换成特殊字符,然后再从头遍历,将特殊字符换回O、将中间的O换成X。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    public class Solution {
    private class Point {
    int x;
    int y;
    Point(int x, int y) {
    this.x = x;
    this.y = y;
    }
    }
    public void solve(char[][] board) {
    if (board == null || board.length == 0 || board[0].length == 0) {
    return;
    }
    int m = board.length, n = board[0].length;
    for (int i = 0; i < n; i++) {
    if (board[0][i] == 'O') {
    bfs(board, 0, i);
    }
    if (board[m-1][i] == 'O') {
    bfs(board, m - 1, i);
    }
    }
    for (int i = 0; i < m; i++) {
    if (board[i][0] == 'O') {
    bfs(board, i, 0);
    }
    if (board[i][n-1] == 'O') {
    bfs(board, i, n - 1);
    }
    }
    for (int i = 0; i < m; i++) {
    for (int j = 0; j < n; j++) {
    if (board[i][j] == 'O') {
    board[i][j] = 'X';
    } else if (board[i][j] == '!') {
    board[i][j] = 'O';
    }
    }
    }
    }
    private void bfs(char[][] board, int x, int y) {
    int m = board.length, n = board[0].length;
    Queue<Point> q = new LinkedList<Point>();
    q.add(new Point(x, y));
    board[x][y] = '!';
    while (!q.isEmpty()) {
    Point curr = q.poll();
    int i = curr.x;
    int j = curr.y;
    if (i > 0 && board[i-1][j] == 'O') { // up
    q.add(new Point(i-1, j));
    board[i-1][j] = '!';
    }
    if (i < m - 1 && board[i+1][j] == 'O') { // down
    q.add(new Point(i+1, j));
    board[i+1][j] = '!';
    }
    if (j > 0 && board[i][j-1] == 'O') { // left
    q.add(new Point(i, j-1));
    board[i][j-1] = '!';
    }
    if (j < n - 1 && board[i][j+1] == 'O') { // left
    q.add(new Point(i, j+1));
    board[i][j+1] = '!';
    }
    }
    }
    }

2016.10.5 Onsite

698. partition-to-k-equal-sum-subsets
  • 给一个数组,判断是否能划分成K个sum相等的子集。
  • 累加当前元素后到达下一层,判断是否达到了targetSum并且确实有元素(防止targetSum == 0且有负数的case)的情况下,开始往后遍历下一个group。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    class Solution {
    public boolean canPartitionKSubsets(int[] nums, int k) {
    if (nums == null || nums.length == 0 || k > nums.length || k < 1) {
    return false;
    }
    if (k == 1) {
    return true;
    }
    int sum = IntStream.of(nums).sum();
    if (sum % k != 0) {
    return false;
    }
    int targetSum = sum / k;
    boolean[] visited = new boolean[nums.length];
    return checkPartition(nums, visited, 0, targetSum, 0, k, 0);
    }
    public boolean checkPartition(int[] nums, boolean[] visited, int startIndex, int targetSum, int currSum, int k, int elementCount) {
    if (k == 0) { // 必须算完才行
    return true;
    }
    if (currSum == targetSum && elementCount > 0) { // 保证每个部分真正有元素存入
    return checkPartition(nums, visited, 0, targetSum, 0, k - 1, 0);
    }
    for (int i = startIndex; i < nums.length; i++) {
    if (!visited[i]) {
    visited[i] = true;
    if (checkPartition(nums, visited, i + 1, targetSum, currSum + nums[i], k, elementCount + 1)) {
    return true;
    }
    visited[i] = false;
    }
    }
    return false;
    }
    }
252. meeting-rooms
  • 给一个Interval的数组,每个Interval含有meeting的开始和结束时间戳,问是否能参加所有会议。
  • 贪心算法,根据start排序,然后取前后两个看有没有overlap。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class Solution {
    public boolean canAttendMeetings(Interval[] intervals) {
    if (intervals == null || intervals.length == 0) {
    return true;
    }
    Arrays.sort(intervals, (a, b) -> a.start - b.start);
    for (int i = 1; i < intervals.length; i++) {
    if (intervals[i].start < intervals[i - 1].end) {
    return false;
    }
    }
    return true;
    }
    }

2018.5.1 电面

97. interleaving-string
  • 给三个字符串s1, s2, s3,判断s3是否由s1和s2拼接而成,这里的拼接可以是交错的,但必须维持字符原本的出现顺序。
  • 方法一:DP。dp[i][j]表示从s1中取i个字符、从s2中取j个字符能否交错组成s3[i + j - 1]。判断时若从s1中取i个字符,即s1[i] == s3[i + j - 1]dp[i - 1][j]能交错形成,或者从s2中取j个字符,即s2[j] == s3[i + j - 1]dp[i][j - 1]能交错,则当前这个也可以,设为true即可。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    public boolean isInterleave(String s1, String s2, String s3) {
    if (s1 == null || s2 == null || s3 == null) {
    return false;
    }
    int len1 = s1.length(), len2 = s2.length(), len3 = s3.length();
    if (len1 + len2 != len3) {
    return false;
    }
    char[] cs1 = s1.toCharArray(), cs2 = s2.toCharArray(), cs3 = s3.toCharArray();
    boolean[][] dp = new boolean [len1 + 1][len2 + 1];
    for (int i = 0; i < dp.length; i++) {
    for (int j = 0; j < dp[0].length; j++) {
    if (i == 0) {
    if (j == 0) {
    dp[i][j] = true;
    } else {
    dp[i][j] = cs2[j - 1] == cs3[i + j - 1] && dp[i][j - 1];
    }
    } else {
    if (j == 0) {
    dp[i][j] = cs1[i - 1] == cs3[i + j - 1] && dp[i - 1][j];
    } else {
    // take char from s1(move i) or from s2(move j)
    dp[i][j] = (dp[i - 1][j] && cs1[i - 1] == cs3[i + j - 1])
    || (dp[i][j - 1] && cs2[j - 1] == cs3[i + j - 1]);
    }
    }
    }
    }
    return dp[len1][len2];
    }
  • 方法二:DFS。也需要利用二维boolean数组标记是否能组成interleaving字符串,若之前已经判断是false了就不用继续DFS了。判断时也是分别尝试从s1中和s2中取字符,然后与s3比较。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public boolean isInterleave(String s1, String s2, String s3) {
    if (s1 == null || s2 == null || s3 == null || s1.length() + s2.length() != s3.length()) {
    return false;
    }
    char[] cs1 = s1.toCharArray(), cs2 = s2.toCharArray(), cs3 = s3.toCharArray();
    boolean[][] invalid = new boolean [cs1.length + 1][cs2.length + 1];
    return dfs(cs1, cs2, cs3, 0, 0, invalid);
    }
    private boolean dfs(char[] cs1, char[] cs2, char[] cs3, int i, int j, boolean[][] invalid) {
    if (invalid[i][j]) {
    return false;
    }
    if (i + j == cs3.length) { // end case: reach the end of s3
    return true;
    }
    if ((i < cs1.length && cs1[i] == cs3[i + j] && dfs(cs1, cs2, cs3, i + 1, j, invalid))
    || (j < cs2.length && cs2[j] == cs3[i + j] && dfs(cs1, cs2, cs3, i, j + 1, invalid))) {
    return true;
    }
    invalid[i][j] = true;
    return false;
    }
  • 方法三:BFS。和DFS类似,将可达的i/j组合存入queue,直到遍历完s3。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    public boolean isInterleave(String s1, String s2, String s3) {
    if (s1 == null || s2 == null || s3 == null || s1.length() + s2.length() != s3.length()) {
    return false;
    }
    char[] cs1 = s1.toCharArray(), cs2 = s2.toCharArray(), cs3 = s3.toCharArray();
    boolean[][] visited = new boolean [cs1.length + 1][cs2.length + 1];
    Queue<int[]> q = new LinkedList<>();
    q.add(new int[] {0, 0});
    while (!q.isEmpty()) {
    int[] curr = q.poll();
    if (visited[curr[0]][curr[1]]) {
    continue;
    }
    if (curr[0] + curr[1] == cs3.length) { // reaches the end of s3
    return true;
    }
    if (curr[0] < cs1.length && cs1[curr[0]] == cs3[curr[0] + curr[1]]) {
    q.add(new int[] {curr[0] + 1, curr[1]});
    }
    if (curr[1] < cs2.length && cs2[curr[1]] == cs3[curr[0] + curr[1]]) {
    q.add(new int[] {curr[0], curr[1] + 1});
    }
    visited[curr[0]][curr[1]] = true;
    }
    return false;
    }

2018.4.6

279. perfect-squares
  • 给一个正整数,求至少由多少个平方数相加能得到它。
  • DP。dp[i]表示数字i至少需要多少个平方数相加,那么对于每个i,遍历i - j * j即可。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public int numSquares(int n) {
    if (n <= 0) {
    return 0;
    }
    int[] dp = new int[n + 1];
    Arrays.fill(dp, Integer.MAX_VALUE);
    dp[0] = 0;
    for (int i = 1; i <= n; i++) {
    for (int j = 1; j * j <= i; j++) {
    dp[i] = Math.min(dp[i], dp[i - j * j] + 1);
    }
    }
    return dp[n];
    }
[???]
  • 给一个BST,给一个区间[min, max],将在区间之外的节点删除。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public TreeNode deleteOutliers(TreeNode root, int min, int max) {
    if (root == null || min > max) {
    return null;
    }
    if (root.val > max) {
    return deleteOutliers(root.left, min, max);
    } else if (root.val < min) {
    return deleteOutliers(root.right, min, max);
    } else {
    root.left = deleteOutliers(root.left, min, root.val - 1);
    root.right = deleteOutliers(root.right, root.val + 1, max);
    return root;
    }
    }

2018.4.6 电面

53. maximum-subarray
  • 给一个int数组,求最大的subarry sum。
  • 方法一:直接O(N)搞定,维护一个当前的sum,要么是前面累加的结果、要么从当前开始加。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class Solution {
    public int maxSubArray(int[] nums) {
    if (nums == null || nums.length == 0) {
    return 0;
    }
    int ans = Integer.MIN_VALUE, prev = 0;
    for (int i = 0; i < nums.length; i++) {
    prev = Math.max(prev + nums[i], nums[i]);
    ans = Math.max(ans, prev);
    }
    return ans;
    }
    }
  • 方法二:分治法,要么纯左边、要么纯右边、要么从交界处向左右取元素。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    class Solution {
    public int maxSubArray(int[] nums) {
    if (nums == null || nums.length == 0) {
    return 0;
    }
    return getMax(nums, 0, nums.length - 1);
    }
    private int getMax(int[] nums, int left, int right) {
    if (left == right) { // end case
    return nums[left];
    }
    int mid = (right + left) / 2;
    int leftMax = getMax(nums, left, mid); // get max subarray only with left elements
    int rightMax = getMax(nums, mid + 1, right); // get max ~ right
    int midMax = getMidMax(nums, left, mid, right); // get max sub that uses both left and right
    return Math.max(leftMax, Math.max(rightMax, midMax));
    }
    private int getMidMax(int[] nums, int left, int mid, int right) {
    int leftMax = Integer.MIN_VALUE, rightMax = Integer.MIN_VALUE;
    int sum = 0;
    for (int i = mid; i >= left; i--) {
    sum += nums[i];
    leftMax = Math.max(sum, leftMax); // at least take one element from left
    }
    sum = 0;
    for (int i = mid + 1; i <= right; i++) {
    sum += nums[i];
    rightMax = Math.max(sum, rightMax); // at least take one element from right
    }
    return leftMax + rightMax;
    }
    }

2018.4.4 Onsite

103. binary-tree-zigzag-level-order-traversal
  • level层面的遍历二叉树,但是遍历顺序不是固定的从左到右,而是蛇形,这一层从左到右、下一行从右到左。
  • 方法一:BFS+Queue,正一下反一下。
  • 方法二:DFS时将level数也传入,对于偶数层则append到对应的List中,对于奇数层则insert到首位。
Longest common subsequence(注意和前面的max substring有异曲同工之妙)
  • 给两个字符串,求最长的公共子序列的长度。
  • DP。用两个指针i和j分别遍历s1和s2,若字符相同则取dp[i - 1][j - 1] + 1,否则取max{dp[i - 1][j], dp[i][j - 1]}.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public int maxCommonSubsequenceLen(String s1, String s2) {
    if (s1 == null || s2 == null) {
    return 0;
    }
    int len1 = s1.length(), len2 = s2.length();
    int[][] dp = new int[len1][len2];
    for (int i = 0; i < len1; i++) {
    for (int j = 0; j < len2; j++) {
    if (s1.charAt(i) == s2.charAt(j)) {
    dp[i][j] = i > 0 && j > 0 ? dp[i - 1][j - 1] + 1 : 1;
    } else {
    dp[i][j] = Math.max(i > 0 ? dp[i - 1][j] : 0, j > 0 ? dp[i][j - 1] : 0);
    }
    }
    }
    return dp[len1 - 1][len2 - 1];
    }
  • Follow-up:如果需要返回任意一个这样的字符串呢?

  • 根据DP table一路回溯,若当前两个字符串的所取字符相等则放入结果中。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    public static String maxCommonSubsequence(String s1, String s2) {
    if (s1 == null || s2 == null) {
    return null;
    }
    int len1 = s1.length(), len2 = s2.length();
    int[][] dp = new int[len1][len2];
    for (int i = 0; i < len1; i++) {
    for (int j = 0; j < len2; j++) {
    if (s1.charAt(i) == s2.charAt(j)) {
    dp[i][j] = i > 0 && j > 0 ? dp[i - 1][j - 1] + 1 : 1;
    } else {
    dp[i][j] = Math.max(i > 0 ? dp[i - 1][j] : 0, j > 0 ? dp[i][j - 1] : 0);
    }
    }
    }
    char[] ret = new char[dp[len1 - 1][len2 - 1]];
    int index = ret.length - 1, i = len1 - 1, j = len2 - 1;
    while (index >= 0) {
    if (s1.charAt(i) == s2.charAt(j)) {
    ret[index--] = s1.charAt(i);
    i--;
    j--;
    } else {
    if (i > 0 && dp[i][j] == dp[i - 1][j]) {
    i--;
    } else if (j > 0 && dp[i][j] == dp[i][j - 1]) {
    j--;
    }
    }
    }
    return new String(ret);
    }

2018.1.21 Onsite

348. design-tic-tac-toe
  • XXOO游戏,两个玩家,每一行、每一列、两条对角线都是一样的话,对应玩家就赢了。要设计一个class,其中包含move函数,给坐标和玩家编号,在图里放,如果该玩家赢了就返回玩家编号。每行、每列需要统计两个玩家各自的个数,如果都是其中一个玩家都就赢了,两条对角线也是。
  • 记录行、列、两条对较线。区分两个玩家的O和X就通过正负一来判断,这样如果有一个玩家赢了就意味着一路相加结果是n或者-n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    class TicTacToe {
    private int[] rows;
    private int[] cols;
    private int diag1, diag2;
    private int n;
    /** Initialize your data structure here. */
    public TicTacToe(int n) {
    rows = new int [n];
    cols = new int [n];
    diag1 = 0;
    diag2 = 0;
    this.n = n;
    }
    /** Player {player} makes a move at ({row}, {col}).
    @param row The row of the board.
    @param col The column of the board.
    @param player The player, can be either 1 or 2.
    @return The current winning condition, can be either:
    0: No one wins.
    1: Player 1 wins.
    2: Player 2 wins. */
    public int move(int row, int col, int player) {
    if (row < 0 || row >= n || col < 0 || row >= n || player < 1 || player > 2) {
    return 0;
    }
    int addVal = player == 1? -1: 1;
    // for diag1
    if (row == col) {
    diag1 += addVal;
    if (isConnect(diag1)) {
    return player;
    }
    }
    // for diag2
    if (row + col == n - 1) {
    diag2 += addVal;
    if (isConnect(diag2)) {
    return player;
    }
    }
    // for row
    rows[row] += addVal;
    if (isConnect(rows[row])) {
    return player;
    }
    // for col
    cols[col] += addVal;
    if (isConnect(cols[col])) {
    return player;
    }
    return 0;
    }
    private boolean isConnect(int val) {
    return val / n != 0;
    }
    }

2017.9.19 Onsite

297. serialize-and-deserialize-binary-tree
  • 实现二叉树的序列化与反序列化。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    public class Codec {
    private final String NULL_NODE = "N";
    private final String SPLITTER = ",";
    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
    StringBuilder sb = new StringBuilder();
    preorder(root, sb);
    return sb.toString();
    }
    private void preorder(TreeNode root, StringBuilder sb) {
    if (root == null) {
    sb.append(NULL_NODE).append(SPLITTER);
    return;
    }
    sb.append(root.val).append(",");
    preorder(root.left, sb);
    preorder(root.right, sb);
    }
    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
    if (data == null || data.length() == 0) {
    return null;
    }
    String[] vals = data.split(SPLITTER);
    Queue<String> q = new LinkedList<>();
    for (String val : vals) {
    q.offer(val);
    }
    return deserialize(q);
    }
    private TreeNode deserialize(Queue<String> q) {
    if (q.isEmpty()) {
    return null;
    }
    String first = q.poll();
    if (first.equals(NULL_NODE)) {
    return null;
    }
    TreeNode root = new TreeNode(Integer.parseInt(first));
    root.left = deserialize(q);
    root.right = deserialize(q);
    return root;
    }
    }
  • follow-up:如果是BST呢?那么在序列化的时候可以直接忽略掉null节点,因为在反序列化的时候可以直接通过val来判定root的值情况。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    public class Codec {
    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
    StringBuilder sb = new StringBuilder();
    preorder(root, sb);
    return sb.toString();
    }
    private void preorder(TreeNode root, StringBuilder sb) {
    if (root == null) {
    return;
    }
    sb.append(root.val).append(",");
    preorder(root.left, sb);
    preorder(root.right, sb);
    }
    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
    if (data == null || data.length() == 0) {
    return null;
    }
    String[] vals = data.split(",");
    Queue<Integer> q = new LinkedList<>();
    for (String val : vals) {
    q.offer(Integer.parseInt(val));
    }
    return deserialize(q);
    }
    private TreeNode deserialize(Queue<Integer> q) {
    if (q.isEmpty()) {
    return null;
    }
    TreeNode root = new TreeNode(q.poll());
    Queue<Integer> smallerQueue = new LinkedList<>();
    while (!q.isEmpty() && q.peek() < root.val) {
    smallerQueue.offer(q.poll());
    }
    root.left = deserialize(smallerQueue);
    root.right = deserialize(q);
    return root;
    }
    }

24. swap-nodes-in-pairs

  • 给一个链表头,要去反转所有相邻的节点,返回反转后的头。不可以修改头的val,这能修改next。
  • Iterative:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public ListNode swapPairs(ListNode head) {
    if (head == null) {
    return null;
    }
    ListNode fakeHead = new ListNode(0);
    fakeHead.next = head;
    ListNode curr = fakeHead;
    while (curr.next != null && curr.next.next != null) {
    ListNode first = curr.next;
    ListNode second = curr.next.next;
    first.next = second.next;
    second.next = first;
    curr.next = second;
    curr = first; // actually it is curr.next.next
    }
    return fakeHead.next;
    }
  • Follow-up: 用Recursive来做呢?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public ListNode swapPairs(ListNode head) {
    if (head == null || head.next == null) {
    return head;
    }
    ListNode temp = head.next;
    head.next = swapPairs(head.next.next);
    temp.next = head;
    return temp;
    }

382. linked-list-random-node

  • 面经里说的是“给一个stream of int”取随机取数字,要求每个数字被取到的概率相同。和这个LC原理相同,假设stream元素为[1, 2, 3],每次只能读入一个数字。一开始读入1,只能保留它,概率为1;然后读入2,这是1和2之间保留谁的概率就是1/2;然后读入3,此时保留的数字与3只能保留一个,为了保证概率均等,需要在0~2中产生随机数,只有等于2的时候才选择索引为2的元素即3,概率为1/3,而上一步保留的那个数字留下来的概率则是1/2 * 2/3 = 1/3,概率依然相等。这个算法还可以推广到取k个元素出来的情况,这样内存中始终就只有非常少量(k + 1)的数字需要读入了。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public class Solution {
    ListNode head;
    java.util.Random random;
    /** @param head The linked list's head.
    Note that the head is guaranteed to be not null, so it contains at least one node. */
    public Solution(ListNode head) {
    this.head = head;
    random = new java.util.Random();
    }
    /** Returns a random node's value. */
    public int getRandom() {
    ListNode curr = head;
    int ret = curr.val;
    for (int i = 1; curr.next != null; i++) {
    curr = curr.next;
    if (random.nextInt(i + 1) == i) {
    ret = curr.val;
    }
    }
    return ret;
    }
    }

2017.10.29 电面

98. validate-binary-search-tree
  • 常见的错误是直接greedy,只判断当前节点和孩子的大小。但BST的定义是比「所有的左子树都大、比右都小」。
  • 方法一:中序遍历时每次都判断前后两个元素的大小关系。又分为recursive和iterative的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    // recursive: 全局的prev记录当前节点前一个是谁,比较大小
    private TreeNode prev = null;
    private boolean validateBST1(TreeNode root) {
    if (root == null) {
    return true;
    }
    if (!validateBST1(root.left)) {
    return false;
    }
    if (prev != null && prev.val >= root.val) {
    return false;
    }
    prev = root;
    return validateBST1(root.right);
    }
    // iterative: 用stack不断深入左节点,直到空再将栈顶与prev比较,然后进入右子树,继续不断向左。
    private boolean validateBST2(TreeNode root) {
    if (root == null) {
    return true;
    }
    Stack<TreeNode> stack = new Stack<>();
    TreeNode prev = null;
    while (root != null || !stack.isEmpty()) {
    while (root != null) {
    stack.push(root);
    root = root.left;
    }
    root = stack.pop();
    if (prev != null && prev.val >= root.val) {
    return false;
    }
    prev = root;
    root = root.right;
    }
    return true;
    }
  • 方法二:分治,为左右子树给定范围(min, max),在递归时更新左子树的上界、更新右子树的下界。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    private boolean validateBST3(TreeNode root) {
    return dvcq(root, Long.MIN_VALUE, Long.MAX_VALUE);
    }
    private boolean isValidBST(TreeNode root, long min, long max) {
    if (root == null) {
    return true;
    }
    if (root.val <= min || root.val >= max) {
    return false;
    }
    return isValidBST(root.left, min, root.val) &&
    isValidBST(root.right, root.val, max);
    }

2018.1.29 电面 + Onsite

235. lowest-common-ancestor-of-a-binary-search-tree
  • 给一个BST和两个其中的节点,求他们的LCA。根据值来划分即可,若p和q的值都在root同侧则递归到那里,一旦发现不同侧就返回当前root。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    if (root == null || p == null || q == null || p == root || q == root) {
    return root;
    }
    if (p == q) {
    return p;
    }
    if (Math.max(p.val, q.val) < root.val) {
    return lowestCommonAncestor(root.left, p, q);
    } else if (Math.min(p.val, q.val) > root.val) {
    return lowestCommonAncestor(root.right, p, q);
    } else {
    return root;
    }
    }
236. lowest-common-ancestor-of-a-binary-tree
  • 给一个任意的二叉树和两个其中的节点,求他们的LCA。无法用值来比较,只能直接查找节点了。分别往左边和右边找,若root为其中一个节点则返回。这样在上层拿到左边和右边的搜索结果后就可以判断出现在哪一侧,若两边都找到了则说明当前节点就是LCA.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    if (root == null) {
    return null;
    }
    if (root == p || root == q) {
    return root;
    }
    TreeNode left = lowestCommonAncestor(root.left, p, q);
    TreeNode right = lowestCommonAncestor(root.right, p, q);
    if (left != null && right != null) {
    return root;
    }
    if (left == null && right == null) {
    return null;
    }
    return left == null? right: left;
    }