Tuesday, June 3, 2014

LeetCode: Reverse Words in a String

Given an input string, reverse the string word by word.

For example,
Given s = "the sky is blue",
return "blue is sky the".

Clarification:

  • What constitutes a word?
    A sequence of non-space characters constitutes a word.
  • Could the input string contain leading or trailing spaces?
    Yes. However, your reversed string should not contain leading or trailing spaces.
  • How about multiple spaces between two words?
    Reduce them to a single space in the reversed string.


We can achieve this by reverse each word first and then reverse the whole string. Here is a example:

string: "the sky is blue"

Reverse each word: "eht yks si eulb"

Reverse the whole string: "blue is sky the"

C++ Code:

/*
 * func: reverse_words
 * goal: reverse the string word by word
 * @param s: input string s
 * return:
 */
/*
 * reverse each word first and then reverse the whole string
 * complexity: time O(n), space O(n)
 */
void reverse_words(string &s){
    string word = "";
    string intermediate = "";
    for(const char &ch : s){
        if(ch == ' '){
            if(word.length() > 0){
                reverse(word.begin(), word.end());
                intermediate += word + " ";
            }
            word.clear();
        }else{
            word += ch;
        }
    }
    if(word.length() > 0){
        reverse(word.begin(), word.end());
        intermediate += word;
    }else{
        size_t space = intermediate.find_last_of(" ");
        if(space != string::npos)
            intermediate.erase(space);
    }
    //Reverse the whole string
    reverse(intermediate.begin(), intermediate.end());
    s = intermediate;
}

Python Code:

# func: reverse words in the string
# @param s: input string
# @return: reversed string
def reverse_words(s):
    words = s.split(" ")
    intermediate = ""
    for word in words:
        if word:
            intermediate += word[::-1] + ' '
    return intermediate[-2::-1]

LeetCode: Evaluate Reverse Polish Notation

Evaluate the value of an arithmetic expression in Reverse Polish Notation.

Valid operators are +, -, *, /. Each operand may be an integer or another expression.

Some examples:

  ["2", "1", "+", "3", "*"] -> ((2 + 1) * 3) -> 9
  ["4", "13", "5", "/", "+"] -> (4 + (13 / 5)) -> 6


We can use a stack to help do the calculation. If current token is an operator, we can pop two operands from the stack and calculate the result and push it back to the stack.

C++ Code:

/*
 * func: evaluate_RPN
 * goal: calculate the result of the input reverse polish notation
 * @param tokens: input tokens list
 * return: the result
 */
int evaluate_RPN(vector<string> &tokens){
    stack<int> evaluation_helper;
    for(const string &str : tokens){
        if(str == "+"){
            int operand_1 = evaluation_helper.top();
            evaluation_helper.pop();
            int operand_2 = evaluation_helper.top();
            evaluation_helper.pop();
            evaluation_helper.emplace(operand_1 + operand_2);
        }else if(str == "-"){
            int operand_1 = evaluation_helper.top();
            evaluation_helper.pop();
            int operand_2 = evaluation_helper.top();
            evaluation_helper.pop();
            evaluation_helper.emplace(operand_2 - operand_1);
        }else if(str == "*"){
            int operand_1 = evaluation_helper.top();
            evaluation_helper.pop();
            int operand_2 = evaluation_helper.top();
            evaluation_helper.pop();
            evaluation_helper.emplace(operand_2 * operand_1);
        }else if(str == "/"){
            int operand_1 = evaluation_helper.top();
            evaluation_helper.pop();
            int operand_2 = evaluation_helper.top();
            evaluation_helper.pop();
            evaluation_helper.emplace(operand_2 / operand_1);
        }else{
            evaluation_helper.emplace(atoi(str.c_str()));
        }
    }
    if(evaluation_helper.size() == 1){
        return evaluation_helper.top();
    }else{
        return 0;
    }
}

Python Code:

# func: calculate the result of input reverse polish notation
# @param tokens: input token list
# @return: result
def evaluate_RPN(tokens):
    stack = []
    for token in tokens:
        if token == "+":
            operand_1 = stack.pop(-1)
            operand_2 = stack.pop(-1)
            stack.append(operand_1 + operand_2)
        elif token == "-":
            operand_1 = stack.pop(-1)
            operand_2 = stack.pop(-1)
            stack.append(operand_2 - operand_1)
        elif token == "*":
            operand_1 = stack.pop(-1)
            operand_2 = stack.pop(-1)
            stack.append(operand_1 * operand_2)
        elif token == "/":
            operand_1 = stack.pop(-1)
            operand_2 = stack.pop(-1)
            if operand_2 * operand_1 < 0:
                stack.append(abs(operand_2) / abs(operand_1) * -1)
            else:
                stack.append(operand_2 / operand_1)
        else:
            stack.append(int(token))
    if len(stack) == 1:
        return stack[0]
    else:
        return 0

LeetCode: Max Points on a Line

Given n points on a 2D plane, find the maximum number of points that lie on the same straight line.


We can have statistic about the slopes of the line made by each two lines and then we can know which line contains most points. Pay attention to the duplicate points and vertical lines.

C++ Code:

/*
 * func: max_points
 * goal: find the maximum number of points on the same line
 * @param points: a vector of points
 * return: maximum number
 */
/*
 * use a map to store the slop info and find the maximum number
 * complexity: time O(n^2), space O(n)
 */
int max_points(vector<Point> &points){
    size_t size = points.size();
    if(size <= 2){
        return size;
    }
    unordered_map<int, int> slopes;
    int result = 0;
    
    for(int i = 0; i < size; ++i){
        slopes.clear();
        int duplicate = 1;
        int vertical = 0;
        int max_p = 0;
        for(int j = 0; j < size; ++j){
            if(i != j){
                //If it is a vertical line
                if(points[i].x == points[j].x){
                    //If it is a duplicate point
                    if(points[i].y == points[j].y){
                        ++duplicate;
                    }else{
                        max_p = max(max_p, ++vertical);
                    }
                }else{
                    int slope = static_cast<int>(1000000 * (points[i].y - points[j].y) / (points[i].x - points[j].x));
                    max_p = max(max_p, ++slopes[slope]);
                }
            }
        }
        result = max(result, max_p + duplicate);
    }
    return result;
}

Python Code:

# func: find the number of maximum points on the same line
# @param points: list of points
# @return: the maximum number of points
# complexity: time O(n^2), space O(n)
def max_points(points):
    size = len(points)
    if size <= 2:
        return size
    result = 0
    for i in xrange(size):
        slopes = {}
        duplicate = 1
        max_p = 0
        vertical = 0
        for j in xrange(size):
            if i != j:
                #For vertical lines
                if points[i].x == points[j].x:
                    if points[i].y == points[j].y:
                        duplicate += 1
                    else:
                        vertical += 1
                        max_p = max(max_p, vertical)
                else:
                    slope = (int) (1000000 * ((float)(points[i].y - points[j].y) / (points[i].x - points[j].x)))
                    if slope in slopes:
                        slopes[slope] += 1
                    else:
                        slopes[slope] = 1
                    max_p = max(max_p, slopes[slope])

        result = max(result, max_p + duplicate)
    return result

LeetCode: Sort List

Sort a linked list in O(n log n) time using constant space complexity.


We can use merge sort to sort linked list. Split the list into single elements and merge them recursively.

C++ Code:

/*
 * func: merge_sort_list
 * goal: merge two sorted list into one
 * @param left_part: one sorted linked list
 * @param right_part: another sorted linked list
 * return: new head after merge
 */
ListNode *merge_sort_list(ListNode *left_part, ListNode *right_part){
    ListNode *left_iter = left_part;
    ListNode *right_iter = right_part;
    ListNode *new_head = nullptr;
    ListNode *new_iter = nullptr;
    while(left_iter && right_iter){
        ListNode *curr = nullptr;
        if(left_iter->val >= right_iter->val){
            curr = right_iter;
            right_iter = right_iter->next;
        }else{
            curr = left_iter;
            left_iter = left_iter->next;
        }
        
        if(new_head == nullptr){
            new_head = curr;
            new_iter = curr;
        }else{
            new_iter->next = curr;
            new_iter = new_iter->next;
        }
    }
    
    if(left_iter){
        if(new_head == nullptr){
            new_head = left_iter;
            new_iter = left_iter;
        }else{
            new_iter->next = left_iter;
            new_iter = new_iter->next;
        }
    }
    
    if(right_iter){
        if(new_head == nullptr){
            new_head = right_iter;
            new_iter = right_iter;
        }else{
            new_iter->next = right_iter;
            new_iter = new_iter->next;
        }
    }
    
    return new_head;
}
/*
 * func: sort_list
 * goal: sort the list in o(n logn) times
 * @param head: head node of the list
 * return: new head
 */
ListNode *sort_list(ListNode *head){
    ListNode *slow = head;
    ListNode *fast = head;
    while(fast->next && fast->next->next){
        slow = slow->next;
        fast = fast->next->next;
    }
    ListNode *right = slow->next;
    slow->next = nullptr;
    ListNode *left_part = sort_list(head);
    ListNode *right_part = sort_list(right);
    ListNode *new_head = merge_sort_list(left_part, right_part);
    return new_head;
}

Python Code:

