Friday, November 23, 2012

Partition List

Problem:
Given a linked list and a value x, partition it such that all nodes less than x come before nodes greater than or equal to x.
You should preserve the original relative order of the nodes in each of the two partitions.
For example,
Given 1->4->3->2->5->2 and x = 3,
return 1->2->2->4->3->5.

Solution:
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *partition(ListNode *head, int x) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
       
        ListNode* small = NULL, *smallHead = NULL, *temp, *bigHead = NULL;
        temp = head;
       
        while (temp != NULL && (temp->val < x)) {
            if (small == NULL) {
                smallHead = small = temp;
            } else {
                small->next = temp;
                small = small->next;
            }
            temp = temp->next;
            // making small->next = NULL is optional
            small->next = NULL;
        }
        bigHead = temp;
        while (temp != NULL && temp->next != NULL) {
            if (temp->next->val < x){
                if (small == NULL) {
                    smallHead = small = temp->next;
                } else {
                    small->next = temp->next;
                    small = small->next;
                }
                temp->next = temp->next->next;
                // making small->next = NULL is optional
                small->next = NULL;
                continue;
            }
            temp = temp->next;
        }
        if (small) {
            small->next = bigHead;
            return smallHead;
        }
        return head;
    }
};

Wednesday, November 21, 2012

Anagrams

Problem:

Given an array of strings, return all groups of strings that are anagrams.
Note: All inputs will be in lower-case.

Solution:
class Solution {
public:
    vector<string> anagrams(vector<string> &strs) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        map<string, map<char, int> > strMap;
        map<map<char, int>, int> mapCount;
        vector<string> result;

        if (strs.size() == 0)
            return result;
       
        for (int i=0; i < strs.size(); i++) {
            map<char, int> charMap;
            string str = strs[i];
            for (int j = 0; j < str.size(); j++) {
                charMap[str[j]] += 1;
            }
            strMap[str] = charMap;
            mapCount[charMap] += 1;
        }
       
        for (int i = 0; i < strs.size(); i++) {
            if (mapCount[strMap[strs[i]]] > 1) {
                result.push_back(strs[i]);
            }
        }
        return result;
    }
};

============ Alternate =============
Instead of using:
            map<char, int> charMap;
            string str = strs[i];
            for (int j = 0; j < str.size(); j++) {
                charMap[str[j]] += 1;
            }
we can sort each strs[i] and map this sorted string to its count, and use this mapping in the same way as we use mapCount above, like:

class Solution {
public:
    vector<string> anagrams(vector<string> &strs) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        vector<string> solution;
        int size = strs.size();
       
        if (size < 2)
            return solution;
       
        map<string, int> count_map;
        for (int i = 0; i < size; i++) {
            string temp = strs[i];
            sort(temp.begin(), temp.end());
            count_map[temp]++;
        }
       
        for (int i = 0; i < size; i++) {
            string temp = strs[i];
            sort(temp.begin(), temp.end());
            if (count_map[temp] > 1)
                solution.push_back(strs[i]);
        }
        return solution;
    }
};


==================== Alternate =====================


class Solution {
public:
    vector<string> anagrams(vector<string> &strs) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        vector<string> solution;
        map<string, vector<string> > hash;
        int size = strs.size();
        if (size < 2)
            return solution;
         
        for (int i = 0; i < size; i++) {
            string sorted_str = strs[i];
            sort(sorted_str.begin(), sorted_str.end());
            if (!hash[sorted_str].empty())
                hash[sorted_str].push_back(strs[i]);
            else {
                vector<string> hash_row;
                hash_row.push_back(strs[i]);
                hash[sorted_str] = hash_row;
            }
        }

        map<string, vector<string> > :: iterator iter;
     
        for (iter = hash.begin(); iter != hash.end(); iter++) {
            vector<string> ans = iter->second;
            int hash_row_size = ans.size();
            if (hash_row_size == 1)
                continue;
            for (int j = 0; j < hash_row_size; j++) {
                solution.push_back(ans[j]);
            }
        }
     
        return solution;
    }
};
 
==== Neat one ====

vector<vector<string>> groupAnagrams(vector<string>& strs) {
        map<string, vector<string> > mp;
        vector<vector<string> > ans;
       
        for (int i = 0; i < strs.size(); i++) {
            string tmp = strs[i];
            sort(tmp.begin(), tmp.end());
            mp[tmp].push_back(strs[i]);
        }
        map<string, vector<string> >::iterator it;
        for (it = mp.begin(); it != mp.end(); it++) {
            ans.push_back(it->second);
        }
        return ans;
}

