Category Archives: Algorithms

Longest Common Substring

Task

Given two strings text1 and text2, return the length of their longest common subsequenceIf there is no common subsequence, return 0.

subsequence of a string is a new string generated from the original string with some characters (can be none) deleted without changing the relative order of the remaining characters.

  • For example, "ace" is a subsequence of "abcde".

common subsequence of two strings is a subsequence that is common to both strings.

 

Example 1:

Input:

 text1 = "abcde", text2 = "ace" 

Output:

 3  

Explanation:

 The longest common subsequence is "ace" and its length is 3.

Example 2:

Input:

 text1 = "abc", text2 = "abc"

Output:

 3

Explanation:

 The longest common subsequence is "abc" and its length is 3.

Example 3:

Input:

 text1 = "abc", text2 = "def"

Output:

 0

Explanation:

 There is no such common subsequence, so the result is 0.

 

Constraints:

  • 1 <= text1.length, text2.length <= 1000
  • text1 and text2 consist of only lowercase English characters.

This problem was taken from Leetcode Longest Common Subsequence

 

Solution

Dynamic programming with memoization.

We create a matrix to store (memoize) the response from previous calculations so we won’t re-calculate them again.

We are starting from the first row comparing every character from column 1 with the first character from row one.

There are two cases:

  • either the character match, then we add 1 and add it with the value from the diagonal to the left of the cell where we are.

Longest Common Substring Step 1

  • the characters don’t match, then we get the MAX of the value above current cell  and the value to the left of the current cell.

Longest Common Substring Step 2

Here again c int the column (first string) matches c in the row (the second string)

so we get the value of last comparison (to the left and up diagonal of the current cell)  which is 1 and add 1 again since characters match.

Longest Common Substring 3

and keep going till we reach the end of both strings which is the answer.

 

Longest Common Substring 4

 

/**
 * @param {string} text1
 * @param {string} text2
 * @return {number}
 */
var longestCommonSubsequence = function(text1, text2) {
    var txt1 = text1.split('');
    var txt2 = text2.split('');

    txt1.unshift('0');
    txt2.unshift('1');

    var l1 = txt1.length;
    var l2 = txt2.length;

    var matrix = [];
    for(var i = 0; i < l2; i ++) {
        matrix[i] = new Array(l1).fill(0);
    }

    var maxCommon = 0;
    
    for(var row = 0; row < l2; row ++) {
        for(var col = 0; col < l1; col ++) {
            var last = 0;

            if(txt1[col] == txt2[row]) {
                var previousDiagonalRowVal = row == 0 || col == 0  ? 0 : matrix[row - 1][col - 1];
                last =  1 + previousDiagonalRowVal;
            }
            else {
                var prevUp = row == 0 ?  0 : matrix[row - 1][col];
                var prevLeft = col == 0 ? 0 : matrix[row][col - 1];               
                last = Math.max(prevUp, prevLeft);
            }
            matrix[row][col] = last;
            maxCommon = last > maxCommon ? last : maxCommon;
    
        }
    }    
    return maxCommon;
};

var text1 = "abcde", text2 = "ace";
var r = longestCommonSubsequence(text1, text2);
console.log(">>", r);

Copy List with Random Pointer

Task

 

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

Construct a deep copy of the list. The deep copy should consist of exactly n brand new nodes, where each new node has its value set to the value of its corresponding original node. Both the next and random pointer of the new nodes should point to new nodes in the copied list such that the pointers in the original list and copied list represent the same list state. None of the pointers in the new list should point to nodes in the original list.

For example, if there are two nodes X and Y in the original list, where X.random --> Y, then for the corresponding two nodes x and y in the copied list, x.random --> y.

Return the head of the copied linked list.

The linked list is represented in the input/output as a list of n nodes. Each node is represented as a pair of [val, random_index] where:

  • val: an integer representing Node.val
  • random_index: the index of the node (range from 0 to n-1) that the random pointer points to, or null if it does not point to any node.

Your code will only be given the head of the original linked list.

 

Example 1:

Input:

 head = [[7,null],[13,0],[11,4],[10,2],[1,0]]

