Tuesday, September 22, 2020

LeetCode Medium: Maximum Number of Vowels in a Substring of Given Length

Hello All,
 
Today’s coding problem is a Medium level LeetCode problem. Problem #1456: Maximum Number of Vowels in a Substring of Given Length
Link to Problem: https://leetcode.com/problems/maximum-number-of-vowels-in-a-substring-of-given-length/
Problem Approach: Sliding Window
Time Complexity: O(n) where n is the number of characters in the input string.
Space Complexity: O(1) extra space
 
 
Accepted Java solution using Sliding Window:
 
class Solution {
    public int maxVowels(String s, int k) {
        if (s==null || s.length()<k) {
            return 0;
        }
        
        int maxVowelCnt = 0;
        int currentVowelCnt = 0;
        for (int i=0; i<=s.length()-k; i++) {
            if (i==0) { //1st window only
                for (int j=i; j<i+k; j++) {
                    if (isVowel(s.charAt(j)))
                        currentVowelCnt++;
                }
            } else { //remaining windows
                if(isVowel(s.charAt(i+k-1)))
                    currentVowelCnt++;
                if(isVowel(s.charAt(i-1)))
                    currentVowelCnt--;
            }
            
            maxVowelCnt = Math.max(maxVowelCnt, currentVowelCnt);
        }
        
        return maxVowelCnt;
    }
    
    private boolean isVowel(char c) {
        return c=='a' || c=='e' || c=='i' || c=='o' || c=='u' ;
    }
}

 
Video Explanation:
 
 
 
Share your thoughts!
Happy Coding!
 
 
 

Monday, September 7, 2020

LeetCode Hard: Find Median from Data Stream

LeetCode Problem #295: Find Median from Data Stream
Link to Problem Description: https://leetcode.com/problems/find-median-from-data-stream/
Category: Applications of Binary Heaps (or Priority Queues)
Solution Approach: Divide elements into 2 mutually-balanced Priority Queues - one having elements smaller than the middle element(s), one having larger.
Time Complexity of Approach:
  • O(lg n) for insertion where n = number of elements seen so far.
  • O(n) for finding the median of elements seen so far. Space Complexity: O(n) 
 
This LeetCode Hard Problem is a pretty neat application of Binary Heaps, which are readily usable as PriorityQueues in Java.
I have discussed the possible approaches to dolve the problem in the adjoining youtube video link and in this blog post I present 2 solutions for the Twin-heap based solution:
 
Approach #1: Using PriorityQueue Collection Class of Java Collections Framework
 
 
class MedianFinder {

    PriorityQueue lesser;
    PriorityQueue greater;
    
    public MedianFinder() {
        lesser = new PriorityQueue<>(10, (o1, o2)->o2-o1);
        greater = new PriorityQueue<>();
    }
    
    public void addNum(int num) { // O(lg n)
        if (lesser.size() == 0 || num < lesser.peek()) {
            lesser.add(num);
        } else {
            greater.add(num);
        }
        
        //Balance the heaps
        if (Math.abs(lesser.size() - greater.size()) <= 1)
            return;
        
        if (lesser.size() < greater.size())
            lesser.add(greater.poll());
        else
            greater.add(lesser.poll());
    }
    
    public double findMedian() { // O(1)
        if (lesser.size() == greater.size())
            return ((double)lesser.peek() + greater.peek())/2;
        
        if (lesser.size() > greater.size())
            return lesser.peek();
        return greater.peek();
    }
}

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder obj = new MedianFinder();
 * obj.addNum(num);
 * double param_2 = obj.findMedian();
 */
 
 
Approach 2: Without using pre-build classes of Java - To test if you can code a Binary Heap data structure from scratch!
 
 
class MyPriorityQueue> {
    private ArrayList q;
    private Comparator comp;
    
    public MyPriorityQueue(int initialCapacity, Comparator cmp) {
        q = new ArrayList(initialCapacity);
        comp = cmp;
    }
    public MyPriorityQueue() {
        q = new ArrayList(10);
        comp = (o1, o2)->o1.compareTo(o2);
    }
    
    private int parent(int idx) {
        int pos = idx+1;
        return pos/2 - 1;
    }
    private int left(int idx) {
        int pos = idx+1;
        return (pos*2) -1;
    }
    private int right(int idx) {
        int pos = idx+1;
        return (pos*2+1) -1;
    }
    
    public void add (E e) {
        q.add(e);
        
        //bubble-up if necessary
        int currentIdx = q.size()-1;
        int parentIdx = parent(currentIdx);
        while (parentIdx >= 0 && comp.compare(e, q.get(parentIdx)) < 0) {
            q.set(currentIdx, q.get(parentIdx));
            q.set(parentIdx, e);
            currentIdx = parentIdx;
            parentIdx = parent(currentIdx);
        }
    }
    
    public int size() {
        return q.size();
    }
    
    public E peek() {
        if (q.isEmpty())
            return null;
        return q.get(0);
    }
    
