Solve Travelling Salesman Problem using DP

Claim

In this post, I am using the same notation as in the video below and the wiki page (https://en.wikipedia.org/wiki/Held%E2%80%93Karp_algorithm)

 

Our problem is given a matrix costs in which costs[i][j] is the traveling cost going from node i to node j, we are asked to return the minimum cost for traveling from node 0 and visit every other node exactly once and return back to node 0.

We use g(x, S) to denote the minimum cost  for a travel which starts from node x, visits every node in S exactly once and returns to node 0. The detailed DP process can be illustrated in the following example.

Let’s say we have a 5×5 costs matrix, which means we have 5 node.

In the first round, we calculate:

g(1, ∅) = costs(1,0)   # minimum cost of travel from node 1 and ends at node 0 without visiting any other node
g(2, ∅) = costs(2,0)
g(3, ∅) = costs(3,0)
g(4, ∅) = costs(4,0)

In the second round, we calculate:

# minimum cost of travel from node 1 and ends at node 0 with visiting only one other node
g(1, {2}) = costs(1,2) + g(2, ∅)   
g(1, {3}) = costs(1,3) + g(3, ∅)
g(1, {4}) = costs(1,4) + g(4, ∅)


# minimum cost of travel from node 2 and ends at node 0 with visiting only one other node
...

In the third round, we calculate:

# minimum cost of travel from node 1 and ends at node 0 with visiting two other nodes
g(1, {2,3}) = min(costs(1,2)+g(2,{3}), costs(1,3)+g(3,{2}))  # either you choose to go from 1 to 2 first, or go from 1 to 3 first   
g(1, {3,4}) = min(costs(1,3)+g(3,{4}), costs(1,4)+g(4,{3}))
g(1, {2,4}) = min(costs(1,2)+g(2,{4}), costs(1,4)+g(4,{2}))


# minimum cost of travel from node 2 and ends at node 0 with visiting two other nodes
...

In the fourth round, we calculate:

# minimum cost of travel from node 1 and ends at node 0 with visiting three other nodes
# either you choose to go from 1 to 2 first, or go from 1 to 3 first or go from 1 to 4 first 
g(1, {2,3,4}) = min(costs(1,2)+g(2,{3,4}), costs(1,3)+g(3,{2,4}), costs(1,4)+g(4,{2,3}))

# minimum cost of travel from node 2 and ends at node 0 with visiting three other nodes
...

 

After the four rounds above, we can calculate our ultimate goal, g(0, {1,2,3,4}):

g(0, {1, 2,3,4}) = min(costs(0,1)+g(1,{2,3,4}),
                       costs(0,2)+g(2,{1,3,4}),
                       costs(0,3)+g(3,{1,2,4}),
                       costs(0,4)+g(4,{1,2,3}))

 

Now here it is an implementation in Python:

from itertools import *
class Solution:
    def shortest(self, costs):
        '''
        costs: List[List[n]]
        '''
        def to_str(l):
            return '-'.join([str(ll) for ll in l])

    # nodes excluding the starting node 
    nodes = set([i for i,v in enumerate(costs) if i])
    g = {}
    for x in xrange(1, len(costs)):
        g[x] = {}
        g[x][''] = costs[x][0]   

    for k in xrange(1, len(costs)-1):
        for x in xrange(1, len(costs)):
            for c in combinations(node-set([x]), k):
                g[x][to_str(c)] = min([costs[x][e] + g[e][to_str(c-set([e]))] for e in c])

    return min([costs[0][e] +g[e][to_str(nodes-set([e]))] for e in nodes])

In the code `g` is a dictionary. Its value its another dictionary. For example, if we want to refer to g(1, {2,3,4}), we can access it by `g[1][‘2-3-4’]`.

 

Let’s analyze the time complexity and the space complexity for this DP algorithm. 

  • Space complexity: before calculating g(0, {1,2,3,4}), we need to store:
g(1, {∅}), g(1, {2}), g(1,{3}), g(1,{4}), g(1,{2,3}), g(1, {2,4}), g(1, {3,4}), g(1, {2,3,4})
g(2, {∅}), g(2, {1}), g(2,{3}), g(2,{4}), g(2,{1,2}), g(2, {1,3}), g(2, {3,4}), g(2, {1,3,4})
...

Therefore, for each row here, we need to store g(.) values of number $latex \sum\limits_{k=1}^{n-2} C_{n-1}^k = O(2^n) $ (n is total number of nodes: 0, 1, .., n-1). We have n-1 rows here. So in total space complexity is $latex O(n2^n)$.

  • Time complexity: we know we have $latex O(n2^n)$ g(.) values to store. We get each of these values by taking min function over O(n) possible candidates(See Python code line 20). Therefore, the total time complexity is $latex O(n^2 2^n)$.

 

Leetcode 156: Binary Tree Upside Down

https://leetcode.com/problems/binary-tree-upside-down/

Binary Tree Upside Down

Total Accepted: 4861 Total Submissions: 13934 Difficulty: Medium

Given a binary tree where all the right nodes are either leaf nodes with a sibling (a left node that shares the same parent node) or empty, flip it upside down and turn it into a tree where the original right nodes turned into left leaf nodes. Return the new root.

For example:
Given a binary tree {1,2,3,4,5},

    1
   / \
  2   3
 / \
4   5

return the root of the binary tree [4,5,2,#,#,3,1].

   4
  / \
 5   2
    / \
   3   1  

confused what "{1,#,2,3}" means? > read more on how binary tree is serialized on OJ.

OJ’s Binary Tree Serialization:

The serialization of a binary tree follows a level order traversal, where ‘#’ signifies a path terminator where no node exists below.

Here’s an example:

   1
  / \
 2   3
    /
   4
    \
     5

The above binary tree is serialized as "{1,2,3,#,#,4,#,#,5}".

 
Code
class Solution:
        # @param root, a tree node
        # @return root of the upside down tree
        def upsideDownBinaryTree(self, root):
            # take care of the empty case
            if not root:
                return root
            # take care of the root
            l = root.left
            r = root.right
            root.left = None
            root.right = None
            # update the left and the right children, form the new tree, update root
            while l:
                newL = l.left
                newR = l.right
                l.left = r
                l.right = root
                root = l
                l = newL
                r = newR
            return root

 

 
Idea
First, let’s walk through what is called “upside down”. I draw an example plot. The original tree roots at 7. To turn it upside down, you first mirror it along a horizontal line. Then, for any branch stretching towards right top (marked as red), you make it as left leaf.
 tree
 
 

Now, the basic idea is to go though from the original root (7), make root’s left child (1) as new parent whose left child will be the original root’s right left (6) and right child will be the original root (7). After that, you set new root to root.left (1), and process it in the same way. As pointed out by (https://leetcode.com/discuss/18410/easy-o-n-iteration-solution-java), the core idea is to “Just think about how you can save the tree information you need before changing the tree structure“.

 

Reference

https://leetcode.com/discuss/30902/iteration-python-solution

https://leetcode.com/discuss/18410/easy-o-n-iteration-solution-java

Leetcode 145: Binary Tree Postorder Traversal

https://leetcode.com/problems/binary-tree-postorder-traversal/

Binary Tree Postorder Traversal

Total Accepted: 76404 Total Submissions: 229965 Difficulty: Hard

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].

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

 

Idea:

This problem is similar to a previous post. However, it can be done using a property regarding preorder and postorder: postorder traverse = reverse of {preorder traverse with right node visited earlier than left node}.

The detailed implementation: modify the previous post code. You still do a preorder traverse but with the order: [root], [right], [left]. To do so, when you push to the stack, you push node.left first then push node.right. So when you pop the stack, you will visit the right node first then sometimes later the left node.  At last, when you return the `res` list, you return its reverse. 

In practice you have two choices, either always append values to `res` left, or use normal append but return `res[::-1]`.

 

Code

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    # An alternative: append left to res
    # def postorderTraversal(self, root):
    #     st = [root]
    #     res = []
    #     while st:
    #         root = st.pop()
    #         if root:
    #             res.insert(0,root.val)
    #             st.append(root.left)
    #             st.append(root.right)
    #     return res
        
    def postorderTraversal(self, root):
        st = [root]
        res = []
        while st:
            root = st.pop()
            if root:
                res.append(root.val)
                st.append(root.left)
                st.append(root.right)
        return res[::-1]

 

Leetcode 144: Binary Tree Preorder Traversal

https://leetcode.com/discuss/23326/very-simple-iterative-python-solution

Binary Tree Preorder Traversal

Total Accepted: 89130 Total Submissions: 240451 Difficulty: Medium

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?

 

Code

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def preorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        stack = [root]
        res = []
        while stack:
            node  = stack.pop()
            if node:
                res += [node.val]
                stack.append(node.right)
                stack.append(node.left)
                
        return res

 

Idea

Preorder: [root], [left], [right]

So we use a stack to add right first then left. so whenever we pop an element out, the left element will always be popped out before the right element. Since we want to return preorder result, whenever an element is popped out, we add it to `res` immediately.

Leetcode 235: Lowest Common Ancestor of a Binary Search Tree (BST)

https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/

Lowest Common Ancestor of a Binary Search Tree

Total Accepted: 30832 Total Submissions: 81197 Difficulty: Easy

Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the BST.

According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes v and w as the lowest node in T that has both v and w as descendants (where we allow a node to be a descendant of itself).”

        _______6______
       /              \
    ___2__          ___8__
   /      \        /      \
   0      _4       7       9
         /  \
         3   5

For example, the lowest common ancestor (LCA) of nodes 2 and 8 is 6. Another example is LCA of nodes 2 and 4 is 2, since a node can be a descendant of itself according to the LCA definition.

 

Code

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        if p.val > q.val:
            p, q = q, p
            
        if p.val < root.val and q.val > root.val:
            return root
        if p.val == root.val or q.val == root.val:
            return root
        
        if p.val < root.val and q.val < root.val:
            return self.lowestCommonAncestor(root.left, p, q)
        else:
            return self.lowestCommonAncestor(root.right, p, q)
            
            

 

Idea

Since it is already a BST, it should be easy. Please go to another post to see how to find lowest common ancestor for normal binary tree.

Leetcode 245: Shortest Word Distance III

https://leetcode.com/problems/shortest-word-distance-iii/

Shortest Word Distance III

Total Accepted: 1824 Total Submissions: 4282 Difficulty: Medium

This is a follow up of Shortest Word Distance. The only difference is now word1 could be the same as word2.

Given a list of words and two words word1 and word2, return the shortest distance between these two words in the list.

word1 and word2 may be the same and they represent two individual words in the list.

For example,
Assume that words = ["practice", "makes", "perfect", "coding", "makes"].

Given word1 = “makes”, word2 = “coding”, return 1.
Given word1 = "makes", word2 = "makes", return 3.

Note:
You may assume word1 and word2 are both in the list.

Code

import sys
class Solution(object):
    def shortestWordDistance(self, words, word1, word2):
        """
        :type words: List[str]
        :type word1: str
        :type word2: str
        :rtype: int
        """
        
        min_dis = sys.maxint
        p1, p2 = min_dis, -min_dis
        for i in xrange(len(words)):
            w = words[i]
            if w not in set([word1, word2]):
                continue
            if word1 == word2:
                p1 = p2
                p2 = i
            else:
                if w == word1:
                    p1 = i
                if w == word2:
                    p2 = i
           
            min_dis = min(min_dis, abs(p1 - p2))
        
        return min_dis

 

Idea

A little similar to the previous post. The only difference is that, if word1==word2, then we need to set `p1` to previous `p2`, and set `p2` to current `i`. We keep unchanged how `min_dis` is determined: min(min_dis, abs(p1 – p2)) 

 

Leetcode 244: Shortest Word Distance II

https://leetcode.com/problems/shortest-word-distance-ii/

Shortest Word Distance II

Total Accepted: 1811 Total Submissions: 5300 Difficulty: Medium

This is a follow up of Shortest Word Distance. The only difference is now you are given the list of words and your method will be called repeatedly many times with different parameters. How would you optimize it?

Design a class which receives a list of words in the constructor, and implements a method that takes two words word1 and word2 and return the shortest distance between these two words in the list.

For example,
Assume that words = ["practice", "makes", "perfect", "coding", "makes"].

Given word1 = “coding”, word2 = “practice”, return 3.
Given word1 = "makes", word2 = "coding", return 1.

Note:
You may assume that word1 does not equal to word2, and word1 and word2 are both in the list.

Code

class WordDistance(object):
    def __init__(self, words):
        """
        initialize your data structure here.
        :type words: List[str]
        """
        word_dic = dict()
        for i in xrange(len(words)):
            w = words[i]
            if word_dic.get(w, None):
                word_dic[w].append(i)
            else:
                word_dic[w] = [i]
        self.word_dic = word_dic

    def shortest(self, word1, word2):
        """
        Adds a word into the data structure.
        :type word1: str
        :type word2: str
        :rtype: int
        """
        l1 = self.word_dic[word1]
        l2 = self.word_dic[word2]
        min_dis = sys.maxint
        p1 = 0
        p2 = 0
        while p1 < len(l1) and p2 < len(l2):
            min_dis = min(min_dis, abs(l1[p1]-l2[p2]))
            if min_dis == 1:
                 return 1            
            if l1[p1] > l2[p2]:
                p2 += 1
            else:
                p1 += 1
        return min_dis

        

# Your WordDistance object will be instantiated and called as such:
# wordDistance = WordDistance(words)
# wordDistance.shortest("word1", "word2")
# wordDistance.shortest("anotherWord1", "anotherWord2")

 

Idea

Since the function `shortest` will be called repeatedly, we need to use more space in trade of less time. We know from the previous post that if we don’t use any space we can have an O(N) algorithm. Therefore, in this problem, we want to achieve shorter time than O(N) by using more space.

What we do is just using a dictionary to store positions for words. (key: word, value: a list of positions in `words` where the word appears). Since we traverse from words[0] to words[-1], we guarantee every position list stores positions in an ascending order. Then, when we call `shortest(words, word1, word2)`. we extract `word1`’s and `word2`’s position lists. 

The crux of the algorithm is that if p1 (a position from word1’s position list) is already larger than p2 (a position from word2’s position list), then every position after p1 in the word1’s position list will have larger distance with p2. Therefore, you don’t need to compare those positions after p1 anymore. Instead, you increase p2 in order to let it be closer to p1.

The time complexity is O(m+n) where m and n are the lengths of l1 and l2. 

 

Reference: https://discuss.leetcode.com/topic/20643/java-solution-using-hashmap

Leetcode 243: Shortest Word Distance

https://leetcode.com/problems/shortest-word-distance/

Shortest Word Distance

Total Accepted: 2492 Total Submissions: 5946 Difficulty: Easy

Given a list of words and two words word1 and word2, return the shortest distance between these two words in the list.

For example,
Assume that words = ["practice", "makes", "perfect", "coding", "makes"].

Given word1 = “coding”, word2 = “practice”, return 3.
Given word1 = "makes", word2 = "coding", return 1.

Note:
You may assume that word1 does not equal to word2, and word1 and word2 are both in the list.

Code

import sys
class Solution(object):
    def shortestDistance(self, words, word1, word2):
        """
        :type words: List[str]
        :type word1: str
        :type word2: str
        :rtype: int
        """
        p1 = p2 = -1
        min_dis = sys.maxint
        for i in xrange(len(words)):
            w = words[i]
            if w not in [word1, word2]:
                continue
            if w == word1:
                p1 = i
            if w == word2:
                p2 = i
            if p1 != -1 and p2 != -1:
                min_dis = min(min_dis, abs(p1 - p2))
        
        return min_dis

 

Idea

Just need O(N) time to traverse the `word` list once. `p1` and `p2` are the last index of seeing word1 and word2. If the current visit word is either word1 or word2, you can update the min distance just by `abs(p1-p2)`.

Leetcode 241: Different Ways to Add Parenthesis

https://leetcode.com/problems/different-ways-to-add-parentheses/

Different Ways to Add Parentheses

Total Accepted: 9627 Total Submissions: 34073 Difficulty: Medium

Given a string of numbers and operators, return all possible results from computing all the different possible ways to group numbers and operators. The valid operators are +,- and *.

Example 1

Input: "2-1-1".

((2-1)-1) = 0
(2-(1-1)) = 2

Output: [0, 2]

Example 2

Input: "2*3-4*5"

(2*(3-(4*5))) = -34
((2*3)-(4*5)) = -14
((2*(3-4))*5) = -10
(2*((3-4)*5)) = -10
(((2*3)-4)*5) = 10

Output: [-34, -14, -10, -10, 10]

 

Code

class Solution(object):
    def diffWaysToCompute(self, input):
        if input.isdigit():
            return [int(input)]
        res = []        
        for i in xrange(len(input)):
            if input[i] in "-+*":
                res1 = self.diffWaysToCompute(input[:i])
                res2 = self.diffWaysToCompute(input[i+1:])
                res += [eval(str(k)+input[i]+str(j)) for k in res1 for j in res2]            
        return res

 

Idea

Deal it with recursion. Whenever you see an operator (`-`, `+` or `*`), you get results from the operator’s left side (line 8) and right side (line 9) and concatenate results from both sides with the operator (line 10) . Remember that to judge if a string represents a pure number you can use `string.isdigit()`. (line 3)

 

Reference

https://leetcode.com/discuss/53566/python-easy-to-understand-solution-divide-and-conquer

Leetcode 22: Generate Parentheses

https://leetcode.com/problems/generate-parentheses/

Generate Parentheses

Total Accepted: 62423 Total Submissions: 186597 Difficulty: Medium

Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.

For example, given n = 3, a solution set is:

"((()))", "(()())", "(())()", "()(())", "()()()"

Code

class Solution(object):
    def generateParenthesis(self, n):
        """
        :type n: int
        :rtype: List[str]
        """
        res = []
        def gen(s, left, right, res):
            if left:
                gen(s+"(", left-1, right, res)
            if right > left:
                gen(s+")", left, right-1, res)
            if not right:
                res += [s]
        gen("", n, n, res)
        return res

Idea

Use recursion to solve this problem. You can also apply this idea to other similar problems, such as print “if else” blocks.

Another variant is that if you know the result of `generateParenthesis(n)` (a list of valid strings), then you can know `generateParenthesis(n+1)` by concatenating “()” in front of all strings in `generateParenthesis(n)`, or add “(” and “)” at the beginning and the end of all strings in `generateParenthesis(n)`.