Output:

 [[7,null],[13,0],[11,4],[10,2],[1,0]]

Example 2:

Input:

 head = [[1,1],[2,1]]

Output:

 [[1,1],[2,1]]

Example 3:

Input:

 head = [[3,null],[3,0],[3,null]]

Output:

 [[3,null],[3,0],[3,null]]

 

Constraints:

  • 0 <= n <= 1000
  • -104 <= Node.val <= 104
  • Node.random is null or is pointing to some node in the linked list.

This problem was taken from https://leetcode.com/problems/copy-list-with-random-pointer/

Solution

 

Brute force using tree traversal

 

/**
 * // Definition for a Node.
 * function Node(val, next, random) {
 *    this.val = val;
 *    this.next = next;
 *    this.random = random;
 * };
 */

/**
 * @param {Node} head
 * @return {Node}
 */
var copyRandomList = function(head) {


    function dfsTraverse(node, visited={}) {
        if(!node) {
            return null;
        }
        
        // if a new node is created, return it. Otherwise you will fall into circular loops
        if(node?.clone) {
            return node?.clone;    
        }
        
        var newNode = new Node(node?.val, null, null);
        node.clone = newNode;
        var next = dfsTraverse(node?.next);
        var random = dfsTraverse(node?.random);

        newNode.next = next;
        newNode.random = random;
        return newNode;
    }

    var result = dfsTraverse(head);
    return result;
};

function Node(val, next, random) {
    this.val = val;
    this.next = next;
    this.random = random;
}
;// [1,null],[2,0],[3,1]

var nodes = {};
nodes['1'] = new Node(1,null,null);
nodes['2'] = new Node(2,null,null);
nodes['3'] = new Node(3,null,null);

nodes['1'].next = nodes['2'];
nodes['1'].random = null;

nodes['2'].next = nodes['3'];
nodes['2'].random = nodes['1'];

nodes['3'].next = null;
nodes['3'].random = nodes['2'];

//console.log("root");
//console.log(nodes['7']);
var result = copyRandomList(nodes['1']);
console.log(result);

 

 

More elegant solution

 

/**
 * // Definition for a Node.
 * function Node(val, next, random) {
 *    this.val = val;
 *    this.next = next;
 *    this.random = random;
 * };
 */

/**
 * @param {Node} head
 * @return {Node}
 */
var copyRandomList = function(head) {
  let cur = head;
  const copy = new Map();

  // add all new nodes and values for now
  while (cur) {
    copy.set(cur, new Node(cur.val));
    cur = cur.next;
  }
  
  cur = head;

  // iterate again and point curent node to the newly created nodes using the key 
  while (cur) {
    copy.get(cur).next = copy.get(cur.next) || null;
    copy.get(cur).random = copy.get(cur.random) || null;
    cur = cur.next;
  }
  
  return copy.get(head);
}


function Node(val, next, random) {
    this.val = val;
    this.next = next;
    this.random = random;
};


// [1,null],[2,0],[3,1]

var nodes = {};
nodes['1'] = new Node(1,null,null);
nodes['2'] = new Node(2,null,null);
nodes['3'] = new Node(3,null,null);

nodes['1'].next = nodes['2'];
nodes['1'].random = null;

nodes['2'].next = nodes['3'];
nodes['2'].random = nodes['1'];

nodes['3'].next = null;
nodes['3'].random = nodes['2'];


var result = copyRandomList(nodes['1']);
console.log(result);

 

 

Range Sum of BST

Task

Given the root node of a binary search tree and two integers low and high, return the sum of values of all nodes with a value in the inclusive range [low, high].

 

Example 1:

Input:

 root = [10,5,15,3,7,null,18], low = 7, high = 15

Output:

 32

Explanation:

 Nodes 7, 10, and 15 are in the range [7, 15]. 7 + 10 + 15 = 32.

Example 2:

Input:

 root = [10,5,15,3,7,13,18,1,null,6], low = 6, high = 10

Output:

 23

Explanation:

 Nodes 6, 7, and 10 are in the range [6, 10]. 6 + 7 + 10 = 23.

 