    public void remove() {
        if (q.isEmpty())
            return;
        q.set(0, q.get(q.size()-1));
        q.remove(q.size()-1);
        minHeapify(0);
    }
    
    private void minHeapify(int i) {
        if (i >= q.size())
            return;
        
        int leastValIdx = i;
        if (left(i) < q.size() && comp.compare(q.get(leastValIdx), q.get(left(i))) > 0)
            leastValIdx = left(i);
        if (right(i) < q.size() && comp.compare(q.get(leastValIdx), q.get(right(i))) > 0)
            leastValIdx = right(i);
        
        if (leastValIdx != i) {
            E tmp = q.get(leastValIdx);
            q.set(leastValIdx, q.get(i));
            q.set(i, tmp);
            
            minHeapify(leastValIdx);
        }
    }
}

class MedianFinder {

    /** initialize your data structure here. */
    MyPriorityQueue lesserElementsMaxHeap;
    MyPriorityQueue greaterElementsMinHeap;
    
    public MedianFinder() {
        lesserElementsMaxHeap = new MyPriorityQueue<>(10, (o1, o2)->o2-o1);
        greaterElementsMinHeap = new MyPriorityQueue<>(); //minHeap by default
    }
    
    public void addNum(int num) {
        Integer lesserMax = lesserElementsMaxHeap.peek();
        if (lesserMax == null || lesserMax>num)
            lesserElementsMaxHeap.add(num);
        else
            greaterElementsMinHeap.add(num);
        
        
        //ensure balanced heaps
        if (Math.abs(lesserElementsMaxHeap.size() - greaterElementsMinHeap.size()) <= 1)
            return;
        
        if (lesserElementsMaxHeap.size() > greaterElementsMinHeap.size()) {
            lesserMax = lesserElementsMaxHeap.peek();
            greaterElementsMinHeap.add(lesserMax);
            lesserElementsMaxHeap.remove();
        } else {
            Integer greaterMin = greaterElementsMinHeap.peek();
            lesserElementsMaxHeap.add(greaterMin);
            greaterElementsMinHeap.remove();
        }
            
    }
    
    public double findMedian() {
        if (lesserElementsMaxHeap.size() == greaterElementsMinHeap.size())
            return ((double)lesserElementsMaxHeap.peek() + greaterElementsMinHeap.peek())/2;
        
        if (lesserElementsMaxHeap.size() > greaterElementsMinHeap.size())
            return lesserElementsMaxHeap.peek();
        else
            return greaterElementsMinHeap.peek();
    }
}

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder obj = new MedianFinder();
 * obj.addNum(num);
 * double param_2 = obj.findMedian();
 */
 
 
 Video explanation for the approach:
 
 
Share your thoughts in comments!
 
<3


 

Tuesday, September 1, 2020

LeetCode Medium: Fruit Into Baskets

Y'day I solved LeetCode medium level problem: #904: Fruit Into Baskets ()

A quick heuristics-based solution with O(n) time-complexity in Java, which has been accepted by the LeetCode judge, is :

  
  class Solution {
    public int totalFruit(int[] tree) {
        Integer[] uniqueTreeTypesIdx = new Integer[2];
        uniqueTreeTypesIdx[0] = 0;
        
        int start = 0;
        int lastConsecutiveStretchStart = 0;
        int maxStretchLength = 1;
        int i = 1; //index counter
        for (i=1; i<tree.length; i++) {
            if (tree[i] != tree[uniqueTreeTypesIdx[0]]) {
                if (uniqueTreeTypesIdx[1] == null) {
                    uniqueTreeTypesIdx[1] = i;
                    lastConsecutiveStretchStart = i;
                } else if (tree[i] != tree[uniqueTreeTypesIdx[1]]) {
                    maxStretchLength = Math.max (maxStretchLength, i-1-start+1);
                    start = lastConsecutiveStretchStart;
                    uniqueTreeTypesIdx[0] = lastConsecutiveStretchStart;
                    uniqueTreeTypesIdx[1] = i;
                    lastConsecutiveStretchStart = i;
                } else { //tree[i] != tree[uniqueTreeTypesIdx[0]] && tree[i] == tree[uniqueTreeTypesIdx[1]] 
                    if (tree[i] == tree[lastConsecutiveStretchStart])
                        ;
                    else
                        lastConsecutiveStretchStart = i;
                }
            } else {
                if (tree[i] == tree[lastConsecutiveStretchStart])
                    ;
                else
                    lastConsecutiveStretchStart = i;
            }
        }
        maxStretchLength = Math.max (maxStretchLength, i-1-start+1);
        return maxStretchLength;
    }
}
  


I would like to re-visit it in near-times, to devise a more elegant and intuitive solution approach, but this is it for now!

Stay tuned for the next one!


Featured Post

interviewBit Medium: Palindrome Partitioning II

Problem Name:  Palindrome Partitioning II Problem Description : https://www.interviewbit.com/problems/palindrome-partitioning-ii/ Problem Ap...