Monday, November 12, 2012

3Sum Closest

Problem:
Given an array S of n integers, find three integers in S such that the sum is closest to a given number, target. Return the sum of the three integers. You may assume that each input would have exactly one solution.

Solution:
class Solution {
public:
    int threeSumClosest(vector<int> &num, int target) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
         int i, j, k, sum, diff, clos1, clos2, storedMinDiff, storedMaxDiff;

        vector<vector<int> > solution;
        sort(num.begin(), num.end());
       
        int length = num.size();
        storedMinDiff = target - num[length - 1] - num[length - 2] - num[length - 3];
        storedMaxDiff = target - num[0] - num[1] - num[2];
       
        for (i = 0; i < num.size(); i++) {
            j = i+1;
            k = num.size() - 1;
            while (j < k) {
                sum = num[i] + num[j] + num[k];
                diff = target - sum;

                if (diff == 0) {
                    return sum;
                } else if (diff < 0) {
                    k--;
                    if (diff != max(storedMinDiff, diff))
                        continue;
                    storedMinDiff = diff;
                    // Closest sum in negatives.
                    clos1 = sum;
                } else if (diff > 0) {
                    j++;
                    if (diff != min(storedMaxDiff, diff))
                        continue;
                    storedMaxDiff = diff;
                    // Closest sum in positives.
                    clos2 = sum;
                }

            }

        }
        if (abs(target - clos1) < abs(target - clos2))
            return clos1;
        else
            return clos2;

    }
};

Best Time to Buy and Sell Stock II

Problem:
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).

Solution:
class Solution {
public:
    int maxProfit(vector<int> &prices) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
       
        if (prices.size() < 2)
            return 0;
        int minSoFar = prices[0];
        int maxSoFar = prices[0];
       
        int bestBuy = 0;
        int totalMaxBestBuy = 0;
       
        int it;
        for(it = 1; it < prices.size(); it++) {
            if (prices[it] >= maxSoFar) {
                maxSoFar = prices[it];
                bestBuy = maxSoFar - minSoFar;
            }
            else{
                // It means prices are going down as well. add previous bestBuy
                totalMaxBestBuy = totalMaxBestBuy + bestBuy;
                minSoFar = prices[it];
                maxSoFar = prices[it];
                bestBuy = 0;
            }
        }
        totalMaxBestBuy = totalMaxBestBuy + bestBuy;
        return totalMaxBestBuy;
    }
};

Add Two Numbers

Problem: 
You are given two linked lists representing two non-negative numbers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.

Solution:
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *addTwoNumbers(ListNode *l1, ListNode *l2) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        int carry = 0;
        ListNode *sol = NULL;
        ListNode *temp = NULL;
   
        while (l1 != NULL || l2 != NULL) {
            int sum =  (l1 != NULL ? l1->val : 0) + (l2 != NULL ? l2->val : 0) + carry;
            carry = sum/10;
            ListNode *node = new ListNode(sum % 10);
       
            if (sol == NULL) {
                sol = node;
                temp = sol;
            }
            else {
                temp->next = node;
                temp = temp->next;
            }
            l1 = (l1 != NULL ? l1->next : NULL);
            l2 = (l2 != NULL ? l2->next : NULL);
        }
       
        if (l1 == NULL && l2 == NULL && carry) {
            ListNode *node = new ListNode(carry);
            temp->next = node;
        }
        return sol;
    }
};

Sunday, November 11, 2012

Remove Element

Problem:
Given an array and a value, remove all instances of that value in place and return the new length.
The order of elements can be changed. It doesn't matter what you leave beyond the new length.

Solution:
class Solution {
public:
    int removeElement(int A[], int n, int elem) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        int i=0, count = 0, j = n-1;
        while (i < n && i <= j) {
            if (A[i] == elem) {
                swap(A[i], A[j--]);
                count++;
                continue;
            }
            i++;
        }
        return n - count;
    }
    void swap(int &a, int &b) {
        int temp;
        temp = a;
        a = b;
        b = temp;
    }
};

=============== Another smaller one =============
class Solution {
public:
    int removeElement(int A[], int n, int elem) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        int iter = 0;
        for (int i = 0; i < n; i++) {
            if (A[i] != elem)
                A[iter++] = A[i];
        }
        return iter;
    }
};

Friday, November 9, 2012

Same Tree

Problem:
Given two binary trees, write a function to check if they are equal or not.
Two binary trees are considered equal if they are structurally identical and the nodes have the same value.

