Skip to content

Instantly share code, notes, and snippets.

@rioshen
Last active August 29, 2015 14:04
Show Gist options
  • Save rioshen/dcf5455f2d537cb36305 to your computer and use it in GitHub Desktop.
Save rioshen/dcf5455f2d537cb36305 to your computer and use it in GitHub Desktop.
Summarization of LeetCode Problems

常用技巧

使用java.util库的函数精简代码

使用Arrays.sort() or Collections.sort()给数组排序

int[] num = {1, 3, 2, 4};
Arrays.sort(num);

相关题目: Two Sum, Three Sum, Four Sum

使用Arrays.fill() or Collections.fill()给数组赋值

boolean[] used = new boolean[](9); // 默认为false
Arrays.fill(used, true);

相关题目: Valid Soduku

类似的还有Arrays.reverse()函数,在此不赘述了。

善用Two Indexes技巧

Two Indexes指在正常的下标移动的同时,用一个额外的index来访问数组,多见于:

  1. for或者while遍历数组的同时,目标数组的下标使用另一个index变量来记录。
  2. 使用两个index从左右两边夹逼数组元素。
/* Remove duplicates from sorted array*/
...
int index = 1; // addtional index
for (int i = 0; i < num.length; i++) { // normal index
    if (A[i] != A[index - 1]) {
        A[index++] = A[i];
    }
}
...

/* Three sum */
...
for (int i = 0; i < num.length - 2; i++) {
    int low = i + 1;
    int high = num.length - 1;
    while (low < high) { // 对数组元素进行夹逼

    }
}
...

善用Java泛型(generic)中的the diamond operator

面试时尽量减少无意义的编码时间,Java的泛型声明比较繁琐:

Set<String, List<String>> set = new HashSet<String, ArrayList<String>>();

可以使用the diamond operator来进行简化:

Set<String, List<String>> set = new HashSet<>();

In Java SE 7 and later, you can replace the type arguments required to invoke the constructor of a generic class with an empty set of type arguments (<>) as long as the compiler can determine, or infer, the type arguments from the context. This pair of angle brackets, <>, is informally called the diamond.

题目列表:

  1. Remove Duplicates from Sorted Array
  2. Remove Duplicates from Sorted Array II
  3. Remove Element
  4. Search in Rotated Sorted Array
  5. Two Sum
  6. Three Sum
  7. Four Sum
  8. [Search in Rotated Sorted ArrayII]
  9. [Median of Two Sorted Arrays]
  10. Longest Consecutive Sequence
  11. [Three Sum Closest]
  12. [Next Permutation]
  13. Valid Soduku
  14. Climbing Stairs
  15. [Trapping Rain Water]
  16. [Rotate Image]
  17. [Plus One]
  18. [Gray Code]
  19. Set Matrix Zeroes
  20. [Gas Station]
  21. [Candy]
  22. [Single Number]
  23. [Single Number II]
  24. [Permutation Sequence]

常用技巧

Linked List基本操作

Linked List Basics 基本上涵盖了Linked List的基本操作,因为是用C/C++实现的,所以要自己实现java版本。

此外,我总结了一些比较常用的代码片段 (Code Snippets),熟练使用能够加快解题速度。

Note: All operations based on the class:

class ListNode {
    int val;
    ListNode next;

    ListNode(int x) { val = x; }
}
1. Reverse a linked list (链表反转).

链表的基本形式是:1 -> 2 -> 3 -> null,做了很多题目之后容易忘记最后一个元素是null,导致反转的时候忘记了处理第一个节点需要指向null.

public ListNode reverse(ListNode head) {
    ListNode prev = null;
    while (head != null) {
        ListNode next = head.next;
        head.next = prev;
        prev = head;
        head = next;
    }
    return prev;
}

相关题目:Reorder List

2. Find the middle point.

利用初中物理追击问题的思路:

两辆车如果速度相差一倍,同向行驶,当快车到达终点的时候,慢车正好行驶到路程一半的地方。

Note: 在对fast进行初始化的时候,有人喜欢将fast直接指向head.next: ListNode fast = head.next;,但是我还是觉得按照物理中的都从起点出发比较好思考。

public ListNode findMiddle(ListNode head) {
    ListNode fast = head;
    ListNode slow = head;
    while (fast != null && fast.next != null) {
        slow = slow.next;
        fast = fast.next.next;
    }
    return slow;
}

相关题目:Reorder List, Remove Nth Node from End of List

3. Find the Nth element.