Constraints:

  • The number of nodes in the tree is in the range [1, 2 * 104].
  • 1 <= Node.val <= 105
  • 1 <= low <= high <= 105
  • All Node.val are unique.

This problem was taken from https://leetcode.com/problems/range-sum-of-bst/

 

Solution

 

DFS Solution

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} low
 * @param {number} high
 * @return {number}
 */
var rangeSumBST = function(root, low, high) {
    if(!root) {
        return;
    }
    console.log(root.val);
    var queue = [root];
    var visited = {};
    var result = 0.0;    

    function dfsTraverse(root, low, high) {
    
        var childNodes = [root?.left, root?.right];

        for(const childNode of childNodes) {
          if(childNode) {
            var val = childNode.val;
            console.log(val);
            dfsTraverse(childNode, low, high);            
          }
        }

    }

    dfsTraverse(root, low, high);
    return result;
};


 function TreeNode(val, left, right) {
     this.val = (val===undefined ? 0 : val)
     this.left = (left===undefined ? null : left)
     this.right = (right===undefined ? null : right)
 }

const treeNode = new TreeNode(
    10,
    new TreeNode(
        5,
        new TreeNode(3),
        new TreeNode(7)
    ),
    new TreeNode(
        15,
        null,
        new TreeNode(18)
    )
)


rangeSumBST(treeNode, 7, 15);

 

BFS solution

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} low
 * @param {number} high
 * @return {number}
 */
var rangeSumBST = function(root, low, high) {
    if(!root) {
        return 0;
    }
    var queue = [root];
    var visited = {};
    var result = 0.0;    
    if(root.val >= low && root.val <= high) {
        result = root.val;
    }
    
    var leftPointer = 0;
    while(leftPointer <= queue.length) {
        var curentNode = queue[leftPointer];
        leftPointer ++;
        var childNodes = [curentNode?.left, curentNode?.right];
        
        for(const childNode of childNodes) {
            if(childNode) {
                console.log(childNode.val)
                if(childNode.val >= low && childNode.val <= high) {
                    result += childNode.val;
                }
                queue.push(childNode);
            }
        }
        
           
    }
    return result;
    
};

 

Moving Average from Data Stream

Task

Given a stream of integers and a window size, calculate the moving average of all integers in the sliding window.

Implement the MovingAverage class:

  • MovingAverage(int size) Initializes the object with the size of the window size.
  • double next(int val) Returns the moving average of the last size values of the stream.

 

Example 1:

Input

["MovingAverage", "next", "next", "next", "next"]
[[3], [1], [10], [3], [5]]

Output

[null, 1.0, 5.5, 4.66667, 6.0]

Explanation

MovingAverage movingAverage = new MovingAverage(3);
movingAverage.next(1); // return 1.0 = 1 / 1
movingAverage.next(10); // return 5.5 = (1 + 10) / 2
movingAverage.next(3); // return 4.66667 = (1 + 10 + 3) / 3
movingAverage.next(5); // return 6.0 = (10 + 3 + 5) / 3

 

Constraints:

  • 1 <= size <= 1000
  • -105 <= val <= 105
  • At most 104 calls will be made to next.

Solution

/**
 * @param {number} size
 */
var MovingAverage = function(size) {
    this.n = size;
    this.queue = [];
    this.average = 0.0;
};

/** 
 * @param {number} val
 * @return {number}
 */
MovingAverage.prototype.next = function(val) {
    var removedVal;
    if(this.queue.length >= this.n) {
        removedVal = this.queue.shift();
        this.average = this.average - removedVal;
    }

    this.queue.push(val);
    
    this.average += val;
    console.log(this.average / this.queue.length);
    return this.average / this.queue.length;
};


var movingAverage = new MovingAverage(3);
movingAverage.next(1); // return 1.0 = 1 / 1
movingAverage.next(10); // return 5.5 = (1 + 10) / 2
movingAverage.next(3); // return 4.66667 = (1 + 10 + 3) / 3
movingAverage.next(5); // return 6.0 = (10 + 3 + 5) / 3

 

Implement pow(x, n)

Task