Solution:
/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    bool isSameTree(TreeNode *p, TreeNode *q) {
        if (p == NULL && q == NULL)
            return true;
        else if (p != NULL && q != NULL)
            return p -> val == q -> val &&
                isSameTree(p -> left, q -> left) &&
                isSameTree(p -> right, q -> right);
        else
            return false;
    }
};

Thursday, November 8, 2012

Merge k Sorted Lists

Problem:
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.

Iterative Solution:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *mergeKLists(vector<ListNode *> &lists) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        vector<ListNode *> sol;
        if (lists.size() == 0)
            return NULL;

        while (lists.size() - 1) {
            mergeTwo(lists[0], lists[1]);
            lists.erase(lists.begin() + 1);
        }
        return lists[0];
    }
  
    void mergeTwo(ListNode* &first, ListNode* &second) {
        ListNode *temp=NULL, *temp2=NULL, *prev=NULL, *iter = first;

        if (first == NULL) {
            first = second;
            return;
        }
        while(second!=NULL) {
            if (iter->val > second->val) {
                if (prev != NULL) {
                    // Insert into first, but not in beginning
                    temp2=second->next;
                    prev->next = second;
                    second->next = iter;
                    iter = second;
                    second = temp2;              
                } else {
                    // Insert into first at the beginning
                    temp = first;
                    temp2 = second->next;
                    first = second;
                    second->next = temp;
                    second = temp2;            
                }
            } else if (iter->next != NULL) {
                prev = iter;
                iter = iter->next;
            } else if (iter->next == NULL) {
                iter->next = second;
                second = NULL;
            }
        }
    }
};

Recursive Solution by Runhe Tian:

class Solution {
public:
    ListNode *mergeKLists(vector<ListNode *> &lists) {
        ListNode *head = NULL;
     for(int i = 0; i < lists.size(); ++i)
   head = mergeTwoLists(head, lists[i]);
  return head;
    }

private: 
    ListNode *mergeTwoLists(ListNode *head1, ListNode *head2) {
  if (head1 == NULL || head2 == NULL)
   return head1 == NULL ? head2 : head1;

        if (head1 -> val < head2 -> val) {
   head1 -> next = mergeTwoLists(head1 -> next, head2);
            return head1;
  } else {
   head2 -> next = mergeTwoLists(head2 -> next, head1);
            return head2;
  }
 }
};

======== Using min heap (make_heap) =====

class Solution {
public:
    struct comp {
      bool operator() (ListNode *a, ListNode *b) {
          int first = (a != NULL ? a -> val: -1);
          int second = (b != NULL ? b -> val: -1);
          return first > second;
      }  
    };
    
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        ListNode *ans = NULL, *head = NULL;
        
        make_heap(lists.begin(), lists.end(), comp());
        while (!lists.empty()) {
            ListNode *node = lists.front();
            pop_heap(lists.begin(), lists.end(), comp());
            
            lists.pop_back();
            if (node == NULL) {
                continue;
            }
            if (ans == NULL) {
                ans = new ListNode(node -> val);
                head = ans;
            } else {
                ans -> next = new ListNode(node -> val);
                ans = ans -> next;
            }         
            if (node -> next) {
                lists.push_back(node -> next);
                push_heap(lists.begin(), lists.end(), comp());
            }
        }
        return head;
    }
};

======== Priority queue by bangbingsyb ====
class Solution {
public:
    struct compNode {
        bool operator()(ListNode *p, ListNode *q) const {
            return p->val>q->val;
        }  
    };

    ListNode *mergeKLists(vector<ListNode *> &lists) {
        priority_queue<ListNode*, vector<ListNode*>, compNode> pq;
        ListNode *dummy = new ListNode(0), *tail = dummy;
        
        for(int i=0; i<lists.size(); i++) 
            if(lists[i]) pq.push(lists[i]);
            
        while(!pq.empty()) {
            tail->next = pq.top();
            tail = tail->next;
            pq.pop();
            if(tail->next) pq.push(tail->next);
        }
        
        return dummy->next;
    }
};
============== Another attempt ==========
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */

class comp {
    public:
        bool operator()(const ListNode *lhs, const ListNode *rhs) {
          int first = (lhs != NULL ? lhs -> val: INT_MIN);
          int second = (rhs != NULL ? rhs -> val: INT_MIN);
          return first > second;
        }
};

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        priority_queue<ListNode*, vector<ListNode*>, comp> pq;
        ListNode *dummy = new ListNode(INT_MIN);
        ListNode *prev = dummy;
        
        for (auto node : lists) {
            pq.push(node);
        }
        
        while (!pq.empty()) {
            ListNode *node = pq.top();pq.pop();
            if (node == NULL) {
                continue;
            }
            dummy -> next = node;
            if (node -> next != NULL) {
                pq.push(node -> next);
            }
            dummy = dummy -> next;
        }
        return prev -> next;
    }
};