# func: merge sort for linked list
# @param head: start sorting node
# @param length: length of the list to be sorted
# return: new head
def merge_sort_list(head, length):
    if length == 0 or length == 1:
        return head

    prev = head
    for _ in xrange(length/2 - 1):
        prev = prev.next

    curr = prev.next
    prev.next = None
    left_part = merge_sort_list(head, length/2)
    right_part = merge_sort_list(curr, length - length/2)

    left_iter = left_part
    right_iter = right_part
    new_head = None
    new_iter = None

    while left_iter and right_iter:
        if left_iter.val >= right_iter.val:
            curr = right_iter
            right_iter = right_iter.next
        else:
            curr = left_iter
            left_iter = left_iter.next

        if not new_head:
            new_head = curr
            new_iter = curr
        else:
            new_iter.next = curr
            new_iter = new_iter.next

    if left_iter:
        if not new_head:
            new_head = left_iter
        else:
            new_iter.next = left_iter

    if right_iter:
        if not new_head:
            new_head = right_iter
        else:
            new_iter.next = right_iter
    return new_head

# func: sort link list in O(n logn)
# @param root: head node
# @return: new head node
def sort_list(head):
    length = 0
    iter = head
    while iter:
        length += 1
        iter = iter.next

    return merge_sort_list(head, length)

LeetCode: Insertion Sort List

Sort a linked list using insertion sort.


Just do it like sorting an array. And for linked list, it is easier to move nodes. Split the linked list into two parts. One is sorted, and the other one is unsorted. Each time find an insertion position for the current node.

C++ Code:

/*
 * func: find_insertion_position
 * goal: find the insertion position for insertion sort
 * @param head: head node
 * @param move: the node needed to be inserted
 * return: the node before the insertion position
 */
ListNode *find_insertion_position(ListNode *head, ListNode *move){
    while(head->next && head->next->val <= move->val){
        head = head->next;
    }
    return head;
}

/*
 * func: insertion_sort
 * goal: insertion sort linked list
 * @param head: head node
 * return: new head node
 */
ListNode *insertion_sort(ListNode *head){
    if(head == nullptr || head->next == nullptr){
        return head;
    }
    ListNode *iter = head;
    ListNode *sentinal = new ListNode(numeric_limits<int>::min());
    while(iter){
        ListNode *next = iter->next;
        
        ListNode *insert_pos = find_insertion_position(sentinal, iter);
        iter->next = insert_pos->next;
        insert_pos->next = iter;
        iter = next;
    }
    return sentinal->next;
}

Python Code:

# func: insertion sort for linked list
# @param head: head node of the linked list
# @return: new head node
def insertion_sort(head):
    def find_insertion_position(new_head, curr):
        while new_head and curr.val > new_head.val:
            new_head = new_head.next
        return new_head

    if not head or not head.next:
        return head
    iter = head
    while iter.next:
        next = iter.next
        #If current value is greater than the previous one, move forward
        if next.val >= iter.val:
            iter = iter.next
            continue
        #Get current node out the link list
        iter.next = next.next
        #Find a inserting position
        if next.val <= head.val:
            next.next = head
            head = next
            continue

        insert_position = find_insertion_position(head, next)

        next.next = insert_position.next
        insert_position.next = next

    return head

Reference Link:

1. LeetCode — Insertion Sort List(C++ Java Python) - 龙之梦 - 博客频道 - CSDN.NET: http://blog.csdn.net/lilong_dream/article/details/20070531

2. [LeetCode] Insertion Sort List | Xiaopeng Xu's Blog: http://xpxu.net/blog/?p=83

Monday, June 2, 2014

LeetCode: LRU Cache

Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and set.

