Wednesday, August 3, 2016

Tree's Pre-Order, In-Order and Post-Order Traverse Summary

To traverse a tree, there r three basic ways: preorder, inorder and postorder. All the three methods can be implemented in recursion or non-recursion. Here to simplify the problem, we assume that the tree is a binary tree.

Recursion Traversal

It is easy to recursively traverse a tree.

1.Preorder: root->left->right
public traverse(TreeNode root){
    visit(root);
    traverse(root.left);
    traverse(root.right);
}

2.Postorder: left->right->root
public traverse(TreeNode root){
    traverse(root.left);
    traverse(root.right);
    visit(root);
}

3.Inorder traverse: left->root->right
public traverse(TreeNode root){
    traverse(root.left);
    visit(root);
    traverse(root.right);
}

Non-recursion Traversal

When implementing traversal using the non-recursive method, we might need some extra space (stack) to store some tree nodes. Morris Inorder Traversal presents a method to traverse a binary tree using O(1) space and O(n) time.

1.Preorder: root->left->right
Considering stack is the first-in-last-out Container, we should push the right node into stack before the left node so that the left node can be popped and visited earlier. //pop root, push right, push left

public traverse(TreeNode root){
    if(root==null) return ;
    Stack<TreeNode> stack = new Stack<>();
    stack.push(root);
    while(!stack.isEmpty()){
        TreeNode node = stack.pop();   //visit the node
        if(node.right!=null) stack.push(node.right);
        if(node.left!=null) stack.push(node.left);
    }
}

2.Postorder: left->right->root
Postorder is similar the preorder, but "first pop the root, then push the left, then push the right". So the visiting is like "root, right, left". In the end, reverse the sequence, we can get "left, right, root".

public traverse(TreeNode root){
    if(root==null) return ;
    Stack<TreeNode> stack = new Stack<>();
    List<TreeNode> list = new ArrayList<>();
    stack.push(root);
    while(!stack.isEmpty()){
        TreeNode node = stack.pop();
        list.add(node);
        if(node.left!=null) stack.push(node.left);     
        if(node.right!=null) stack.push(node.right);  
    }
    for(int i = list.size()-1;i>=0;i--){
        visit(list.get(i));
    }
}

3.Inorder traverse: left->root->right

public traverse(TreeNode root){
    if(root==null) return ;
    Stack<TreeNode> stack = new Stack<>();
    List<TreeNode> list = new ArrayList<>();
    TreeNode cur = root;
    while(true){
        if(root.left!=null){
            stack.push(root);
            cur = cur.left;
        }else{
            if(stack.isEmpty()){
                break;
            }else{
                TreeNode parent = stack.pop(); //visit the node now
                cur = parent.right;
            }
        }
    }
}

Morris Inorder Traverse

public traverse(TreeNode root){
    if(root==null) return ;
    TreeNode cur = root;
    while(cur!=null){
        if(cur.left==null){    //visit the right node next;
             visit(cur);
             cur = cur.right;
        }else{
             TreeNode predecessor = cur.left;
             //find the Predecessor of the cur node.
             while(predecessor.right!=null && predecessor.right!=cur) predecessor = predecessor.right;
             if(predecessor.right==null){
                 //have not visited cur node's left tree, build the blue link in the graph
                 predecessor.right = cur;
                 cur = cur.left;
             }else{
                 //have visited cur node's left tree, remove the blue link in the graph
                 predecessor.right = null;
                 visit(cur);
                 cur = cur.right;
             }

        }
    }
}

Thursday, July 21, 2016

DFS Summary - Leetcode

0.Basic Concepts

Depth First Search (DFS) is a method for traversing or searching tree or graph data structures.

1.When do we apply DFS?

It is frequently used to
  1) explore some features/characteristics in the tree/graph.
  2) search specific nodes/paths/connected components(int the graph)

2. Typical Examples

Some tips:
1. A leaf node can only be represented as "node.left==null && node.right==null".


explore tree features:
[100] Same Tree
[101] Symmetric Tree
[104] Maximum Depth of Binary Tree
[110] Balanced Binary Tree

Search for specific node/connected components
[200] Number of Islands
[323] Number of Connected Components in an Undirected Graph

Path-related
[112] Path Sum
[129] Sum Root to Leaf Numbers
[337] House Robber III
[366] Find Leaves of Binary Tree

Related recursive problems
[105] Construct Binary Tree from Preorder and Inorder Traversal
[108] Convert Sorted Array to Binary Search Tree




Thursday, July 14, 2016

Dynamic Programming Summary - Leetcode

0.Basic Concepts

Dynamic Programming (DP): is a method for solving a complex problem by breaking it down into a collection of simpler subproblems, solving each of those subproblems just once, and storing their solutions - ideally, using a memory-based data structure.

1.The difference between DP and divide-and-conquer

