Tag Archives: algorithms

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 lays 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) );

 

Check if string has all unique characters

Task

Implement an algorithm to determine if a string (of characters from ‘a’ to ‘z’) has all unique characters or not.

Example 1:

var s = "abcde";
returns true;

Example 2:

var s = "abcade";
returns false;

Solution

Solution 1: The brute force solution will be to iterate through all characters and compare with all other characters.

function areCharactersUnique(s) {
    for(let i=0; i < s.length; i++) {
        for(let j=0; j < s.length; j++) {
            if(i == j)
                continue;
            if(s[i] == s[j]) {
                return false;
            }
        }

    }
    return true;
}

var s = "abcade";

console.log(areCharactersUnique(s));

Solution 2: Using an array (or hashMap table) with key equal to the ASCII character code.

function areCharactersUnique(s) {
    var checker = new Array(26);
    for(let i=0; i < s.length; i++) {
        var pos = s[i].charCodeAt(0) - 'a'.charCodeAt(0);
        if(typeof checker[pos] != 'undefined') {
            return false;
        }
        checker[pos] = 1;
    }
    return true;
}

var s = "abcde";

console.log(areCharactersUnique(s));

Let’s make it more challenging and prohibit the use of additional data structures like count array, hash, etc.

Solution 3: Using bitwise operations to store into 32 bit if one of all 26 characters is presented or not.
We have 26 letters (from a to z). Let’s imagine that we could have 26 empty slots that we could set up to true if the character exists, pretty much as if we have a hashTable.
for simplicity I will use only 6 slots (from a to G) instead of all 26 that represent the whole alphabet.
In addition we have to mention that the slots are actually 32 (this is usually the length of an integer in JavaScript but we need only 26)

6 5 4 3 2 1 0
G F E D C B A
false false false false false false false

Given the string: ‘ABCG’ for example we will end up with this matrix.

6 5 4 3 2 1 0
G F E D C B A
true false false false true true true

But this could be stored into 32bit value using bitwise operations. The binary representation of the matrix above will be:  1000111

The solution:

function areCharactersUnique(s) {
    var checker = 0;

    for(let i=0; i < s.length; i++) {
        // Charcode of a is 97 but we want to start with 0
        var val = s[i].charCodeAt(0) - 'a'.charCodeAt(0);
        // & - Sets each bit to 1 if both bits are 1
        // examples: 
        // 1 & 10 = 0
        // 1 & 101 = 1
        if(checker & ( 1 << val)) {
            return false;
        }

        // | - Sets each bit to 1 if one of two bits is 1
        // examples:
        // 1 | 10 = 11
        // 1 | 1 = 1
        checker = checker | ( 1 << val);
    }
    return true;
}

what we just did:
– we started with creating a loop to go through all characters
– we set up an empty value checker to store if the character is used or not (this is the binary representation of the matrix above)
– (line 6) grabbing the value for each letter in the string but removing ‘a’ = 97 so ‘a’ character will be equal to 0 and z = 26
– (line 19) we are setting the position of the character into the checker to true using bitwise shift left (1 << a) and preserving other already set positions using bitwise | ‘or’
– (line 11) using the same technique but with bitwise & ‘and’ we check if the character position is set to true or not.

Here is a step by  step example for ‘ABCG’ character:
– initially checker = 0 // or the binary representation is '00000000...0'
– we are going to insert a using Zero fill left shift. This is basically going to add ‘1’ followed by as many ‘0’ to the right of the checker as the value of ‘a’ is. In this case 0, so schecker = 1 // binary '00000000...1

– next step inserting ‘b’ follows the same procedure: b = 1, (1 << b) = 2 '000000...10' but we also want to preserve whatever was already inserted so we use bitwise ‘|’ ‘or’ which sets each bit to 1 if one of two bits is 1.
so
checker = checker | (1 << b) = 2
or the binary representation will be:
checker = '00000000...1' | (1 << b) = '000000000...11'

and the same for the rest of the characters

var a = 0;
var b = 1;
var c = 2;
var d = 3;
var e = 4;
var f = 5;
var g = 6;

var s = "abcg";
var checker = 0; 

checker = checker | (1 << a);   // 1
checker = checker | (1 << b);   // 11
checker = checker | (1 << c);   // 111
checker = checker | (1 << g);   // 1000111

Let’s modify the problem, and ask to return the index of the first unique character in the string.
For example for string ‘abcac’ the return will be the index of b – ‘1’
This problem is asked in Leetcode

/**
 * @param {string} s
 * @return {number}
 */