get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
set(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.


According to Wikipedia, LRU is discarding the least recently used items first. This algorithm requires keeping track of what was used when, which is expensive if one wants to make sure the algorithm always discards the least recently used item. For querying, we need a hashmap to ensure the query time is O(1). And remove the least recently used entry, we can use a doubly linked list to ensure modification in O(1).

C++ Code:

class LRUCache{
public:
    /*
     * struct: CacheEntry
     * desc: to represent each cache entry
     * key: the key for quering
     * value: the actual value needed
     * prev: a pointer pointing to its previous node
     * next: a pointer pointing to its next node
     */
    struct CacheEntry{
        int key;
        int value;
        CacheEntry *prev;
        CacheEntry *next;
        CacheEntry(int k, int v) : key(k), value(v), prev(nullptr), next(nullptr) {}
    };
    
    /*
     * func: constructor
     * @param capacity: the number of most nodes the cache can hold
     */
    LRUCache(int capacity){
        this->capacity = capacity;
        curr_size = 0;
        head = nullptr;
        tail = nullptr;
    }
    
    /*
     * func: get
     * goal: get the value based on input key
     * @param key: input key
     * return: value found. If not found, return -1
     */
    int get(int key){
        if(cache_map.find(key) != cache_map.end()){
            CacheEntry *entry = cache_map[key];
            move_to_head(entry);
            return entry->value;
        }else{
            return -1;
        }
    }
    
    /*
     * func: set
     * goal: set the key and value into the cache, if the key is in the
             cache, update the entry. If not, insert it into the cache.
     * @param key: input key
     * @param value: input value
     * return:
     */
    void set(int key, int value){
        if(get(key) != -1){
            CacheEntry *entry = cache_map[key];
            entry->value = value;
            move_to_head(entry);
        }else{
            if(curr_size >= capacity){
                cache_map.erase(tail->key);
                remove_last();
            }else{
                ++curr_size;
            }
            CacheEntry *entry = new CacheEntry(key, value);
            if(curr_size == 1){
                head = entry;
                tail = entry;
            }
            move_to_head(entry);
            cache_map.emplace(key, entry);
        }
    }
private:
    int capacity;
    int curr_size;
    unordered_map<int, CacheEntry*> cache_map;
    CacheEntry *head;
    CacheEntry *tail;
    
    /*
     * func: move_to_head
     * goal: move the input entry to the head of the list
     * @param entry: input entry
     * return:
     */
    void move_to_head(CacheEntry *entry){
        if(entry == head){
            return;
        }
        
        if(entry->prev){
            entry->prev->next = entry->next;
        }
        if(entry->next){
            entry->next->prev = entry->prev;
        }
        
        if(entry == tail){
            tail = entry->prev;
        }
        
        if(head){
            entry->next = head;
            head->prev = entry;
        }
        
        head = entry;
        entry->prev = nullptr;
    }
    
    /*
     * func: remove_last
     * goal: remove the last node in the list
     * return:
     */
    void remove_last(){
        if(tail){
            if(tail->prev){
                tail->prev->next = nullptr;
            }else{
                head = nullptr;
            }
            CacheEntry *garbage = tail;
            tail = tail->prev;
            delete garbage;
            
        }
    }
};

Python Code:

class LRUCache:
    class CacheEntry:
        def __init__(self, k, v):
            self.key = k
            self.value = v
            self.next = None
            self.prev = None
    
    # func: constructor
    # @param capacity: the maximum capacity
    def __init__(self, capacity):
        self.capacity = capacity
        self.curr_size = 0
        self.cache_map = {}
        self.head = None
        self.tail = None

    # func: get the value based on the key
    # @param key: input key
    # return: the value found. If not found, return -1
    def get(self, key):
        if key in self.cache_map:
            entry = self.cache_map[key]
            self.move_to_head(entry)
            return entry.value
        else:
            return -1
    
    # func: set the key and value into the cache, if key is in the cache
    #       update the value. If not, insert it into the cache
    # @param key: input key
    # @param value, input value
    # @return:
    def set(self, key, value):
        if key in self.cache_map:
            entry = self.cache_map[key]
            entry.value = value
            self.move_to_head(entry)
        else:
            if self.curr_size >= self.capacity:
                self.cache_map.pop(self.tail.key)
                self.remove_tail()
            else:
                self.curr_size += 1
            new_entry = self.CacheEntry(key, value)
            if self.curr_size == 1:
                self.head = new_entry
                self.tail = new_entry

            self.cache_map[key] = new_entry
            self.move_to_head(new_entry)

    # func: move the input entry to the head of the list
    # @param entry: input entry
    # @return:
    def move_to_head(self, entry):
        if self.head == entry:
            return

        if entry.prev:
            entry.prev.next = entry.next
        if entry.next:
            entry.next.prev = entry.prev

        if entry == self.tail:
            self.tail = entry.prev
        if self.head:
            entry.next = self.head
            self.head.prev = entry

        self.head = entry
        entry.prev = None

    # func: remove the tail node from the list
    # @return:
    def remove_tail(self):
        if self.tail:
            if self.tail.prev:
                self.tail.prev.next = None
            else:
                self.head = None
            self.tail = self.tail.prev

LeetCode: Binary Tree Postorder Traversal

Given a binary tree, return the postorder traversal of its nodes' values.

For example:
Given binary tree {1,#,2,3},

   1
    \
     2
    /
   3

return [3,2,1].


Also, we can use the same idea as preorder traversal: using a stack to simulate recursion. But for postorder, we need to insert a new value to the head of the list each time.

C++ Code:

/*
 * func: postorder_traversal
 * goal: postorder traverse the tree
 * @param root: root node of the tree
 * return: traversal list
 */
vector<int> postorder_traversal(TreeNode *root){
    vector<int> result;
    if(root == nullptr){
        return result;
    }
    stack<TreeNode *> postorder_helper;
    postorder_helper.emplace(root);
    while(!postorder_helper.empty()){
        TreeNode *curr = postorder_helper.top();
        result.emplace(result.begin(), curr->val);
        postorder_helper.pop();
        if(curr->left){
            postorder_helper.emplace(curr->left);
        }
        if(curr->right){
            postorder_helper.emplace(curr->right);
        }
    }
    
    return result;
}

Python Code:

# func: postorder traversal tree
# @param root: root node of the tree
# @return: traversal list
def postorder_traversal(root):
    if not root:
        return []
    result = []
    stack = [root]
    while stack:
        curr = stack[-1]
        result.insert(0, curr.val)
        stack.pop(-1)
        if curr.left:
            stack.append(curr.left)
        if curr.right:
            stack.append(curr.right)

    return result

LeetCode: Binary Tree Preorder Traversal

Given a binary tree, return the preorder traversal of its nodes' values.

For example:
Given binary tree {1,#,2,3},

   1
    \
     2
    /
   3

return [1,2,3].

Note: Recursive solution is trivial, could you do it iteratively?


We can use a stack to simulate the recursive call.

C++ Code:

/*
 * func: preorder_traversal
 * goal: give the preorder traversal of the tree
 * @param root: root node
 * return: preorder traversal list
 */
vector<int> preorder_traversal(TreeNode *root){
    vector<int> result;
    if(root == nullptr){
        return result;
    }
    stack<TreeNode *> preorder_helper;
    preorder_helper.emplace(root);
    while(!preorder_helper.empty()){
        TreeNode *curr = preorder_helper.top();
        result.emplace_back(curr->val);
        preorder_helper.pop();
        if(curr->right)
            preorder_helper.emplace(curr->right);
        if(curr->left)
            preorder_helper.emplace(curr->left);
    }
    return result;
}

Python Code:

# func: preorder traversal of the tree
# @param root: root node
# @return: traversal list
def preorder_traversal(root):
    if not root:
        return []
    result = []
    stack = [root]
    while stack:
        curr = stack[-1]
        stack = stack[:-1]
        result.append(curr.val)
        if curr.right:
            stack.append(curr.right)
        if curr.left:
            stack.append(curr.left)
    return result

LeetCode: Reorder List

Given a singly linked list L: L0L1→…→Ln-1Ln,
reorder it to: L0LnL1Ln-1L2Ln-2→…

You must do this in-place without altering the nodes' values.

For example,
Given {1,2,3,4}, reorder it to {1,4,2,3}.


The approach is that we can reverse the list from middle point to the end first and then connect the the reversed list to the original list one by one. Here is an example:

1 → 2 → 3 → 4 → 5

Reverse from the middle: 1 → 2 → 5 → 4 → 3

Connect the original one to the reversed one: 1 → 5 → 2 → 4 → 3

C++ Code:

/*
 * func: reorder_list
 * goal: reorder the linked list
 * @param head: head node of the list
 * return:
 */
void reorder_list(ListNode *head){
    if(head == nullptr || head->next == nullptr){
        return;
    }
    //Find the middle pointer
    ListNode *slow = head;
    ListNode *fast = head;
    while(fast->next && fast->next->next){
        slow = slow->next;
        fast = fast->next->next;
    }
    
    //Reverse from iter_s->next to the end
    ListNode *prev = slow;
    ListNode *curr = slow->next;
    prev->next = nullptr;
    while(curr) {
        ListNode *next = curr->next;
        curr->next = prev;
        prev = curr;
        curr = next;
    }
    
    //Reorder
    ListNode *first = head;
    ListNode *second = prev;
    while(first && second && first != second){
        ListNode *tmp = second->next;
        second->next = first->next;
        first->next = second;
        first = second->next;
        second = tmp;
    }
}

Python Code:

# func: reorder the input list
# @param head: head node of the list
# @return:
def reorder_list(head):
    if not head or not head.next:
        return None
    slow = head
    fast = head
    while fast.next and fast.next.next:
        slow = slow.next
        fast = fast.next.next

    #Reverse from slow to the end
    prev = slow
    curr = slow.next
    prev.next = None
    while curr:
        next = curr.next
        curr.next = prev
        prev = curr
        curr = next

    #Reorder
    first = head
    second = prev
    while first and second and first != second:
        next = second.next
        second.next = first.next
        first.next = second

        first = second.next
        second = next

Reference Link:

1. 水中的鱼: [LeetCode] Reorder List, Solution: http://fisherlei.blogspot.com/2013/11/leetcode-reorder-list-solution.html

LeetCode: Linked List Cycle II

Given a linked list, return the node where the cycle begins. If there is no cycle, return null.

Follow up:

Can you solve it without using extra space?


If O(n) space can be used, we can use a set to store all nodes that have been visited. Then the first node that cannot be inserted into the set will be the start node. Here is another O(1) space solution: Assume the slow node and fast node meet at the kth node in the linked list. Then the steps for slow node is X + n * Y + K where X is the number of nodes before the start of the cycle and Y is the number of nodes in the cycle. And the steps for the fast node is X + m * Y + K. Therefore:

2 * (X + n * Y + K) == X + m * Y + K

X + K = (m - 2 * n) * Y

Hence, if we move forward from the meet point by X steps, it will be the start point. There is an image in the reference link that can help understand.

C++ Code:

/*
 * func: find_cycle_start
 * goal: find the start point of the cycle if there exists in the linked list
 * @param head: head node of the linked list
 * return: if there is no cycle, return NULL, else return that point
 */
ListNode *find_cycle_start(ListNode *head){
    ListNode *slow = head;
    ListNode *fast = head;
    //Find if there eixsts circle first
    while(slow != nullptr && fast != nullptr && fast->next != nullptr){
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast){
            break;
        }
    }
    if(fast == nullptr || fast->next == nullptr){
        return nullptr;
    }
    slow = head;
    while(slow != fast){
        slow = slow->next;
        fast = fast->next;
    }
    return fast;
}

Python Code:

# func: find the start point of the cycle if there exists in the linked list
# @param head: head node
# @return: the start point of the cycle
def find_cycle_start(head):
    slow = head
    fast = head
    while slow and fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        if slow == fast:
            break
    if not fast or not fast.next:
        return None
    slow = head
    while slow != fast:
        slow = slow.next
        fast = fast.next
    return fast

Reference Link:

1.水中的鱼: [LeetCode] Linked List Cycle II, Solution: http://fisherlei.blogspot.com/2013/11/leetcode-linked-list-cycle-ii-solution.html

LeetCode: Linked List Cycle

Given a linked list, determine if it has a cycle in it.

Follow up:

Can you solve it without using extra space?


We can set up two pointers, a slow one and a fast one, the slow one move one step forward each time and the fast move two steps forward each time. If there exists a cycle, the slow one and fast will meet each other some time.

C++ Code:

/*
 * func: has_cycle
 * goal: to determine if there is a cycle in the linked list
 * @param head: head node
 * return: true or false
 */
bool has_cycle(ListNode *head){
    if(head == nullptr || head->next == nullptr){
        return false;
    }
    ListNode *slow = head;
    ListNode *fast = head;
    while(slow != nullptr && fast != nullptr && fast->next != nullptr){
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast){
            return true;
        }
    }
    return false;
}

Python Code:

# func: to check if there is a cycle in the linked list
# @param head: head node of the list
# @return: True or False
def has_cycle(head):
    if not head or not head.next:
        return False
    slow = head
    fast = head
    while slow and fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        if slow == fast:
            return True
    return False

LeetCode: Word Break II

Given a string s and a dictionary of words dict, add spaces in s to construct a sentence where each word is a valid dictionary word.

Return all such possible sentences.

For example, given
s = "catsanddog",
dict = ["cat", "cats", "and", "sand", "dog"].

A solution is ["cats and dog", "cat sand dog"].


DFS can be used for this problem. We can start from the first letter of the string and find if current substring is a word is in the dictionary.

C++ Code:

/*
 * func: word_break_helper
 * goal: dfs helper function for word_break
 * @param s: input string s
 * @param start: start searching index
 * @param sentence: current sentence
 * @param dict: input word dict
 * @param result: final result set
 * return:
 */
void word_break_helper(string s, int start, string &sentence, unordered_set<string> &dict, vector<string> &result){
    if(start < 0){
        result.emplace_back(sentence.substr(1));
        return;
    }
    for(int i = start; i >= 0; --i){
        string tmp_word = s.substr(i, start - i + 1);
        size_t curr_length = sentence.length();
        if(dict.find(tmp_word) != dict.end()){
            sentence.insert(0, " " + tmp_word);
            word_break_helper(s, i-1, sentence, dict, result);
            sentence = sentence.substr(sentence.length() - curr_length);
        }
    }
}

/*
 * func: word_break
 * goal: return all possible sentences that can be constructed from the input string and dict
 * @param s: input string
 * @param dict: input dict
 * return: all possible sentences
 */
vector<string> word_break(string s, unordered_set<string> &dict){
    vector<string> result;
    if(s.length() == 0 || dict.size() == 0){
        return result;
    }
    string sentence = "";
    word_break_helper(s, s.length()-1, sentence, dict, result);
    return result;
}

Python Code:

# func: eturn all possible sentences that can be constructed from the input string and dict
# @param s: input string
# @param dict: input dict
# @return: all possible sentences
def word_break(s, dict):
    if not s or not dict:
        return []
    result = []

    def word_break_helper(start, sentence):
        if start < 0:
            result.append(sentence[1:])
            return

        for i in xrange(start, -1, -1):
            tmp_word = s[i:start+1]
            if tmp_word in dict:
                next_sentence = ' ' + tmp_word + sentence
                word_break_helper(i-1, next_sentence[:])

    word_break_helper(len(s)-1, '')
    return result

The test cases for this problem is not so fair. The first time, I did the dfs from 0 and it told me time limit exceed. But when I changed the start to the last letter in the string, it will pass.

LeetCode: Word Break

Given a string s and a dictionary of words dict, determine if s can be segmented into a space-separated sequence of one or more dictionary words.

For example, given
s = "leetcode",
dict = ["leet", "code"].

Return true because "leetcode" can be segmented as "leet code".


We can use dynamic programming to solve this problem. Use split[i] to represent if until ith position of the string it can be split. When we get to (i+1)th position, we can start from 0 to check if s[j..i+1] is a word in dict. When we reach the end, we can get the result.

C++ Code:

/*
 * func: word_break
 * goal: to find out if the input string can be split into words in the dict
 * @param s: input string
 * @param dict: input dictionary
 * return: true or false
 */
/*
 * split[i] stands for if s[0..i] can be split into words in the dict
 * complexity: time O(n^2), space O(n)
 */
bool word_break(string s, unordered_set<string> &dict){
    if(s.length() ==0 || dict.size() == 0){
        return false;
    }
    vector<bool> split(s.length()+1, false);
    split[0] = true;
    for(int i = 0; i < s.length(); ++i){
        for(int j = 0; j <= i; ++j){
            string tmp_word = s.substr(j, i - j + 1);
            if(dict.find(tmp_word) != dict.end() && split[j]){
                split[i+1] = true;
            }
        }
    }
    
    return split[s.length()];
}

Python Code:

# func: determine if the input string can be split into words in the dictionary
# @param s: input string
# @param dict: input dictionary
# @return: True of False
# complexity: time O(n^2), space O(n)
def word_break(s, dict):
    if not s or not dict:
        return False
    splitable = [False] * (len(s)+1)
    splitable[0] = True
    for i in xrange(len(s)):
        for j in xrange(i+1):
            tmp_word = s[j: i+1]
            if tmp_word in dict and splitable[j-1]:
                splitable[i] = True
                break

    return splitable[-1]

Saturday, May 31, 2014

LeetCode: Copy List with Random Pointer

A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null.

Return a deep copy of the list.


We can copy each nodes first and make the copied node connected to the old one. And then it is easier to copy the random pointer by node.next.random = node.random.next because each node, its next node is its copied node.

C++ Code:

/*
 * func: copy_list_with_random_pointer
 * goal: deep copy a list with random pointer
 * @param head: head node of the list
 * return: new head node of the list
 */
RandomListNode *copy_list_with_random_pointer(RandomListNode *head){
    if(head == nullptr){
        return nullptr;
    }
    
    RandomListNode *iter = head;
    //Copy the list sequentially first
    //t1->t1_cpy->t2->t2_cpy->NULL
    while(iter != nullptr){
        //Get current node's next first
        RandomListNode *next = iter->next;
        //Copy current node
        iter->next = new RandomListNode(iter->label);
        //Connect copied node to the next node
        iter->next->next = next;
        //Go to the next node
        iter = next;
    }

    iter = head;
    while(iter != nullptr){
        //Get current node's next node first
        RandomListNode *next = iter->next->next;
        //Copy random pointer
        iter->next->random = iter->random == nullptr ? nullptr : iter->random->next;
        //Go to the next node
        iter = next;
    }
    
    //Set the new head
    RandomListNode *new_head = head->next;
    iter = head;
    while(iter != nullptr){
        RandomListNode *next = iter->next->next;
        //Connect the current node's next to the next node's copy node
        iter->next->next = next == nullptr ? nullptr : next->next;
        //Re-connect current node to its original next node
        iter->next = next;
        //Go to the next node
        iter = next;
    }


    return new_head;
}

Python Code:

# func: copy linked list with random pointers
# @param head: head node of the list
# @return: new head node
def copy_random_list(head):
    if not head:
        return None

    #Copy nodes first
    curr = head
    while curr:
        next_node = curr.next
        curr.next = RandomListNode(curr.label)
        curr.next.next = next_node
        curr = next_node

    #Copy random pointer
    curr = head
    while curr:
        next_node = curr.next.next
        curr.next.random = curr.random.next if curr.random else None
        curr = next_node

    #Split two lists
    new_head = head.next
    curr = head
    while curr:
        next_node = curr.next.next
        curr.next.next = next_node.next if next_node else None
        curr.next = next_node
        curr = next_node
    return new_head

LeetCode: Single Number II

Given an array of integers, every element appears three times except for one. Find that single one.

Note:
Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?


We can solve this in bit level. Each integer is also a bit array of size 32. We can count the number of 1 occurred in each bit. If its 4 or 1, then the bit of the single number is also 1. Otherwise it is 0.

C++ Code:

/*
 * func: single_number
 * goal: find the single number in an array that occurs once
 * @param A: input array A
 * @param n: size of the array
 * return: the single number
 */
int singleNumber(int A[], int n) {
    vector<int> tmp;
    for(int i = 0; i < 32; ++i){
        int bit_count = 0;
        int curr_bit = 1 << i;
        for(int j = 0; j < n; j++){
            if(A[j] & curr_bit)
                ++bit_count;
        }
        (bit_count % 3) ? tmp.emplace_back(1) : tmp.emplace_back(0);
    }
    int num = 1;
    int result = 0;
    for(const int &digit : tmp){
        if(digit){
            result += num;
        }
        num <<= 1;
    }
    return result;
}

Python Code:

# func: find the single number in a list
# @param A: input list
# @return: single number
def single_numer(A):
    if not A:
        return 0
    digits = []
    for i in xrange(32):
        curr = 1 << i
        digit_count = 0
        for num in A:
            if num & curr:
                digit_count += 1
        if digit_count % 3:
            digits.append(1)
        else:
            digits.append(0)

    result = 0
    bit_helper = 1
    for digit in digits:
        if digit == 1:
            result += bit_helper
        bit_helper <<= 1

    if digits[-1] == 1:
        return result - (1 << 32)
    else:
        return result

LeetCode: Single Number

Given an array of integers, every element appears twice except for one. Find that single one.

Note:
Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?


We can use the binary operation xor to find out the single number. Since a xor a = 0 and 0 xor 0 = 0 and 0 xor 1 = 1.

C++ Code:

/*
 * func: single_number
 * goal: find the single number in an array that occurs once
 * @param A: input array A
 * @param n: size of the array
 * return: the single number
 */
int single_number(int A[], int n){
    if(n <= 0){
        return 0;
    }
    int num = A[0];
    for(int i=1; i<n; ++i){
        num ^= A[i];
    }
    return num;
}

Python Code:

# func: find the single number in a list
# @param A: input list
# @return: single number
def single_numer(A):
    if not A:
        return 0
    result = A[0]
    for i in xrange(1, len(A)):
        result ^= A[i]

    return result

LeetCode: Candy

There are N children standing in a line. Each child is assigned a rating value.

You are giving candies to these children subjected to the following requirements:

  • Each child must have at least one candy.
  • Children with a higher rating get more candies than their neighbors.

What is the minimum candies you must give?


We can start distribution by giving each child 1 candy. And then if child i has a higher rating than child i-1, give him one more. Then from back to the front, if child i has a higher rating than child i+1, given him the number of candies as child i+1's plus 1.

C++ Code:

/*
 * func: candy
 * goal: find the minimum candies needed to distribute
 * @param ratings: ratings of the children
 * return: minimum candies
 */
/*
 * distribute from start to end and then from end to start
 * complexity: time O(n), space O(n)
 */
int candy(vector<int> &ratings){
    if(ratings.size() == 0){
        return 0;
    }
    vector<int> candies(ratings.size(), 0);
    candies[0] = 1;
    for(int i = 1; i < ratings.size(); ++i){
        //If the child's ratings at i is higher than i-1, he should have at least one more candy
        candies[i] = ratings[i] > ratings[i-1] ? candies[i-1]+1 : 1;
    }
    //check from end to the start to ensure every child get what they need to have
    int total_candy = candies[ratings.size() - 1];
    for(int i = ratings.size() - 2; i >= 0; --i){
        candies[i] = (ratings[i] > ratings[i+1] && candies[i+1] + 1 > candies[i]) ? candies[i+1] + 1 : candies[i];
        total_candy += candies[i];
    }
    return total_candy;
}

Python Code:

# func: find the minimum candies
# @param ratings: the ratings of child
# @return: minimum candies needed
# complexity: time O(n), space O(n)
def candy(ratings):
    if not ratings:
        return 0
    candies = [1] * len(ratings)
    for i in xrange(1, len(ratings)):
        if ratings[i] > ratings[i-1]:
            candies[i] = candies[i-1]+1

    total_candies = candies[-1]
    for i in xrange(len(ratings)-2, -1, -1):
        if ratings[i] > ratings[i+1] and candies[i] < candies[i+1] + 1:
            candies[i] = candies[i+1]+1
        total_candies += candies[i]

    return total_candies

LeetCode: Gas Station

There are N gas stations along a circular route, where the amount of gas at station i is gas[i].

You have a car with an unlimited gas tank and it costs cost[i] of gas to travel from station i to its next station (i+1). You begin the journey with an empty tank at one of the gas stations.

Return the starting gas station's index if you can travel around the circuit once, otherwise return -1.

Note:
The solution is guaranteed to be unique.


Since it is a circle, we can assume the start point is 0, when the gas is not enough to use, we can make the start point backwards one stop and see if current start point is available.

C++ Code:

/*
 * func: can_complete_circuit
 * goal: find the starting gas station that can make the truck travel the circuit
 * @param gas: gas vector
 * @param cost: cost vector
 * return: starting index
 */
int can_complete_circuit(vector<int> &gas, vector<int> &cost){
    int stop_num = gas.size();
    int start = 0;
    int end = stop_num-1;
    int current = 0;
    int remaining = 0;
    while(current <= end){
        remaining += gas[current] - cost[current];
        while(remaining < 0 && end != current){
            start = end;
            end = end-1;
            remaining += gas[start] - cost[start];
        }
        ++current;
    }
    
    return remaining < 0 ? -1 : start;
}

Python Code:

# func: find the starting gas station that can make the truck travel the circuit
# @param gas: gas list
# @param cost: cost list
# @return: the start position
def can_complete_circuit(gas, cost):
    start = 0
    end = len(gas)-1
    current = 0
    remaining = 0
    while current <= end:
        remaining += gas[current] - cost[current]
        while remaining < 0 and current != end:
            start = end
            end -= 1
            remaining += gas[start] - cost[start]
        current += 1

    if remaining < 0:
        return -1
    else:
        return start

LeetCode: Clone Graph

Clone an undirected graph. Each node in the graph contains a label and a list of its neighbors.


We can use BFS to search the graph can clone it at the same time. A map can be used to store those nodes that are already cloned.

C++ Code:

/*
 * func: clone_graph_helper
 * goal: helper function to perform BFS
 * @param node: current node to be cloned
 * @param visited: node already existed in the new graph
 * return: newly cloned node
 */
UndirectedGraphNode *clone_graph_helper(UndirectedGraphNode *node, unordered_map<int, UndirectedGraphNode*> &visited){
    UndirectedGraphNode *new_node = new UndirectedGraphNode(node->label);
    visited[node->label] = new_node;
    for(int i = 0; i < node->neighbors.size(); ++i){
        if(visited.find(node->neighbors[i]->label) != visited.end()){
            new_node->neighbors.emplace_back(visited[node->neighbors[i]->label]);
        }else{
            new_node->neighbors.emplace_back(clone_graph_helper(node->neighbors[i], visited[node->neighbors[i]->label]));
        }
    }
    
    return new_node;
}

/*
 * func: clone_graph
 * goal: clone a undirected graph
 * @param node: a start node
 * return: a cloned start node
 */
UndirectedGraphNode *clone_graph(UndirectedGraphNode *node){
    if(node == nullptr){
        return nullptr;
    }
    unordered_map<int, UndirectedGraphNode*> visited;
    UndirectedGraphNode* new_node = clone_graph_helper(node, visited);
    return new_node;
}

Python Code:

# func: clone graph
# @param node: start node
# @return: new start node
def clone_graph(node):
    if not node:
        return None

    visited = {}
    def clone_graph_helper(start):
        new_node = UndirectedGraphNode(start.label)
        visited[start.label] = new_node
        for neighbor in start.neighbors:
            if neighbor.label in visited:
                new_node.neighbors.append(visited[neighbor.label])
            else:
                new_node.neighbors.append(clone_graph_helper(neighbor))
        return new_node

    return clone_graph_helper(node)

Friday, May 30, 2014

LeetCode: Palindrome Partitioning II

Given a string s, partition s such that every substring of the partition is a palindrome.

Return the minimum cuts needed for a palindrome partitioning of s.

For example, given s = "aab",
Return 1 since the palindrome partitioning ["aa","b"] could be produced using 1 cut.


The problem can be transformed to the following: minimum[i:n] means the minimum cut we needed for substring s[i:n], and it could be the minimum of minimum[j+1:n]+1 for all i <= j < n.

C++ Code:

/*
 * func: min_cut
 * goal: find the minimum number of min_cut so that each partition is a palindrome
 * @param s: input string
 * return: minimum cut
 */
int min_cut(string s){
    size_t str_length = s.length();
    if(str_length == 0){
        return 0;
    }
    //a 2D array which indicate if substring(i, j) is a palindrome
    vector<vector<bool> > palindrome(str_length, vector<bool>(str_length, false));
    for(int i = 0; i < str_length; ++i){
        //Check palindrome taking s[i] as symmetry axis
        int left = i;
        int right = i;
        while(left >= 0 && right < str_length && s[left] == s[right]){
            palindrome[left][right] = true;
            --left;
            ++right;
        }
        //Check palindrome taking s[i] s[i+1] as symmetry axis
        left = i;
        right = i+1;
        while(left >= 0 && right < str_length && s[left] == s[right]){
            palindrome[left][right] = true;
            --left;
            ++right;
        }
    }
    vector<int> minimum_cut(str_length, str_length);
    for(int i = 0; i < str_length; ++i){
        if(palindrome[0][i]){
            minimum_cut[i] = 0;
        }else{
            int min_cut_now = str_length;
            for(int j = 1; j <= i; ++j){
                if(palindrome[j][i] && min_cut_now > minimum_cut[j-1] + 1){
                    min_cut_now = minimum_cut[j-1] + 1;
                }
            }
            minimum_cut[i] = min_cut_now;
        }
    }
    return minimum_cut.back();
}

Python Code:

# func: find the minimum cut needed for a palindrome partitioning of string
# @param s: input string
# @return: minimum cut
def min_cut(s):
    if not s:
        return 0
    palindrome = [[False] * len(s) for _ in xrange(len(s))]
    minimum_cut = [len(s) - i for i in xrange(len(s)+1)]
    for i in xrange(len(s)-1, -1, -1):
        for j in xrange(i, len(s)):
            if s[i] == s[j] and (j-i < 2 or palindrome[i+1][j-1]):
                palindrome[i][j] = True
                minimum_cut[i] = min(minimum_cut[i], minimum_cut[j+1] + 1)

    return minimum_cut[0]-1

Reference Link:

1. 水中的鱼: [LeetCode] Palindrome Partitioning II, Solution: http://fisherlei.blogspot.com/2013/03/leetcode-palindrome-partitioning-ii.html

LeetCode: Palindrome Partitioning

Given a string s, partition s such that every substring of the partition is a palindrome.

Return all possible palindrome partitioning of s.

For example, given s = "aab",
Return

  [
    ["aa","b"],
    ["a","a","b"]
  ]


Use DFS to check every possible partition.

C++ Code:

/*
 * func: valid_palindrome
 * goal: to validate the input string
 * @param s: input string
 * @param left: left searching index
 * @param right: right searching index
 * return:
 */
bool valid_palindrome(string s, int left, int right){
    while(left <= right){
        if(s[left] != s[right]){
            return false;
        }else{
            ++left;
            --right;
        }
    }
    return true;
}

/*
 * func: generate_combination
 * goal: use DFS to generate combinations
 * @param s: input string
 * @param start: start searching index
 * @param combination: current combination
 * @param result: final result set
 * return:
 */
void generate_combination(string s, int start, vector<string> &combination, vector<vector<string> > &result){
    if(start >= s.length()){
        result.emplace_back(combination);
        return;
    }
    for(int i = start; i < s.length(); ++i){
        if(valid_palindrome(s, start, i)){
            string tmp = s.substr(start, i-start+1);
            combination.emplace_back(tmp);
            generate_combination(s, i+1, combination, result);
            combination.pop_back();
        }
    }
}


/*
 * func: palindrome_partition
 * goal: return all possible palindrome partitioning of input string
 * @param s: input string
 * return: all possible combinations
 */
vector<vector<string> > palindrome_partition(string s){
    vector<vector<string> > result;
    vector<string> combination;
    generate_combination(s, 0, combination, result);
    return result;
}

Python Code:

# func: valid if current string is a palindrome
# @param start: start searching index
# @param end: end searching index
# @return: True or False
def valid_palindrome(s, start, end):
    while start <= end:
        if s[start] != s[end]:
            return False
        else:
            start += 1
            end -= 1
    return True

# func: find all possible palindrome partitioning of input string
# @param s: input string
# @return: all possible partitions
def palindrome_partition(s):
    result = []
    combination = []
    def palindrome_partition_helper(start):
        if start >= len(s):
            result.append(combination[:])
            return

        for i in xrange(start, len(s)):
            if valid_palindrome(s, start, i):
                combination.append(s[start:i+1])
                palindrome_partition_helper(i+1)
                combination.pop(-1)
    palindrome_partition_helper(0)
    return result

LeetCode: Surrounded Regions

Given a 2D board containing 'X' and 'O', capture all regions surrounded by 'X'.

A region is captured by flipping all 'O's into 'X's in that surrounded region.

For example,

X X X X
X O O X
X X O X
X O X X

After running your function, the board should be:

X X X X
X X X X
X X X X
X O X X


To solve this problem, we can find those 'O' that is not in the surrounded first and then the remaining parts are in surrounded regions. To achieve this, we can start from the boarder line of the matrix and perform a BFS to find all 'O' that can be reached from the 'O' on boarder line.

C++ Code:

/*
 * func: surrounded_region
 * goal: find all regions surrounded by 'X'
 * @param board: check board
 * return:
 */
/*
 * find all the 'O' that can be reached from 'O' on boarder line. 
 * Then the remaining is surrounded.
 * complexity: time O(n^2), space O(n^2)
 */
void surrounded_region(vector<vector<char> > &board){
    if(board.size() <= 2){
        return;
    }
    size_t board_size_row = board.size();
    size_t board_size_column = board[0].size();
    queue<pair<int, int> > BFS_helper;
    
    //Find 'O' in row boarder line
    for(int column = 0; column < board_size_column; ++column){
        if(board[0][column] == 'O'){
            board[0][column] = 'N';
            BFS_helper.emplace(pair<int, int>{0, column});
        }
        if(board[board_size_row-1][column] == 'O'){
            board[board_size_row-1][column] = 'N';
            BFS_helper.emplace(pair<int, int>{board_size_row-1, column});
        }
    }
    //Find 'O' in column boarded line
    for(int row = 0; row < board_size_row; ++row){
        if(board[row][0] == 'O'){
            board[row][0] = 'N';
            BFS_helper.emplace(pair<int, int>{row, 0});
        }
        if(board[row][board_size_column-1] == 'O'){
            board[row][board_size_column-1] = 'N';
            BFS_helper.emplace(pair<int, int>{row, board_size_column-1});
        }
    }
    //Use a queue to do BFS
    while(!BFS_helper.empty()){
        pair<int, int> current = BFS_helper.front();
        BFS_helper.pop();
        if(current.first > 0 && board[current.first-1][current.second] == 'O'){
            board[current.first-1][current.second] = 'N';
            BFS_helper.emplace(pair<int, int>{current.first-1, current.second});
        }
        if(current.first < board_size_row-1 && board[current.first+1][current.second] == 'O'){
            board[current.first+1][current.second] = 'N';
            BFS_helper.emplace(pair<int, int>{current.first+1, current.second});
        }
        if(current.second > 0 && board[current.first][current.second-1] == 'O'){
            board[current.first][current.second-1] = 'N';
            BFS_helper.emplace(pair<int, int>{current.first, current.second-1});
        }
        if(current.second < board_size_column-1 && board[current.first][current.second+1] == 'O'){
            board[current.first][current.second+1] = 'N';
            BFS_helper.emplace(pair<int, int>{current.first, current.second+1});
        }
    }
    
    for(int row = 0; row < board_size_row; ++row){
        for(int column = 0; column < board_size_column; ++column){
            if(board[row][column] == 'O'){
                board[row][column] = 'X';
            }
            if(board[row][column] == 'N'){
                board[row][column] = 'O';
            }
        }
    }
}

Python Code:

# func: find all regions surrounded by 'X'
# @param board: input board
# @return:
# complexity: time O(n^2), space O(n^2)
def surrounded_region(board):
    if len(board) <= 2:
        return
    row_top = board[0]
    row_bottom = board[len(board)-1]
    column_left = [row[0] for row in board]
    column_right = [row[len(row)-1] for row in board]

    queue = []
    for i in xrange(len(row_top)):
        if row_top[i] == 'O':
            board[0][i] = 'N'
            queue.append((0, i))
        if row_bottom[i] == 'O':
            board[-1][i] = 'N'
            queue.append((len(board)-1, i))

    for j in xrange(len(column_left)):
        if column_left[j] == 'O':
            board[j][0] = 'N'
            queue.append((j, 0))
        if column_right[j] == 'O':
            board[j][-1] = 'N'
            queue.append((j, len(board[0])-1))

    while queue:
        x_curr, y_curr = queue.pop(0)
        if x_curr - 1 > 0 and board[x_curr-1][y_curr] == 'O':
            board[x_curr-1][y_curr] = 'N'
            queue.append((x_curr-1, y_curr))
        if x_curr + 1 < len(board) and board[x_curr+1][y_curr] == 'O':
            board[x_curr+1][y_curr] = 'N'
            queue.append((x_curr+1, y_curr))
        if y_curr - 1 > 0 and board[x_curr][y_curr-1] == 'O':
            board[x_curr][y_curr-1] = 'N'
            queue.append((x_curr, y_curr-1))
        if y_curr + 1 < len(board[0]) and board[x_curr][y_curr+1] == 'O':
            board[x_curr][y_curr+1] = 'N'
            queue.append((x_curr, y_curr+1))

    for i in xrange(len(board)):
        for j in xrange(len(board[0])):
            if board[i][j] == 'O':
                board[i][j] = 'X'
            if board[i][j] == 'N':
                board[i][j] = 'O'

LeetCode: Sum Root to Leaf Numbers

Given a binary tree containing digits from 0-9 only, each root-to-leaf path could represent a number.

An example is the root-to-leaf path 1->2->3 which represents the number 123.

Find the total sum of all root-to-leaf numbers.

For example,

    1
   / \
  2   3

The root-to-leaf path 1->2 represents the number 12.
The root-to-leaf path 1->3 represents the number 13.

Return the sum = 12 + 13 = 25.


We can start from the root node and perform a breadth first search. If one path reaches to the end, push the current num into a list and finally add them together.

C++ Code:

/*
 * func: root_leaf_sum_helper
 * goal: helper function for root_leaf_sum to perform BFS
 * @param root: root node of the tree
 * @param vec: vector to store each value
 * @param num: current num in string format
 * return:
 */
void root_leaf_sum_helper(TreeNode *root, vector<string> &vec, string num){
    if(root == nullptr){
        return;
    }else if(root->left == nullptr && root->right == nullptr){
        num += char(root->val + '0');
        vec.emplace_back(num);
        return;
    }else{
        num += char(root->val + '0');
        root_leaf_sum_helper(root->right, vec, num);
        root_leaf_sum_helper(root->left, vec, num);
    }
}

/*
 * func: root_leaf_sum
 * goal: find the total sum of all root-to-leaf numbers
 * @param root: root node of the tree
 * return: total sum
 */
int root_leaf_sum(TreeNode *root){
    vector<string> vec;
    string num = "";
    root_leaf_sum_helper(root, vec, num);
    int result = 0;
    for(const string &str : vec){
        result += atoi(str.c_str());
    }
    return result;
}

Python Code:

# func: find the sum of all numbers constructed from root to leaf
# @param root: root node of the tree
# @return: sum
def sum_root_leaf(root):
    if not root:
        return 0
    else:
        def sum_root_leaf_helper(val, node):
            if not node.left and not node.right:
                return val
            elif not node.left:
                return sum_root_leaf_helper(val * 10 + node.right.val, node.right)
            elif not node.right:
                return sum_root_leaf_helper(val * 10 + node.left.val, node.left)
            else:
                return sum_root_leaf_helper(val * 10 + node.left.val, node.left) + \
                sum_root_leaf_helper(val * 10 + node.right.val, node.right)
        return sum_root_leaf_helper(root.val, root)

Thursday, May 29, 2014

LeetCode: Longest Consecutive Sequence

Given an unsorted array of integers, find the length of the longest consecutive elements sequence.

For example,
Given [100, 4, 200, 1, 3, 2],
The longest consecutive elements sequence is [1, 2, 3, 4]. Return its length: 4.

Your algorithm should run in O(n) complexity.


Since the problem asked us to solve it in O(n), we cannot use any sort-based approach. Hence, we need to find a way to record the current longest sequence. A hashmap can be used here. Each time a element is inserted to the map, we check to see if element-1 is in the map. If it exists, we set the value of element to map[element-1]+1. Then we check to see if element+1 is in the map. It it exists, we update the value of element and the end of sequence starting from element+1. At the end, we can reset the value of the starting element in the sequence. Until now, the starting element and ending element of the sequence have the latest length info.

C++ Code:

/*
 * func: longest_consecutive
 * goal: find the longest consecutive sequence
 * @param num: input number array
 * return: the length of longest consecutive sequence
 */
int longest_consecutive(vector<int> &num){
    int max_length = 0;
    unordered_map<int, int> count_helper;
    for(const int &number : num){
        if(count_helper.find(number) != count_helper.end()){
            continue;
        }
        count_helper[number] = 1;
        
        //If the previous value is in the map, connection them together
        if(count_helper.find(number-1) != count_helper.end()){
            count_helper[number] = count_helper[number-1] + 1;
        }
        //If the next value is in the map, update the biggest number's value
        if(count_helper.find(number+1) != count_helper.end()){
            int last = number+1+count_helper[number+1]-1;
            count_helper[number] = count_helper[last] = count_helper[number] + count_helper[last];
        }
        //Update the count of the smallest value in the chain
        if(count_helper.find(number-1) != count_helper.end()){
            int first = number-1-count_helper[number-1]+1;
            count_helper[first] = count_helper[number];
        }
        
        max_length = max(max_length, count_helper[number]);
    }
    return max_length;
}

Python Code:

# func: find the longest consecutive sequence
# @param num: input number list
# @return: the length of the sequence
def longest_consecutive(num):
    count_helper = {}
    max_length = 0
    for number in num:
        if number not in count_helper:
            count_helper[number] = 1

            #Find if number-1 is in the map
            if number-1 in count_helper:
                count_helper[number] = count_helper[number-1] + 1

            #Find if number+1 in the map
            if number+1 in count_helper:
                #Find the last element starting from number+1
                last = count_helper[number+1] + number
                #Update the value in last and number
                count_helper[last] = count_helper[number] = count_helper[number] + count_helper[last]
            #Find the start position of the sequence
            if number-1 in count_helper:
                first = number - count_helper[number-1]
                count_helper[first] = count_helper[number]

            max_length = max(max_length, count_helper[number])

    return max_length

LeetCode: Word Ladder II

Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from start to end, such that:

  1. Only one letter can be changed at a time
  2. Each intermediate word must exist in the dictionary

For example,

Given:
start = "hit"
end = "cog"
dict = ["hot","dot","dog","lot","log"]

Return

  [
    ["hit","hot","dot","dog","cog"],
    ["hit","hot","lot","log","cog"]
  ]

Note:

  • All words have the same length.
  • All words contain only lowercase alphabetic characters.


We can still use BFS to find the shortest path for this problem. What we need to do additionally is record the path when we do BFS. Here we can use a map to record the list of words that can transform to one word in the dict. Then we can use backtracking to generate the path.

C++ Code:

/*
 * func: generate_path
 * goal: generate shortest parth from the the map
 * @param prev_map: a word map to show the words connected to
 * @param path: a path vector
 * @param word: current word
 * @param result: final result set
 * return:
 */
void generate_path(unordered_map<string, vector<string> > &prev_map, vector<string> &path,
                   string word, vector<vector<string> > &result){
    if(prev_map[word].size() == 0){
        path.emplace_back(word);
        vector<string> curr_path = path;
        reverse(curr_path.begin(), curr_path.end());
        result.emplace_back(curr_path);
        path.pop_back();
        return;
    }
    
    path.push_back(word);
    for(auto iter = prev_map[word].begin(); iter != prev_map[word].end(); ++iter){
        generate_path(prev_map, path, *iter);
    }
    path.pop_back();
}

/*
 * func: find_ladders
 * goal: find all shortest transformation sequences
 * @param start: start string
 * @param end: end string
 * @param dict: words dict
 * return: all possible sequences
 */
vector<vector<string> > find_ladders(string start, string end, unordered_set<string> &dict){
    vector<vector<string> > result;
    
    unordered_map<string, vector<string> > prev_map;
    for(auto it = dict.begin(); it != dict.end(); ++it){
        prev_map[*it] = vector<string>();
    }
    
    vector<unordered_set<string> > candidates(2);
    int current = 0;
    int previous = 1;
    candidates[current].insert(start);
    
    while(true){
        current = !current;
        previous = !previous;
        
        for(auto it = candidates[previous].begin(); it != candidates[previous].end(); ++it){
            dict.erase(*it);
        }
        
        candidates[current].clear();
        for(auto it = candidates[previous].begin(); it != candidates[previous].end(); ++it){
            for(size_t pos = 0; pos < it->size(); ++pos){
                string word = *it;
                for(char c = 'a'; c <= 'z'; ++c){
                    word[pos] = c;
                    if(dict.count(word) > 0){
                        prev_map[word].push_back(*it);
                        candidates[current].insert(word);
                    }
                }
            }
        }
        
        if(candidates[current].size() == 0){
            return result;
        }
        if(candidates[current].count(end)){
            break;
        }
    }
    
    vector<string> path;
    generate_path(prev_map, path, end, result);
    
    return result;
}

Python Code:

# func: find all shortest transformation sequences
# @param start: start string
# @param end: end string
# @param dict: words dict
# @return: all sequences
def find_ladders(start, end, dict):
    dict.add(end)
    dict.add(start)
    result = []
    path = []
    #a map store current word as key and words can connected to it as value
    prev_map = {word:[] for word in dict}
    #two sets help BFS
    current = set()
    previous = set()
    current.add(start)
    while end not in current:
        current, previous = previous, current
        for word in previous:
            dict.remove(word)

        current.clear()
        for word in previous:
            for i in xrange(len(word)):
                for ch in 'abcdefghijklmnopqrstuvwxyz':
                    transformed = word[:i] + ch + word[i+1:]
                    if transformed in dict:
                        prev_map[transformed].append(word)
                        current.add(transformed)

        if not current:
            return []
    #Generate path based on the prev_map
    #It is a backtracking
    def generate_path(word):
        if len(prev_map[word]) == 0:
            path.append(word)
            result.append(path[::-1])
            path.pop(-1)
            return

        path.append(word)
        for prev_word in prev_map[word]:
            generate_path(prev_word)

        path.pop(-1)

    generate_path(end)
    return result

LeetCode: Word Ladder

Given two words (start and end), and a dictionary, find the length of shortest transformation sequence from start to end, such that:

  1. Only one letter can be changed at a time
  2. Each intermediate word must exist in the dictionary

For example,

Given:
start = "hit"
end = "cog"
dict = ["hot","dot","dog","lot","log"]

As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
return its length 5.

Note:

  • Return 0 if there is no such transformation sequence.
  • All words have the same length.
  • All words contain only lowercase alphabetic characters.


We can treat the dictionary as a graph, and those pairs of words that can transform to each other are connected in the graph. The this problem is to find the shortest path from start word to the end word. Since the edge is bidirectional. We can use BFS to find the shortest path. In my solution, a queue is used to help BFS.

C++ Code:

/*
 * func: ladder_length
 * goal: find the shortest length of transformation
 * @param start: start word
 * @param end: end word
 * @param dict: word dictionary
 * return: shortest length
 */
/*
 * Use a queue to do BFS
 * complexity: time O(n), space O(n)
 */
int ladder_length(string start, string end, unordered_set<string> &dict){
    if(dict.size() == 0){
        return 0;
    }
    
    queue<string> word_queue;
    queue<int> distance_queue;
    
    word_queue.emplace(start);
    distance_queue.emplace(1);
    
    while(!word_queue.empty()){
        string curr_word = word_queue.front();
        int curr_dis = distance_queue.front();
        
        word_queue.pop();
        distance_queue.pop();
        
        for(int i=0; i<curr_word.length(); ++i){
            char tmp_c = curr_word[i];
            for(char c = 'a'; c <= 'z'; ++c){
                curr_word[i] = c;
                if(dict.find(curr_word) != dict.end()){
                    word_queue.emplace(curr_word);
                    distance_queue.emplace(curr_dis+1);
                    dict.erase(curr_word);
                }
                if(curr_word == end){
                    return curr_dis + 1;
                }
            }
            curr_word[i] = tmp_c;
        }
    }
    
    return 0;
}

Python Code:

# func: find the shortest length of transformation
# @param start: start word
# @param end: end word
# @param dict: words dict
# @return: shortest length
def ladder_length(start, end, dict):
    word_queue = [start]
    distance_queue = [1]

    while word_queue:
        curr_word = word_queue[0]
        curr_dis = distance_queue[0]

        word_queue.pop(0)
        distance_queue.pop(0)

        for i in xrange(len(curr_word)):
            for c in 'abcdefghijklmnopqrstuvwxyz':
                transformed_word = curr_word[:i] + c + curr_word[i+1:]
                if transformed_word in dict:
                    word_queue.append(transformed_word)
                    distance_queue.append(curr_dis+1)
                    dict.remove(transformed_word)
                if transformed_word == end:
                    return curr_dis + 1
    return 0

LeetCode: Valid Palindrome

Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignoring cases.

For example,
"A man, a plan, a canal: Panama" is a palindrome.
"race a car" is not a palindrome.

Note:
Have you consider that the string might be empty? This is a good question to ask during an interview.

For the purpose of this problem, we define empty string as valid palindrome.


If the size of the string is even, the start checking position will be the middle two chars. Else if it is odd, the start checking position is the middle char.

C++ Code:

/*
 * func: is_palindrome
 * goal: to test if the input string is a palindrome
 * @param s: input string
 * return: true or false
 */
/*
 * find the symmetry axis and then validate
 * complexity: time O(n), space O(n)
 */
bool is_palindrome(string s){
    string convert = "";
    //remove non-alphanumeric chars
    for(const char &ch : s){
        if(isalnum(ch)){
            convert += isupper(ch) ? (ch + 32) : ch;
        }
    }
    int s_length = convert.length();
    if(s_length <= 1){
        return true;
    }
    //Find symmetry axis
    int mid_left = 0;
    int mid_right = 0;
    if(s_length & 1){
        mid_left = s_length/2;
        mid_right = s_length/2;
    }else{
        mid_left = s_length/2 - 1;
        mid_right = s_length/2;
    }
    while(mid_left >= 0 && mid_right <s_length){
        if(convert[mid_left] == convert[mid_right]){
            --mid_left;
            ++mid_right;
        }else{
            return false;
        }
    }
    return true;
}

Python Code:

# func: to determine if the input string is a palindrome
# @param s: input string
# @return: True or False
def is_palindrome(s):
    str = ''.join(x.lower() for x in s if x.isalnum())
    if len(str) <= 1:
        return True
    if len(str) & 1:
        mid_left = len(str)/2
        mid_right = len(str)/2
    else:
        mid_left = len(str)/2 - 1
        mid_right = mid_left + 1

    while mid_left >= 0 and mid_right < len(str):
        if str[mid_left] == str[mid_right]:
            mid_left -= 1
            mid_right += 1
        else:
            return False
    return True

LeetCode: Binary Tree Maximum Path Sum

Given a binary tree, find the maximum path sum.

The path may start and end at any node in the tree.

For example:
Given the below binary tree,

       1
      / \
     2   3

Return 6.


For a binary tree, the max sum path may be one of the following situations:

1. The path only contains the root node.

2. The path contains the root node and its left subtree path.

3. The path contains the root node and its right subtree path.

4. The path contains the root node, its left subtree path and right subtree path.

5. The path is from its left subtree path, and doesn't include root node.

6. The path is from its right subtree path, and doesn't include root node.

C++ Code:

/*
 * func: binary_tree_max_path_sum_helper
 * goal: helper function to find the maximum value
 * @param root: root node of the tree
 * @param max_sum: current max sum
 * return: max sum passing current node
 */
int binary_tree_max_path_sum_helper(TreeNode *root, int &max_sum){
    if(root == nullptr){
        return 0;
    }
    
    //Get the maximum value the path of which includes root.left
    int left_max = binary_tree_max_path_sum_helper(root->left, max_sum);
    //Get the maximum value the path of which includes root.left
    int right_max = binary_tree_max_path_sum_helper(root->right, max_sum);
    //Get the maximum value the path of which includes root node
    int current_max = max(root->val, max(root->val + left_max, root->val + right_max));
    //Update global maximum value
    max_sum = max(max(current_max, root->val + left_max + right_max), max_sum);
    
    return current_max;
}
 
/*
 * func: binary_tree_max_path_sum
 * goal: find the maximum path sum, the path may start and end at any node
 * @param root: root node of the tree
 * return: maximum path sum
 */
int binary_tree_max_path_sum(TreeNode *root){
    if(root == nullptr){
        return 0;
    }
    int max_sum = root->val;
    binary_tree_max_path_sum_helper(root, max_sum);
    return max_sum;
}

Python Code:

# func: find the maximum path sum
# @param root: root node of the tree
# @return: maximum value
def max_path_sum(self, root):
    if not root:
        return 0
    self.max_value = root.val
    def max_path_sum_helper(node):
        if not node:
            return 0
        left_value = max_path_sum_helper(node.left)
        right_value = max_path_sum_helper(node.right)

        current_value = max(node.val, node.val+left_value, node.val+right_value)
        self.max_value = max(self.max_value, current_value, node.val + left_value + right_value)

        return current_value
    max_path_sum_helper(root)
    return self.max_value

Wednesday, May 28, 2014

LeetCode: Best Time to Buy and Sell Stock III

Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete at most two transactions.

Note:
You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).