Implement pow(x, n), which calculates x raised to the power n (i.e., xn).

 

Example 1:

Input:

 x = 2.00000, n = 10

Output:

 1024.00000

Example 2:

Input:

 x = 2.10000, n = 3

Output:

 9.26100

Example 3:

Input:

 x = 2.00000, n = -2

Output:

 0.25000

Explanation:

 2

-2

 = 1/2

2

 = 1/4 = 0.25

 

Constraints:

  • -100.0 < x < 100.0
  • -231 <= n <= 231-1
  • -104 <= xn <= 104

This problem was taken from https://leetcode.com/problems/powx-n/

 

Solution

 

Brute force solution:

Straight forward, but we have to consider negative ranges.

/**
 * @param {number} x
 * @param {number} n
 * @return {number}
 */
var myPow = function(x, n) {
    
    if(n === 0) {
        return 1;
    }

    var N = n;
    var X = x;
    var result = 1;
    
    if(n < 0) {
        X = 1 / x;
        N = -n;
    }
    
    for(var i = 0; i < N; i ++) {
        result = result * X;
    }  
    return result;
};

 

Approach 2: Fast Power Algorithm Recursive

  • divide n so you immediately cut the computation time in half to logarithmic one.

pow of 2 to the power of 6

 

 

/**
 * @param {number} x
 * @param {number} n
 * @return {number}
 */
var myPow = function(x, n) {
    
    function fastPow(x, n) {
        if(n < 1) {
            return 1;
        }
        var half = fastPow(x, Math.floor(n / 2));

        if(n % 2 == 0) {
            return half * half;
        }
        else {
            return half * half * x;
        }
    }
    
    var X = x;
    var N = n;
    if(n < 0) {
        X = 1 / x;
        N = -n;        
    }
    return fastPow(X, N);
    
};

 

 

Longest Common Prefix

Task

 

Write a function to find the longest common prefix string amongst an array of strings.

If there is no common prefix, return an empty string "".

 

Example 1:

Input:

 strs = ["flower","flow","flight"]

Output:

 "fl"

Example 2:

Input:

 strs = ["dog","racecar","car"]

Output:

 ""

Explanation:

 There is no common prefix among the input strings.

 

Constraints:

  • 1 <= strs.length <= 200
  • 0 <= strs[i].length <= 200
  • strs[i] consists of only lower-case English letters.

This problem was taken from Leetcode Longest Common Prefix

 

Solution

 

Let’s examine this example:

 strs = ["flower","flow","flight"]

the longest common substring is:

 "fl"

Solution 1: Horizontal scanning

We could assume that the whole word could be the common one so we set prefix = ‘flower’
Then we would compare with the rest words and keep removing last character until prefix becomes empty (meaning no common substring was found) or until we have the common substring.

prefix flower flow flight
flower flower flower flower
flowe flowe flowe flowe
flow flow flow flow
flo flo flo flo
fl fl fl fl

 

/**
 * @param {string[]} strs
 * @return {string}
 */
var longestCommonPrefix = function(strs) {
    var prefix = strs[0];
    for(var i = 1; i < strs.length; i ++ ) {
        while(strs[i].indexOf(prefix) != 0) {
            prefix = prefix.substring(0, prefix.length - 1);
            if(prefix == "")
                return '';
        }
    }
    return prefix;    
};

 what we just did:
– set up prefix to be the whole 1st word strs[0]
– compare prefix with the second word (strs[1]) and if there is no match, remove the last symbol and keep going until it finds match.

Complexity Analysis

  • Time complexity : O(S) , where S is the sum of all characters in all strings.In the worst case all n strings are the same. The algorithm compares the string S1 with the other strings [S_2 \ldots S_n] There are S character comparisons, where S is the sum of all characters in the input array.
  • Space complexity : O(1). We only used constant extra space.

 

Solution 2: Vertical scanning

Similar but optimized for cases like the one above where we have very short common substring, and we don’t want to scan the whole word.

 

prefix flower flow flight
f f f f
fl fl fl fl
flo flo flo flo

 

/**
 * @param {string[]} strs
 * @return {string}
 */