var firstUniqChar = function(s) {
    
    let lastSingle = null;
    let hashMap = {};
    for(var i = 0;i < s.length; i ++) {
        var val = s[i];
        hashMap[val] =  hashMap[val] == undefined ? i: 'not-unique';        
    }

    for(let i=0; i < s.length; i++) {
        var key = s[i];
        if(hashMap[key] != 'not-unique') {
            return hashMap[key];
        }
    }
    return -1;

}

 

LRU Cache

Task

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

get(key) – Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
put(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.

The cache is initialized with a positive capacity.

Follow up:
Could you do both operations in O(1) time complexity?

Example:

LRUCache cache = new LRUCache( 2 /* capacity */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // returns 1
cache.put(3, 3);    // evicts key 2
cache.get(2);       // returns -1 (not found)
cache.put(4, 4);    // evicts key 1
cache.get(1);       // returns -1 (not found)
cache.get(3);       // returns 3
cache.get(4);       // returns 4

This problem was taken from Leetcode

Solution

The brute force solution will be to use array, to push every new element at the top, and on ‘get’ to pop out the element and to put it at the top of the array.

A better solution will be to use hashmap where the element retrieval will be O(1) (constant) but the hashmap is not keeping track of the order of the elements. To solve this problem we are going to link elements in the hashnmap table with double linked list, where each element will point to its previous and next sibling.
On every ‘get’ operation we are going to re-link the element and it’s siblings.

When the cache reaches the capacity we are going to remove the least used node from the bottom.

put(1,1) put(2,2) get(1) put(3,3) get(2) put(4,4) get(1) get(3) get(4)
return 1 -1 -1 3 4
result 1 2,1 1,2 3,1 => (2) 3,1 4,3 => (1) 4,3 3,4 4,3

 

class LRUCache {

  constructor(capacity) {
    
        this.head = null;
        this.tail = null;
        this.capacity = capacity;
        this.count = 0;
    this.hashMap  = new Map();    
  }

 
  get(key) {
    var node = this.hashMap.get(key);
    if(node) {
      if(node == this.head) {
        // node is already at the head, just return the value
        return node.val;
      }			
      if(this.tail == node && this.tail.prev) {
        // if the node is at the tail,
        // set tail to the previous node if it exists.
        this.tail = this.tail.prev;
        this.tail.next = null;
      }
      // link neibouring nodes together
      if(node.prev)
        node.prev.next = node.next;
      if(node.next)
        node.next.prev = node.prev;			
      // add the new head node
      node.prev = null;
      node.next = this.head;
      this.head.prev = node;
      this.head = node;

      return node.val;
    }
    return -1;
  }

  put(key, val) {
    this.count ++;
    var newNode = { key, val, prev: null, next: null };

    if(this.head == null) {
      // this.hashMap is empty creating new node
      this.head =  newNode;
      this.tail = newNode;
    }
    else {
      var oldNode = this.hashMap.get(key);
      if(oldNode) {
        // if node with the same key exists, 
        // clear prev and next pointers before deleting the node.
        if(oldNode.next) {
          if(oldNode.prev)
            oldNode.next.prev = oldNode.prev;
          else
            this.head = oldNode.next;
        }
        if(oldNode.prev) {					
          oldNode.prev.next = oldNode.next;
          if(oldNode == this.tail)
            this.tail = oldNode.prev;
        }
        // removing the node
        this.hashMap.delete(key);
        this.count --;				
      }

      // adding the new node and set up the pointers to it's neibouring nodes			
      var currentHead = this.head;
      currentHead.prev = newNode;				
      newNode.next = currentHead;
      this.head = newNode;

      if(this.tail == null)
        this.tail = currentHead;

      if(this.count == this.capacity + 1) {
        // remove last nove if over capacity
        var lastNode = this.tail;
        this.tail = lastNode.prev;
        if(!this.tail) {
          //debugger;
        }
        this.tail.next = null;
        this.hashMap.delete(lastNode.key);
        this.count --;
      }

    }
    this.hashMap.set(key, newNode);
    return null;
  }
}

 

Trapping Rain Water

Task

Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.


image was borrowed from leetcode

The above elevation map is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) are being trapped. Thanks Marcos for contributing this image!

Example:

Input:

 [0,1,0,2,1,0,1,3,2,1,2,1]

Output:

 6

This problem was taken from Leetcode

Solution

 

The brute force approach: for each element we go to the right and find the maximum height of the bar, then we go to the left and do the same.

For any element the maximum amount of the water that could be trapped will be the minimum of left height and right height, minus the height of the bar.

So for the array [0,1,0,2,1,0,1,3,2,1,2,1] we go all the way to the right and calculate the max right value, starting from first element ‘0’ max right will be 0. ‘1’ – max right is ‘1’ and so on.
We repeat the same from last element ‘1’ to the first one.

Then the trapped water for the first column will be:  min(maxRight, maxLeft) – theArrayElement[n]

the array 0 1 0 2 1 0 1 3 2 1 2 1
max right 0 1 1 2 2 2 2 3 3 3 3 3
max left 3 3 3 3 3 3 3 3 2 2 2 1
collected
water
0 0 1 0 1 2 1 0 0 1 0 0

 

The complexity will be O(n2)

/**
 * @param {number[]} height
 * @return {number}
 */
var trap = function(height) {
    if(height.length < 2)
        return 0;

    let findMaxLeft = function(idx, height) {
        let max = 0;
        for(let i =idx;i >= 0; i --) {
            max = Math.max(max, height[i]);
        }
        return max;
    }

    let findMaxRight = function(idx, height) {
        let max = 0;
        for(let i = idx;i < height.length; i ++) {
            max = Math.max(max, height[i]);
        }
        return max;
    }  

    let collectedWater = 0;
    for(let i = 0;i < height.length; i ++) {

        const maxLeft = findMaxLeft(i, height);
        const maxRight = findMaxRight(i, height);

        let min = Math.min(maxLeft, maxRight);
        collectedWater += (min - height[i]);
    }

    return collectedWater;
};

The better solution: find all max left and max right with one loop, then do a second loop for each element in the array, and calculate trapped water.

/**
 * @param {number[]} height
 * @return {number}
 */
var trap = function(height) {
    let maxLeftArray = [], maxRightArray = [];
    let maxLeft = 0, maxRight = 0;
    const ln = height.length;
    let trappedWater = 0;

    for(let i = 0;i < height.length; i ++) {
        maxLeftArray[i] = Math.max(height[i], maxLeft);
        maxLeft = maxLeftArray[i];

        maxRightArray[ln - i - 1] = Math.max(height[ln - i - 1], maxRight);
        maxRight = maxRightArray[ln - i - 1];
    }

    for(let i = 0;i < height.length; i ++) {
        trappedWater += Math.min(maxLeftArray[i], maxRightArray[i]) - height[i];
    }
    return trappedWater;

};
what we just did:

– With one loop find the max left and right bar on each side.
– for any element the maximum amount of the water that could be trapped will be the minimum of left height and right height, minus the height of the bar.

Array VS Hash Table

Hash table tutorial

Find element in Array

function fuindInArray() {
  var t0 = performance.now();

  for(var q = 0; q < data2.length; q++) {
      if( data2[q] == '106112407') {
          console.log(data2["106112407"]);
          break;
      }
  }

Find element in Hash Table

function findInHashtable() {
  var t0 = performance.now();

  console.log(data1["106112407"]);

  var t1 = performance.now();
  document.querySelector('#result1').value = "Call took " + (t1 - t0) + " milliseconds.";
}

 


Majority element in array

Task

Given an array of size n, find the majority element. The majority element is the element that appears more than ⌊ n/2 ⌋ times.

You may assume that the array is non-empty and the majority element always exist in the array.

Example 1:

Input:

 [3,2,3]

Output:

 3

Example 2:

Input:

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

Output:

 2

This problem was taken from Leetcode

Solution

The solution:

Create an object (or associative array, depends of the language), iterate through all elements in the array adding them to the newly created object using the element value as a key. If element exists increase the value +1.
Keep track of mostly repeated element and return it at the end.

var arr = [3, 3, 4, 2, 4, 2, 4, 4];


/**
 * @param {number[]} nums
 * @return {number}
 */
var majorityElement = function(nums) {
  var elements = {};
  var majorityElement = nums[0];
  var majorityElementCount = 1;
  for(var i in nums) {
  	var element = nums[i];
  	elements[element] = typeof elements[element] == 'undefined' ? 1 : elements[element] + 1;
  	if(elements[element] > majorityElementCount) {
  		majorityElement = element;
  		majorityElementCount = elements[element];
  	}
  }    
  return majorityElement;
};

console.log(">>", majorityElement(arr));

– Create an object (line 9)
– Iterate through each element in the array feeling up the object with the elements from the array where the element value would be the key of the element (line 14)
– Each time when the element exists, add + 1
– Keep track of mostly repeated element (Lines 16-17)

Could we optimize the code? Slightly. When we add + 1 we could check if the value is already greater than the length of the array / 2 and if se we know that this is the majority element because no other element could have greater value.

...
    if(elements[element] > nums.length / 2) {
        return element;
    }
...

 

Maximum Subarray

Task

Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.

Example:

Input:

 [-2,1,-3,4,-1,2,1,-5,4],

Output:

 6

Explanation:

 [4,-1,2,1] has the largest sum = 6.

This problem was taken from Leetcode

Solution

The solution:

Brut force.

The straight forward solution could be to iterate through all elements in the array and calculate the subarray values and compare them.

In the example above[-2,1,-3,4,-1,2,1,-5,4] we will do:

starting index: 1
-2 = -2
-2, 1 = -1
-2, 1, -3 = -4
-2, 1, -3, 4 = 0
-2, 1, -3, 4, -1 = -1
-2, 1, -3, 4, -1, 2 = 1
-2, 1, -3, 4, -1, 2, 1 = 2
-2, 1, -3, 4, -1, 2, 1, -5 = -3
-2, 1, -3, 4, -1, 2, 1, -5, 4 = 1

starting index: 2
1 = 1
1, -3 = -2
1, -3, 4 = 2
1, -3, 4, -1 = 1
1, -3, 4, -1, 2 = 3
1, -3, 4, -1, 2, 1 = 4
1, -3, 4, -1, 2, 1, -5 = -1
1, -3, 4, -1, 2, 1, -5, 4 = 3

starting index: 3
-3 = -3
-3, 4 = 1
-3, 4, -1 = 0
-3, 4, -1, 2 = 2
-3, 4, -1, 2, 1 = 3
-3, 4, -1, 2, 1, -5 = -2
-3, 4, -1, 2, 1, -5, 4 = 2

starting index: 4
4 = 4
4, -1 = 3
4, -1, 2 = 5
4, -1, 2, 1 = 6
4, -1, 2, 1, -5 = 1
4, -1, 2, 1, -5, 4 = 5

… and so on till the last element in the array.

So the winner clearly is 4, -1, 2, 1 = 6, but this approach will take a lot of repetitions. Interestingly there is a linear solution called: Kadane’s algorithm.

Using Kadene’s algorithm.

The basic idea of this algorithm is to break the array into a sets of mutually exclusive sets, calculate their sums and find the largest one.

First let’s look closely of what we are doing to find the maximum sum using brut force. We are splitting the array to a sets of all possible contiguous sub arrays and we calculate their sum. This means that:
– if the array contains only negative values we don’t really need to split the array cause the answer will be the largest value in the array. i.e. [-1,-5,-3] = -1 (the one close to 0)
– if this is a mixed array with negative and positive values the max sum of contiguous sub array will be > 0 so we could ignore any case where the sum is negative.

This way we could iterate through each element of the array nums[i]  where i is the index of the element in the array (starting with the first one nums[0]), and calculate the sum (let’s call it max_here = max_here + nums[i] ).
If we get a negative result we already know for sure that this is not what we are looking for and we set up max_here to the next element in the array max_here = nums[i]

So in the example above: [-2,1,-3,4,-1,2,1,-5,4]
We are starting by setting up both params to the first element in the array: max_here =max_so_far = nums[0] . We are going to use max_so_far to store the maximum sum discovered so far, and max_here to calculate the maximum sum so far. Once again if the max_sum is negative, we just set it up to be equal to the next element in the array nums[i] so on the next iteration max_sum = nums[i-1] + nums[i]

Starting with setting up max_here = max_so_far = nums[0] = -2

i nums[i]    action described max_here max_so_far
1 1  sum = max_here + nums[1], which is:
sum = -2 + 1 = -1 which is smaller than nums[1] so max_here = nums[1] = 1 (line 10 in the code snipped below)
and since max_here > max_so_far,  max_so_far = max_here =1 (line 11)
1 1
2 -3  sum = max_here + nums[2] which is:
sum = 1 + (-3) = - 2 which is bigger than nums[2] which is -3 so max_here = sum = -2But max_here is smaller than max_so_far so max_so_far stays equal to 1
-2 1
3 4  sum = -2 + 4 = 2 which is < than 4 so  max_here = nums[4] = 4 which is > max_so_far so max_so_far = max_here = 4 4 4
4 -1   sum = 4 - 1 = 3 > – 1 so
max_here = sum = 3
3 4
5 2  sum = 3 + 2 = 5
which is > than nums[5] = 2 so
max_here = max_so_far = sum = 5
5 5
6 1  sum = 5 + 1 = 6
which is > than nums[6] = 1 so
max_here = max_so_far = sum = 6
6 6
7 -5  sum = 6 + (-5) = 1
which is > than nums[7] = -5 so
max_here = 1
1 6
8 4  sum = 1 + 4 = 5
which is > than nums[8] = 4 so
max_here = 5 but
max_here < max_so_far so
max_so_far stays the same: 6 which is the maximum sum here.
5 6

 

/**
 * @param {number[]} nums
 * @return {number}
 */
var maxSubArray = function(nums) {
    var max_here = max_so_far = nums[0];
    

    for(var i=1;i < nums.length; i ++) {
        max_here = Math.max(max_here + nums[i], nums[i]);
        max_so_far = Math.max(max_so_far, max_here);
    }    

    return max_so_far;
};