We can divide the array into two parts. We can find the maximum profit in prices[0:i] with one transaction and then find the maximum profit in prices[i+1:end] with one transaction. Then we can find the maximum sum of profit in prices[0:i] and profit in prices[i+1:end].

C++ Code:

/*
 * func: maximum_profit
 * goal: find the maximum profits if at most two transactions are permitted
 * @param prices: daily price of the stock
 * return: maximum profit
 */
/*
 * find the maximum profit in [0:i] + [i+1:end]
 * complexity: time O(n), space O(n)
 */
int maximum_profit(vector<int> &prices){
    if(prices.size() == 0){
        return 0;
    }
    vector<int> max_profit_array;
    int hold_price = prices[0];
    int max_profit = 0;
    for(const int &price : prices){
        int curr_profit = price - hold_price;
        max_profit = max(max_profit, curr_profit);
        max_profit_array.emplace_back(max_profit);
        if(price < hold_price){
            hold_price = price;
        }
    }
    
    int final_profit = max_profit_array.back();
    hold_price = prices[prices.size()-1];
    max_profit = 0;
    for(int i=prices.size()-2; i >= 0; --i){
        int curr_profit = hold_price - prices[i];
        max_profit = max(max_profit, curr_profit);
        final_profit = max(final_profit, max_profit + max_profit_array[i]);
        hold_price = max(hold_price, prices[i]);
    }
    
    return final_profit;
}

Python Code:

# func: find the maximum profit if at most two transactions are permitted
# @param prices: daily price of the stock
# @return: the max profit
# complexity: time O(n), space O(n)
def maximum_profit(prices):
    if not prices:
        return 0
    hold_price = prices[0]
    max_profit = 0
    max_profit_until = []
    for price in prices:
        curr_profit = price - hold_price
        #Find the maximum profit until now for one transaction
        max_profit = max(max_profit, curr_profit)
        max_profit_until.append(max_profit)
        hold_price = min(price, hold_price)

    #Find the maximum profit from i to the end
    hold_price = price[-1]
    max_profit = 0
    final_profit = max_profit_until[-1]
    for i in xrange(len(prices)-2, -1, -1):
        curr_profit = hold_price - prices[i]
        max_profit = max(max_profit, curr_profit)
        final_profit = max(final_profit, max_profit + max_profit_until[i])
        hold_price = max(hold_price, prices[i])
    return final_profit

LeetCode: Best Time to Buy and Sell Stock II

Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times). However, you may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).


To make the maximum profit, we can do it greedily. When the price is increasing, we hold. When the price is decreasing, we watch.

C++ Code:

/* 
 * func: maximum_profit
 * goal: find the maximum profit if we can complete as many transaction as possible
 * @param prices: daily price of the stock
 * return: maximum profit
 */
/*
 * Do it greedily.
 * complexity: time O(n), space O(1)
 */
int maximum_profit(vector<int> &prices){
    int max_profit = 0;
    for(int i=1; i<prices.size(); ++i){
        //If price is increasing, we hold
        if(prices[i] >= prices[i-1]){
            max_profit += prices[i] - prices[i-1];
        }
    }
    return max_profit;
}

Python Code:

# func: find the maximum profits if we can complete multiple transactions
# @param prices: daily price of the stock
# @return: maximum price
# complexity: time O(n), space O(1)
def maximum_profit(prices):
    max_profit = 0
    for i in xrange(1, len(prices)):
        if prices[i] >= prices[i-1]:
            max_profit += prices[i] - prices[i-1]
    return max_profit

LeetCode: Best Time to Buy and Sell Stock

Say you have an array for which the ith element is the price of a given stock on day i.

If you were only permitted to complete at most one transaction (ie, buy one and sell one share of the stock), design an algorithm to find the maximum profit.


We can keep record the maximum profit we can make while reading price data.

C++ Code:

/*
 * func: best_time_buy_sell
 * goal: find the maximum profit if only one trasaction permitted
 * @param prices: the prices of stock
 * return: maximum profit
 */
/*
 * keep recording the profit when reading the price data
 * complexity: time O(n), space O(1)
 */
int best_time_buy_sell(vector<int> &prices){
    int max_profit = 0;
    if(prices.size() <= 1){
        return 0;
    }
    int curr_min = prices[0];
    int curr_max = prices[0];
    for(const int &price : prices){
        //If current price is lower than the minimum
        //Reset the minimum and maximum
        //The reason for resetting the curr_max is that we
        //Can only find a higher price occuring later than
        //Current price.
        if(price < curr_min){
            curr_min = price;
            curr_max = price;
        }
        //If current price is higher than the maximum
        //Then a potential higher profit may be found
        if(price > curr_max){
            curr_max = price;
        }
        max_profit = max(max_profit, curr_max-curr_min);
    }
    
    return max_profit;
}

Python Code:

# func: find the maximum profit if only one transaction is permitted
# @param prices: daily price of the stock
# @return: maximum price
# complexity: time O(n), space O(1)
def maximum_profit(prices):
    if not prices:
        return 0
    max_profit = 0
    curr_min = prices[0]
    curr_max = prices[0]
    for price in prices:
        if price < curr_min:
            curr_min = price
            curr_max = price

        if price > curr_max:
            curr_max = price

        max_profit = max(max_profit, curr_max - curr_min)

    return max_profit

Tuesday, May 27, 2014

LeetCode: Triangle

Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.

For example, given the following triangle

[
     [2],
    [3,4],
   [6,5,7],
  [4,1,8,3]
]

The minimum path sum from top to bottom is 11 (i.e., 2 + 3 + 5 + 1 = 11).

Note:
Bonus point if you are able to do this using only O(n) extra space, where n is the total number of rows in the triangle.


We can create an array of the same size as the last row of the triangle, so the minimum sum to each element in the row is the minimum sum from its above left and above right.

C++ Code:

/*
 * func: minimum_path_sum
 * goal: find the minimum path sum from top to bottom.
 * @param triangle: input number triangle
 * return: minimum sum
 */
int minimum_path_sum(vector<vector<int> > &triangle){
    if(triangle.size() == 0){
        return 0;
    }
    vector<int> sum(triangle.size(), 0);
    sum[0] = triangle[0][0];
    for(int i=1; i<triangle.size(); ++i){
        vector<int> prev_sum = sum;
        for(int j = 0; j < triangle[i].size(); ++j){
            if(j == 0){
                sum[j] = prev_sum[j] + triangle[i][j];
            }else if(j == triangle[i].size()-1){
                sum[j] = prev_sum[j-1] + triangle[i][j];
            }else{
                sum[j] = min(prev_sum[j-1], prev_sum[j]) + triangle[i][j];
            }
        }
    }
    
    int min_sum = sum[0];
    for(const int &it : sum){
        min_sum = std::min(it, min_sum);
    }
    
    return min_sum;
}

Python Code:

# func: find the minimum path sum from top to bottom
# @param triangle: input triangle
# @return: minimum sum
def minimum_path_sum(triangle):
    if not triangle:
        return 0
    
    #Compute the sum bottom up
    prev_sum_list = triangle[-1]
    for i in xrange(2, len(triangle)+1):
        curr_sum_list = []
        for j in xrange(len(triangle[-i])):
            curr_sum_list += [triangle[-i][j] + min(prev_sum_list[j], prev_sum_list[j+1])]

        prev_sum_list = curr_sum_list

    return prev_sum_list[0]