var longestCommonPrefix = function(strs) {
    var prefix;    
    for(var i = 0; i < strs[0].length; i ++ ) {
        var c = strs[0][i];
        for(var j = 0; j < strs.length; j++) {
            if(strs[j][i] != c) {
                return strs[0].substring(0, i);
            }
        }
    }
    return strs[0];    
};

 what we just did:
– Iterate through the words like they are in column.
– compare each character (starting with the first one) between all words. When we find a mismatch, remove the last (mismatched) character and return truncates strs[0]

Roman to Integer

Task

 

Roman numerals are represented by seven different symbols: IVXLCD and M.

 

Symbol                Value

I             1
V             5
X             10
L             50
C             100
D             500
M             1000

 

For example, 2 is written as II in Roman numeral, just two one’s added together. 12 is written as XII, which is simply X + II. The number 27 is written as XXVII, which is XX + V + II.

Roman numerals are usually written largest to smallest from left to right. However, the numeral for four is not IIII. Instead, the number four is written as IV. Because the one is before the five we subtract it making four. The same principle applies to the number nine, which is written as IX. There are six instances where subtraction is used:

  • I can be placed before V (5) and X (10) to make 4 and 9.
  • X can be placed before L (50) and C (100) to make 40 and 90.
  • C can be placed before D (500) and M (1000) to make 400 and 900.

Given a roman numeral, convert it to an integer.

 

Example 1:

Input:

 s = "III"

Output:

 3

Example 2:

Input:

 s = "IV"

Output:

 4

Example 3:

Input:

 s = "IX"

Output:

 9

Example 4:

Input:

 s = "LVIII"

Output:

 58

Explanation:

 L = 50, V= 5, III = 3.

Example 5:

Input:

 s = "MCMXCIV"

Output:

 1994

Explanation:

 M = 1000, CM = 900, XC = 90 and IV = 4.

 

Constraints:

  • 1 <= s.length <= 15
  • s contains only the characters ('I', 'V', 'X', 'L', 'C', 'D', 'M').
  • It is guaranteed that s is a valid roman numeral in the range [1, 3999].

This problem was taken from Leetcode Roman To Integer

 

Solution

Solution 1: Left to right pass

/**
 * @param {string} s
 * @return {number}
 */
var romanToInt = function(s) {
    var len = s.length;
    var i = 0;
    var map = {
        'I': 1,
        'V': 5,
        'X': 10,
        'L': 50,
        'C': 100,
        'D': 500,
        'M': 1000
    }
    var sum = 0;
    while(i < len) {
        var currentVal = map[ s[i] ];
        var nextVal = map[ s[i + 1] ];
        if( currentVal < nextVal) {
            sum += nextVal - currentVal;
            i ++;            
        }
        else {
            sum += currentVal;
        }
        i ++;
    }
    return sum;
};

Solution 2: Left to right (or right to left) pass improved

/**
 * @param {string} s
 * @return {number}
 */
var romanToInt = function(s) {
    var len = s.length;
    var i = 0;
    var map = {
        'I': 1,
        'IV': 4,
        'V': 5,
        'IX': 9, 
        'X': 10,
        'XL': 40,
        'L': 50,
        'XC': 90,
        'C': 100,
        'CD': 400,
        'D': 500,
        'CM': 900,
        'M': 1000
    }
    var sum = 0;
    while(i < len) {
        var currentVal = map[ s[i] ];
        var nextVal = map[ s[i + 1] ];
        if( currentVal < nextVal) {
            var sumbol = s[i] + s[i+1];
            sum += map[sumbol];
            i ++;            
        }
        else {
            sum += currentVal;
        }
        i ++;
    }
    return sum;
};

Solution3: Right to left pass

In the “subtraction” cases, such as XC, we’ve been updating our running sum as follows:

sum += value(C) - value(X)

However, notice that this is mathematically equivalent to the following:

sum += value(C)
sum -= value(X)

Utilizing this means that we can process one symbol each time we go around the main loop. We still need to determine whether or not our current symbol should be added or subtracted by looking at the neighbour though.