多见于查找倒数第N个元素类的题目,需要注意的时候N是从1开始计数的,所以在遍历链表的时候要么i = 1 要么 i < n - 1CTCI使用了后者。

ListNode fast = head;
for (int i = 0; i < n - 1; i++) {
    fast = fast.next;
    if (fast == null) {
        return null;
    }
}
4. Dummy node.

Dummy Node的使用多针对Single List没有前向指针的问题,保证链表的head不会在删除操作中丢失,基本用法如下:

ListNode dummy = new ListNode(Integer.MIN_VALUE);
ListNode current = dummy;
...
while (current != null) {
    ...
    current.next = blahblahblah;
    ...
    current = current.next;
}
...
return dummy.next;

除此之外,还有一种用法比较少见,就是使用dummy node来进行head的删除操作,比如 Remove Duplicates From Sorted List II,一般的方法current = current.next是无法删除head元素的,所以这个时候如果有一个dummy node在head的前面.

综合以上两种情况,我理解是dummy node使用在头结点 (head) 无法确定的时候,所谓无法确定包括:

  • head可能被删除
  • head可能被修改

相关题目:Remove Duplicates From Solrted List II, Remove Nth Node From End of List (这道题因为使用的dummy node,所以在遍历的时候直接使用了n,而不是n-1

题目列表:

  1. Add Two Numbers
  2. Partition Lists
  3. Remove Duplicates From Sorted List
  4. Remove Duplicates From Solrted List II
  5. [Linked List Cycle](https://github.com/rioshen/leetcode-solutions/- [] blob/master/java/LinkedListCycle.java)
  6. Linked List Cycle II
  7. [Rotate List]
  8. Remove Nth Node From End of List
  9. Reverse Linked List II
  10. [Swap Nodes in Paris]
  11. [Reverse Nodes in k-group]
  12. [Copy List with Random Pointer]
  13. [Reorder List]
  14. [LRU Cache]
  15. Merge Two Sorted Lists
/* Insertion Sort */
public void insertion(int[] A) {
    if (A == null) return;
    for (int i = 1; i < A.length; i++) {
        int temp = A[i];
        int j = i;
        while (j > 0 && A[j - 1] > temp) {
            A[j] = A[j - 1];
            j--;
        }
        A[j] = temp;
    }
}

非递归实现树的遍历

树的遍历有递归和非递归两种,LeetCode涉及的都是 iteration 方式。遍历基本上分成四种: preorder, postorder, inorder, level order (zigzag order 算是一个变体)。

就不说递归了,优点是实现简单,缺点是对与函数调用栈消耗较大,如果不是优化结果(with out memorization)会出现 overlapping problem 甚至导致 stack overflow.

用非递归方式实现树的遍历,有三种方法:

  1. 根据遍历的定义,如下面提到的 preorder traversal 的方法1
  2. 根据递归方式,用程序模拟函数调用栈的实现方式,如 preorder 方法1 和 inorder
  3. Morris Traversal,60/70年代Morris这位大神为了节省计算机资源发明的O(1) space complexity解法,有兴趣的可以看看

1和2都可以考虑,针对不同的遍历顺序,实现起来难易度不同

Note: All operations are based on the class:

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;

    TreeNode(int x) { val = x; }
}

preorder traversal.

方法 1

public List<Integer> preorder(TreeNode root) {
    List<Integer> res = new ArrayList<>();
    if (root == null) return res;

    Stack<TreeNode> stack = new Stack<>();
    stack.push(root);
    while (!stack.empty()) {
        TreeNode node = stack.pop();
        res.add(node.val);
        if (node.right != null) stack.push(node.right);
        if (node.left != null) stack.push(node.left);
    }

    return res;
}
  • 描述

维护一个栈,先将根节点压栈,此后每次从栈中读取栈顶元素当做访问节点,分别将children按照先右后左的顺序压栈。

  • 分析

这个程序是根据前序遍历的定义解决的。我理解是因为这并不是一个DFS的solution (并没有做到 as deep as you can), 只是对访问顺序的模拟。

方法 2

  • 深度优先 (DF): preorder, postorder, inorder - stack

Depth First Traversal: go in subtree as deep as you can, backtrack. Differ on the order of visiting root, left, right subtrees.

public List<Integer> preorder(TreeNode root) {
    List<Integer> res = new ArrayList<>();
    if (root == null) return res;

    Stack<TreeNode> stack = new Stack<>();
    TreeNode node = root;
    while (node != null || !stack.empty()) {
        if (node != null) {
            res.add(node.val);
            stack.add(node);
            node = node.left;
        } else {
            node = stack.pop();
            node = node.right;
        }
    }

    return res;
}
  • 描述
  1. 维护一个栈 s 和一个当前节点 n。初始时将 n 赋值为根节点。

  2. 逐个访问当前节点 n 的左子链上的节点,并推入栈中,直到没有左儿子。

  3. 取出栈顶的节点,将 n 赋值为该节点的右儿子。

  4. 不断执行 2,3,直到栈为空且当前节点也为空。

  • 分析

该方法模拟了递归的前序遍历中程序调用栈的行为过程:在调用栈中,会不断的递归进入左儿子链中,直到没有左儿子,再进入对右儿子的处理中。与递归方法的调用栈的不同之处在于,内层 while 循环将递归方法中针对左儿子链上所有节点的递归过程集中到了一起。

postorder traversal

方法

public List<Integer> inorderTraversal(TreeNode root) {
    List<Integer> res = new ArrayList<>();
    if (root == null) return res;

    Stack<TreeNode> stack = new Stack<>();
    TreeNode node = root;
    while (node != null || !stack.empty()) {
        if (node != null) {
            stack.push(node);
            node = node.left;
        } else {
            node = stack.pop();
            res.add(node.val);
            node = node.right;
        }
    }

    return res;
}
  • 描述
  1. 维护一个栈 s 和一个当前节点 n。初始时将 n 赋值为根节点。

  2. 将当前节点 n 的左子链上的节点逐个推入栈中,直到没有左儿子。

  3. 取出栈顶的节点,访问该节点,将 n 赋值为该节点的右儿子。

  4. 不断执行 2,3,直到栈为空且当前节点也为空。

  • 分析

跟前序遍历的方法二很类似。唯一的不同是访问当前节点的时机:前序遍历在入栈前访问,而中序遍历在出栈后访问。

inorder traversal

public List<Integer> postorderTraversal(TreeNode root) {
    List<Integer> result = new LinkedList<Integer>();
    if (root == null) {
        return result;
    }

    Stack<TreeNode> stack = new Stack<TreeNode>();
    stack.push(root);
    TreeNode prev = root;
    while (!stack.empty()) {
        TreeNode node = stack.peek();
        if ((node.left == null && node.right == null)
            || node.left == prev || node.right == prev) {
            result.add(stack.pop().val);
            prev = node;
            continue;
        }
        
        if (node.right != null) {
            stack.push(node.right);
        }
        if (node.left != null) {
            stack.push(node.left);
        }
    }

    return result;
    
}
  • 分析

根据后续遍历的定义,如果当前访问的不是叶子节点则继续向下层遍历。

level order traversal

  • 广度优先 (BF): level order / zigzag order - queue

Breadth First Traversal: visit each node, starting from the lowest level and moving down by level, visiting nodes from left to right.

public List<List<Integer>> levelOrder(TreeNode root) {
    List<List<Integer>> result = new ArrayList<List<Integer>>();
    if (root == null) return result;

    Queue<TreeNode> queue = new LinkedList<TreeNode>();
    queue.add(root);
    while (!queue.isEmpty()) {
        List<Integer> level = new ArrayList<Integer>();

        int size = queue.size(); // record current level size
        for (int i = 0; i < size; i++) {
            TreeNode node = queue.poll();
            level.add(node.val);
            if (node.left != null) queue.add(node.left);
            if (node.right != null) queue.add(node.right);
        }

        result.add(level);
    }

    Collections.reverse(result);
    return result;
}

问题列表

  1. Binary Tree Preorder Traversal
  2. Binary Tree Inorder Traversal
  3. Binary Tree Level Order Traversal
  4. Binary Tree Level Order Traversal II
  5. Binary Tree Postorder Traversal
  6. Binary Tree Zigzag Level Order Traversal
  7. [Recover Binary Search Tree]
  8. Same Tree
  9. Balanced Tree
  10. Flatten Binary Tree to Linked List
  11. Symmetric Tree
  12. [Populating Next Right Pointers in Each Node II]
  13. Construct Binary Tree from Preorder and Inorder Traversal
  14. Construct Binary Tree from Inorder and Postorder Traversal
  15. [Unique Binary Search Trees]
  16. [Unique Binary Search Tress II]
  17. Validate Binary Search Tree
  18. Convert Sorted Array to Binary Search Tree
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment