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