This way we could start from the most right symbol an initialize the sym with it, since every most right symbol will always be added to the sum.

 

/**
 * @param {string} s
 * @return {number}
 */
var romanToInt = function(s) {
    var len = s.length;
    var i = len - 1;
    var map = {
        'I': 1,
        'V': 5,
        'X': 10,
        'L': 50,
        'C': 100,
        'D': 500,
        'M': 1000
    }
    var sum = map[ s[i] ];
    i --;
    while(i > -1) {
        var currentVal = map[ s[i] ];
        var prevVal = map[ s[i + 1] ];
        if( currentVal < prevVal) {
            sum -= currentVal;          
        }
        else {
            sum += currentVal;
        }
        i --;
    }
    return sum;
};

Container With Most Water

Task

 

Given n non-negative integers a1, a2, ..., an , where each represents a point at coordinate (i, ai)n vertical lines are drawn such that the two endpoints of the line i is at (i, ai) and (i, 0). Find two lines, which, together with the x-axis forms a container, such that the container contains the most water.

Notice that you may not slant the container.

 

Example 1:

Input:

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

Output:

 49

Explanation:

 The above vertical lines are represented by array [1,8,6,2,5,4,8,3,7]. In this case, the max area of water (blue section) the container can contain is 49.

Example 2:

Input:

 height = [1,1]

Output:

 1

Example 3:

Input:

 height = [4,3,2,1,4]

Output:

 16

Example 4:

Input:

 height = [1,2,1]

Output:

 2

 

This problem was taken from Leetcode Container With Most Water

 

Solution

A better than brute force solution is to use a variation of “sliding doors” algorithm.

Let’s consider this case: [1,3,4,3]. The area with most water will be the one with highest height and length.

To find it we set up two pointers: one at position 0, and one at the end of the array. The amount of water that could be collected here is min(leftPointerValue, rightPointerValue) * length,
where length is rightPointer – leftPointer. Which is 4.

Now it’s clear that if rightPointerValue > leftPointerValue there is no point of keep moving rightPointer because we won’t get any bigger amount of water since it will always be limited by the leftPointerValue (height) and the length will always be smaller than the previous length.

So in this case we will move the leftPointer forward to evaluate the next case.

Here the amount of the water collected is min(leftPointerValue, rightPointerValue) * length which is min(3, 3) * 3 = 9.

Nex we continue evaluating all cases till leftPointer = rightPointer (length = 0), and we didn’t find bigger amount of water collected, so the answer we found on the second evaluation is the right answer: 9.

 

/**
 * @param {number[]} height
 * @return {number}
 */
var maxArea = function(height) {


  var maxArea = 0;
  var pLeft = 0;
  var pRight = height.length - 1;
  var len = pRight - pLeft;

  while(len > 0) {
    var pLeftVal = height[pLeft];
    var pRightVal = height[pRight];

    if(pLeftVal > pRightVal) {
      maxArea = Math.max( len * pRightVal, maxArea );
      pRight --;
    }
    else {
      maxArea = Math.max( len * pLeftVal, maxArea );
      pLeft ++;
    }
    len --;
  }    
  return maxArea;
};



var height = [1,8,6,2,5,4,8,3,7];
console.log( maxArea(height) );

 

Unique-paths

Task

A robot is located at the top-left corner of a m x n grid (marked ‘Start’ in the diagram below).

The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked ‘Finish’ in the diagram below).

How many possible unique paths are there?

Note: m and n will be at most 100.

Example 1:

Input:

 m = 3, n = 2

Output:

 3

Explanation:

From the top-left corner, there are a total of 3 ways to reach the bottom-right corner:
1. Right -> Right -> Down
2. Right -> Down -> Right
3. Down -> Right -> Right

Example 2:

Input:

 m = 7, n = 3

Output:

 28

This problem was taken from Leetcode unique paths and Leetcode_unique_paths_part_II

Solution

Since we can move only right or down on every cell in the first row we will have only one place from where we can come and this is the cell before. And same for the first vertical row.

Unique Paths