Divide-and-conquer partitions the problem into disjoint subproblems while DP partitions the problem into overlapped subproblems.

2.Typical DP problems

  • A problem can have many solutions, each solution has a value, try to find the solution with optimal value.
  • Try to find the total number of solutions of a problem.

3.Solution

  1. Define the structure of an optimal solution.
  2. Recursively define the value of an optimal solution.
  3. Compute the value of an optimal solution in a bottom-up fashion.
    If we only need the value of the optimal solution, the above 3 steps are enough.
  4. Construct an optimal solution from computed information. (Usually, step 4 requires us to maintain some additional information to construct the optimal solution.)

4.Typical Examples (examples are all from Leetcode.com)

I strongly recommend you to read the following two articles: 



[Type 1] find the solution with optimal value 

  • [Type 1-1] Index-based based bottom-up method:
    [A typical question 1-1]
     Given an array arr[], find the continuous subarray which has the maximum sum, output the sum value.
    e.g., [1, -2, 3, 4, -2] -> 7 (becuase 3+4=7)

    [How to define dp[i]]

    The "optimal subsolution dp[i]" can be constructed in 2 ways:
        1.dp[i] is the optimal solution for the subarray arr[0:i]
        
    e.g., in the example, dp = [1,1,3,7,7]
        The output should be dp[arr.length-1], which is 7.

        2.dp[i] is the optimal solution whose last element is arr[i] for the subarray arr[0:i] 
        e.g., in the example, dp = [1,-1,3,7,5]
        The output should be maximum value in dp[], which is 7.
        (A brief explanation: dp[1]=-1 is because dp[1] must contain the lase element arr[1], so the subarray can be [1,-2] or [-2], apparently the first subarray's sum is larger. So dp[1] = 1+(-2) = 1.


    [Bottom up process]
    The bottom up process is from i=0 to i=arr.length-1. This is different from value-based problem. We will talk about this later.

    [Practice]
      • array:
      1. [53] Maximum Subarray
      2. [152] Maximum Product of Subarray
      3. [32] Longest Valid Parentheses
      4. [300] Longest Increasing Subsequence
      5. [368] Largest Divisible Subset
      6. [139] Word Break
      • matrix:
      1. [120] Triangle
      2. [64] Minimum Path Sum
      3. [221] Maximal Square
      4. [174] Dungeon Game (Note, from bottom right to top left)
      5. [354] Russian Doll Envelopes 
      6. [363] Max Sum of Rectangle No Larger Than K
      7. [32] Longest Valid Parentheses
  • [Type 1-2] Index-based bottom-up method, with multiple states.
    [Question 1-2]
     adds some extra constraints on type 1-1. The constraint might lead to multiple states. Sometimes, solution 1-1 could solve such situation. However, sometimes it does not work.

    [Solution 1-2]
     use extra arrays each corresponding to one condition. (Or add one dimension, e.g., array arr[] ->matrix arr[][], dp[i][0] corresponds to state 0, dp[i][1] corresponds to state 1, etc).

    [Practice]
      1. [198] House Robber
        ([soln], two states: rob the first house/not, the bottom-up method needs to maintain 2 arrays, one corresponding to "rob the 1st house", another corresponding to "not rob the 1st house". Similarly, we can also maintain a matrix arr[][], arr[i][0] corresponds to "not rob the first house" while arr[i][1] corresponds to "rob the first house".)
      2. [213] House Robber II
      3. [256] Paint House (3 states/colors)
      4. [265] Paint House II (k states/colors)
      5. [276] Paint Fence
      6. [123] Best Time to Buy and Sell Stock III
      7. [188] Best Time to Buy and Sell Stock IV
      8. [309] Best Time to Buy and Sell Stock with Cooldown
         dp[start][end] variation: The substructure (subarray) is arr[start:end] rather than arr[0:end]. We can understand in this way: A subarray ending at j can starts from i = 0 to j-1. Each i corresponds to a state. So we should construct dp as dp[i][j], which represents the optimal solution for subarray[i:j].
      1. [132] Palindrome Partitioning II
      2. [87] Scramble String
      3. [312] Burst Balloons
  • [Type 1-3] Value-based bottom-up method
    [A typical q
    uestion 1-3] "Given a number n and an array arr[], try to find the minimum set of numbers from arr whose sum is equal to n".

    [How to define dp[i]] dp[i] corresponds to the optimal set whose sum is equal to i.

    [Bottom up process] The bottom up process is from i=0 to i=n. This is the biggest difference between index-based problem and value-based problem. In the index-based problem, the bottom up process is from i=0 to i=arr.length-1.

    [Practice]
      1. [322] Coin Change
      2. [279] Perfect Squares
      3. [343] Integer Break

[Type 2] Two arrays "match" DP 

This type of questions will give two strings, s1 and s2. The question is whether s1 can be transformed/matched to s2 in a given way. The typical solution is to construct a m*n matrix, check whether s1[:i] can be transformed/matched to s2[:j]. Construct the matrix in a bottom-up way.

[Practice]

[Type 3] total # of solutions

Wednesday, June 15, 2016

Rate limiter (请求速率限制问题)

http://blog.gssxgss.me/not-a-simple-problem-rate-limiting/

The solution is Token Bucket.
Token Bucket is a bucket which has a fixed capacity. We put tokens into the token with a fixed rate. When a request comes, if there is no token in the bucket, the request will be denied. Otherwise, the request will be approved and the number of tokens in the bucket decrease by one.


static class TokenBucket {

  private final int capacity;
  private final int tokensPerSeconds;
  private int tokens = 0;
  private long timestamp = System.currentTimeMillis();

  public TokenBucket(int tokensPerUnit, TimeUnit unit) {
    //set capacity and rate
    capacity = tokensPerSeconds = (int) (tokensPerUnit / unit.toSeconds(1L));
  }

  public boolean take() {
    //calc the # of tokens added into bucket since last request time
    long now = System.currentTimeMillis();
    tokens += (int) ((now - timestamp) * tokensPerSeconds / 1000);
 
    //cannot overflow the capacity
    if (tokens > capacity) tokens = capacity;
 
    //if no token, won't reply to the request
    if (tokens < 1) return false;
    //if having enough token, reply
    else{
         timestamp = now;
         tokens--;
         return true;
    }
  }

}

public static void main(String[] args) throws InterruptedException {
  TokenBucket bucket = new TokenBucket(250, TimeUnit.MINUTES);
  Thread.sleep(1000L);
  for (int i = 0; i < 5; i++) {
    System.out.println(bucket.take());
  }
  Thread.sleep(1000L);
  for (int i = 0; i < 5; i++) {
    System.out.println(bucket.take());
  }
}

Saturday, June 4, 2016

Introduction to basic collections in Python

In this post, we will introduce operations of basic collections (list, array, stack, dequeue, queue, priority queue (heap), set and dictionary) in Python.

-------------------------------------------------------------------------------------------------------
list,array,stack

初始化        
    []
    [1,"a","cde"]
    array         [0]*5     ----> generate [0,0,0,0,0]
    fake matrix        [[0]*5]*6 
                        ----> generate [0]5*6    ----->但是又一个问题:[0]*5生成了一个list,是一个引用,这个引用*6,即会产生六个相同的引用,改变其中任意一个,会相应的改变其他5个。因而这个方法并不适合残生matrix
    matrix        list comprehension: *****是一个创建新list的操作*****
        pattern: 生成一个新的值+for循环
        e.g., [0]*5 for i in range(6)
增    
    增一个元素:    append(elem)
                insert(index, elelm)
    增加一整个collection中的元素    x=y+z (y,z are lists) or y.extend(z)
删
    pop():删除最后一个元素
    pop(index): 删除第i个元素
    remove(elem): 删除第一个elem
查
    if elem in list:查是否有
    list.index(elem):返回elem的index
    list.count(elem):返回elem的个数
    list[-1], list[3:]
改
    list[index] = A: 改某一个特定的index的元素

    list = [1,2,3]
    list1 = [10 if elem==2 else elem for elem in list]
    list comprehension: 
        轻量级循环,压缩for if else

copy
    y = x 拷贝,如果改变y中的元素,也会改变x中的元素
    y = x[:] 拷贝,如果改变y中immutable的元素,不会改变x中对应的immutable的元素。但是如果改变y中mutable的元素,也会改变x中mutable的元素

reoder:
    sort
        针对tuple的情况:
            list.sort(key=lambda x:x[1]) lambda是函数入口,reverse默认是False,从小到大
        e.g.,
            list = [('a',1), ('b',3), ('c',2)]
            list.sort(key=lambda x:x[1], reverse=True)
        针对self-define class, 传入cmp function
        e.g.,
            def cmp(x1, x2):
                ......
            list.sort(cmp)

    reverse

global的方法
    sorted(list)
    max(list)
    min(list)
    len(list)
    sum(list)

Iterable and Iterator
Iteratble and Iterator 都有 __iter__ 方法。该方法会在for循环中指向下一个元素
Iterator 有next方法
String, List, Tuple, Dictionary 都是Iterable的

-------------------------------------------------------------------------------------------------------
dequeue

初始化
    from collections import deque
    x = deque([])
增
    入队一个元素
    x.append(elem): append the element into the queue
    x.appendleft(elem): prepend the element
删
    出队一个元素    
    x.pop(): pop the last element
    x.popleft(): pop the first element
查
    查top元素
    x[0]
    x[-1]
改
    不存在

list不能同时实现 O(1)的pop() 和 O(1)的append(elem)
因为如果是用     insert(0, elem)和pop(), insert 是O(n)  
            append(elem), pop(0), pop(0) 是O(n)
-------------------------------------------------------------------------------------------------------
priority queue (heap)

1.heapq
初始化
    []: 如果是空,则可以直接用列表的形式
    heapq.heapify([1,2,3]),如果不为空,必须放在heapify里面
增
    heapq.heappush(heap, elem)
删
    heapq.heappop(heap)
if elem自定义了__cmp__方法,则heapq会调用__cmp__来进行排序
else if elem是一个tuple,会根据tuple的第一个元素来进行排序
默认是从小到大排序,如果想要从大到小排序,对值取负

2.Queue.PriorityQueue

-------------------------------------------------------------------------------------------------------
set
初始化
    set(Iterable)
    e.g., set([1,2,3]), set("abc") = set(['a', 'c', 'b']), set(["a":1, "b":2, "c":3]) = set(['a', 'c', 'b'])
增     add()
删     remove(): raise Exception if not exists
    discard(): won't raise Exception
查    x in set
-------------------------------------------------------------------------------------------------------
dictionary

mutable类型的变量不能作为dictionary的key值


if表达中会导致True,False的变量,如None,空字符串 “” 

Thursday, May 26, 2016

LinkedList Summary - Leetcode

Coding interview questions about LinkedList mainly can be divided into the following 3 kinds:
1.Basic operations to a single list including add, delete, access and change.
2.reorder a list
3.Partition and merge

Basic operations to a single list

access

160. Intersection of Two Linked Lists
142. Linked List Cycle II (two pointer)
141. Linked List Cycle (two pointer)
About problems using two pointers, pls read my another blog CS Interview - Two Pointer.

delete

237. Delete Node in a Linked List
83. Remove Duplicates from Sorted List
82. Remove Duplicates from Sorted List II
19. Remove Nth Node From End of List

[solution key]
1.dummy node
2.triple <prev, cur, next>

[explanation]
Once the question requires removing a node from a linked list, it is possible that the head of the list is removed. Thus, we need a dummy node which prepends the list.
    dummy = ListNode(0)
    dummy.next = head

If we use "cur" to represent the "delete node", before we do deletion, we need to record the node before the "cur" and the node after "cur", which are indicated as "prev" and "next".
Thus, the deletion process can be written as:
    pre.next = next
This form is not fixed. It can be "cur.next = cur.next.next". It also can be "cur.next = prev" if we want to reverse the link. But basically, we need the triplet <prev, cur, next> to change the link.

[example]
83. Remove Duplicates from Sorted List
[Question]
Given a sorted linked list, delete all duplicates such that each element appear only once. For example, Given 1->1->2, return 1->2. Given 1->1->2->3->3, return 1->2->3.
[Solution]
# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def deleteDuplicates(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """

        #define dummy
        dummy = ListNode(0)
        dummy.next = head
   
        pre = dummy
        cur = head
        while cur:
            val = cur.val
            #change the link
            while cur.next and cur.next.val ==val:
                cur.next = cur.next.next
            #move to next iteration
            pre = cur
            cur = cur.next
        return dummy.next

Reorder a list
Reorder the list given the requirement
234. Palindrome Linked List
206. Reverse Linked List
92. Reverse Linked List II
143. Reorder List
61. Rotate List
25. Reverse Nodes in k-Group
24. Swap Nodes in Pairs

Reordering is to adjust links among nodes. When changing a node's position, we need to record the nodes before and behind the changed node, and rebuild the links accordingly.

Sort
147. Insertion Sort List
148. Sort List
Sorting, in fact, is a special reordering method.

Partition and merge

328. Odd Even Linked List
86. Partition List
23. Merge k Sorted Lists
21. Merge Two Sorted Lists

When partitioning a linked list to two, pay attention to the last nodes. Break their original links to assign their next nodes as "None".

e.g.,
[question]
86. Partition List
Given a linked list and a value x, partition it such that all nodes less than x come before nodes greater than or equal to x.
You should preserve the original relative order of the nodes in each of the two partitions.

For example,
Given 1->3->4->5->2->6 and x = 4,
return 1->3->2->4->5->6.

[solution]
1.first partition the list to two lists: 1->3->2 and 4->5->6


2.concat the two lists

[code]
class Solution(object):
    def partition(self, head, x):
        """
        :type head: ListNode
        :type x: int
        :rtype: ListNode
        """
        dummyS = ListNode(-1)
        small = dummyS
        dummyL = ListNode(1)
        large = dummyL
     
        cur = head
        while cur:
            if cur.val<x:
                small.next = cur
                small = small.next
            else:
                large.next = cur
                large = large.next
            cur = cur.next

        small.next = dummyL.next
        large.next = None
        return dummyS.next