============ Another ========
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
struct comp {
    bool operator()(ListNode *a, ListNode *b) {
        return a -> val > b -> val;
    }
};

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        ListNode *head = NULL, *trav = NULL;
        if (lists.size() == 0) {
            return head;
        }
        priority_queue<ListNode *, vector<ListNode *>, comp> pq;
        
        for (auto list : lists) {
            if (list != NULL) {
                pq.push(list);
            }
        }
        
        while (!pq.empty()) {
            ListNode *node = pq.top(); pq.pop();
            if (node -> next != NULL) {
                pq.push(node -> next);
            }
            if (trav == NULL) {
                head = node;
                trav = head;
            } else {
                trav -> next = node;
                trav = trav -> next;
            }
        }
        return head;
    }
};

======== Leetcode explanation =======

Solution


Approach 1: Brute Force

Intuition & Algorithm
  • Traverse all the linked lists and collect the values of the nodes into an array.
  • Sort and iterate over this array to get the proper value of nodes.
  • Create a new sorted linked list and extend it with the new nodes.
As for sorting, you can refer here for more about sorting algorithms.
Complexity Analysis
  • Time complexity : O(N\log N) where N is the total number of nodes.
    • Collecting all the values costs O(N) time.
    • A stable sorting algorithm costs O(N\log N) time.
    • Iterating for creating the linked list costs O(N) time.
  • Space complexity : O(N).
    • Sorting cost O(N) space (depends on the algorithm you choose).
    • Creating a new linked list costs O(N) space. 

Approach 2: Compare one by one

Algorithm
  • Compare every \text{k} nodes (head of every linked list) and get the node with the smallest value.
  • Extend the final sorted linked list with the selected nodes.
Current
1 / 11
Complexity Analysis
  • Time complexity : O(kN) where \text{k} is the number of linked lists.
    • Almost every selection of node in final linked costs O(k) (\text{k-1} times comparison).
    • There are N nodes in the final linked list.
  • Space complexity :
    • O(n) Creating a new linked list costs O(n) space.
    • O(1) It's not hard to apply in-place method - connect selected nodes instead of creating new nodes to fill the new linked list. 

Approach 3: Optimize Approach 2 by Priority Queue

Algorithm
Almost the same as the one above but optimize the comparison process by priority queue. You can refer here for more information about it.
Complexity Analysis
  • Time complexity : O(N\log k) where \text{k} is the number of linked lists.
    • The comparison cost will be reduced to O(\log k) for every pop and insertion to priority queue. But finding the node with the smallest value just costs O(1) time.
    • There are N nodes in the final linked list.
  • Space complexity :
    • O(n) Creating a new linked list costs O(n) space.
    • O(k) The code above present applies in-place method which cost O(1) space. And the priority queue (often implemented with heaps) costs O(k) space (it's far less than N in most situations). 

Approach 4: Merge lists one by one

Algorithm
Convert merge \text{k} lists problem to merge 2 lists (\text{k-1}) times. Here is the merge 2 lists problem page.
Complexity Analysis
  • Time complexity : O(kN) where \text{k} is the number of linked lists.
    • We can merge two sorted linked list in O(n) time where n is the total number of nodes in two lists.
    • Sum up the merge process and we can get: O(\sum_{i=1}^{k-1} (i*(\frac{N}{k}) + \frac{N}{k})) = O(kN).
  • Space complexity : O(1)
    • We can merge two sorted linked list in O(1) space. 

Approach 5: Merge with Divide And Conquer

Intuition & Algorithm
This approach walks alongside the one above but is improved a lot. We don't need to traverse most nodes many times repeatedly
  • Pair up \text{k} lists and merge each pair.
  • After the first pairing, \text{k} lists are merged into k/2 lists with average 2N/k length, then k/4k/8 and so on.
  • Repeat this procedure until we get the final sorted linked list.
Thus, we'll traverse almost N nodes per pairing and merging, and repeat this procedure about \log_{2}{k} times.
Divide_and_Conquer{align = "center"}
Complexity Analysis
  • Time complexity : O(N\log k) where \text{k} is the number of linked lists.
    • We can merge two sorted linked list in O(n) time where n is the total number of nodes in two lists.
    • Sum up the merge process and we can get: O\big(\sum_{i=1}^{log_{2}{k}}N \big)= O(N\log k)
  • Space complexity : O(1)
    • We can merge two sorted linked lists in O(1) space.