Then after we figured out that there is only one way to reach each cell in the first row and the first column (which is from the cell before) we could start calculating possible ways to go to the next cells.
Let’s look at the cell in the second row and second column.There are actually two possible ways to go there: from the cell above, and the cell before, so 2 possible ways. (figure below).
The cell in the third column on the second row: same 1 way from the cell above, and from the cell before. But since there are already 2 ways to reach the cell before the total ways to reach this cell is: 1 + 2 = 3.

 

The solution:

/**
 * @param {number} m
 * @param {number} n
 * @return {number}
 */
var uniquePaths = function(m, n) {
    
    var memo = [];

    for(var i=0;i < n; i ++) {
        for(var j = 0; j < m; j ++) {
            var index = (i * m) + j; 
            if(i == 0) {
                memo[index] = 1;
            }
            else if(j == 0) {
                memo[index] = 1;
            }
            else {
                var up = index - m;
                var left = index - 1;
                memo[index] = memo[up] + memo[left];
            }
        }
    }
    return memo[memo.length - 1];
}

console.log(uniquePaths(7,3));

 

Unique paths with obstacles.

 

/**
 * @param {number[][]} obstacleGrid
 * @return {number}
 */
var uniquePathsWithObstacles = function(obstacleGrid) {
    var m = obstacleGrid[0].length;
    var n = obstacleGrid.length;
    var row = 0;
    if(obstacleGrid[0][0] == 1)
        return 0;

    var memo = [];

    for(var i=0;i < n; i ++) {
        for(var j = 0; j < m; j ++) {
            var index = (i * m) + j; 
            if(i == 0) {
                if(obstacleGrid[i][j] == 1 || (j > 0 && memo[index -1] == 0))
                    memo[index] = 0;
                else                
                    memo[index] = 1;
            }
            else if(j == 0) {
                if(obstacleGrid[i][j] == 1 || (i > 0 && memo[index - m] == 0))
                    memo[index] = 0;
                else                
                    memo[index] = 1;
            }
            else {
                var up = index - m;
                var left = index - 1;
                if(obstacleGrid[i][j] == 1)
                    memo[index] = 0;
                else
                    memo[index] = memo[up] + memo[left];
            }
            row += memo[index] ? 0 : 1;
        }
        if(row == m)
            return 0;            
        row = 0;
    }
    return memo[memo.length - 1];
};


var grid = [
  [0,0,0],
  [0,1,0],
  [0,0,0]
];

console.log(uniquePathsWithObstacles(grid));

 

Intersection of Two Linked Lists

Task

Write a program to find the node at which the intersection of two singly linked lists begins.

For example, the following two linked lists:

begin to intersect at node c1.

 

Example 1:

This problem was taken from Leetcode

Solution

We are not asked to compare the values inside the linked lists but the list node objects, so we could ignore the values of the list.

Approach 1: Brute Force

For each node ai in list A, traverse the entire list B and check if any node in list B coincides with ai.

Complexity Analysis

  • Time complexity : O(mn).
  • Space complexity : O(1).

Approach 2: Calculating the length of the two linked lists and compare the elements that could potentially intersect.

  • Time complexity : O(m+n).
  • Space complexity : O(m) or O(n).
 function ListNode(val) {
      this.val = val;
      this.next = null;
 }

headA = new ListNode(4);
headA.next = new ListNode(1);


headB = new ListNode(5);
headB.next = new ListNode(0);
headB.next.next = new ListNode(1);

headA.next.next = headB.next.next.next = new ListNode(8);
headB.next.next.next.next = headA.next.next.next = new ListNode(4);
headB.next.next.next.next.next = headA.next.next.next.next = new ListNode(5);


/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

/**
 * @param {ListNode} headA
 * @param {ListNode} headB
 * @return {ListNode}
 */


var getIntersectionNode = function(headA, headB) {
      let node = headA;
      let countA = 0;
      while(node != null ) {            
            node = node.next;           
            countA ++;       
      }    

      node = headB;
      let countB = 0;
      while(node != null ) {            
            node = node.next;           
            countB ++;       
      }    


      let longList, shortList, diff, iteratorLongLength,iteratorShortLength;
      if(countA > countB) 
        longList = headA, shortList = headB,  diff = countA - countB;
      else
        longList = headB, shortList = headA, diff =  countB - countA;


      let i = 0;
      while(shortList != null) {
        if(i < diff ) {
              longList = longList.next; 
        }
        else {
              console.log("long list, short list", longList.val, shortList.val);
              if(longList == shortList) 
                  return longList.val;
              longList = longList.next; 
              shortList = shortList.next;     
        }        

      i ++;      
      }
};


console.log (getIntersectionNode(headA, headB) );

what we just did:
– we calculated the length of the first list to be 5 and the second 6 (first and the second loop)
– the third loop is doing two things:
– first since the difference between the shorter and the longer list is 1 we move the cursor to the second element of the longer list (lines 59-61)
– after we position the longer list cursor at the second element we could start comparing (line 64)

If we execute the function we will see this result:

long list, short list 0 4
long list, short list 1 1
long list, short list 8 8

And the third element is exactly where the intersection is.

Approach 3: Traverse both lists and when reaching the end of each one, move the pointer to the opposite list and traverse again till intersection is found.

  • Time complexity : O(m+n).
  • Space complexity : O(m) or O(n).

This basically is the same concept as in the example above, just written in a bit more elegant way.

 function ListNode(val) {
      this.val = val;
      this.next = null;
 }

headA = new ListNode(4);
headA.next = new ListNode(1);


headB = new ListNode(5);
headB.next = new ListNode(0);
headB.next.next = new ListNode(1);

headA.next.next = headB.next.next.next = new ListNode(8);
headB.next.next.next.next = headA.next.next.next = new ListNode(4);
headB.next.next.next.next.next = headA.next.next.next.next = new ListNode(5);


/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

/**
 * @param {ListNode} headA
 * @param {ListNode} headB
 * @return {ListNode}
 */


var getIntersectionNode = function(headA, headB) {
      let nodeA = headA;
      let nodeB = headB;
      let swapA = false;
      let swapB = false;
      var i = 0;
      while(nodeA != null && nodeB!=null ) {
            // node A            
            if(!swapA && nodeA.next == null) {
                  nodeA = headB;
                  swapA = true;
            }
            else {
                  nodeA = nodeA.next;
            }
            // node B
            if(!swapB && nodeB.next == null) {
                  nodeB = headA;
                  swapB = true;
            }
            else {
                  nodeB = nodeB.next;
            }            


            if(nodeA === nodeB)
                  return nodeA.val;

      }    
};


console.log (getIntersectionNode(headA, headB) );

what we just did:
– traverse listA and listB till we reach the end of each one (lines 47 and 55) .
– once we reach the end of each list we point the cursor to the opposite list (lines 43 and 51)

Approach 4: Hash Table

Traverse list A and store the address / reference to each node in a hash set. Then check every node bi in list B: if bi appears in the hash set, then bi is the intersection node.

Complexity Analysis

  • Time complexity : O(m+n).
  • Space complexity : O(m) or O(n).
 function ListNode(val) {
      this.val = val;
      this.next = null;
 }

// link-list A: [4,1,8,4,5]
// link-list B: [5,0,1,8,4,5]

headA = new ListNode(4);
headA.next = new ListNode(1);


headB = new ListNode(5);
headB.next = new ListNode(0);
headB.next.next = new ListNode(1);

headA.next.next = headB.next.next.next = new ListNode(8);
headB.next.next.next.next = headA.next.next.next = new ListNode(4);
headB.next.next.next.next.next = headA.next.next.next.next = new ListNode(5);


/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

/**
 * @param {ListNode} headA
 * @param {ListNode} headB
 * @return {ListNode}
 */


var getIntersectionNode = function(headA, headB) {
      let hashMap = {}; 
      let node = headA;
      while(node != null ) {
            
            hashMap[node.val] = node;
            node = node.next;                  
      }    

      node = headB;
      while(node != null) {
            let val = node.val;
            if(hashMap[val] == node) {
                  return val;
            }
            node = node.next;
      }
};

console.log ("result: ", getIntersectionNode(headA, headB) );