Leetcode and other problems in C#

Last updated on
299 min read

Table of Contents

Slowest Key

Link - Slowest Key

Naive Approach

public char SlowestKey(int[] releaseTimes, string keysPressed) {
	int prev = releaseTimes[0];
	char output = keysPressed[0];
	int maxDist = releaseTimes[0];

	for(int i = 1; i < releaseTimes.Length; i++){
		int diff = releaseTimes[i] - prev;

		if(diff > maxDist || (diff == maxDist && keysPressed[i] > output)){
			maxDist = diff;
			output = keysPressed[i];
		}

		prev = releaseTimes[i];
	}

	return output;
}

Subsets

Link - Subsets

Using DFS

public IList<IList<int>> Subsets(int[] nums) {
	IList<IList<int>> output = new List<IList<int>>();

	this.Combinations(nums, 0, output, new List<int>());
	return output;
}

public void Combinations(int[] nums, int currIndex, IList<IList<int>> output, List<int> tempList){
	output.Add(new List<int>(tempList));

	for(int i = currIndex; i < nums.Length; i++){
		tempList.Add(nums[i]);
		this.Combinations(nums, i+1, output, tempList);
		tempList.RemoveAt(tempList.Count-1);
	}
}

Using Iteration

Initially add the empty array to the output list. Next, iterate through all the numbers of the input array, and on each iteration, create a copy of all the items from the output list and add the number from the current iteration. See the below example.

Example

input = [1, 2]

Initial: []

Iteration 1: [], [1]

Iteration 2: [], [1], [2], [1,2]

public IList<IList<int>> Subsets(int[] nums) {
	IList<IList<int>> output = new List<IList<int>>();

	output.Add(new List<int>());

	for(int i = 0; i < nums.Length; i++){
		int outN = output.Count;

		for(int j = 0; j < outN; j++){
			List<int> subset = new List<int>(output[j]);
			subset.Add(nums[i]);
			output.Add(subset);
		}

	}

	return output;
}

Minimum Initial Energy to Finish Tasks

Link - Minimum Initial Energy to Finish Tasks

Greedy Approach

public int MinimumEffort(int[][] tasks) {
	Array.Sort(tasks, (a,b) => (a[1] - a[0]) - (b[1] - b[0]));

	int output = 0;

	foreach(int[] task in tasks){
		output = Math.Max(output + task[0], task[1]);
	}

	return output;
}

Coin Change 2

Link - Coin Change 2

Top Down Appraoch

public int Change(int amount, int[] coins) {
	return this.Recursion(amount, coins, 0);
}

public int Recursion(int amount, int[] coins, int index){
	if(amount < 0){
		return 0;
	}

	if(amount == 0){
		return 1;
	}

	if(index == coins.Length){
		return 0;
	}

	return this.Recursion(amount, coins, index + 1, map) + this.Recursion(amount - coins[index], coins, index, map);
}

Top Down Approach with Memoization

public int Change(int amount, int[] coins) {
	Dictionary<string, int> map = new Dictionary<string, int>();
	return this.Recursion(amount, coins, 0, map);
}

public int Recursion(int amount, int[] coins, int index, Dictionary<string, int> map){
	if(amount < 0){
		return 0;
	}

	if(amount == 0){
		return 1;
	}

	if(index == coins.Length){
		return 0;
	}

	string key = amount.ToString() + "$" + index.ToString();

	if(map.ContainsKey(key)){
		return map[key];
	}

	map[key] = this.Recursion(amount, coins, index + 1, map) + this.Recursion(amount - coins[index], coins, index, map);

	return map[key];
}

132 Pattern

Link - 132 Pattern

Brute Force (Time Limit Exceeded Error)

public bool Find132pattern(int[] nums) {
	if(nums.Length < 3){
		return false;
	}

	for(int i = 0; i < nums.Length; i++){
		for(int j = i+1; j < nums.Length; j++){
			for(int k = j+1; k < nums.Length; k++){
				if(nums[k] > nums[i] && nums[j] > nums[k]){
					return true;
				}
			}
		}
	}

	return false;
}

Using Stack

public bool Find132pattern(int[] nums) {
	if(nums.Length < 3){
		return false;
	}

	int a3 = Int32.MinValue;
	Stack<int> stack = new Stack<int>();

	for(int i = nums.Length - 1; i >= 0; i--){
		if(nums[i] < a3){
			return true;
		}
		else{
			while(stack.Count > 0 && nums[i] > stack.Peek()){
				a3 = stack.Pop();
			}
		}
		stack.Push(nums[i]);
	}

	return false;
}

Throne Inheritance

Link - Throne Inheritance

Using Dictionary and DFS

public class ThroneInheritance {

    string king;
    Dictionary<string, List<string>> map;
    HashSet<string> dead;

    public ThroneInheritance(string kingName) {
        this.king = kingName;
        map = new Dictionary<string, List<string>>();
        dead = new HashSet<string>();
    }

    public void Birth(string parentName, string childName) {
        if(!map.ContainsKey(parentName)){
            map[parentName] = new List<string>();
        }

        map[parentName].Add(childName);
    }

    public void Death(string name) {
        dead.Add(name);
    }

    public IList<string> GetInheritanceOrder() {
        List<string> list = this.GetList(king);
        List<string> output = new List<string>();

        foreach(string item in list){
            if(!dead.Contains(item)){
                output.Add(item);
            }
        }

        return output;
    }

    public List<string> GetList(string parent){
        List<string> res = new List<string>();
        res.Add(parent);

        if(!map.ContainsKey(parent)){
            map[parent] = new List<string>();
        }

        foreach(string child in map[parent]){
            res.AddRange(this.GetList(child));
        }

        return res;
    }
}

Special Array With X Elements Greater Than or Equal X

Link - Special Array With X Elements Greater Than or Equal X

Naive Approach

public int SpecialArray(int[] nums) {
	Array.Sort(nums);

	int N = nums.Length;

	for(int i = 0; i < N; i++){
		int remNum = N - i;

		if(nums[i] >= remNum && (i-1 < 0 || remNum > nums[i-1])){
			return remNum;
		}
	}

	return -1;
}

Possible Bipartition

Link - Possible Bipartition

Breadth First Search

    public bool PossibleBipartition(int N, int[][] dislikes) {
        List<List<int>> map = new List<List<int>>();

        for(int i = 0; i <= N; i++){
            map.Add(new List<int>());
        }

        foreach(int[] edge in dislikes){
            map[edge[0]].Add(edge[1]);
            map[edge[1]].Add(edge[0]);
        }

        int[] color = new int[N+1];

        for(int i = 1; i <= N; i++){
            if(color[i] == 0){
                color[i] = 1;
                Queue<int> bfsQueue = new Queue<int>();
                bfsQueue.Enqueue(i);

                while(bfsQueue.Count > 0){
                    int index = bfsQueue.Dequeue();

                    foreach(int j in map[index]){
                        if(color[j] == 0){
                            color[j] = color[index] == 1 ? 2 : 1;
                            bfsQueue.Enqueue(j);
                        }
                        else{
                            if(color[j] == color[index]){
                                return false;
                            }
                        }
                    }
                }
            }
        }

        return true;
    }

Breadth First Search

public bool PossibleBipartition(int N, int[][] dislikes) {
	List<List<int>> map = new List<List<int>>();

	for(int i = 0; i <= N; i++){
		map.Add(new List<int>());
	}

	foreach(int[] edge in dislikes){
		map[edge[0]].Add(edge[1]);
		map[edge[1]].Add(edge[0]);
	}

	int[] color = new int[N+1];

	for(int i = 1; i <= N; i++){
		if(this.DFS(map, i, color) == false){
			return false;
		}
	}

	return true;
}

public bool DFS(List<List<int>> map, int index, int[] color){
	foreach(int j in map[index]){
		if(color[j] == 0){
			color[j] = color[index] == 1 ? 2 : 1;
			if(this.DFS(map, j, color) == false){
				return false;
			}
		}
		else{
			if(color[j] == color[index]){
				return false;
			}
		}
	}
	return true;
}

Largest Number

Link - Largest Number

Using Custom Comparator Class

public class Solution {
    public string LargestNumber(int[] nums) {
        string[] arr = new string[nums.Length];

        // Converts the given array to string array
        for(int i = 0; i < nums.Length; i++){
            arr[i] = nums[i].ToString();
        }

        // Sort string in descending order
        Array.Sort(arr, new CustomComparator());

        string output = "";

        // If the output is [0,0...] then return output should be "0" and not "00"
        if(arr[0] == "0"){
            return "0";
        }

        // Concatenating the sorted array
        for(int i = 0; i < nums.Length; i++){
            output += arr[i];
        }

        return output;
    }
}

// Custom Comparator function
public class CustomComparator : IComparer<string>{
    public int Compare(string a, string b){
        string temp1  = a + b;
        string temp2 = b + a;

        return string.Compare(temp2, temp1);
    }
}

People Whose List of Favorite Companies Is Not a Subset of Another List

Link - People Whose List of Favorite Companies Is Not a Subset of Another List

Brute Force Method

public IList<int> PeopleIndexes(IList<IList<string>> favoriteCompanies) {
	List<int> output = new List<int>();

	bool[] boolArray = new bool[favoriteCompanies.Count];

	for(int i = 0; i < favoriteCompanies.Count; i++){
		HashSet<string> map = new HashSet<string>();
		foreach(string word in favoriteCompanies[i]){
			map.Add(word);
		}
		for(int j = 0; j < favoriteCompanies.Count; j++){
			if(i != j ){
				if(boolArray[j] == true){
					continue;
				}
				boolArray[j] = boolArray[j] || this.IsSubset(favoriteCompanies, j, map);
			}
		}
	}

	for(int i = 0; i < boolArray.Length; i++){
		if(boolArray[i] == false){
			output.Add(i);
		}
	}

	return output;
}

public bool IsSubset(IList<IList<string>> fc, int child, HashSet<string> map){
	if(fc[child].Count > map.Count){
		return false;
	}

	foreach(string item in fc[child]){
		if(!map.Contains(item)){
			return false;
		}
	}

	return true;
}

Max Difference You Can Get From Changing an Integer

Link - Max Difference You Can Get From Changing an Integer

Solution

public int MaxDiff(int num) {
	string inp = num.ToString();

	char[] options = new char[]{'0','1','2','3','4','5','6','7','8','9'};

	int max = Int32.MinValue;

	for(int i = 0; i < inp.Length; i++){
		for(int j = 0; j < options.Length; j++){
			max = Math.Max(max, Int32.Parse(inp.Replace(inp[i], options[j])));
		}
	}

	int min = Int32.MaxValue;

	for(int i = 0; i < inp.Length; i++){
		for(int j = 0; j < options.Length; j++){

			int temp = Int32.Parse(inp.Replace(inp[i], options[j]));

			// This condition will check if the number has any preceding zeroes
			// It will check if the number is not 0
			if(temp.ToString().Length == inp.Length && temp != 0){
				min = Math.Min(min, temp);
			}
		}
	}
	return max - min;
}

Search in a Binary Search Tree

Link - Search in a Binary Search Tree

To solve this problem simply check if a given node value is equal to the key(value to be searched). If they match then return that node. If the node value is greater than the key then repeat the process by switching to right child of the tree. Else switch to the left child of the tree.

This solution is similar to both the recursive and iterative approaches.

Using Recursion

public TreeNode SearchBST(TreeNode root, int val) {
    if(root == null){
        return null;
    }

    if(root.val == val){
        return root;
    }
    else if(root.val > val) {
        return this.SearchBST(root.left, val);
    }
    else{
        return this.SearchBST(root.right, val);
    }
}

Using Iteration

    public TreeNode SearchBST(TreeNode root, int val) {
        if(root == null){
            return null;
        }

        while(root != null){
            if(root.val == val){
                return root;
            }
            else if(root.val > val){
                root = root.left;
            }
            else{
                root = root.right;
            }
        }

        return null;
    }

Island Perimeter

Link - Island Perimeter

Using Naive Approach

public int IslandPerimeter(int[][] grid) {
	int rows = grid.Length;

	if(rows == 0){
		return 0;
	}

	int cols = grid[0].Length;

	int count = 0;

	for(int i = 0; i < rows; i++){
		for(int j = 0; j < cols; j++){
			if(grid[i][j] == 1){
				count += this.Perimeter(grid, i, j, rows, cols);
			}
		}
	}


	return count;
}

public int Perimeter(int[][] grid, int i, int j, int rows, int cols){
	int count = 0;

	if(i == 0){
		count++;
	}

	if(i == rows-1){
		count++;
	}

	if(j == 0){
		count++;
	}

	if(j == cols-1){
		count++;
	}

	if(i > 0 && grid[i-1][j] == 0){
		count++;
	}

	if(i < rows-1 && grid[i+1][j] == 0){
		count++;
	}

	if(j > 0 && grid[i][j-1] == 0){
		count++;
	}

	if(j < cols-1 && grid[i][j+1] == 0){
		count++;
	}

	return count;
}

Using Depth First Search

public int IslandPerimeter(int[][] grid) {
	int rows = grid.Length;

	if(rows == 0){
		return 0;
	}

	int cols = grid[0].Length;

	bool[,] visited = new bool[rows, cols];

	int[] dirR = new int[]{0, 0, 1, -1};
	int[] dirC = new int[]{1, -1, 0, 0};

	for(int i = 0; i < rows; i++){
		for(int j = 0; j < cols; j++){
			if(grid[i][j] == 1){
				return this.DFS(grid, i, j, rows, cols, visited, dirR, dirC);
			}
		}
	}

	return 0;
}

public int DFS(int[][] grid, int i, int j, int rows, int cols, bool[,] visited, int[] dirR, int[] dirC){
	if(i < 0 || j < 0 || i >= rows || j >= cols){
		return 1;
	}

	if(visited[i,j] == true){
		return 0;
	}

	if(grid[i][j] == 0){
		return 1;
	}

	visited[i,j] = true;

	int count = 0;

	for(int a = 0; a < 4; a++){
		int newI = i + dirR[a];
		int newJ = j + dirC[a];

		count += this.DFS(grid, newI, newJ, rows, cols, visited, dirR, dirC);
	}

	return count;
}

Number of Steps to Reduce a Number to Zero

Link - Number of Steps to Reduce a Number to Zero

public int NumberOfSteps (int num) {
    int count = 0;

    while(num != 0){
        if(num % 2 == 0){
            num /= 2;
        }
        else{
            num -= 1;
        }
        count++;
    }

    return count;
}

Increasing Triplet Subsequence

Link - Increasing Triplet Subsequence

Naive Solution

public bool IncreasingTriplet(int[] nums)
{
	int min = Int32.MaxValue;
	int max = Int32.MaxValue;

	foreach(int n in nums){
		if(n <= min){
			min = n;
		}
		else if (n <= max){
			max = n;
		}
		else{
			return true;
		}
	}

	return false;
}

Bottom Up Approach (Generic Solution - holds good for any number of increasing subsequence)

public bool IncreasingTriplet(int[] nums)
{
	int n = nums.Length;

	if (n <= 2)
	{
		return false;
	}

	int[] dp = new int[n+1];

	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j < i; j++)
		{
			if (nums[i-1] > nums[j-1])
			{
				dp[i] = Math.Max(dp[i], 1 + dp[j]);

				if (dp[i] >= 2)
				{
					return true;
				}
			}
		}
	}

	return false;
}

Search in Rotated Sorted Array

Link - Search in Rotated Sorted Array

Binary Search

First find the index where the array starts i.e the smallest element in the array. Then perform a typical binary search but with respect to the actual lowest index instead of 0th index.

public int Search(int[] nums, int target) {
	int n = nums.Length;

	// Find min element
	int low = 0;
	int high = n - 1;

	while(low < high){
		int mid = low + ((high - low)/2);
		if(nums[mid] > nums[high]){
			low = mid + 1;
		}
		else{
			high = mid;
		}
	}

	// Perform relative binary search
	int startIndex = low;
	low = 0;
	high = n - 1;

	while(low <= high){
		int mid = low + ((high - low)/2);
		int relativeMid = (mid + startIndex) % n;

		if(nums[relativeMid] == target){
			return relativeMid;
		}
		else if(nums[relativeMid] < target){
			low = mid + 1;
		}
		else{
			high = mid - 1;
		}
	}

	return -1;
}

Find GCD and LCM of two or more numbers

Finding GCD (Greatest Common Divisor)

GCD is also called as HCF (Highest Common Factor). It’s the largest number that divides both of them. See the following code to find the GCD of two numbers.

public int GCD(int a, int b){
    if (b == 0){
      return a;
    }
    return this.GCD(b, a % b);
}

Finding LCM (Least Common Multiple)

It is the smallest positive integer that is divisible by both the numbers. LCM can be found by using the following formula.

LCM = (a * b) / GCD

public int LCM(int a, int b){
    return (a*b) / this.GCD(a,b);
}

Finding LCM without using GCD

In this method first find the maximum of two numbers. Then start looping through the large number until both the numbers return 0 remainder. See the following code

public static int LCM(int a, int b){
    int maxNum = Math.Max(a,b);

    while(true){
        if(maxNum % a == 0 && maxNum % b == 0){
            return maxNum;
        }
        maxNum++;
    }
    return maxNum;
}

Finding GCD of more than 2 numbers

Recursively call the GCD function (for 2 numbers) to get the GCD of N numbers.

public static int GCD(int numbers){
    int output = numbers[0];

    for(int i = 1; i < numbers.Length; i++){
        output = this.GCD(output, numbers[i]);
    }
    return output;
}

Finding LCM of more than 2 numbers

Use the same formula that we used earlier recursively to get the LCM

public static int LCM(int numbers){
    int output = numbers[0];

    for(int i = 1; i < numbers.Length; i++){
        output = (output * numbers[i]) / (this.GCD(output, numbers[i]));
    }
    return output;
}

Letter Combinations of a Phone Number

Link - Letter Combinations of a Phone Number

Using Recursive Approach

public IList<string> LetterCombinations(string digits) {
	List<string> output = new List<string>();

	if(digits.Length == 0){
		return output;
	}

	Dictionary<int, string> map = new Dictionary<int, string>();
	map[2] = "abc";
	map[3] = "def";
	map[4] = "ghi";
	map[5] = "jkl";
	map[6] = "mno";
	map[7] = "pqrs";
	map[8] = "tuv";
	map[9] = "wxyz";

	this.Combinations(digits, 0, "", map, output);

	return output;
}

public void Combinations(string digits, int currIndex, string comb, Dictionary<int, string> map, List<string> output){
	if(currIndex == digits.Length){
		output.Add(comb);
		return;
	}

	string combns = map[Convert.ToInt32(digits[currIndex] - '0')];

	foreach(char c in combns){
		this.Combinations(digits, currIndex + 1, comb + c, map, output);
	}
}

Coin Change

Link - Coin Change

Using Top Down Approach (Time Limit Exceeded)

public int CoinChange(int[] coins, int amount) {
	int val = this.Change(coins, amount, 0);

	if(val == 10000 || val == 10001){
		return -1;
	}
	else{
		return val;
	}
}

public int Change(int[] coins, int amount, int index){
	if(amount < 0){
		return 10000;
	}

	if(index == coins.Length){
		return 10000;
	}

	if(amount == 0){
		return 0;
	}


	int val = Math.Min(1 + this.Change(coins, amount - coins[index], index),this.Change(coins, amount, index + 1));
	return val;
}

Using Top Down Approach using Memoization (Time Limit Exceeded)

public int CoinChange(int[] coins, int amount) {
	Dictionary<string, int> map = new Dictionary<string, int>();

	int val = this.Change(coins, amount, 0, map);

	if(val == 10000 || val == 10001){
		return -1;
	}
	else{
		return val;
	}
}

public int Change(int[] coins, int amount, int index, Dictionary<string, int> map){
	if(amount < 0){
		return 10000;
	}

	if(index == coins.Length){
		return 10000;
	}

	if(amount == 0){
		return 0;
	}

	string key = amount.ToString() + "$" + index.ToString();
	if(map.ContainsKey(key)){
		return map[key];
	}

	map[key] = Math.Min(1 + this.Change(coins, amount - coins[index], index, map),this.Change(coins, amount, index + 1, map));
	return map[key];
}

Using Bottom Up Approach

public int CoinChange(int[] coins, int amount) {
	int[] dp = new int[amount + 1];

	for(int i = 0; i <= amount; i++){
		dp[i] = amount + 1;
	}

	dp[0] = 0;

	for(int i = 1; i <= amount; i++){
		for(int j = 0; j < coins.Length; j++){
			if(coins[j] <= i){
				dp[i] = Math.Min(dp[i], 1 + dp[i - coins[j]]);
			}
		}
	}

	return dp[amount] > amount ? -1 : dp[amount];
}

Maximum Ice Cream Bars

Link - Maximum Ice Cream Bars

Using Greedy Approach

public int MaxIceCream(int[] costs, int coins) {
	Array.Sort(costs);

	int count = 0;

	foreach(int cost in costs){
		if(coins >= cost){
			count++;
			coins -= cost;
		}
		else{
			break;
		}
	}

	return count;
}

Divide Two Integers

Link - Divide Two Integers

Repeated Subtraction

Repeatedly subtract the dividend by the divisor and update the counter each time. The isPositive flag is used to check if the quotient is going to be positive or negative and based on this flag the count is incremented or decremented each time.

public int Divide(int dividend, int divisor) {
    int count = 0;
    int tempDividend = Math.Abs(dividend);
    int tempDivisor = Math.Abs(divisor);

    bool isPositive = false;

    if((tempDividend == dividend && tempDivisor == divisor) || (tempDividend != dividend && tempDivisor != divisor)){
        isPositive = true;
    }

    while(tempDividend - tempDivisor >= 0){
        tempDividend = tempDividend - tempDivisor;

        if(isPositive){
            count++;
        }
        else{
            count--;
        }
    }
    return count;
}

Stream of Characters

Link - Stream of Characters

Using Trie Data Structure

public class StreamChecker {

    Trie root;
    StringBuilder sb;

    public StreamChecker(string[] words) {
        root = new Trie();
        sb = new StringBuilder();

        foreach(string word in words){
            int n = word.Length;
            Trie node = root;

            for(int i = n-1; i >= 0; i--){
                char c = word[i];

                if(node.next[c-'a'] == null){
                    node.next[c-'a'] = new Trie();
                }

                node = node.next[c-'a'];
            }
            node.isWord = true;
        }
    }

    public bool Query(char letter) {
        sb.Append(letter);
        Trie node = root;

        for(int i = sb.Length-1; i >= 0 && node != null; i--){
            char c = sb[i];

            node = node.next[c-'a'];
            if(node != null && node.isWord){
                return true;
            }
        }

        return false;
    }
}

public class Trie{
    public Trie[] next;
    public bool isWord;

    public Trie(){
        next = new Trie[26];
        isWord = false;
    }
}

Permutations II

Link - Permutations II

Using Recursion

public IList<IList<int>> PermuteUnique(int[] nums) {
	IList<IList<int>> output = new List<IList<int>>();

	int N = nums.Length;

	bool[] visited = new bool[N];
	Array.Sort(nums);
	this.DFS(nums, visited, new List<int>(), output);

	return output;
}

public void DFS(int[] nums, bool[] visited, List<int> temp, IList<IList<int>> output){
	if(temp.Count == nums.Length){
		List<int> tempList = new List<int>();
		tempList.AddRange(temp);
		output.Add(tempList);
	}

	for(int i = 0; i < nums.Length; i++){
		if(visited[i]){
			continue;
		}

		if(i > 0 && nums[i] == nums[i-1] && !visited[i-1]){
			continue;
		}

		visited[i] = true;
		temp.Add(nums[i]);
		this.DFS(nums, visited, temp, output);
		temp.RemoveAt(temp.Count - 1);
		visited[i] = false;
	}

}

Distribute Candies

Link - Distribute Candies

Using Set

public int DistributeCandies(int[] candyType) {
	HashSet<int> set = new HashSet<int>();

	foreach(int item in candyType){
		set.Add(item);
	}

	return Math.Min(set.Count, candyType.Length/2);
}

Longest Palindrome

Link - Longest Palindrome

Using Dictionary

public int LongestPalindrome(string s) {
	Dictionary<char, int> map = new Dictionary<char, int>();

	foreach(char c in s){
		if(!map.ContainsKey(c)){
			map[c] = 0;
		}

		map[c]++;
	}

	int sumOddCount = 0;
	int sumEvenCount = 0;
	bool oddFound = false;

	foreach(var item in map){
		if(item.Value % 2 == 0){
			sumEvenCount += item.Value;
		}
		else{
			sumOddCount += item.Value - 1;
			oddFound = true;
		}
	}

	if(oddFound){
		return sumOddCount + sumEvenCount + 1;
	}
	else{
		return sumEvenCount;
	}
}

Search Insert Position

Link - Search Insert Position

Binary Search

public int SearchInsert(int[] nums, int target) {
	int N = nums.Length;
	int low = 0;
	int high = N - 1;

	while(low <= high){
		int mid = low + ((high - low)/ 2);

		if(nums[mid] == target){
			return mid;
		}
		else if(nums[mid] < target){
			low = mid + 1;
		}
		else{
			high = mid - 1;
		}
	}

	return low;
}

Unique Paths

Link - Unique Paths

Using Bottom Up Approach

public int UniquePaths(int m, int n) {
	int[,] dp = new int[m,n];

	for(int i = 0; i < m; i++){
		dp[i,0] = 1;
	}

	for(int i = 0; i < n; i++){
		dp[0,i] = 1;
	}

	for(int i = 1; i < m; i++){
		for(int j = 1; j < n; j++){
			dp[i,j] = dp[i-1,j] + dp[i,j-1];
		}
	}

	return dp[m-1,n-1];
}

Using Bottom Up Approach (Space Optimized)

public int UniquePaths(int m, int n) {
	int a = Math.Min(m,n);
	int b = Math.Max(m,n);

	int[] dp = new int[a];

	for(int i = 0; i < a; i++){
		dp[i] = 1;
	}

	for(int i = 1; i < b; i++){
		for(int j = 1; j < a; j++){
			dp[j] += dp[j-1];
		}
	}

	return dp[a-1];
}

Using Formula

ans = (a + b)!/(a! * b!) where a = m-1 and b = n-1

Count Good Nodes in Binary Tree

Link - Count Good Nodes in Binary Tree

Using DFS

public int GoodNodes(TreeNode root) {
	int count = 0;
	this.GoodNodes(root, Int32.MinValue, ref count);

	return count;
}

public void GoodNodes(TreeNode root, int maxValue, ref int count){
	if(root == null){
		return;
	}

	if(root.val >= maxValue){
		count++;
	}

	this.GoodNodes(root.left, Math.Max(root.val, maxValue), ref count);
	this.GoodNodes(root.right, Math.Max(root.val, maxValue), ref count);
}

Decode Ways

Link - Decode Ways

Using Dynamic Programming (Top Down)

public int NumDecodings(string s) {
	int N = s.Length;
	if(N == 0){
		return 0;
	}

	int[] dp = new int[N+1];
	dp[N] = 1;
	dp[N-1] = s[N-1] != '0' ? 1 : 0;

	for(int i = N-2; i >= 0; i--){
		if(s[i] == '0'){
			continue;
		}
		else{
			dp[i] = Convert.ToInt32(s.Substring(i, 2)) <= 26 ? dp[i+1] + dp[i+2] : dp[i+1];
		}
	}

	return dp[0];
}

Widest Vertical Area Between Two Points Containing No Points

Link - Widest Vertical Area Between Two Points Containing No Points

Using Sorting

public int MaxWidthOfVerticalArea(int[][] points) {
	Array.Sort(points, (a,b) => a[0] - b[0]);

	int maxSoFar = 0;

	for(int i = 1; i < points.Length; i++){
		maxSoFar = Math.Max(maxSoFar, points[i][0] - points[i-1][0]);
	}

	return maxSoFar;
}

Unique Paths III

Link - Unique Paths III

Using DFS with Backtracking

public class Solution {
    int count = 0;
    int visitCount = 0;

    public int UniquePathsIII(int[][] grid) {
        int rows = grid.Length;

        if(rows == 0){
            return 0;
        }

        int cols = grid[0].Length;

        int startR = 0;
        int startC = 0;

        for(int i = 0; i < rows; i++){
            for(int j = 0; j < cols; j++){
                if(grid[i][j] == 1 || grid[i][j] == 0){
                    visitCount++;
                }

                if(grid[i][j] == 1){
                    startR = i;
                    startC = j;
                }
            }
        }

        this.Traverse(grid, startR, startC, new HashSet<string>(), rows, cols);

        return count;
    }

    public void Traverse(int[][] grid, int i, int j, HashSet<string> visited, int rows, int cols){
        string loc = i.ToString() + "$" + j.ToString();

        if(i < 0 || j < 0 || i >= rows || j >= cols || grid[i][j] == -1 || visited.Contains(loc)){
            return;
        }

        if(grid[i][j] == 2 && visited.Count == visitCount){
            count++;
            return;
        }

        visited.Add(loc);

        this.Traverse(grid, i+1, j, visited, rows, cols);
        this.Traverse(grid, i, j+1, visited, rows, cols);
        this.Traverse(grid, i-1, j, visited, rows, cols);
        this.Traverse(grid, i, j-1, visited, rows, cols);

        visited.Remove(loc);
    }
}

Roman to Integer

Link - Roman to Integer

Create a map table which has key and values of roman numbers and its corresponding integer value. Now loop through the given roman number input. If you find that the value of the previous character is greater than the current one, then add it to the result. Else subtract the previous num value. Keep doing this until the end of the input to get the integer value.

Consider the following examples

  • VIII - Here V(prevNum) is greater than I(currNum) so add it to the result.
  • IV - Here I(prevNum) is lesser than V so subtract from the result.
public int RomanToInt(string s) {
    Dictionary<char,int> MAP = new Dictionary<char,int>();

    MAP['I'] = 1;
    MAP['V'] = 5;
    MAP['X'] = 10;
    MAP['L'] = 50;
    MAP['C'] = 100;
    MAP['D'] = 500;
    MAP['M'] = 1000;

    int  preNum = 0;
    int result = 0;

    for(int i = 0; i < s.Length; i++){
        int currNum = MAP[s[i]];

        if(preNum >= currNum){
            result += currNum;
        }
        else{
            // preNum needs to be deducted twice since it has been added already in the previous iteration
            result = result - (2 * preNum) + currNum;
        }
        preNum = currNum;
    }

    return result;
}

Maximum Average Subarray I

Link - Maximum Average Subarray I

This problem can be solved using the Sliding Window Technique. See the below solution.

Sliding Window Method

In this technique, first find the sum of k (i.e. window size) items in the input array, set it as the maxsum for now. Now find the sum by looping through the remaining elements in the array, that is adding the next element to the sliding window and remove the first element from the window. During this process, if the current sum is more than the max sum, then replace max sum with the current sum. Now at the end of the loop, you will have the maximum sum from the given array. Divide that number by the k which will give you maximum average.

public double FindMaxAverage(int[] nums, int k) {
    int currSum = 0;
    int maxSum = 0;

    for(int i = 0; i< k; i++ ){
        currSum += nums[i];
    }
    maxSum = currSum;

    int startPointer = 0;
    int endPointer = k;

    while(endPointer < nums.Length){
        currSum += nums[endPointer] - nums[startPointer];
        if(currSum > maxSum){
            maxSum = currSum;
        }
        endPointer++;
        startPointer++;
    }

    return (double)maxSum/k;
}

First Unique Character in a String

Link - First Unique Character in a String

Using Dictionary

public int FirstUniqChar(string s) {
	Dictionary<char, int> output = new Dictionary<char, int>();

	for(int i = 0; i < s.Length; i++){
		if(output.ContainsKey(s[i])){
			output[s[i]] = -1;
		}
		else{
			output[s[i]] = i;
		}
	}

	foreach(var item in output){
		if(item.Value != -1){
			return item.Value;
		}
	}

	return -1;
}

Maximum Swap

Link - Maximum Swap

Using Greedy Approach

public int MaximumSwap(int num) {
	int[] lastOne = new int[10];

	Array.Fill(lastOne, -1);

	List<int> nums = this.ConvertToArray(num);

	for(int i = 0; i < nums.Count; i++){
		lastOne[nums[i]] = i;
	}

	for(int i = 0; i < nums.Count; i++){
		for(int j = 9; j >= 1; j--){
			if(lastOne[j] != -1 && j > nums[i] && i < lastOne[j]){
				nums[lastOne[j]] = nums[i];
				nums[i] = j;

				return this.ConvertToNum(nums);
			}
		}
	}

	return this.ConvertToNum(nums);
}

public List<int> ConvertToArray(int num){
	List<int> output = new List<int>();

	while(num > 0){
		output.Add(num%10);
		num /= 10;
	}

	output.Reverse();

	return output;
}

public int ConvertToNum(List<int> nums){
	string num = "";

	for(int i = 0; i < nums.Count; i++){
		num += nums[i];
	}

	return Convert.ToInt32(num);
}

Permutation in String

Link - Permutation in String

Using Sliding Window Technique

public bool CheckInclusion(string s1, string s2) {
	Dictionary<char, int> freqs = new Dictionary<char, int>();

	foreach(char c in s1){
		if(freqs.ContainsKey(c)){
			freqs[c] += 1;
		}
		else{
			freqs[c] = 1;
		}
	}

	int start = 0;
	int end = 0;
	int size = freqs.Count;

	while(end < s2.Length){
		char c = s2[end];

		if(freqs.ContainsKey(c)){
			freqs[c] = freqs[c] - 1;
			if(freqs[c] == 0){
				size--;
			}
		}
		end++;

		while(size == 0){
			char c2 = s2[start];

			if(freqs.ContainsKey(c2)){
				freqs[c2] = freqs[c2] + 1;
				if(freqs[c2] > 0){
					size++;
				}
			}

			if(end - start == s1.Length){
				return true;
			}
			start++;
		}
	}

	return false;
}

To Lower Case

Link - To Lower Case

Using OR operation

Setting the 5th bit of all the characters in the string will give you the required result.

public string ToLowerCase(string str) {
		string output = "";

		for(int i = 0; i < str.Length; i++){
				output += (char)(str[i] | (1 << 5));
		}

		return output;
}

We could improve the time complexity by using StringBuilder instead of string if the input strings are long.

Using ASCII code conversion

Convert each character to lowercase by subtracting ‘A’ and then adding ‘a’.

public string ToLowerCase(string str) {
		char[] temp = str.ToCharArray();

		for(int i = 0; i < temp.Length; i++){
				if(temp[i] >= 'A' && temp[i] <= 'Z'){
						temp[i] = (char)(temp[i] - 'A' + 'a');
				}
		}

		return new string(temp);
}

Random Point in Non-overlapping Rectangles

Link - Random Point in Non-overlapping Rectangles

int[][] rectangles;
SortedDictionary<int, int> map;
Random rand;
int area;

public Solution(int[][] rects) {
	rand = new Random();
	map = new SortedDictionary<int,int>();

	rectangles = rects;

	for(int i = 0 ; i < rectangles.Length; i++){
		area += (rectangles[i][2] - rectangles[i][0] + 1) * (rectangles[i][3] - rectangles[i][1] + 1);
		map.Add(area, i);
	}
}

public int[] Pick() {
	int randNum = rand.Next(area) + 1;

	int rectIndex = map.First(x => x.Key >= randNum).Value;

	int x = rand.Next(rectangles[rectIndex][0], rectangles[rectIndex][2] + 1);
	int y = rand.Next(rectangles[rectIndex][1], rectangles[rectIndex][3] + 1);

	return new int[]{x,y};
}

Partition Labels

Link - Partition Labels

Using Greedy Approach

public IList<int> PartitionLabels(string S) {
	int[] last = new int[26];

	for(int i = 0 ; i < S.Length; i++){
		last[S[i] - 'a'] = i;
	}

	List<int> output = new List<int>();

	int lastAppear = 0; // Stores the maximum index that can fit into the group for a given i;
	int subStart = 0;   // Stores the index of the start of the sug group
	for(int i = 0; i < S.Length; i++){
		lastAppear = Math.Max(lastAppear, last[S[i] - 'a']);

		if(i == lastAppear){
			output.Add(lastAppear - subStart + 1);
			subStart = lastAppear + 1;
		}
	}

	return output;
}

Max Dot Product of Two Subsequences

Link - Max Dot Product of Two Subsequences

Using Bottom Up Appraoch

public int MaxDotProduct(int[] nums1, int[] nums2) {
	int m = nums1.Length;
	int n = nums2.Length;

	int[,] dp = new int[m+1,n+1];

	for(int i = 0; i <= m; i++){
		for(int j = 0; j <= n; j++){
			if(i == 0 || j == 0){
				dp[i,j] = Int32.MinValue;
				continue;
			}
			dp[i,j] = Math.Max(
				(nums1[i-1] * nums2[j-1]) + Math.Max(0, dp[i-1,j-1]),
				Math.Max(dp[i-1,j], dp[i,j-1])
			);
		}
	}

	return dp[m,n];
}

Single Number

Link - Single Number

Using Hashtable

This can be solved using a hashtable or a set. But this solution takes linear space. Can we solve using constant space? See the next approach.

Using Bitwise XOR

Perform bitwise XOR operation iteratively on all the elements of the array. The repeated elements will be cancelled out during this operation, except for the number which has occurred only twice. See the code below for the solution.

public int SingleNumber(int[] nums) {
    int num = 0;

    for(int i = 0; i < nums.Length; i++){
        num ^= nums[i];
    }

    return num;
}

Time and Space Complexities

ApproachTime ComplexitySpace Complexity
Hashtable or SetO(n)O(n)
Bitwise XORO(n)O(1)

Two Sum II - Input array is sorted

Link - Two Sum II - Input array is sorted

Two Pointer Approach

public int[] TwoSum(int[] numbers, int target) {
	int index1 = 0;
	int index2 = numbers.Length - 1;

	while(index1 < index2){
		int temp = numbers[index1] + numbers[index2];
		if(temp == target){
			break;
		}
		else if(temp < target){
			index1++;
		}
		else{
			index2--;
		}
	}

	return new int[]{index1+1, index2+1};
}

Non-overlapping Intervals

Link - Non-overlapping Intervals

Greedy Approach

public int EraseOverlapIntervals(int[][] intervals) {
	int N = intervals.Length;

	if(N == 0){
		return 0;
	}

	Array.Sort(intervals, (a,b) => a[0] - b[0]);

	int result = 0;
	int minEnd = intervals[0][1];

	for(int i = 1; i < N; i++){
		int[] curr = intervals[i];

		if(minEnd > curr[0]){
			minEnd = Math.Min(minEnd, curr[1]);
			result++;
		}
		else{
			minEnd = curr[1];
		}
	}

	return result;
}

Lemonade Change

Link - Lemonade Change

Greedy Approach (Naive Solution)

public bool LemonadeChange(int[] bills) {
	int bill5 = 0;
	int bill10 = 0;

	foreach(int item in bills){
		if(item == 5){
			bill5++;
		}
		else if(item == 10){
			bill10++;
			if(bill5 >= 1){
				bill5--;
			}
			else{
				return false;
			}
		}
		else{
			if(bill5 >= 1 && bill10 >= 1){
				bill5--;
				bill10--;
			}
			else if(bill5 >= 3){
				bill5 -= 3;
			}
			else{
				return false;
			}
		}
	}

	return true;
}

Convert Binary Number in a Linked List to Integer

Link - Convert Binary Number in a Linked List to Integer

A binary number can be converted to decimal using this - ((2^0) * 1st bit) + ((2^1) * 2nd bit) ...... (2^(n-1) * nth bit). Use this to find the decimal value.

public int GetDecimalValue(ListNode head) {
    if(head == null){
        return 0;
    }

    int num = 0;

    while(head != null){
        num = (2 * num) + head.val;
        head = head.next;
    }

    return num;
}

Sort Array by Increasing Frequency

Link - Sort Array by Increasing Frequency

Using Sorting with Dictionary

public int[] FrequencySort(int[] nums) {
	Dictionary<int,int> freq = new Dictionary<int,int>();

	foreach(int num in nums){
		if(!freq.ContainsKey(num)){
			freq[num] = 0;
		}

		freq[num]++;
	}

	Tuple<int,int>[] freqCount = new Tuple<int,int>[freq.Count];

	int i = 0;

	foreach(var item in freq){
		freqCount[i] = new Tuple<int,int>(item.Key,item.Value);
		i++;
	}

	Array.Sort(freqCount, (a,b) => a.Item2 == b.Item2 ? b.Item1 - a.Item1 : a.Item2 - b.Item2);

	int[] output = new int[nums.Length];
	i = 0;
	foreach(var item in freqCount){
		int num = item.Item1;
		int count = item.Item2;

		while(count != 0){
			output[i] = num;
			count--;
			i++;
		}
	}

	return output;
}

Assign Cookies

Link - Assign Cookies

The highest number of children can be provided with the cookies by providing the biggest cookies to the children with the highest greed factor. So first sort both the greed factors and the cookie sizes. Then create two pointers each pointing to greed factor array and cookie size arrays. Starting both the pointers from the ends of the array, check if the cookie size is greater than or equal to the greed factor of a chile. If yes, then increase the counter and shift both the pointers by one step left. If not, then just decrement the greed pointer by one step. Keep doing this to get the required result.

public int FindContentChildren(int[] g, int[] s) {
    Array.Sort(g);
    Array.Sort(s);

    int gPointer = g.Length - 1;
    int sPointer = s.Length - 1;

    int count = 0;

    while(gPointer >= 0 && sPointer >= 0){
        if(s[sPointer] >= g[gPointer]){
            count++;
            gPointer--;
            sPointer--;
        }
        else{
            gPointer--;
        }
    }

    return count;
}

Find Valid Matrix Given Row and Column Sums

Link - Find Valid Matrix Given Row and Column Sums

Greedy Approach

public int[][] RestoreMatrix(int[] rowSum, int[] colSum) {
	int rows = rowSum.Length;
	int cols = colSum.Length;

	int[][] result = new int[rows][];

	for(int i = 0; i < rows; i++){
		result[i] = new int[cols];

		for(int j = 0; j < cols; j++){
			int val = Math.Min(rowSum[i], colSum[j]);

			result[i][j] = val;
			rowSum[i] -= val;
			colSum[j] -= val;
		}
	}

	return result;
}

Number of 1 Bits

Link - Number of 1 Bits

Bitwise Operation

Simply AND the given number by 1. If the LSB was 1 then the AND operation will return you 1, else 0. Right shift the number by 1 bit and keep repeating this to get the hamming distance.

public int HammingWeight(uint n) {
	int count = 0;

	while(n > 0){
		count += (int)(n & 1);
		n = n >> 1;
	}

	return count;
}

Max Consecutive Ones III

Link - Max Consecutive Ones III

Using Two Pointer or Sliding Window Approach

public int LongestOnes(int[] A, int K) {
	int n = A.Length;
	int sIndex = 0;
	int lIndex = 0;

	int usedK = 0;
	int maxSoFar = 0;

	while(lIndex < n && sIndex < n){
		if(A[lIndex] == 0){
			if(usedK < K){
				maxSoFar = Math.Max(maxSoFar, lIndex - sIndex + 1);
				usedK++;
				lIndex++;
			}
			else{
				if(A[sIndex] == 0){
					usedK--;
				}
				sIndex++;
			}
		}
		else{
			maxSoFar = Math.Max(maxSoFar, lIndex - sIndex + 1);
			lIndex++;
		}
	}

	return maxSoFar;
}

3Sum With Multiplicity

Link - 3Sum With Multiplicity

Naive Approach

public int ThreeSumMulti(int[] arr, int target) {
	int N = arr.Length;

	int count = 0;

	for(int i = 0; i < N; i++){
		for(int j = i+1; j < N; j++){
			for(int k = j+1; k < N; k++){
				if(arr[i] + arr[j] + arr[k] == target){
					count++;
				}
			}
		}
	}

	return count;
}

Three Pointer Approach

public int ThreeSumMulti(int[] arr, int target) {
	int N = arr.Length;

	Array.Sort(arr);

	int count = 0;

	for(int i = 0; i < N; i++){
		int T = target - arr[i];
		int j = i+1;
		int k = N-1;

		while(j < k){
			if(arr[j] + arr[k] < T){
				j++;
			}
			else if(arr[j] + arr[k] > T){
				k--;
			}
			else if(arr[j] != arr[k]){
				int left = 1;
				int right = 1;

				while(j+1 < k && arr[j] == arr[j+1]){
					left++;
					j++;
				}

				while(j < k-1 && arr[k-1] == arr[k]){
					right++;
					k--;
				}

				count += left * right;
				count %= 1000000007;

				j++;
				k--;
			}
			else{
				count += (k-j+1) * (k-j)/2;
				count %= 1000000007;
				break;
			}
		}
	}

	return count;
}

Largest Substring Between Two Equal Characters

Link - Largest Substring Between Two Equal Characters

Naive Solution - Pre-Computated(Time Limit Exceeded Error)

public int MaxLengthBetweenEqualCharacters(string s) {
	int[] first = new int[26];
	int[] last = new int[26];

	Array.Fill(first, -1);
	Array.Fill(last, -1);

	for(int i = 0; i < s.Length; i++){
		int val = s[i] - 'a';

		if(first[val] == -1){
			first[val] = i;
		}

		last[val] = i;
	}

	int maxSoFar = -1;

	for(int i = 0; i < 26; i++){
		if(first[i] == -1){
			continue;
		}

		maxSoFar = Math.Max(maxSoFar, last[i] - first[i] - 1);
	}

	return maxSoFar;
}

Pascal’s Triangle

Link - Pascal’s Triangle

public IList<IList<int>> Generate(int numRows) {
    IList<IList<int>> output = new List<IList<int>>();

    if(numRows == 0){
        return output;
    }
    if (numRows >=1){
        output.Add(new List<int> {1});
    }
    if (numRows >= 2){
        output.Add(new List<int>{1,1});
    }

    List<int> temp = new List<int>() {1,1};

    for(int i = 3; i <= numRows; i++){
        List<int> temp2 = new List<int>();

        temp2.Add(1);

        for(int j = 1; j < temp.Count; j++){
            temp2.Add(temp[j-1] + temp[j]);
        }

        temp2.Add(1);
        output.Add(temp2);
        temp = temp2;
    }

    return output;
}

Check If Two String Arrays are Equivalent

Link - Check If Two String Arrays are Equivalent

Naive Approach

public bool ArrayStringsAreEqual(string[] word1, string[] word2) {
	StringBuilder sb1 = new StringBuilder();
	StringBuilder sb2 = new StringBuilder();

	foreach(string word in word1){
		sb1.Append(word);
	}

	foreach(string word in word2){
		sb2.Append(word);
	}

	return sb1.ToString() == sb2.ToString();
}
var arrayStringsAreEqual = function (word1, word2) {
  let string1 = "";
  let string2 = "";

  word1.forEach((val) => (string1 += val));
  word2.forEach((val) => (string2 += val));

  return string1 == string2;
};

Encode and Decode TinyURL

Link - Encode and Decode TinyURL

Using Dictionary

Dictionary<string, string> map;
int index = 0;
public Codec(){
	map = new Dictionary<string, string>();
}

// Encodes a URL to a shortened URL
public string encode(string longUrl) {
	map[index.ToString()] = longUrl;
	index++;
	return (index-1).ToString();
}

// Decodes a shortened URL to its original URL.
public string decode(string shortUrl) {
	if(map.ContainsKey(shortUrl)){
		return map[shortUrl];
	}
	else{
		return "";
	}
}

Path Sum

Link - Path Sum

Using Depth First Search

public bool HasPathSum(TreeNode root, int sum) {
	if(root == null){
		return false;
	}

	sum -= root.val;

	if(root.left == null && root.right == null){
		return sum == 0 ? true : false;
	}

	return this.HasPathSum(root.left, sum) || this.HasPathSum(root.right, sum);
}

Squares of a Sorted Array

Link - Squares of a Sorted Array

Naive Approach

public int[] SortedSquares(int[] nums) {
	for(int i = 0; i < nums.Length; i++){
		nums[i] *= nums[i];
	}

	Array.Sort(nums);

	return nums;
}

Using Two Pointer Approach

public int[] SortedSquares(int[] A) {
	int N = A.Length;

	int[] output = new int[N];

	int sIndex = 0;
	int eIndex = N - 1;

	int outIndex = N-1;

	while(outIndex >= 0){
		int absS = Math.Abs(A[sIndex]);
		int absE = Math.Abs(A[eIndex]);

		int large;

		if(absS < absE){
			large = absE;
			eIndex--;
		}
		else{
			large = absS;
			sIndex++;
		}

		output[outIndex] = large * large;
		outIndex--;
	}

	return output;
}

Linked List Cycle II

Link - Linked List Cycle II

Using Fast and Slow (Floyd’s Cycle Detection Algorithm)

Watch this video which explains how to find the starting point of a cycle in a linked list

public ListNode DetectCycle(ListNode head) {
	ListNode slow = head;
	ListNode fast = head;

	while(fast != null && fast.next != null){
		slow = slow.next;
		fast = fast.next.next;

		if(fast == slow){
			ListNode slow2 = head;

			while(slow != slow2){
				slow = slow.next;
				slow2 = slow2.next;
			}

			return slow;
		}
	}

	return null;
}

Missing Number

Link - Missing Number

Best Approach

Actual Sum of n numbers is given by n(n+1)/2. That will be your actual sum. Find the sum of all the numbers in the given array, that will be your given sum. The difference between the actual sum and the given sum will give you the missing number.

public int MissingNumber(int[] nums) {
    int N = nums.Length;
    int givenSum = 0;
    int actualSum = (N * (N + 1))/2;

    foreach(int num in nums){
        givenSum += num;
    }

    return actualSum - givenSum;
}

// The above solution has a time complexity of O(n) with a constant space requirement.

Minimum Add to Make Parentheses Valid

Link - Minimum Add to Make Parentheses Valid

Greedy Approach (O(n) space complexity)

public int MinAddToMakeValid(string S) {
	if(S.Length == 0){
		return 0;
	}

	Stack<char> st = new Stack<char>();

	int i = 1;
	st.Push(S[0]);

	while(i < S.Length){
		if(st.Count == 0){
			st.Push(S[i]);
		}
		else if(st.Peek() == '(' && S[i] == ')'){
			st.Pop();
		}
		else{
			st.Push(S[i]);
		}

		i++;
	}

	return st.Count;
}

Greedy Approach (O(1) space complexity)

public int MinAddToMakeValid(string S) {
	int openCount = 0;
	int closeCount = 0;

	foreach(char c in S){
		if(c == '('){
			openCount++;
		}
		else if(openCount > 0){
			openCount--;
		}
		else{
			closeCount++;
		}
	}

	return openCount + closeCount;
}

Maximum Frequency Stack

Maximum Frequency StackScore of Parentheses

Link - Maximum Frequency Stack

Using Dictionary with Stack

Dictionary<int,int> freq;
Dictionary<int,Stack<int>> groupFreq;
int maxFreq;

public FreqStack() {
	freq = new Dictionary<int,int>();
	groupFreq = new Dictionary<int, Stack<int>>();
	maxFreq = 0;
}

public void Push(int x) {
	if(freq.ContainsKey(x)){
		freq[x]++;
	}
	else{
		freq[x] = 1;
	}

	if(freq[x] > maxFreq){
		maxFreq = freq[x];
	}

	if(groupFreq.ContainsKey(freq[x])){
		groupFreq[freq[x]].Push(x);
	}
	else{
		groupFreq[freq[x]] = new Stack<int>();
		groupFreq[freq[x]].Push(x);
	}
}

public int Pop() {
	if(groupFreq.ContainsKey(maxFreq)){
		int x = groupFreq[maxFreq].Pop();
		freq[x]--;
		if(groupFreq[maxFreq].Count == 0){
			maxFreq--;
		}
		return x;
	}

	return -1;
}

First Bad Version

Link - First Bad Version

Using Binary Search

public int FirstBadVersion(int n) {
	int low = 1;
	int high = n;

	while(low < high){
		int mid = low + ((high - low)/2);

		bool isBad = this.IsBadVersion(mid);

		if(isBad){
			high = mid;
		}
		else{
			low = mid + 1;
		}
	}

	return low;
}

Smallest String With A Given Numeric Value

Link - Smallest String With A Given Numeric Value

Greedy Approach

public string GetSmallestString(int n, int k) {
	char[] output = new char[n];
	Array.Fill(output, 'a');
	k -= n;

	int i = n - 1;

	while(k > 0){
		int newChar = Math.Min(k, 25);
		output[i] = Convert.ToChar(97 + newChar);
		k -= newChar;
		i--;
	}

	return new string(output);
}

Shuffle String

Link - Shuffle String

Using Naive Approach

public string RestoreString(string s, int[] indices) {
	int N = s.Length;

	char[] output = new char[N];

	for(int i = 0; i < N ; i++){
		output[indices[i]] = s[i];
	}

	return new string(output);
}

Repeated DNA Sequences

Link - Repeated DNA Sequences

Using HashSet

public IList<string> FindRepeatedDnaSequences(string s) {
	HashSet<string> result = new HashSet<string>();
	HashSet<string> set = new HashSet<string>();

	if(s.Length <= 10){
		return result.ToList();
	}

	for(int i = 0; i < s.Length - 9; i++){
		string curr = s.Substring(i, 10);

		if(!set.Contains(curr)){
			set.Add(curr);
			continue;
		}

		if(!result.Contains(curr)){
			result.Add(curr);
		}

	}

	return result.ToList();
}

Get Maximum in Generated Array

Link - Get Maximum in Generated Array

Linear Space Solution

public int GetMaximumGenerated(int n) {
	if(n == 0){
		return 0;
	}

	int[] arr = new int[n+1];

	int max = 1;

	arr[0] = 0;
	arr[1] = 1;

	for(int i = 2; i <= n; i++){
		if(i % 2 == 0){
			arr[i] = arr[i/2];
		}
		else{
			arr[i] = arr[i/2] + arr[(i/2)+1];
		}

		max = Math.Max(arr[i], max);
	}

	return max;
}

The k Strongest Values in an Array

Link - The k Strongest Values in an Array

Using Custom Comparator for Sorting

public class Solution {
    public int[] GetStrongest(int[] arr, int k) {
		// Step 1: Sort the array and find the median as per definition given in the problem
        Array.Sort(arr);
        int N = arr.Length;
        int med = arr[(N - 1)/2];

		// Step 2: Find the absolute difference between the array value and median and store them in a new array.
        int[][] medArray = new int[N][];

        for(int i = 0; i < N; i++){
            medArray[i] = new int[2];
            medArray[i][0] = arr[i];
            medArray[i][1] = Math.Abs(med - arr[i]);
        }

		// Step 3: Sort the array as per the conditions in the problem statement using custom comapator function
        Array.Sort(medArray, new CustomComparator());

		// Step 4: Output the first k elements of the sorted array
        int[] output = new int[k];
        for(int i = 0; i < k; i++){
            output[i] = medArray[i][0];
        }

        return output;
    }
}

public class CustomComparator: IComparer<int[]>{
    public int Compare(int[] value1, int[] value2){
        if(value1[1] == value2[1]){
            return value2[0] - value1[0];
        }
        else {
            return value2[1] - value1[1];
        }
    }
}

#### House Robber II



Link - [House Robber II](https://leetcode.com/problems/house-robber-ii/)

> Using DP (Bottom Up Approach)

```csharp
public int Rob(int[] nums) {
	int N = nums.Length;

	if(N == 0){
		return 0;
	}
	else if(N < 2){
		return nums[0];
	}

	return Math.Max(this.DP(nums, 0, N-2), this.DP(nums, 1, N-1));
}

public int DP(int[] nums, int low, int high){
	int prev = 0;
	int curr = 0;

	for(int i = low; i <= high; i++){
		int temp = Math.Max(prev + nums[i], curr);
		prev = curr;
		curr = temp;
	}

	return curr;
}

Range Sum Query - Mutable

Link - Range Sum Query - Mutable

Using Segment Tree

public class NumArray {

    int[] tree;
    int n;

    public NumArray(int[] nums) {
        n = nums.Length;

        if(n > 0){
            tree = new int[2*n];
            this.BuildSegmentTree(nums);
        }
    }

    // O(n) time complexity
    private void BuildSegmentTree(int[] nums){
        for(int i = 0, j = n; j < 2*n; i++, j++){
            tree[j] = nums[i];
        }

        for(int i = n-1; i > 0; i--){
            tree[i] = tree[2*i] + tree[2*i+1];
        }
    }

    // O(log N) time complexity
    public void Update(int i, int val) {
        int pos = i + n;
        tree[pos] = val;

        while(pos > 0){
            int left = pos;
            int right = pos;

            if(pos % 2 == 0){
                right = pos + 1;
            }
            else{
                left = pos - 1;
            }

            tree[pos/2] = tree[left] + tree[right];
            pos /= 2;
        }
    }

    // O(log N) time complexity
    public int SumRange(int i, int j) {
        int l = i + n;
        int r = j + n;

        int sum = 0;

        while(l <= r){
            if(l % 2 == 1){
                sum += tree[l];
                l++;
            }
            if(r % 2 == 0){
                sum += tree[r];
                r--;
            }

            l /= 2;
            r /= 2;
        }

        return sum;
    }
}

Set Mismatch

Link - Set Mismatch

Using Frequency Count

public int[] FindErrorNums(int[] nums) {
	int N = nums.Length;

	int[] freq = new int[N+1];

	// Finding the frequency of all numbers in the array
	for(int i = 0; i < N; i++){
		freq[nums[i]]++;
	}

	int missing = 0;
	int repeat = 0;


	for(int i = 1; i < freq.Length; i++){
		if(freq[i] > 1){
			repeat = i;
		}
		else if(freq[i] == 0){
			missing = i;
		}
	}

	return new int[] {repeat, missing};
}

Teemo Attacking

Link - Teemo Attacking

Using Merge Interval

public int FindPoisonedDuration(int[] timeSeries, int duration) {
	int N = timeSeries.Length;

	if(N == 0){
		return 0;
	}

	int total = 0;

	for(int i = 0; i < N-1; i++){
		total += Math.Min(timeSeries[i+1] - timeSeries[i], duration);
	}

	return total + duration;
}

Race Car

Link - Race Car

Breadth First Approach (Time Limit Exceeded Error)

public int Racecar(int target) {
	int pos = 0;
	int speed = 1;

	int depth = 0;

	Queue<Tuple<int,int>> bfsQueue = new Queue<Tuple<int,int>>();
	bfsQueue.Enqueue(new Tuple<int,int>(pos, speed));

	while(bfsQueue.Count > 0){
		int count = bfsQueue.Count;

		for(int i = 0; i < count; i++){
			Tuple<int,int> temp = bfsQueue.Dequeue();

			int tempPos = temp.Item1;
			int tempSpeed = temp.Item2;

			if(tempPos == target){
				return depth;
			}

			bfsQueue.Enqueue(new Tuple<int,int>(tempPos+tempSpeed, tempSpeed*2));
			var A = new Tuple<int,int>(tempPos, -1);
			var R = new Tuple<int,int>(tempPos, 1);
			bfsQueue.Enqueue(tempSpeed >= 0 ? A : R);
		}
		depth++;
	}

	return depth;
}

Breadth First Approach with Pruning (Time Limit Exceeded Error)

public int Racecar(int target) {
	int pos = 0;
	int speed = 1;

	int depth = 0;

	Queue<string> bfsQueue = new Queue<string>();
	bfsQueue.Enqueue(pos + "$" + speed);

	HashSet<string> visited = new HashSet<string>();

	while(bfsQueue.Count > 0){
		int count = bfsQueue.Count;

		for(int i = 0; i < count; i++){
			string temp = bfsQueue.Dequeue();

			int tempPos = Convert.ToInt32(temp.Split("$")[0]);
			int tempSpeed = Convert.ToInt32(temp.Split("$")[1]);

			if(tempPos == target){
				return depth;
			}
			// Pruning 1 - Preveting duplication of efforts
			if(visited.Contains(tempPos + "$" + tempSpeed)){
				continue;
			}

			visited.Add(tempPos + "$" + tempSpeed);

			// Pruning 2 - Reducing the search boundary
			if(Math.Abs(tempPos + tempSpeed - target) < target){
				bfsQueue.Enqueue((tempPos+tempSpeed) + "$" + (tempSpeed*2));
			}

			if(Math.Abs(tempPos - target) < target){
				var A = tempPos + "$-1";
				var R = tempPos +  "$1";
				bfsQueue.Enqueue(tempSpeed >= 0 ? A : R);
			}
		}
		depth++;
	}

	return depth;
}

Water Bottles

Link - Water Bottles

Log N method

public int NumWaterBottles(int numBottles, int numExchange) {
	int output = numBottles;
	int rem = numBottles;

	while(rem/numExchange > 0){
		output += (rem/numExchange);
		rem = rem -((rem/numExchange)*numExchange) + (rem/numExchange);
	}

	return output;
}

Best Time to Buy and Sell Stock

Link - Best Time to Buy and Sell Stock

Brute Force Method

In this approach try all the combinations and check which one returns you the maximum profit.

public int MaxProfit(int[] prices) {
    int maxProfit = 0;

    for(int i = 0; i < prices.Length; i++){
        for(int j = i+1; j < prices.Length; j++){
            if(prices[j] - prices[i] > maxProfit){
                maxProfit = prices[j] - prices[i];
            }
        }
    }
    return maxProfit;
}

This solution has a time complexity of O(n^2) and space complexity of O(1).

Better Solution

Create two variables min price and max profit. Start looping through the array, whenever a price lower than the current min price is found, update the min price. Else find the difference between the current price and the min price and update the max profit variable if the difference is greater than the current max profit. This will give you the maximum profit from the given stock info.

public int MaxProfit(int[] prices) {
    int minPrice = Int32.MaxValue;
    int maxProfit = 0;

    for(int i = 0; i < prices.Length; i++){
        if(prices[i] < minPrice){
            minPrice = prices[i];
        }
        else if(prices[i] - minPrice > maxProfit){
            maxProfit = prices[i] - minPrice;
        }
    }

    return maxProfit;
}

The above solution has a time complexity of O(n) and space complexity of O(1).

| Approach | Time Complexity | Space Complexity | | | :-: | :—: | | Brute Force | O(n^2) | O(1) | | Better Solution | O(n) | O(1) |

Fancy Sequence

Link - Fancy Sequence

Naive Solution - Pre-Computated (Time Limit Exceeded Error)

public class Fancy {
    List<long> lst;

    public Fancy() {
        lst = new List<long>();
    }

    public void Append(int val) {
        lst.Add(val);
    }

    public void AddAll(int inc) {
        for(int i = 0; i < lst.Count; i++){
            lst[i] %= 1000000007;
            lst[i] += inc;
        }
    }

    public void MultAll(int m) {
        for(int i = 0; i < lst.Count; i++){
            lst[i] %= 1000000007;
            lst[i] *= m;
        }
    }

    public int GetIndex(int idx) {
        if(idx >= lst.Count){
            return -1;
        }

        return (int)(lst[idx] % 1000000007);
    }
}

Naive Solution - Post-Computed (Lazy Propogation) (Time Limit Exceeded Error)

public class Fancy {
    List<long> lst;
    List<Tuple<int, char, int>> ops;

    public Fancy() {
        lst = new List<long>();
        ops = new List<Tuple<int, char, int>>();
    }

    public void Append(int val) {
        lst.Add(val);
    }

    public void AddAll(int inc) {
        ops.Add(new Tuple<int, char, int>(lst.Count - 1, 'a', inc));
    }

    public void MultAll(int m) {
        ops.Add(new Tuple<int, char, int>(lst.Count - 1, 'm', m));
    }

    public int GetIndex(int idx) {
        if(idx >= lst.Count){
            return -1;
        }

        long val = lst[idx];

        foreach(var item in ops){
            if(item.Item1 < idx){
                continue;
            }

            val %= 1000000007;

            if(item.Item2 == 'a'){
                val += item.Item3;
            }
            else{
                val *= item.Item3;
            }
        }

        return (int)(val % 1000000007);
    }
}

Design a Stack With Increment Operation

Link - Design a Stack With Increment Operation

Using Static Array

public class CustomStack {
    int[] list;
    int currentPointer;
    int max;
    public CustomStack(int maxSize) {
        list = new int[maxSize];
        currentPointer = 0;
        this.max = maxSize;
    }

    public void Push(int x) {
        if(currentPointer == max){
            return;
        }
        else{
            list[currentPointer] = x;
            currentPointer++;
        }
    }

    public int Pop() {
        if(currentPointer == 0){
            return -1;
        }
        else{
            currentPointer--;
            return list[currentPointer];
        }
    }

    public void Increment(int k, int val) {
        int maxPointer = Math.Min(k, currentPointer);

        for(int i = 0; i < maxPointer; i++){
            Console.WriteLine(i.ToString());
            list[i] = list[i] + val;
        }
    }
}

Binary Tree Paths

Link - Binary Tree Paths

Updating next pointer

public IList<string> BinaryTreePaths(TreeNode root) {
	List<string> output = new List<string>();

	if(root == null){
		return output;
	}

	this.Paths(root, output, "");
	return output;
}

public void Paths(TreeNode root, List<string> output, string path){
	if(root.left == null && root.right == null){
		output.Add(path + root.val);
		return;
	}

	if(root.left != null){
		this.Paths(root.left, output, path + root.val + "->");
	}

	if(root.right != null){
		this.Paths(root.right, output, path + root.val + "->");
	}
}

Complement of Base 10 Integer

Link - Complement of Base 10 Integer

Using Bitwise Operation

public int BitwiseComplement(int N) {
	if(N == 0){
		return 1;
	}

	int power = 1;

	while(power <= N){
		power *= 2;
	}

	return power - N - 1;
}

Buddy Strings

Link - Buddy Strings

Naive Solution

public bool BuddyStrings(string A, string B) {
	if(A.Length != B.Length){
		return false;
	}

	if(A == B){
		int[] count = new int[26];

		for(int i = 0; i < A.Length; i++){
			count[A[i] - 'a']++;

			if(count[A[i] - 'a'] > 1){
				return true;
			}
		}

		return false;
	}
	else{

		int first = -1;
		int second = -1;

		for(int i = 0; i < A.Length; i++){
			if(A[i] != B[i]){
				if(first == -1){
					first = i;
				}
				else if(second == -1){
					second = i;
				}
				else{
					return false;
				}
			}
		}

		return second != -1 && A[first] == B[second] && A[second] == B[first];
	}
}

Maximum XOR of Two Numbers in an Array

Link - Maximum XOR of Two Numbers in an Array

Naive Solution (O(n^2) time complexity) (Time Limit Exceeded Error)

public int FindMaximumXOR(int[] nums) {
	int max = 0;

	for(int i = 0; i < nums.Length; i++){
		for(int j = i+1; j < nums.Length; j++){
			max = Math.Max(max, nums[i] ^ nums[j]);
		}
	}

	return max;
}

Using Trie

public class Solution {
    public int FindMaximumXOR(int[] nums) {
        int n = nums.Length;

        Trie root = new Trie();

        foreach(int num in nums){
            Trie tempNode = root;
            string bin = this.NumToBin(num);

            foreach(char c in bin){
                int bit = c == '0' ? 0 : 1;

                if(tempNode.children[bit] == null){
                    tempNode.children[bit] = new Trie();
                }

                tempNode = tempNode.children[bit];
            }
        }

        int maxSoFar = 0;

        foreach(int num in nums){
            Trie tempNode = root;
            string bin = this.NumToBin(num);
            int sum = 0;
            int power = 31;

            foreach(char c in bin){
                int bit = c == '0' ? 0 : 1;
                int otherBit = c == '0' ? 1 : 0;

                if(tempNode.children[otherBit] != null){
                    sum += (int)Math.Pow(2, power);
                    tempNode = tempNode.children[otherBit];
                }
                else{
                    tempNode = tempNode.children[bit];
                }

                power--;
            }

            maxSoFar = Math.Max(maxSoFar, sum);
        }

        return maxSoFar;
    }

    public string NumToBin(int num){
        string output = Convert.ToString(num, 2);
        output = new string('0', 32-output.Length) + output;
        return output;
    }
}

public class Trie{
    public Trie[] children;

    public Trie(){
        children = new Trie[2];
    }
}

Longest Common Subsequence

Link - Longest Common Subsequence

Using dynamic Programming (Time limit exceeded)

public int LongestCommonSubsequence(string text1, string text2) {
	int m = text1.Length;
	int n = text2.Length;

	return this.LCS(text1, text2, m-1, n-1);
}

public int LCS(string text1, string text2, int m, int n){
	if(m < 0 || n < 0){
		return 0;
	}

	if(text1[m] == text2[n]){
		return 1 + this.LCS(text1, text2, m-1, n-1);
	}
	else{
		return Math.Max(this.LCS(text1, text2, m-1, n), this.LCS(text1, text2, m, n-1));
	}
}

Using DP with memoization (Time limit exceeded)

public int LongestCommonSubsequence(string text1, string text2) {
	int m = text1.Length;
	int n = text2.Length;

	int[,] memo = new int[m,n];

	return this.LCS(text1, text2, m-1, n-1, memo);
}

public int LCS(string text1, string text2, int m, int n, int[,] memo){
	if(m < 0 || n < 0){
		return 0;
	}

	if(text1[m] == text2[n]){
		memo[m,n] = 1 + this.LCS(text1, text2, m-1, n-1, memo);
	}
	else{
		memo[m,n] = Math.Max(this.LCS(text1, text2, m-1, n, memo), this.LCS(text1, text2, m, n-1, memo));
	}

	return memo[m,n];
}

Bottom Up Approach

public int LongestCommonSubsequence(string text1, string text2) {
	int m = text1.Length;
	int n = text2.Length;

	int[,] dp = new int[m+1,n+1];

	for(int i = 1; i <= m; i++){
		for(int j = 1; j <= n; j++){
			if(text1[i-1] == text2[j-1]){
				dp[i,j] = 1 + dp[i-1,j-1];
			}
			else{
				dp[i,j] = Math.Max(dp[i-1,j], dp[i,j-1]);
			}
		}
	}

	return dp[m,n];
}

Bottom Up Approach (Space Optimised)

public int LongestCommonSubsequence(string text1, string text2) {
	int m = text1.Length;
	int n = text2.Length;

	int[] dp = new int[m+1];
	int[] prev = new int[m+1];

	// Scan through all rows and then through all columns
	for(int j = 1; j <= n; j++){
		for(int i = 1; i <= m; i++){
			if(text1[i-1] == text2[j-1]){
				dp[i] = 1 + prev[i-1];
			}
			else{
				dp[i] = Math.Max(dp[i-1], prev[i]);
			}
		}
		prev = (int[])dp.Clone(); // Copy array without reference
	}

	return prev[m];
}

Implement Queue using Stacks

Link - Implement Queue using Stacks

O(1) Push and O(N) Pop time Approach

This approach requires two stacks.

During the Enqueue operation push the element straight into the main stack.

During the Dequeue operation, check if the sub stack has any items. If the sub stack has items then pop it straight away and return. If it’s empty then pop all the items from the main stack and put it into the sub stack and then pop the element from the sub stack and return.

Peek operation is similar to the Pop.

And for the Empty method check the lengths of both the stacks.

public class MyQueue {

    Stack<int> main;
    Stack<int> sub;
    /** Initialize your data structure here. */
    public MyQueue() {
        main = new Stack<int>();
        sub = new Stack<int>();
    }

    /** Push element x to the back of queue. */
    // Enqueue Operation
    public void Push(int x) {
        main.Push(x);
    }

    /** Removes the element from in front of queue and returns that element. */
    // Dequeue Operation
    public int Pop() {
        if(sub.Count == 0){
            while(main.Count != 0){
                sub.Push(main.Pop());
            }
        }
        if(sub.Count == 0){
            return - 1;
        }
        else{
            return sub.Pop();
        }
    }

    /** Get the front element. */
    public int Peek() {
        if(sub.Count == 0){
            while(main.Count != 0){
                sub.Push(main.Pop());
            }
        }
        if(sub.Count == 0){
            return - 1;
        }
        else{
            return sub.Peek();
        }
    }

    /** Returns whether the queue is empty. */
    public bool Empty() {
        return main.Count == 0 && sub.Count == 0;
    }
}

LRU Cache

Link - LRU Cache

Using Dictionary and Doubly Linked List

public class LRUCache {
    Dictionary<int, Node> map;
    DoubleLinkedList dll;
    int totalCapacity;
    int count;

    public LRUCache(int capacity) {
        map = new Dictionary<int, Node>();
        dll = new DoubleLinkedList();
        totalCapacity = capacity;
        count = 0;
    }

    public int Get(int key) {
        if(map.ContainsKey(key)){
            Node tempNode = map[key];
            dll.RemoveNode(tempNode);
            dll.InsertNode(tempNode);
            return tempNode.val;
        }
        else{
            return -1;
        }
    }

    public void Put(int key, int value) {
        if(map.ContainsKey(key)){
            map[key].val = value;
            this.Get(key);
            return;
        }

        if(count >= totalCapacity){
            map.Remove(dll.head.key);
            dll.RemoveNode(dll.head);
            count--;
            Console.WriteLine(count.ToString());
        }

        Node tempNode = new Node(key, value);
        map[key] = tempNode;
        dll.InsertNode(tempNode);
        count++;
    }
}

public class Node{
    public int key;
    public int val;
    public Node next;
    public Node prev;

    public Node(int _key, int _val){
        this.key = _key;
        this.val = _val;
        this.prev = null;
        this.next = null;
    }
}

public class DoubleLinkedList{
    public Node head;
    public Node tail;

    public DoubleLinkedList(){
        head = null;
        tail = null;
    }

    public void InsertNode(Node newNode){
        if(head == null && tail == null){
            head = newNode;
            tail = newNode;
        }
        else{
            newNode.prev = tail;
            tail.next = newNode;
            tail = newNode;
        }
    }

    public void  RemoveNode(Node removeNode){
        if(removeNode == head && removeNode == tail){
            head = null;
            tail = null;
        }
        else if(removeNode == head){
            head = head.next;
        }
        else if(removeNode == tail){
            tail = tail.prev;
        }
        else{
            Node temp1 = removeNode.prev;
            Node temp2 = removeNode.next;
            temp1.next = temp2;
            temp2.prev = temp1;
        }
    }
}

Decrease Elements To Make Array Zigzag

Link - Decrease Elements To Make Array Zigzag

Intuitive Approach

public int MovesToMakeZigzag(int[] nums) {
	int n = nums.Length;
	if(n <= 1){
		return 0;
	}

	int even = 0;
	int odd = 0;

	// Finding the amount by which odd indices should be updated to match the given condition
	// For odd indices
	for(int i = 1; i < n; i+=2){
		int min = Math.Min(nums[i-1], i+1 < n ? nums[i+1] : Int32.MaxValue);

		if(min <= nums[i]){
			odd += (nums[i] - min + 1);
		}
	}

	// Finding the amount by which even indices should be updated to match the given condition
	// For Even Indices
	for(int i = 0; i < n; i+=2){
		int min = Math.Min(i > 0 ? nums[i-1] : Int32.MaxValue, i+1 < n ? nums[i+1] : Int32.MaxValue);

		if(min <= nums[i]){
			even += (nums[i] - min + 1);
		}
	}

	// The minimum of odd and even will give the required result
	return Math.Min(even, odd);
}

Swimming Championship

Problem Statement

The World Swimming Championship this year has been very exciting with frequent shifts in the positions of the swimmers on the leader board. There are N Swimmers participating in the multi-race Championship. They are all assigned points after each race. The winner of the race is awarded N points, the runner-up gets N - 1 points, and so on until the last swimmer, who gets 1 point. Two swimmers cannot finish a race in the same spot. The Champion is the swimmer who has the highest points after all the races are over. If more than one swimmer has the same highest point total, they are all awarded the World Champion title. The time for the final race has now come. Some swimmers may have missed some earlier races, but all the swimmers are now present for the final race. Based on the total number of points that each swimmer has just before the final race, can you estimate how many swimmers still have a chance of being the Champion?

Examples

8,10,9 -> 3

15,14,15,12,14 -> 4

13,14,6,7 -> 2

2,8,6,4 -> 3

Using Greedy Approach

public static int SwimChamp(int[] arr){
	int N = arr.Length;

	// Sort the array in descending order
	Array.Sort(arr);
	Array.Reverse(arr);

	int lps = 1; // Lowest Possible Score
	int hps = N; // Higher Possible Score
	int count = 1;

	int maxSoFar = 0;

	for(int i = 1; i < arr.Length; i++){
		maxSoFar = Math.Max(maxSoFar, arr[i-1] + lps);
		lps++;

		if(arr[i] + hps >= maxSoFar){
			count++;
		}
		else{
			break;
		}
	}

	return count;
}

Balanced Binary Tree

Link - Balanced Binary Tree

Recursion

bool output = true;
public bool IsBalanced(TreeNode root) {
	this.MaxDepth(root);
	return output;
}

public int MaxDepth(TreeNode root){
	if(root == null){
		return 0;
	}

	int left = this.MaxDepth(root.left);
	int right = this.MaxDepth(root.right);

	if(Math.Abs(left - right) > 1){
		output = false;
	}

	return 1 + Math.Max(left, right);
}

Custom Sort method using IComparable and IComparer interface

Problem Statement

Given an array of students. Sort them by their age. If two or more students have the same age then sort them by the alphabetical order of their name.

Solution 1 (Using IComparable Interface)

public class Program
{
	static void Main(string[] args)
	{
		List<Student> students = new List<Student>();
		students.Add(new Student("LMN", 11));
		students.Add(new Student("XYZ", 12));
		students.Add(new Student("ABC", 12));
		students.Add(new Student("DEF", 13));

		students.Sort();

		foreach(var item in students)
		{
			Console.WriteLine(item.Name + " - " + item.Age.ToString());
		}

		// Output will be
		// LMN - 11
		// ABC - 12
		// XYZ - 12
		// DEF - 13

		Console.ReadLine();
	}

	public class Student : IComparable<Student>
	{
		string name;
		int age;

		public string Name { get { return name; } }
		public int Age { get { return age; } }

		public Student(string _name, int _age)
		{
			this.name = _name;
			this.age = _age;
		}

		public int CompareTo(Student other)
		{
			if(this.Age < other.Age)
			{
				return -1;
			}
			else if(this.Age > other.Age)
			{
				return 1;
			}
			else
			{
				return this.Name.CompareTo(other.Name);
			}
		}
	}
}

Solution 2 (Using IComparer Interface)

public class Program
{
	static void Main(string[] args)
	{
		List<Student> students = new List<Student>();
		students.Add(new Student("LMN", 11));
		students.Add(new Student("XYZ", 12));
		students.Add(new Student("ABC", 12));
		students.Add(new Student("DEF", 13));

		AscendingOrder order = new AscendingOrder();
		students.Sort(order);

		foreach(var item in students)
		{
			Console.WriteLine(item.Name + " - " + item.Age.ToString());
		}

		// Output will be
		// LMN - 11
		// ABC - 12
		// XYZ - 12
		// DEF - 13

		Console.ReadLine();
	}

	public class Student
	{
		string name;
		int age;

		public string Name { get { return name; } }
		public int Age { get { return age; } }

		public Student(string _name, int _age)
		{
			this.name = _name;
			this.age = _age;
		}
	}

	public class AscendingOrder: IComparer<Student>
	{
		public int Compare(Student student1, Student student2)
		{
			if (student1.Age < student2.Age)
			{
				return -1;
			}
			else if (student1.Age > student2.Age)
			{
				return 1;
			}
			else
			{
				return string.Compare(student1.Name, student2.Name);
			}
		}
	}
}

Merge Two Sorted Lists

Link - Merge Two Sorted Lists

Naive Approach

Traverse through both the lists and store the numbers in two different lists. Then merge them into a single list using the two-pointer technique. Now push this list into a linked list form.

Recursive Approach

Create a dummy node and make it as the head node for the resulting solution (This is done to handle certain edge cases where any of the input lists are empty). Call the merge function with the dummy node along with both the lists. The merge function compares the value of both the lists and sets the next pointer of the head based on the comparison. Then call the merge function recursively to get the desired result.

Note: This solution performs the operation in-place, so the space requirement is constant, but since the stack is involved in recursive calls the space complexity for this solution will be O(m + n) where m and n is the size of both the lists.

public ListNode MergeTwoLists(ListNode l1, ListNode l2) {
    ListNode dummy = new ListNode(Int32.MinValue);

    this.Merge(dummy, l1, l2);

    return dummy.next;
}

public void Merge(ListNode head, ListNode l1, ListNode l2){
    // If anyone of the lists has reached the end then all the remaining nodes of the other list are greater than the first list.
    if(l1 == null){
        head.next = l2;
        return;
    }
    else if(l2 == null){
        head.next = l1;
        return;
    }

    if(l1.val < l2.val){
        head.next = l1;
        head = head.next;
        this.Merge(head, l1.next, l2);
    }
    else{
        head.next = l2;
        head = head.next;
        this.Merge(head, l1, l2.next);
    }
}

| Approach | Time Complexity | Space Complexity | | | :-: | :—: | | Naive Approach | O(m + n) | O(m + n) | | Recursive Approach | O(m + n) | O(m + n) (since stack memory will be used for recursive function call) |

Counting Bits

Link - Counting Bits

Bitwise Solution

public int[] CountBits(int num) {
	int[] output = new int[num + 1];
	output[0]= 0;

	for(int i = 1; i <= num; i++){
		// For even numbers 1000(8) will be same as 100(4)
		if(i % 2 == 0){
			output[i] = output[i>>1];
		}
		// For odd numbers 111(7) will be same as 1 + 110(6)
		else{
			output[i] = output[i-1] + 1;
		}
	}

	return output;
}

String Compression

Problem Statement

Given an input string, find its Run Length Encoded string.

For example, given AAABBCCCD return A3B2C3D1.

Also, write the function to decode the encoded string.

Encode Function

Start looping through the string array (starting from 2nd index) and check if current character and the previous character are same. If they are same then increment the charCounter. Else append the previous character and the charCount to the output string. This should give you the run-length encoded string.

public static string Encode(string input){
    string output = "";

    int charCount = 1;
    for(int i = 1; i < input.Length; i++){
        if(input[i] == input[i-1]){
            charCount++;
        }
        else{
            output += input[i-1] + charCount.ToString();
            charCount = 1;
        }
    }
    return output;
}

Decode Function

Every even indexed value is the character and odd indexed value will be its count. Loop through the input and append the character to the output string count number of times. This will give you the decoded input.

public static string Decode(string input){
    string output  = "";

    int i = 0;
    while(i < input.Length){
        char value = input[2*i];
        int count = Int32.Parse(input[2*i].ToString());

        for(int j = 0; j < count; j++){
            output += value;
        }

        i++;
    }
}
// The code above for decoding of the string is a basic version. This function will fail if a character repeats more than 9 times. Suppose the input is A10 then the function will throw an error.

Linked List Cycle

Link - Linked List Cycle

Using HashSet

Recursively add the nodes into a HashSet. But before adding it to the HashSet check if that node already exists in the HashSet. If it already exists then it infers that the given list has a cycle.

public bool HasCycle(ListNode head) {
	if(head == null){
		return false;
	}

	HashSet<ListNode> temp = new HashSet<ListNode>();

	while(head != null){
		if(temp.Contains(head)){
			return true;
		}
		else{
			temp.Add(head);
		}
		head = head.next;
	}

	return false;
}

Using Fast and Slow Pointer

Use two pointers. One pointer jumps one node at a time and the other jumps two nodes at a time. If both the pointers intersect at any point in time then it means that it has a cycle.

public bool HasCycle(ListNode head) {
	if(head == null){
		return false;
	}

	ListNode fast = head;
	ListNode slow = head;

	while(fast != null && fast.next != null){
		slow = slow.next;
		fast = fast.next.next;

		if(fast == slow){
			return true;
		}
	}

	return false;
}

Count Square Submatrices with All Ones

Link - Count Square Submatrices with All Ones

Using Bottom-Up Approach

    public int CountSquares(int[][] matrix) {
        int row = matrix.Length;

        if(row == 0){
            return 0;
        }

        int col = matrix[0].Length;

        int[,] dp = new int[row+1, col+1];

        int count = 0;

        for(int i = 1; i <= row; i++){
            for(int j = 1; j <= col; j++){
                if(matrix[i-1][j-1] == 1){
                    dp[i,j] = Math.Min(dp[i-1,j], Math.Min(dp[i,j-1], dp[i-1,j-1])) + 1;
                    count += dp[i,j];
                }
            }
        }

        return count;
    }

Invert Binary Tree

Link - Invert Binary Tree

Using Depth First Search

public TreeNode InvertTree(TreeNode root) {
	if(root == null){
		return root;
	}

	TreeNode left = this.InvertTree(root.left);
	TreeNode right = this.InvertTree(root.right);

	root.right = left;
	root.left = right;

	return root;
}

Using Breadth First Search

public TreeNode InvertTree(TreeNode root) {
	if(root == null){
		return root;
	}

	Queue<TreeNode> bfsQueue = new Queue<TreeNode>();
	bfsQueue.Enqueue(root);

	while(bfsQueue.Count > 0){
		TreeNode temp = bfsQueue.Dequeue();

		TreeNode tempLeft = temp.left;
		TreeNode tempRight = temp.right;

		temp.left = tempRight;
		temp.right = tempLeft;

		if(temp.left != null){
			bfsQueue.Enqueue(temp.left);
		}

		if(temp.right != null){
			bfsQueue.Enqueue(temp.right);
		}
	}

	return root;
}

Course Schedule III

Link - Course Schedule III

Using Priority Queue

public class Solution {
    public int ScheduleCourse(int[][] courses) {
        // Sort the courses in the increasing order of last day
        Array.Sort(courses, (a,b) => a[1] - b[1]);

        PriorityQueue pq = new PriorityQueue();
        int time = 0;

        foreach(int[] c in courses){
            // if current day plus the duration is on or before last day, then that course can be taken. So enqueue to the priority queue
            if(time + c[0] <= c[1]){
                pq.Enqueue(c[0]);
                time += c[0];
            }
            else{
                // if there's a course which has lesser duration than the current course in the priority queue, then replace it and update the time
                if(pq.Count != 0 && pq.Peek() > c[0]){
                    int temp = pq.Dequeue();
                    time = time - temp + c[0];
                    pq.Enqueue(c[0]);
                }
            }
        }

        return pq.Count;
    }
}

public class PriorityQueue{
    List<int> queue = null;
    int heapSize = -1;
    public int Count { get { return queue.Count; } }

    public PriorityQueue()
    {
        queue = new List<int>();
    }

    private int LeftChild(int i)
    {
        return (i * 2) + 1;
    }

    private int RightChild(int i)
    {
        return (i * 2) + 2;
    }

    private void Swap(int i, int j)
    {
        int temp = queue[i];
        queue[i] = queue[j];
        queue[j] = temp;
    }

    private void MaxHeapify(int i)
    {
        int left = this.LeftChild(i);
        int right = this.RightChild(i);

        int highest = i;

        if (left <= heapSize && queue[highest] < queue[left])
        {
            highest = left;
        }

        if (right <= heapSize && queue[highest] < queue[right])
        {
            highest = right;
        }

        if (highest != i)
        {
            this.Swap(highest, i);
            this.MaxHeapify(highest);
        }
    }

    private void BuildMaxHeap(int i)
    {
        while (i >= 0 && queue[(i - 1) / 2] < queue[i])
        {
            this.Swap(i, (i - 1) / 2);
            i = (i - 1) / 2;
        }
    }

    public void Enqueue(int i)
    {
        queue.Add(i);
        heapSize++;
        this.BuildMaxHeap(heapSize);
    }

    public int Dequeue()
    {
        if (heapSize > -1)
        {
            int returnVal = queue[0];
            queue[0] = queue[heapSize];
            queue.RemoveAt(heapSize);
            heapSize--;
            this.MaxHeapify(0);
            return returnVal;
        }
        else
        {
            throw new Exception("Queue is empty");
        }
    }

    public int Peek()
    {
        if (heapSize > -1)
        {
            return queue[0];
        }
        else
        {
            throw new Exception("Queue is empty");
        }
    }
}

Remove Linked List Elements

Link - Remove Linked List Elements

Iterative Approach

Initially create a dummy node and then assign the next pointer of the dummy node to the head. This is done to avoid issues during certain edge cases (Suppose a case where the head value matches the target number, then you will need a pointer to maintain another head). Create two pointers previous and current. These two pointers are required whenever a matching node is found. When a matching node is found in the current node simply update the next pointer of the previous node to point to the next pointer of the current node. Keep repeating this iteratively to get the required result.

public ListNode RemoveElements(ListNode head, int val) {
    ListNode dummy = new ListNode(0);
    dummy.next = head;

    ListNode prev = dummy;
    ListNode curr = head;

    while(curr != null){
        if(curr.val == val){
            prev.next = curr.next;
        }
        else{
            prev = curr;
        }

        curr = curr.next;
    }

    return dummy.next;
}

// This solution has a time complexity of O(n) and space complexity of O(1).

Maximal Square

Link - Maximal Square

Recursion (Time Limit Exceeded)

public int MaximalSquare(char[][] matrix) {
	int row = matrix.Length;

	if(row == 0){
		return 0;
	}

	int col = matrix[0].Length;

	int max = 0;

	for(int i = row - 1; i >= 0; i--){
		for(int j = col - 1; j >= 0; j--){
			if(matrix[i][j] == '1'){
				max = Math.Max(max, this.MaximalSquare(matrix, i, j));
			}
		}
	}

	return max * max;
}

public int MaximalSquare(char[][] matrix, int row, int col){
	if(row < 0 || col < 0){
		return 0;
	}

	if(matrix[row][col] == '1'){
		return 1 + Math.Min(
			this.MaximalSquare(matrix, row - 1, col),
			Math.Min(
				this.MaximalSquare(matrix, row - 1, col - 1),
				this.MaximalSquare(matrix, row, col - 1)
			)
		);
	}

	return 0;
}

Recursion with Memoization (Not really efficient)

public int MaximalSquare(char[][] matrix) {
	int row = matrix.Length;

	if(row == 0){
		return 0;
	}

	int col = matrix[0].Length;

	int max = 0;

	int[,] memo = new int[row,col];

	// Initialize all the elements of the memoization array to -1
	for(int i = 0; i < row; i++){
		for(int j = 0; j < col; j++){
			memo[i,j] = -1;
		}
	}

	for(int i = row - 1; i >= 0; i--){
		for(int j = col - 1; j >= 0; j--){
			if(matrix[i][j] == '1'){
				max = Math.Max(max, this.MaximalSquare(matrix, i, j, memo));
			}
		}
	}

	return max * max;
}

public int MaximalSquare(char[][] matrix, int row, int col, int[,] memo){
	if(row < 0 || col < 0){
		return 0;
	}

	if(memo[row,col] != -1){
		return memo[row,col];
	}

	if(matrix[row][col] == '1'){
		memo[row,col] = 1 + Math.Min(
			this.MaximalSquare(matrix, row - 1, col, memo),
			Math.Min(
				this.MaximalSquare(matrix, row - 1, col - 1, memo),
				this.MaximalSquare(matrix, row, col - 1, memo)
			)
		);
	}
	else{
		memo[row,col] = 0;
	}

	return memo[row,col];
}

Bottom Up Approach

public int MaximalSquare(char[][] matrix) {
	int rows = matrix.Length;

	if(rows == 0){
		return 0;
	}

	int cols = matrix[0].Length;

	int[,] dp = new int[rows,cols];

	int maxSoFar = 0;

	for(int i = 0; i < rows; i++){
		for(int j = 0; j < cols; j++){

			if(i == 0 || j == 0){
				dp[i,j] = matrix[i][j] == '1' ? 1 : 0;
			}
			else if(matrix[i][j] == '1'){
				dp[i,j] = 1 + Math.Min(Math.Min(dp[i,j-1], dp[i-1,j]), dp[i-1,j-1]);
			}

			maxSoFar = Math.Max(maxSoFar, dp[i,j]);
		}
	}

	return maxSoFar * maxSoFar;
}

Even Odd Tree

Link - Even Odd Tree

Using Breadth First Search

public bool IsEvenOddTree(TreeNode root) {
	if(root == null){
		return true;
	}

	Queue<TreeNode> bfsQueue = new Queue<TreeNode>();
	bfsQueue.Enqueue(root);

	int level = 1;

	while(bfsQueue.Count > 0){
		int count = bfsQueue.Count;

		int prevValue;

		if(level % 2 == 1){
			prevValue = 0;
		}
		else{
			prevValue = Int32.MaxValue;
		}

		for(int i = 0; i < count; i++){
			TreeNode tempNode = bfsQueue.Dequeue();

			if((level % 2 == 1 && tempNode.val % 2 == 1 && tempNode.val > prevValue) || (level % 2 == 0 && tempNode.val % 2 == 0 && tempNode.val < prevValue)){
				if(tempNode.left != null){
					bfsQueue.Enqueue(tempNode.left);
				}
				if(tempNode.right != null){
					bfsQueue.Enqueue(tempNode.right);
				}

				prevValue = tempNode.val;
			}
			else{
				return false;
			}

		}

		level++;
	}

	return true;
}

Find Largest Value in Each Tree Row

Link - Find Largest Value in Each Tree Row

Breadth-First Search Approach

Perform a level order traversal on the given tree. As and when each level is traversed push the max value of that level to the list to get the required result.

public IList<int> LargestValues(TreeNode root) {
    List<int> output = new List<int>();

    if(root == null){
        return output.ToArray();
    }

    Queue<TreeNode> bfsQueue = new Queue<TreeNode>();
    bfsQueue.Enqueue(root);

    while(bfsQueue.Count > 0){
        int count = bfsQueue.Count;

        int max = Int32.MinValue;

        for(int i = 0; i < count; i++){
            TreeNode tempNode = bfsQueue.Dequeue();

            max = Math.Max(tempNode.val, max);

            if(tempNode.left != null){
                bfsQueue.Enqueue(tempNode.left);
            }

            if(tempNode.right != null){
                bfsQueue.Enqueue(tempNode.right);
            }
        }

        output.Add(max);
    }
    return output.ToArray();
}

Depth First Search Approach

Create a list that maintains the maximum value of nodes for a level. Recursively call the FindLargest function and update the max value to the list for that specific level.

public IList<int> LargestValues(TreeNode root) {
    List<int> output = new List<int>();

    this.FindLargest(root, output, 0);

    return output.ToArray();
}

public void FindLargest(TreeNode root, List<int> output, int count){
    if(root == null){
        return;
    }

    if(count < output.Count){
        output[count] = Math.Max(root.val, output[count]);
    }
    else{
        output.Add(root.val);
    }

    this.FindLargest(root.left, output, count + 1);
    this.FindLargest(root.right, output, count + 1);
}

Sort Colors

Link - Sort Colors

Two Pointer Approach

Create two pointers one pointing to the start and the other pointing to the end of the array. Start checking from the beginning of the array, if you find a zero, swap it with the zero pointer. If you find a two then swap it with the two pointer. Keep doing this until the two pointer to get the required result.

public void SortColors(int[] nums) {
    int zeroPt = 0;
    int twoPt = nums.Length - 1;

    int i = 0;

    while(i <= twoPt){
        if(nums[i] == 0){
            int temp = nums[i];
            nums[i] = nums[zeroPt];
            nums[zeroPt] = temp;
            zeroPt++;
            i++;
        }
        else if(nums[i] == 2){
            int temp = nums[i];
            nums[i] = nums[twoPt];
            nums[twoPt] = temp;
            twoPt--;
        }
        else{
            i++;
        }
    }
}

Contiguous Array

Link - Contiguous Array

Using Dictionary

public int FindMaxLength(int[] nums) {
	Dictionary<int,int> map = new Dictionary<int,int>();

	map[0] = -1;

	int maxLength = 0;
	int count = 0;

	for(int i = 0; i < nums.Length; i++){
		if(nums[i] == 0){
			count++;
		}
		else{
			count--;
		}

		if(map.ContainsKey(count)){
			maxLength = Math.Max(maxLength, i - map[count]);
		}
		else{
			map[count] = i;
		}
	}

	return maxLength;
}

Subdomain Visit Count

Link - Subdomain Visit Count

Dictionary Approach

Simply split the given input into two parts. Take the domain part of it and pass it through a string splitter function which will return all the subdomains in that string. Now loop through all the subdomains and put it in the dictionary along with the count. Repeat this across all the domains. Once done, loop through the dictionary and return the values in the desired format.

public IList<string> SubdomainVisits(string[] cpdomains) {
    Dictionary<string, int> dict = new Dictionary<string, int>();
    List<string> output = new List<string>();

    for(int i = 0; i < cpdomains.Length; i++){
        string[] split = cpdomains[i].Split(' ');
        int num = Int32.Parse(split[0]);
        List<string> domains = this.StringSplitter(split[1]);

        for(int j = 0 ; j < domains.Count; j++){
            if(dict.ContainsKey(domains[j])){
                dict[domains[j]] += num;
            }
            else{
                dict[domains[j]] = num;
            }
        }
    }

    foreach(var item in dict){
        output.Add(item.Value + " " + item.Key);
    }
    return output;
}

public List<string> StringSplitter(string input){
    List<string> temp = new List<string>();
    temp.Add(input);
    while(true){
        int index = input.IndexOf(".");
        if(index == -1){
            break;
        }
        input = input.Substring(index+1);
        temp.Add(input);
    }
    return temp;
}

Prison Cells After N Days

Link - Prison Cells After N Days

Using HashSet

public int[] PrisonAfterNDays(int[] cells, int N) {
	int count = 0;
	HashSet<string> set = new HashSet<string>();

	bool hasCycle = false;

	for(int i = 0; i < N; i++){
		int[] next = this.NextDay(cells);

		string key = string.Concat(next);

		if(!set.Contains(key)){
			set.Add(key);
			count++;
		}
		else{
			hasCycle = true;
			break;
		}
		cells = next;
	}

	if(hasCycle){
		int n = N % count;

		for(int i = 0; i < n; i++){
			cells = this.NextDay(cells);
		}

	}

	return cells;
}

public int[] NextDay(int[] cells){
	int[] temp = new int[cells.Length];

	for(int i = 1; i < cells.Length - 1; i++){
		temp[i] = cells[i-1] == cells[i+1] ? 1 : 0;
	}

	return temp;
}

Find the Difference

Link - Find the Difference

Using Dictionary

public char FindTheDifference(string s, string t) {
	Dictionary<char,int> map = new Dictionary<char,int>();

	foreach(char c in s){
		if(!map.ContainsKey(c)){
			map[c] = 0;
		}

		map[c] += 1;
	}

	foreach(char c in t){
		if(!map.ContainsKey(c) || map[c] == 0){
			return c;
		}
		map[c] -= 1;
	}

	return ' ';
}

Using Bit Manipulation

XOR all the characters from both the strings. The repeated characters will cancel out, leaving behind the result.

public char FindTheDifference(string s, string t) {
	int res = 0;

	foreach(char c in s){
		res ^= c;
	}

	foreach(char c in t){
		res ^= c;
	}

	return Convert.ToChar(res);
}

Detect Cycles in 2D Grid

Link - Detect Cycles in 2D Grid

Depth First Search Approach

public bool ContainsCycle(char[][] grid) {
	int rows = grid.Length;

	if(rows == 0){
		return false;
	}

	int cols = grid[0].Length;

	bool[,] visited = new bool[rows,cols];

	for(int i = 0; i < rows; i++){
		for(int j = 0; j < cols; j++){
			if(!visited[i,j] && this.HasCycle(i, j, -1, -1, rows, cols, visited, grid)){
				return true;
			}
		}
	}

	return false;
}

private bool HasCycle(int curX, int curY, int lastX, int lastY, int rows, int cols, bool[,] visited, char[][] grid){
	visited[curX,curY] = true;

	int[] dx = new int[] {0, 1, 0, -1};
	int[] dy = new int[] {1, 0, -1, 0};

	for(int a = 0; a < 4; a++){
		int newX = curX + dx[a];
		int newY = curY + dy[a];

		// !(lastX == newX && lastY == newY) - used to prevent the dfs from going back to the previously traversed node to avoid repetition.
		if(newX >= 0 && newX < rows && newY >= 0 && newY < cols && grid[newX][newY] == grid[curX][curY] && !(lastX == newX && lastY == newY)){
			if(visited[newX,newY] || this.HasCycle(newX, newY, curX, curY, rows, cols, visited, grid)){
				return true;
			}
		}
	}

	return false;
}

License Key Formatting

Link - License Key Formatting

public string LicenseKeyFormatting(string S, int K) {
    StringBuilder output = new StringBuilder();

    int i = S.Length - 1;
    int count = 0;

    while(i >= 0){
        char c = char.ToUpper(S[i]);
        if(c == '-'){
            i--;
        }
        else if(count != 0 && count % K == 0){
            output.Insert(0, '-');
            count = 0;
        }
        else{
            output.Insert(0, c);
            count++;
            i--;
        }
    }

    return output.ToString();
}

Sqrt(x)

Link - Sqrt(x)

Naive Approach

The naive way to solve this would be to square the number starting from 0 and see if it is equal to or greater than the given number. This will give you the result.

public int MySqrt(int x) {
    if (x <= 1){
        return x;
    }

    int i = 1;
    while(x >= i * i){
        i++;
    }

    return i - 1;
}

This approach has a time complexity of O(sqrt(n)). See the next approach which solves it logarithmic time complexity.

Binary Search Approach

Iteratively perform binary search operation to get the required result.

public int MySqrt(int x) {
    if(x == 0){
        return 0;
    }

    int left = 1;
    int right = x;
    int mid = 0;

    while(left <= right){
        mid = left + (right - left)/2;
        if(mid == x / mid){
            return mid;
        }
        else if (mid > x / mid){
            right = mid - 1;
        }
        else{
            left = mid + 1;
        }
    }
    return right;
}

Newtons Method

Initially set n the same as the given input number. Then recursively call the Newtons formula to until the difference between output and n are small. This should give you the required result.

public int MySqrt(int x) {
    if(x == 0){
        return 0;
    }

    double n = x;
    double output;

    while(true){
        output = 0.5 * (n + (x / n));
        if(Math.Abs(output - n) < 0.1){
            break;
        }

        n = output;
    }

    return (int)output;
}

Reconstruct Itinerary

Link - Reconstruct Itinerary

Using Depth First Search

public IList<string> FindItinerary(IList<IList<string>> tickets) {
	Dictionary<string, List<string>> map = new Dictionary<string, List<string>>();
	List<string> output = new List<string>();

	// Storing the tickets in an adjacency list
	foreach(List<string> item in tickets){
		if(!map.ContainsKey(item[0])){
			map[item[0]] = new List<string>();
		}

		map[item[0]].Add(item[1]);
	}

	// Sorting the values in lexicographical order
	foreach(List<string> item in map.Values){
		item.Sort();
	}

	int totalTickets = tickets.Count;

	output.Add("JFK");
	this.DFS(map, "JFK", output, totalTickets, 0);

	return output;
}

public bool DFS(Dictionary<string, List<string>> map, string cur, List<string> output, int totalTickets, int usedTickets){
	if(!map.ContainsKey(cur)){
		return false;
	}

	List<string> tempList = map[cur];

	for(int i = 0; i < tempList.Count; i++){
		string tempPath = tempList[i];
		tempList.RemoveAt(i);
		output.Add(tempPath);

		usedTickets++;

		bool flag = this.DFS(map, tempPath, output, totalTickets, usedTickets);

		if(flag){
			return true;
		}

		if(totalTickets == usedTickets){
			return true;
		}

		// Backtracking
		tempList.Insert(i, tempPath);
		output.RemoveAt(output.Count - 1); // Remove the last element from the path

		usedTickets--;
	}

	return false;
}

Jump Game II

Link - Jump Game II

Using Greedy Approach

public int Jump(int[] nums) {
	int count = 0;
	int currEnd = 0;
	int currMax = 0;

	for(int i = 0; i < nums.Length - 1; i++){
		currMax = Math.Max(currMax, i + nums[i]);
		if(i == currEnd){
			count++;
			currEnd = currMax;
		}
	}

	return count;
}

Power of Two

Link - Power of Two

Iterative Approach

Keep dividing the given number by 2 until reaches 1.

public bool IsPowerOfTwo(int n) {
    // return false if the given number is a negative number or zero
    if(n <= 0){
        return false;
    }

    while(n % 2 == 0){
        n /= 2;
    }

    return n == 1;
}

Bitwise Approach

If the given number is a power of 2 then the binary form of that number will have all of its bits as 0 except for the most significant bit which will be 1. Simply perform an AND operation with n-1 which have all the bits 1 except for the MSB which will be 0. If the AND operation returns zero then it’s a power of two.

  public bool IsPowerOfTwo(int n) {
      if(n <= 0){
          return false;
      }

      return (n & (n - 1)) == 0;
  }

Maximum Subarray

Link - Maximum Subarray

Using Dynamic Programming

    public int MaxSubArray(int[] nums) {
        int[] dp = new int[nums.Length];
        int maxSum = nums[0];

        dp[0] = nums[0];

        for(int i = 1; i < nums.Length; i++){
            dp[i] = dp[i-1] > 0 ? dp[i-1] + nums[i] : nums[i];
            maxSum = Math.Max(dp[i], maxSum);
        }

        return maxSum;
    }

This solution has a time and space complexity of O(n).

Better Solution

This approach is similar to the previous one, except that we avoid using a redundant array to store all the values. Instead, two variables are used to store the current max sum and the overall max sum.

public int MaxSubArray(int[] nums) {
    int maxSum = nums[0];
    int currSum = nums[0];

    for(int i = 1; i < nums.Length; i++){
        currSum = Math.Max(currSum + nums[i], nums[i]);
        maxSum = Math.Max(currSum, maxSum);
    }

    return maxSum;
}
// This has a time complexity of O(n) and space complexity of O(1).

Deletion Distance

Problem Statement

The deletion distance of two strings is the minimum number of characters you need to delete in the two strings in order to get the same string. For instance, the deletion distance between “heat” and “hit” is 3:

  • By deleting ‘e’ and ‘a’ in “heat”, and ‘i’ in “hit”, we get the string “ht” in both cases.
  • We cannot get the same string from both strings by deleting 2 letters or fewer.

Given the strings str1 and str2, write an efficient function deletionDistance that returns the deletion distance between them. Explain how your function works, and analyze its time and space complexities.

Bottom Up Approach

Take the example of dog and frog. Three characters will have to be removed to match the strings.

	""	d	o	g
""	0	1	2	3
f	1	2	3	4
r	2	3	4	5
o	3	4	3	4
g	4	5	4	3
public static int DeletionDistance(string str1, string str2)
{
  int m = str1.Length;
  int n = str2.Length;

  int[,] memo = new int[m+1, n+1];

  for(int i = 0; i <= m; i++){
	for(int j = 0; j <= n; j++){
	  if(i == 0){
		memo[i,j] = j;
	  }
	  else if(j == 0){
		memo[i,j] = i;
	  }
	  else if(str1[i-1] == str2[j-1]){
		memo[i,j] = memo[i-1,j-1];
	  }
	  else{
		memo[i,j] = 1 + Math.Min(memo[i-1,j], memo[i, j-1]);
	  }
	}
  }

  return memo[m,n];
}

Bottom Up Approach (Space Optimized)

public static int DeletionDistance(string str1, string str2)
{
  string minStr = str1.Length < str2.Length ? str1 : str2;
  string maxStr = str1.Length < str2.Length ? str2 : str1;

  int m = minStr.Length;
  int n = maxStr.Length;

  int[] prevMemo = new int[m+1];
  int[] currMemo = new int[m+1];

  for(int i = 0; i <= n; i++){
	for(int j = 0; j <= m; j++){
	  if(i == 0){
		currMemo[j] = j;
	  }
	  else if(j == 0){
		currMemo[j] = i;
	  }
	  else if(maxStr[i-1] == minStr[j-1]){
		currMemo[j] = prevMemo[j-1];
	  }
	  else{
		currMemo[j] = 1 + Math.Min(currMemo[j-1], prevMemo[j]);
	  }
	}

	prevMemo = currMemo;
	currMemo = new int[m+1];

  }

  return prevMemo[m];
}

Minimum Deletions to Make String Balanced

Link - Minimum Deletions to Make String Balanced

Greedy Approach

public int MinimumDeletions(string s) {
	int aCount = 0;
	int bCount = 0;

	foreach(char c in s){
		if(c == 'a'){
			aCount++;
		}
	}

	int minSoFar = aCount;

	foreach(char c in s){
		if(c == 'a'){
			aCount--;
		}
		else{
			bCount++;
		}

		minSoFar = Math.Min(minSoFar, aCount + bCount);
	}

	return minSoFar;
}

Remove Palindromic Subsequences

Link - Remove Palindromic Subsequences

Naive Solution

case 1: Length of the string is 0 then the result is 0 case 2: Given string is palindrome then the result is 1 case 3: In all other cases the result is 2

public int RemovePalindromeSub(string s) {
	if(s.Length == 0){
		return 0;
	}

	int i = 0;
	int j = s.Length - 1;

	while(i < j){
		if(s[i] == s[j]){
			i++;
			j--;
		}
		else{
			return 2;
		}
	}

	return 1;
}

Crawler Log Folder

Link - Crawler Log Folder

Using One Pass

public int MinOperations(string[] logs) {
	int count = 0;

	foreach(string log in logs){
		if(log == "../"){
			if(count != 0){
				count--;
			}
		}
		else if(log != "./"){
			count++;
		}
	}

	return count;
}

Add Two Numbers

Link - Add Two Numbers

Basic Approach

In this approach we find both the numbers using the FindNum method. This method iteratively loops through the linkedlist until the end and forms the number. Then find the sum of both the numbers. Now take this sum and put it in a new linked list by finding the mod of the sum and then dividing it by 10. Repeat this process until the sum is 0.

public ListNode AddTwoNumbers(ListNode l1, ListNode l2) {
    ListNode output = new ListNode(0);
    ListNode temp = output;

    int sum = this.FindNum(l1) + this.FindNum(l2);

    do{
        temp.next = new ListNode(sum % 10);
        sum = sum / 10;
        temp = temp.next;
    } while(sum != 0);

    return output.next;
}

public int FindNum(ListNode node){
    int num = node.val;
    int power = 1;

    while(node.next != null){
        node = node.next;
        power *= 10;
        num += (node.val * power);
    }
    return num;
}

The time and space complexity of this approach is O(n).

The above method works, but fails when the linked list is very long i.e. if the number crosses the Int32 overflow. To overcome this problem use the next appraoach.

Best Approach

This approach takes the sum of both the heads of the linked lists, takes out the carry value and then puts it in a new linked list. Move the pointer to the next node and repeat this process. Keep doing this to get the sum. See the below code for the solution.

public ListNode AddTwoNumbers(ListNode l1, ListNode l2) {
    ListNode output = new ListNode(0);
    ListNode temp = output;

    int carry = 0;

    while(l1 != null || l2 != null){
        int firstNum = 0;
        int secondNum = 0;

        if(l1 != null){
            firstNum = l1.val;
            l1 = l1.next;
        }

        if(l2 != null){
            secondNum = l2.val;
            l2 = l2.next;
        }

        int sum = firstNum + secondNum;

        // Puts only the least significant number into the ListNode
        temp.next = new ListNode((carry + sum) % 10);
        // This will get the new carry that will be used in the next iteration
        carry = (carry + sum) / 10;
        temp = temp.next;
    }

    // If there is a carry from the last iteration of the loop then create a new node with the carry as the value
    if (carry != 0){
        temp.next = new ListNode(carry);
    }

    return output.next;
}
// Even this approach has the same time and space complexitites.

Sum of Root To Leaf Binary Numbers

Link - Sum of Root To Leaf Binary Numbers

Using Recursion

public int SumRootToLeaf(TreeNode root) {
	return this.Sum(root, 0);
}

public int Sum(TreeNode node, int valueSoFar){
	if(node == null){
		return 0;
	}

	valueSoFar = valueSoFar * 2 + node.val;

	if(node.left == null && node.right == null){
		return valueSoFar;
	}
	else{
		return this.Sum(node.left, valueSoFar) + this.Sum(node.right, valueSoFar);
	}
}

Rotting Oranges

Link - Rotting Oranges

Breadth-First Search Approach

public int OrangesRotting(int[][] grid) {
    int R = grid.Length;
    int C = grid[0].Length;

    Queue<int> bfsQueue = new Queue<int>();

    int depth = 0;

    // Find all the rotten oranges and add them to the queue.
    for(int r = 0; r < R; r++){
        for(int c = 0; c < C; c++){
            if(grid[r][c] == 2){
                int loc = (r * C) + c;
                bfsQueue.Enqueue(loc);
            }
        }
    }

    // These are the surrounding coordinates of a given coordinate
    int[] surrR = new int[] {-1, 0, 1, 0};
    int[] surrC = new int[] {0, -1, 0, 1};

    while(bfsQueue.Count != 0){
        int count = bfsQueue.Count;

        for(int k = 0; k < count; k++){
            int loc = bfsQueue.Dequeue();

            int r = loc / C;
            int c = loc % C;

            // Check all the four surroundings for any fresh oranges and add them to the queue
            for(int i = 0; i < 4; i++){
                int row = r + surrR[i];
                int col = c + surrC[i];

                if(row >= 0 && row < R && col >= 0 && col < C && grid[row][col] == 1){
                    grid[row][col] = 2;
                    int loc2 = (row * C) + col;
                    bfsQueue.Enqueue(loc2);
                }
            }
        }

        // If queue is empty then are no more fresh oranges hence don't increase the depth.
        if(bfsQueue.Count != 0){
            depth++;
        }
    }

    // Check if there are any oranges which are fresh, if yes then return -1.
    for(int i = 0; i < R; i++){
        for(int j = 0; j < C; j++){
            if(grid[i][j] == 1){
                return -1;
            }
        }
    }

    return depth;
}
// This has a time and space complexity of O(N).

Longest Word in Dictionary through Deleting

Link - Longest Word in Dictionary through Deleting

Using Sorting

public string FindLongestWord(string s, IList<string> d) {
	string output = "";

	List<string> dCopy = new List<string>(d);
	// Sorting the list in the descending and lexicographical order
	dCopy.Sort((a,b) => a.Length == b.Length ? a.CompareTo(b) : b.Length - a.Length);

	foreach(string item in dCopy){
		if(IsSubsequence(s, item)){
			return item;
		}
	}

	return output;
}

public bool IsSubsequence(string s, string d){
	if(d.Length > s.Length){
		return false;
	}

	int i = 0;
	int j = 0;

	while(i < s.Length && j < d.Length){
		if(s[i] == d[j]){
			j++;
		}
		i++;
	}

	return j == d.Length;
}

Without Sorting

public string FindLongestWord(string s, IList<string> d) {
	string output = "";

	foreach(string item in d){
		if(IsSubsequence(s, item)){
			if(item.Length > output.Length || (item.Length == output.Length && item.CompareTo(output) < 1)){
				output = item;
			}
		}
	}

	return output;
}

public bool IsSubsequence(string s, string d){
	if(d.Length > s.Length){
		return false;
	}

	int i = 0;
	int j = 0;

	while(i < s.Length && j < d.Length){
		if(s[i] == d[j]){
			j++;
		}
		i++;
	}

	return j == d.Length;
}

Reorder List

Link - Reorder List

Efficient Approach

Initially perform Rabbit and Hare approach to find the middle of the list. Once the middle node is found, reverse the list starting from that middle node. Now update the next pointers of both the head and the last node to get the required result.

public void ReorderList(ListNode head) {
	if(head == null || head.next == null){
		return;
	}

	ListNode slow = head;
	ListNode fast = head;
	ListNode prev = null;

	while(fast != null && fast.next != null){
		prev = slow;
		slow = slow.next;
		fast = fast.next.next;
	}

	prev.next = null;

	ListNode l2 = this.Reverse(slow);

	this.Merge(head, l2);

}

public ListNode Reverse(ListNode head){
	ListNode prev = null;
	ListNode curr = head;
	ListNode next = null;

	while(curr != null){
		next = curr.next;
		curr.next = prev;
		prev = curr;
		curr = next;
	}

	return prev;
}

public void Merge(ListNode l1, ListNode l2){
	while(l1 != null){
		ListNode temp1 = l1.next;
		ListNode temp2 = l2.next;

		l1.next = l2;

		if(temp1 == null){
			break;
		}

		l2.next = temp1;
		l1 = temp1;
		l2 = temp2;
	}
}

Using Stack

This problem can also be solved using Stack. Simply perform the Rabbit and Hare approach first. Once you have the fast pointer at the end of the list, take the slow pointer and move all the nodes after that to a Stack until the end of the list. Now update the next pointer of the nodes of both the head and the popped item from the stack. Keep repeating this until the Stack is empty.

| Approach | Time Complexity | Space Complexity | | | :-: | :—: | | Efficient Approach | O(n) | O(1) | | Using Stack | O(n) | O(n) |

Search Algorithms

Linear Search

This search algorithm searches for the element by looking for the element in the whole array one at a time.

int[] inpArray = new int[] {10, 5, 12, 34, 88, 23, 9};
int element = 23;

public int LinearSearch(int[] inpArray, int element){
    for(int i = 0; i < inpArray.Length; i++){
        if(inpArray[i] == element){
          // Return the index of the first occurence of the element in the array
          return i;
        }
    }

    // Return -1 if the element is not foud
    return - 1;
}

Binary search

This search technique requires that the input array is sorted. Here, simply divide the given array in half and check if the element lies in the first or second half. Keep repeating this process to get the required result.

int[] inpArray = new int[] {5, 9, 10, 12, 23, 34, 88};
int element = 23;

public int BinarySearch(int[] inpArray, int element)
{
    int low = 0;
    int high = inpArray.Length - 1;

    while (low <= high)
    {
        int mid = (low + high) / 2;
        if (inpArray[mid] == element)
        {
            return mid;
        }
        else if (inpArray[mid] < element)
        {
            low = mid + 1;
        }
        else
        {
            high = mid - 1;
        }
    }
    return -1;
}
AlgorithmInput typeTime ComplexitySpace Complexity
Linear SearchData need not be sortedO(n)O(1)
Binary SearchSorted Data requiredO(log(n))O(1)

Find Peak Element

Link - Find Peak Element

Brute Force Approach

public int FindPeakElement(int[] nums) {
	int n = nums.Length;
	if(n == 0){
		return 0;
	}
	else if(n == 1){
		return 0;
	}

	// Check if the first element is the peak element
	if(nums[0] > nums[1]){
		return 0;
	}

	// Check if the last element is the peak element
	if(nums[n-1] > nums[n-2]){
		return n - 1;
	}

	// Iteratively check all the elements in the array if it is a peak element.
	for(int i = 1; i < n-1; i++){
		if(nums[i] > nums[i-1] && nums[i] > nums[i+1]){
			return i;
		}
	}

	return 0;
}

Binary Search Approach

The Binary search algorithm is used mostly with the problems where the input array is sorted, but in this case, we know that the input array is not in a sorted format. But it works because, at a certain part of the array where peak element lies, that part will be in a sorted format. We use this property along with Binary search to find the peak element.

Consider the following example with the input array:

[5,6,4,3,2,9,3]

In the above example, the peak elements are 6 and 9.

Let’s apply binary search on the array:

left: 0
right: 6
mid: 3

Check if mid[3] < mid[4]. In this scenario, yes it is. So this implies that there has to be a peak element in the right half of the array. Why is that?. Consider the element mid[4], it’s left element is lesser than it. And the right element can be either of the two:

  • Higher than mid[4] - Then we repeat the same process, that is the right of mid[5] should have at least one peak element.
  • Lower than mid[4]. If mid[5] is lower than mid[4] then mid[4] is the peak element.

Repeat this process by dividing the array into half on each traversal, and this will get you the required solution.

public int FindPeakElement(int[] nums) {
	int left = 0;
	int right = nums.Length - 1;

	while(left < right){
		int mid = (left + right)/2;

		if(nums[mid] < nums[mid+1]){
			left = mid + 1;
		}
		else{
			right = mid;
		}
	}
	return left;
}
ApproachTime ComplexitySpace Complexity
Brute ForceO(n)O(1)
Binary SearchO(log n)O(1)

Convert to Base -2

Link - Convert to Base -2

The question seems a bit tricky since it’s asking to convert the number to base -2. Ignore the base -2 for now, consider the base to be 2, how do we approach this. Simply divide the number by 2 and store the remainder. The new number will be number divided by 2. Keep repeating this process until the number is 0 or 1. At the end of this process you will have the binary representation of the number.

Same steps work for base -2 also. Just keep dividing by -2 instead of 2 recursively to get the output.

Recursive Approach

public string BaseNeg2(int N) {
    // the condition has to be N == 0 or N == 1 and not N < 2 since N becomes a negative number when divided by two
    if(N == 0 || N == 1){
        return N.ToString();
    }
    // if mod 2 remainder is 0 then prepend 0 else prepend 1
    if (N % 2 == 0){
        return this.BaseNeg2(N / -2) + "0";
    }
    else{
        return this.BaseNeg2((N - 1) / -2) + "1";
    }
}

This can solved using iterative approach also. Loop through the N, store the remainder and divide the number by -2. Keep doing this until the number is 0.

Iterative Approach (Fastest Method)

public string BaseNeg2(int N) {
    string output = "";
    do{
        if (N % 2 == 0){
            N = N / -2;
            // The remainder has to be prepended since the process starts from least significant bit to most significant bit
            output = "0" + output;
        }
        else{
            N = (N - 1) / -2;
            output = "1" + output;
        }
    } while(N != 0);

    return output.ToString();
}
// In the above code we have used a do-while loop since the loop has to run atleast once. Consider when the input is 0, the output will be null if we use while loop. Using do-while loop will return 0 when the input is 0.

// The above code can be used for base `any integer` by just making some minor tweaks.

Thread Safe Stack

Problem Statement

Design a Thread Safe Stack Class

Solution

using System;
using System.Collections.Generic;
using System.Threading;

public class Program
{
	public static void Main()
	{
		ThreadSafeStack<int> tss = new ThreadSafeStack<int>();

		for(int i = 0; i < 50; i++){
			Thread t = new Thread(() => tss.Push(i));
			t.Start();
		}

		for(int i = 0; i < 50; i++){
			Thread t = new Thread(() => tss.Push(i));
			t.Start();
		}

		for(int i = 0; i < 100; i++){
			Thread t = new Thread(() => {
				int val = tss.Pop();
				Console.WriteLine(val.ToString());
			});
			t.Start();
		}
	}
}

public class ThreadSafeStack<T>{

	List<T> list;
	public ThreadSafeStack(){
		list = new List<T>();
	}

	public void Push(T val){
		Console.WriteLine(val.ToString());
		lock(list){
			list.Add(val);
		}
	}

	public T Pop(){
		lock(list){
			T val = default(T);
			if(list.Count > 0){
				val = list[list.Count-1];
				list.RemoveAt(list.Count-1);
			}

			return val;
		}
	}
}

Guess Number Higher or Lower

Link - Guess Number Higher or Lower

Using Binary Search

public int GuessNumber(int n) {
	int low = 1;
	int high = n;

	while(low <= high){
		int mid = low + ((high - low)/2);

		int apiResponse = this.guess(mid);

		if(apiResponse == 0){
			return mid;
		}
		else if(apiResponse == -1){
			high = mid - 1;
		}
		else if(apiResponse == 1){
			low = mid + 1;
		}
	}

	return -1;
}

Remove Covered Intervals

Link - Remove Covered Intervals

Using Sorting + Greedy

public int RemoveCoveredIntervals(int[][] intervals) {

	// Sort the intervals based on the the starting time. If starting times are same then sort them based on interval size. i.e if there's (1,2) and (1,4) then after sorting it should be (1,4), (1,2)
	Array.Sort(intervals, (a,b) => a[0] != b[0] ? a[0] - b[0] : (b[1] - b[0]) - (a[1] - a[0]));

	int count = intervals.Length;

	int[] curr = intervals[0];

	for(int i = 1; i < intervals.Length; i++){
		if(curr[0] <= intervals[i][0] && intervals[i][1] <= curr[1]){
			count--;
		}
		else{
			curr = intervals[i];
		}
	}

	return count;
}

Number of Recent Calls

Link - Number of Recent Calls

Using Queue

public class RecentCounter {
    Queue<int> myQ;
    public RecentCounter() {
        myQ = new Queue<int>();
    }

    public int Ping(int t) {
        myQ.Enqueue(t);

        while(myQ.Peek() < t-3000){
            myQ.Dequeue();
        }

        return myQ.Count;
    }
}

Sorting Algorithm - Selection Sort

Problem Statement

Sort integer array using Selection Sort

Solution

In this sorting technique, during each iteration, find the smallest element by scanning through each element and then swap it with the element at the current index.

public void SelectionSort(int[] arr){
	int N = arr.Length;

	for(int i = 0; i < N; i++){
		int minIndex = i;

		for(int j = i+1; j < N; j++){
			if(arr[j] < arr[minIndex]){
				minIndex = j;
			}
		}

		int temp = arr[minIndex];
		arr[minIndex] = arr[i];
		arr[i] = temp;
	}
}
// The above sorting technique has quadratic time complexity and constant space requirement. This sorting technique takes only O(n) swaps, so this approach should be preferred when the write operation is costly.

Make Two Arrays Equal by Reversing Sub-arrays

Link - Make Two Arrays Equal by Reversing Sub-arrays

Using Dictionary

public bool CanBeEqual(int[] target, int[] arr) {
	Dictionary<int, int> map = new Dictionary<int, int>();

	foreach(int item in arr){
		if(!map.ContainsKey(item)){
			map[item] = 0;
		}

		map[item] += 1;
	}

	foreach(int item in target){
		if(!map.ContainsKey(item)){
			return false;
		}
		else{
			if(map[item] > 0){
				map[item] -= 1;
			}
			else{
				return false;
			}
		}
	}

	return true;
}

Common Operating Systems Questions

type: interview-questions

Note: Following list was curated by going through various articles and blog posts

  • Memory Management

  • Threading

  • File System Management

  • Process Scheduling

Valid Perfect Square

Link - Valid Perfect Square

Using Summataion

Sum of n odd numbers is given by n^2. Below solution uses this method

public bool IsPerfectSquare(int num) {
	int i = 1;

	while(num > 0){
		num -= i;
		i += 2;
	}

	return num == 0;
}

Range Sum of Sorted Subarray Sums

Link - Range Sum of Sorted Subarray Sums

Using Prefix Sum

public int RangeSum(int[] nums, int n, int left, int right) {
	List<int> perms = new List<int>();

	if(nums.Length == 0){
		return 0;
	}

	this.DP(nums, perms);
	perms.Sort();

	int output = 0;

	for(int i = left-1; i < right; i++){
		output += perms[i];
	}

	return output;
}

public void DP(int[] nums, List<int> perms){
	for(int i = 0; i < nums.Length; i++){
		int temp = 0;
		for(int j = i ; j < nums.Length; j++){
			temp += nums[j];
			perms.Add(temp);
		}
	}
}

H-Index

Link - H-Index

public int HIndex(int[] citations) {
	int N = citations.Length;
	Array.Sort(citations);

	for(int i = 0; i < N; i++){
		if(citations[i] >= N-i){
			return N-i;
		}
	}

	return 0;
}

Special Positions in a Binary Matrix

Link - Special Positions in a Binary Matrix

Using 2-Pass

public int NumSpecial(int[][] mat) {
	int rows = mat.Length;

	if(rows == 0){
		return 0;
	}

	int cols = mat[0].Length;

	int[] rowCount = new int[rows];
	int[] colCount = new int[cols];

	for(int i = 0; i < rows; i++){
		for(int j = 0; j < cols; j++){
			if(mat[i][j] == 1){
				rowCount[i] += 1;
				colCount[j] += 1;
			}
		}
	}

	int output = 0;

	for(int i = 0; i < rows; i++){
		for(int j = 0; j < cols; j++){
			if(mat[i][j] == 1 && rowCount[i] == 1 && colCount[j] == 1){
				output++;
			}
		}
	}

	return output;
}

Minimum Deletion Cost to Avoid Repeating Letters

Link - Minimum Deletion Cost to Avoid Repeating Letters

Naive Approach

minCost += (group sum of repeated characters) - (max number in that group)

public int MinCost(string s, int[] cost) {
	int minCost = 0;

	int groupSum = cost[0];
	int groupMax = cost[0];
	for(int i = 1; i < s.Length; i++){
		if(s[i] == s[i-1]){
			groupMax = Math.Max(groupMax, cost[i]);
			groupSum += cost[i];
		}
		else{
			minCost += groupSum - groupMax;
			groupMax = cost[i];
			groupSum = cost[i];
		}
	}

	minCost += groupSum - groupMax; // To add the min cost if the group is part of last set of repeated characters

	return minCost;
}

Design Linked List

Link - Design Linked List

Using Dynamic List

public class MyLinkedList {
    List<int> values;
    int tailIndex;

    public MyLinkedList() {
        values = new List<int>();
        tailIndex = -1;
    }

    public int Get(int index) {
        if(index <= tailIndex){
            return values[index];
        }
        else{
            return -1;
        }
    }

    public void AddAtHead(int val) {
        values.Insert(0, val);
        tailIndex++;
    }

    public void AddAtTail(int val) {
        values.Add(val);
        tailIndex++;
    }

    public void AddAtIndex(int index, int val) {
        if(index <= tailIndex + 1){
            values.Insert(index, val);
            tailIndex++;
        }
    }

    public void DeleteAtIndex(int index) {
        if(index <= tailIndex){
            values.RemoveAt(index);
            tailIndex--;
        }
    }
}

Find the sum of two integers without using + or - operators

We could use bitwise operators to find the sum of two numbers.

How does bitwise addition work. Let’s see how bitwise addition works using an example.

Consider the numbers 5 and 6 which can be represented as 0101 and 0110 in binary

0 1 0 1
0 1 1 0
-
1 0 1 1
-

So in the above example we are performing XOR operation on the two bits from right to left. If there is a carry (when it is 1 and 1) then 1 is added to the next significant bit.

To find sum we can used XOR operation. Similarly, to add the carry bit, perform AND operation first and then right rotate it by one bit. In the above example AND operation of two numbers would give us 0100 and after right rotating it, it will be 1000. Keep repeating this process recursively until the AND operation returns 0 i.e. there are no carry bits.This will give us the sum of two numbers.

Below is the JavaScript code to do this.

function GetSum(a, b) {
  let sum = a ^ b;
  let carry = (a & b) << 1;

  if (sum & carry) {
    return GetSum(sum, carry);
  }
  return sum ^ carry;
}

GetSum(5, 6); // 11
GetSum(-5, 6); // 1

K Closest Points to Origin

Link - K Closest Points to Origin

Using Priority Queue (Min Heap)

public int[][] KClosest(int[][] points, int K) {
	List<int[]> output = new List<int[]>();

	PriorityQueue<int[]> pq = new PriorityQueue<int[]>();

	for(int i = 0; i < points.Length; i++){
		int distance = (int)(Math.Pow(points[i][0], 2) + Math.Pow(points[i][1], 2));
		pq.Enqueue(distance, points[i]);
	}

	for(int i = 0; i < K; i++){
		output.Add(pq.Dequeue());
	}

	return output.ToArray();
}

Implement Priority Queue (Max Heap)

Following are the list of abstract methods associated with Priority Queue Data Structure

  • Enqueue - Adds an item to the Priority Queue
  • Dequeue - Removes the item with the highest priority from the Priority Queue

Good to Know

Max Heap Property

Max Heap property defines that every node should have its value less than or equal to its parent node. This property is the foundation for max heaps. And priority queues use max heaps internally.

Implementation

This implementation uses an array (internally) to store the items.

Finding the left and right children of a node

The left and right child in an array is given by 2i + 1 and 2i + 2 respectively.

private int LeftChild(int i)
{
    return (i * 2) + 1;
}

private int RightChild(int i)
{
    return (i * 2) + 2;
}

Find Max Heapify for a given node

To Max Heapify a given node, find the maximum among the left, right and parent nodes. If the node with the maximum value is not the same as root then swap the nodes between the highest and the parent node. Now recursively call this function again on the highest node. For a given node, this function moves the values across the heap (starting from the given node towards the leaf) so that the max-heap property is valid.

private void MaxHeapify(int i)
{
    int left = this.LeftChild(i);
    int right = this.RightChild(i);

    int highest = i;

    if (left <= heapSize && queue[highest] < queue[left])
    {
        highest = left;
    }

    if (right <= heapSize && queue[highest] < queue[right])
    {
        highest = right;
    }

    if (highest != i)
    {
        this.Swap(highest, i);
        this.MaxHeapify(highest);
    }
}

Build Max Heap for a given node

Iteratively check if a given node is greater than its root node. If yes, then swap those values and update the pointer to the root node. Repeat this until the root node.

private void BuildMaxHeap(int i)
{
    while (i >= 0 && queue[(i - 1) / 2] < queue[i])
    {
        this.Swap(i, (i - 1) / 2);
        i = (i - 1) / 2;
    }
}

Enqueue

Initially add the new item to the end of the list. Once the new item is added, the max heap property might be violated. So call the BuildMaxHeap function, which moves the newly added item to its appropriate place.

public void Enqueue(int i)
{
    queue.Add(i);
    heapSize++;
    this.BuildMaxHeap(heapSize);
}

Dequeue

The item at index 0 will have the highest element in the given Queue, but we cannot remove it straight away from the list since that might violate the max-heap property. So, swap the numbers which are in 0th index and the last index of the list. Now store the last item (which is the max item from the heap) from the list in a temporary variable. Remove the last item from the list. Now the item at 0th index is violating the max-heap property, so simply call the MaxHeapify function on this node to fix that. Return the temporary variable which is the item to be dequeued.

public int Dequeue()
{
    if (heapSize > -1)
    {
        int returnVal = queue[0];
        queue[0] = queue[heapSize];
        queue.RemoveAt(heapSize);
        heapSize--;
        this.MaxHeapify(0);
        return returnVal;
    }
    else
    {
        throw new Exception("Queue is empty");
    }
}

Combine all these functions into a single class to get the Priority Queue. See the below code for the complete implementation.

public class PriorityQueue{
    List<int> queue = null;
    int heapSize = -1;
    public int Count { get { return queue.Count; } }

    public PriorityQueue()
    {
        queue = new List<int>();
    }

    private int LeftChild(int i)
    {
        return (i * 2) + 1;
    }

    private int RightChild(int i)
    {
        return (i * 2) + 2;
    }

    private void Swap(int i, int j)
    {
        int temp = queue[i];
        queue[i] = queue[j];
        queue[j] = temp;
    }

    private void MaxHeapify(int i)
    {
        int left = this.LeftChild(i);
        int right = this.RightChild(i);

        int highest = i;

        if (left <= heapSize && queue[highest] < queue[left])
        {
            highest = left;
        }

        if (right <= heapSize && queue[highest] < queue[right])
        {
            highest = right;
        }

        if (highest != i)
        {
            this.Swap(highest, i);
            this.MaxHeapify(highest);
        }
    }

    private void BuildMaxHeap(int i)
    {
        while (i >= 0 && queue[(i - 1) / 2] < queue[i])
        {
            this.Swap(i, (i - 1) / 2);
            i = (i - 1) / 2;
        }
    }

    public void Enqueue(int i)
    {
        queue.Add(i);
        heapSize++;
        this.BuildMaxHeap(heapSize);
    }

    public int Dequeue()
    {
        if (heapSize > -1)
        {
            int returnVal = queue[0];
            queue[0] = queue[heapSize];
            queue.RemoveAt(heapSize);
            heapSize--;
            this.MaxHeapify(0);
            return returnVal;
        }
        else
        {
            throw new Exception("Queue is empty");
        }
    }
}

See the following lines of code which uses the above implementation.

PriorityQueue queue = new PriorityQueue();
queue.Enqueue(5);
queue.Enqueue(3);
queue.Enqueue(12);
queue.Enqueue(1);
queue.Dequeue(); // 15
queue.Dequeue(); // 5
queue.Dequeue(); // 3
queue.Dequeue(); // 1

Design Circular Queue

Link - Design Circular Queue

Using List

public class MyCircularQueue {
    int[] data;
    int fPtr;
    int rPtr;
    int len;

    public MyCircularQueue(int k) {
        data = new int[k];
        fPtr = 0;
        rPtr = -1;
        len = 0;
    }

    public bool EnQueue(int value) {
        if(!this.IsFull()){
            rPtr = (rPtr + 1) % data.Length;
            data[rPtr] = value;
            len++;
            return true;
        }
        else{
            return false;
        }
    }

    public bool DeQueue() {
        if(!this.IsEmpty()){
            fPtr = (fPtr + 1) % data.Length;
            len--;
            return true;
        }
        else{
            return false;
        }
    }

    public int Front() {
        return this.IsEmpty() ? -1 : data[fPtr];
    }

    public int Rear() {
        return this.IsEmpty() ? -1 : data[rPtr];
    }

    public bool IsEmpty() {
        return len == 0;
    }

    public bool IsFull() {
        return len == data.Length;
    }
}

Game of Life

Link - Game of Life

m * n Solution

public void GameOfLife(int[][] board) {
	int[] neighbors = {0, 1, -1};

	int rows = board.Length;
	int cols = board[0].Length;

	int[,] copy = new int[rows,cols];

	for(int i = 0; i < rows; i++){
		for(int j = 0; j < cols; j++){
			copy[i,j] = board[i][j];
		}
	}

	for(int row = 0; row < rows; row++){
		for(int col = 0; col < cols; col++){
			int liveNeighbors = 0;

			for(int i = 0; i < 3; i++){
				for(int j = 0; j < 3; j++){

					if(!(neighbors[i] == 0 && neighbors[j] == 0)){
						int newRow = row + neighbors[i];
						int newCol = col + neighbors[j];

						if(newRow >= 0 && newRow < rows && newCol >= 0 && newCol < cols && copy[newRow,newCol] == 1){
							liveNeighbors += 1;
						}
					}
				}
			}

			if(copy[row,col] == 1 && (liveNeighbors < 2 || liveNeighbors > 3)){
				board[row][col] = 0;
			}

			if(copy[row,col] == 0 && liveNeighbors == 3){
				board[row][col] = 1;
			}
		}
	}
}

Minimum Number of Vertices to Reach All Nodes (star)

Link - Minimum Number of Vertices to Reach All Nodes

In-Degree Nodes Approach

All the nodes which donot have any incoming edge is the solution for this method.

public IList<int> FindSmallestSetOfVertices(int n, IList<IList<int>> edges) {
	HashSet<int> startNodes = new HashSet<int>();

	for(int i = 0 ; i < n; i++){
		startNodes.Add(i);
	}

	foreach(var item in edges){
		int to = item[1];
		startNodes.Remove(to);
	}

	return startNodes.ToList();
}

Cheapest Flights Within K Stops

Link - Cheapest Flights Within K Stops

Using Depth First Search (Time Limit Exceeded)

public int FindCheapestPrice(int n, int[][] flights, int src, int dst, int K) {
	int[,] graph = new int[n,n];

	foreach(int[] item in flights){
		int fNode = item[0];
		int tNode = item[1];
		int dist = item[2];

		graph[fNode, tNode] = dist;
	}

	HashSet<int> visited = new HashSet<int>();

	int minDistance = this.DFS(src, dst, graph, visited, K+1, n);

	return minDistance == Int32.MinValue ? -1 : minDistance;
}

public int DFS(int fNode, int tNode, int[,] graph, HashSet<int> visited, int depth, int n){
	if(depth < 0){
		return -1;
	}

	if(fNode == tNode){
		return 0;
	}

	if(visited.Contains(fNode)){
		return -1;
	}

	visited.Add(fNode);

	int minDistance = Int32.MaxValue;

	for(int i = 0; i < n; i++){
		if(graph[fNode,i] != 0){
			int min = this.DFS(i, tNode, graph, visited, depth - 1, n);

			if(min == -1 || min == Int32.MaxValue){
				continue;
			}

			minDistance = Math.Min(minDistance, graph[fNode,i] + min);
		}
	}

	visited.Remove(fNode);

	return minDistance;
}

Using Breadth First Search

public int FindCheapestPrice(int n, int[][] flights, int src, int dst, int K) {
	Dictionary<int, List<int[]>> graph = new Dictionary<int, List<int[]>>();

	foreach(int[] item in flights){
		int fNode = item[0];
		int tNode = item[1];
		int dist = item[2];

		if(!graph.ContainsKey(fNode)){
			graph.Add(fNode, new List<int[]>(){new int[]{tNode, dist}});
		}
		else{
			graph[fNode].Add(new int[]{tNode, dist});
		}
	}

	int minCost = Int32.MaxValue;
	Queue<int[]> bfsQueue = new Queue<int[]>();

	bfsQueue.Enqueue(new int[]{src,0});

	int level = 0;

	while(bfsQueue.Any() && level <= K+1){
		int size = bfsQueue.Count;

		for(int i = 0; i < size; i++){
			var tempNode = bfsQueue.Dequeue();
			int toNode = tempNode[0];
			int toDistance = tempNode[1];

			if(toNode == dst){
				minCost = Math.Min(minCost, toDistance);
			}

			// Checking if the to node exists in the graph
			if(!graph.ContainsKey(toNode)){
				continue;
			}

			foreach(int[] toCityNode in graph[toNode]){
				// Don't consider that path if the distance is already greater than minCost;
				if(toDistance + toCityNode[1] > minCost){
					continue;
				}

				bfsQueue.Enqueue(new int[]{toCityNode[0], toDistance + toCityNode[1]});
			}
		}

		level++;
	}

	return minCost == Int32.MaxValue ? -1 : minCost;
}

Using Bellman-Ford Algorithm

public class Solution {
    public int FindCheapestPrice(int n, int[][] flights, int src, int dst, int K) {
        Dictionary<int, List<Tuple<int,int>>> graph = new Dictionary<int, List<Tuple<int,int>>>();

        for(int i = 0; i < n; i++){
            graph[i] = new List<Tuple<int,int>>();
        }

        foreach(int[] edge in flights){
            int f = edge[0];
            int t = edge[1];
            int w = edge[2];

            graph[f].Add(new Tuple<int,int>(t, w));
        }

        Queue<Node> bfsQueue = new Queue<Node>();
        bfsQueue.Enqueue(new Node(src, 0, 0));

        int[] dist = new int[n];
        Array.Fill(dist, 1000000);

        dist[src] = 0;

        while(bfsQueue.Count > 0){
            Node tempNode = bfsQueue.Dequeue();
            int currNode = tempNode.node;
            int currWeight = tempNode.weight;
            int currStops = tempNode.stops;

            foreach(var edge in graph[currNode]){
                int toNode = edge.Item1;
                int toWeight = edge.Item2;

                if(dist[toNode] > currWeight + toWeight && currStops <= K){
                    dist[toNode] = currWeight + toWeight;
                    bfsQueue.Enqueue(new Node(toNode, dist[toNode], currStops + 1));
                }

            }
        }

        return dist[dst] != 1000000 ? dist[dst] : -1;
    }
}

public class Node{
    public int node;
    public int weight;
    public int stops;

    public Node(int node, int weight, int stops){
        this.node = node;
        this.weight = weight;
        this.stops = stops;
    }
}

Russian Doll Envelopes

Link - Russian Doll Envelopes

Using Dynamic Programming

public int MaxEnvelopes(int[][] envelopes) {
	Array.Sort(envelopes, (a,b) => a[0] == b[0] ? a[1] - b[1] : a[0] - b[0]);

	int N = envelopes.Length;

	int[] memo = new int[N];

	memo[0] = 1;

	for(int i = 1; i < N; i++){
		memo[i] = 1;

		int[] curr = envelopes[i];

		for(int j = 0; j < i; j++){
			int[] prev = envelopes[j];
			int prevMemo = memo[j];

			if(prev[0] < curr[0] && prev[1] < curr[1]){
				if(prevMemo + 1 > memo[i]){
					memo[i] = 1 + prevMemo;
				}
			}

		}
	}

	return memo.Max();
}

Reach a Number

Link - Reach a Number

Breadth First Search

public int ReachNumber(int target) {
	if(target == 0){
		return 0;
	}

	int steps = 0;
	int currNum = 0;
	int stepSize = 0;

	Queue<int> bfsQueue = new Queue<int>();
	bfsQueue.Enqueue(0);

	while(true){
		int count = bfsQueue.Count;
		stepSize++;

		for(int i = 0; i < count; i++){
			int temp = bfsQueue.Dequeue();

			if(temp == target){
				return steps;
			}

			bfsQueue.Enqueue(temp + stepSize);
			bfsQueue.Enqueue(temp - stepSize);
		}
		steps++;
	}

	return steps;
}

Greedy Approach

public int ReachNumber(int target) {
	target = Math.Abs(target); // The steps of a target are regardless of the sign

	int steps = 0;
	int sum = 0;

	// Keep updating the sum until you reach the target number
	while(sum < target){
		steps++;
		sum += steps;
	}

	while((sum - target) % 2 != 0){
		steps++;
		sum += steps;
	}

	return steps;
}

Consecutive Numbers Sum

Link - Consecutive Numbers Sum

Consecutive number is of the form,

x + (x + 1) + (x + 2) + .... + k terms = N

where N is the given number.

This series leads to,

kx + k(k-1)/2 = N

which can be written as,

kx = (N - k(k-1)/2)

So, for a solution to exist x must be an integer which can be found using the condition

(N - k(k-1)/2) % k == 0

To find the range up to which the above condition has to be checked, the condition, N - k(k-1)/2 has to be positive since we want the sum of positive consecutive integers.

N - k(k-1)/2 > 0
2N > k(k-1)
2N > k^2

This gives us,

sqrt(2N) > k

Use these equations to get the desired result.

public int ConsecutiveNumbersSum(int N) {
    int count = 1; // It is started with one since the given number itself is a consecutive number

    for(int k = 2; k < Math.Sqrt(2 * N); k++){
        if((N - ((k * (k - 1))/ 2)) % k == 0){
            count++;
        }
    }

    return count;
}

Interval List Intersections

Link - Interval List Intersections

Using Iteration

public int[][] IntervalIntersection(int[][] A, int[][] B) {
	List<int[]> output = new List<int[]>();

	int i = 0;
	int j = 0;

	while(i < A.Length && j < B.Length){
		int start = Math.Max(A[i][0], B[j][0]);
		int end = Math.Min(A[i][1], B[j][1]);

		if(start <= end){
			output.Add(new int[]{start, end});
		}

		if(A[i][1] < B[j][1]){
			i++;
		}
		else{
			j++;
		}
	}

	return output.ToArray();
}

Find Common Characters

Link - Find Common Characters

Naive Approach

public IList<string> CommonChars(string[] A) {
	List<string> output = new List<string>();

	int[] count = new int[26];

	Array.Fill(count, 200);

	foreach(string word in A){

		int[] tempCount = new int[26];

		foreach(char c in word){
			int val = c - 'a';
			tempCount[val] += 1;
		}

		for(int i = 0; i < 26; i++){
			count[i] = Math.Min(count[i], tempCount[i]);
		}
	}

	for(int i = 0; i < 26; i++){
		if(count[i] == 200 || count[i] == 0){
			continue;
		}

		char c = Convert.ToChar(97 + i);

		for(int j = 0; j < count[i]; j++){
			output.Add(c.ToString());
		}
	}


	return output;
}

Online Stock Span

Link - Permutation in String

Using Stack

public class StockSpanner {
    Stack<int> prices;
    Stack<int> weights;

    public StockSpanner() {
        prices = new Stack<int>();
        weights = new Stack<int>();
    }

    public int Next(int price) {
        int weight = 1;
        while(prices.Count != 0 && prices.Peek() <= price){
            prices.Pop();
            weight += weights.Pop();
        }

        prices.Push(price);
        weights.Push(weight);
        return weight;
    }
}

Maximum Width of Binary Tree

Link - Maximum Width of Binary Tree

Using Sorting & Binary Search

public int WidthOfBinaryTree(TreeNode root) {
	return this.DFS(root, 0, 1, new List<int>(), new List<int>());
}

public int DFS(TreeNode root, int level, int order, List<int> left, List<int> right){
	if(root == null){
		return 0;
	}

	if(left.Count == level){
		left.Add(order);
		right.Add(order);
	}
	else{
		right[level] = order;
	}

	int curr = right[level] - left[level] + 1;
	int cLeft = this.DFS(root.left, level+1, 2*order, left, right);
	int cRight = this.DFS(root.right, level+1, 2*order+1, left, right);

	return Math.Max(curr, Math.Max(cLeft, cRight));
}

Arranging Coins

Link - Arranging Coins

Using Loops

public int ArrangeCoins(int n) {
	int count = 0;

	int stepSize = 1;
	while(n >= stepSize){
		count++;
		n -= stepSize;
		stepSize++;
	}

	return count;
}

Find Minimum in Rotated Sorted Array

Link - Find Minimum in Rotated Sorted Array

Binary Search

public int FindMin(int[] nums) {
	int low = 0;
	int high = nums.Length - 1;

	while(low < high){
		int mid = (low + high)/2;
		// If mid is greater than high then a solution lies in te right half of section
		if(nums[mid] > nums[high]){
			low = mid + 1;
		}
		else{
			high = mid;
		}
	}

	return nums[low];
}

Word Search II

Link - Word Search II

Using Trie and DFS

public class Solution {
    public IList<string> FindWords(char[][] board, string[] words) {
        List<string> output = new List<string>();

        int rows = board.Length;
        if(rows == 0){
            return output;
        }
        int cols = board[0].Length;

        TrieNode root = this.BuildTrie(words);

        for(int i = 0; i < rows; i++){
            for(int j = 0; j < cols; j++){
                this.DFS(board, root, i, j, output, rows, cols);
            }
        }

        return output;
    }

    public void DFS(char[][] board, TrieNode root, int r, int c, List<string> output, int rows, int cols){
        char ch = board[r][c];

        if(ch == '#' || root.next[ch-'a'] == null){
            return;
        }

        root = root.next[ch-'a'];

        if(root.word != null){
            output.Add(root.word);
            root.word = null;
        }

        board[r][c] = '#';

        if(r > 0){
            this.DFS(board, root, r-1, c, output, rows, cols);
        }
        if(c > 0){
            this.DFS(board, root, r, c-1, output, rows, cols);
        }
        if(r < rows-1){
            this.DFS(board, root, r+1, c, output, rows, cols);
        }
        if(c < cols-1){
            this.DFS(board, root, r, c+1, output, rows, cols);
        }
        board[r][c] = ch;

    }

    public TrieNode BuildTrie(string[] words){
        TrieNode root = new TrieNode();

        foreach(string w in words){
            TrieNode tempNode = root;

            foreach(char c in w){
                int i = c - 'a';

                if(tempNode.next[i] == null){
                    tempNode.next[i] = new TrieNode();
                }

                tempNode = tempNode.next[i];
            }
            tempNode.word = w;
        }
        return root;
    }

}


public class TrieNode{
    public String word;
    public TrieNode[] next = new TrieNode[26];
}

Permutation Sequence

Link - Permutation Sequence

Using Iterative Approach

public string GetPermutation(int n, int k) {
	int[] fact = new int[n];

	fact[0] = 1;
	for(int i = 1; i < n ; i++){
		fact[i] = fact[i-1] * i;
	}

	List<int> map = new List<int>();
	for(int i = 0; i < n; i++){
		map.Add(i+1);
	}

	string output = "";
	k--;

	for(int i = n; i > 0; i--){
		int index = k / fact[i-1];
		k = k % fact[i-1];
		output += map[index];
		map.RemoveAt(index);
	}

	return output;
}

Longest Common Prefix

Link - Longest Common Prefix

Brute Force Approach

Initially find the string with the smallest length out of the given strings. Then loop through all the strings character by character and check if the characters match. Add the character to an output string whenever the character is found in all the strings. Repeat this until the smallest string length to get the desired result.

public string LongestCommonPrefix(string[] strs) {

    if (strs.Length == 0){
        return "";
    }

    int sLength = strs[0].Length;
    string output = "";

    // Finding the string with the smallest length
    for(int i = 1 ; i < strs.Length ; i++){
        if(strs[i].Length < sLength){
            sLength = strs[i].Length;
        }
    }

    if (sLength == 0){
        return "";
    }

    for (int i = 0 ; i < sLength ; i++){
        for (int j = 0 ; j < strs.Length ;j++){
            if (strs[0][i] != strs[j][i]){
                return output;
            }
        }
        output += strs[0][i];
    }
    return output;
}

Number of Good Pairs

Link - Number of Good Pairs

Using Naive Approach

public int NumIdenticalPairs(int[] nums) {
	int count = 0;

	for(int i = 0; i < nums.Length; i++){
		for(int j = i+1; j < nums.Length; j++){
			if(nums[i] == nums[j]){
				count++;
			}
		}
	}

	return count;
}

Using Sorting

public int NumIdenticalPairs(int[] nums) {
	int count = 0;
	int i = 0;
	Array.Sort(nums);

	for(int j = 1; j < nums.Length; j++){
		if(nums[j] == nums[i]){
			count += j - i;
		}
		else{
			i = j;
		}
	}

	return count;
}

Check If a Word Occurs As a Prefix of Any Word in a Sentence

Link - Check If a Word Occurs As a Prefix of Any Word in a Sentence

Using Linear Scan

public int IsPrefixOfWord(string sentence, string searchWord) {
	string[] words = sentence.Split(' ');

	for(int i = 0; i < words.Length; i++){
		if(this.CustomContains(words[i], searchWord)){
			return i + 1;
		}
	}

	return -1;
}

public bool CustomContains(string parent, string child){
	if(child.Length > parent.Length){
		return false;
	}

	for(int i = 0; i < child.Length; i++){
		if(parent[i] != child[i]){
			return false;
		}
	}

	return true;
}

Find First and Last Position of Element in Sorted Array

Link - Find First and Last Position of Element in Sorted Array

Linear Scan using two pointers

    public int[] SearchRange(int[] nums, int target) {
        int start = 0;
        int end = nums.Length - 1;

        while(start <= end){
            if(nums[start] == target && nums[end] == target){
                return new int[]{start, end};
            }

            if(nums[start] != target){
                start++;
            }

            if(nums[end] != target){
                end--;
            }
        }

        return new int[]{-1, -1};
    }

Using Binary Search

public int[] SearchRange(int[] nums, int target) {
	int[] output = new int[] {-1, -1};

	if(nums.Length == 0){
		return output;
	}

	int first = this.FirstBinarySearch(nums, target);

	// Target element not found
	if(nums[first] != target){
		return output;
	}

	output[0] = first;
	output[1] = this.LastBinarySearch(nums, target);

	return output;
}

public int FirstBinarySearch(int[] nums, int target){
	int low = 0;
	int high = nums.Length - 1;

	int mid = 0;

	while(low < high){
		mid = low + ((high - low)/2);
		if(nums[mid] < target){
			low = mid + 1;
		}
		else{
			high = mid;
		}
	}

	return low;
}

public int LastBinarySearch(int[] nums, int target){
	int low = 0;
	int high = nums.Length - 1;

	int mid = 0;

	while(low < high){
		mid = low + ((high - low)/2) + 1;
		if(nums[mid] > target){
			high = mid - 1;
		}
		else{
			low = mid;
		}
	}

	return high;
}

Maximize Sum Of Array After K Negations

Link - Maximize Sum Of Array After K Negations

Greedy Approach

public int LargestSumAfterKNegations(int[] A, int K) {
	Array.Sort(A);

	for(int i = 0; i < A.Length && K > 0; i++){
		if(A[i] < 0){
			A[i] = -A[i];
			K--;
		}
	}

	if(K % 2 == 1){
		return this.ArraySum(A) - (2 * this.MinElement(A));
	}
	else{
		return this.ArraySum(A);
	}
}

public int ArraySum(int[] A){
	int sum = 0;

	foreach(int item in A){
		sum += item;
	}

	return sum;
}

public int MinElement(int[] A){
	int min = Int32.MaxValue;

	foreach(var item in A){
		min = Math.Min(min, item);
	}

	return min;
}

Can Place Flowers

Link - Can Place Flowers

Greedy Solution

public bool CanPlaceFlowers(int[] flowerbed, int n) {
	// dge cases
	if(flowerbed.Length == 1){
		if(flowerbed[0] == 0 && n <= 1){
			return true;
		}
		else if(flowerbed[0] == 1 && n == 0){
			return true;
		}
		else{
			return false;
		}
	}

	for(int i = 0; i < flowerbed.Length; i++){
		if(this.CanBePlaced(flowerbed, i)){
			n--;
			flowerbed[i] = 1;
		}
	}

	return n <= 0;
}

private bool CanBePlaced(int[] flowerbed, int i){
	if(flowerbed[i] == 1){
		return false;
	}

	if(i == 0){
		return flowerbed[i+1] == 0 ? true : false;
	}
	else if (i == flowerbed.Length -1){
		return flowerbed[i-1] == 0 ? true : false;
	}
	else{
		return flowerbed[i-1] == 0 && flowerbed[i+1] == 0 ? true : false;
	}
}

N-ary Tree Postorder Traversal

Link - N-ary Tree Postorder Traversal

Using Recursion

public IList<int> Postorder(Node root) {
	List<int> output = new List<int>();
	this.DFS(root, output);
	return output;
}

public void DFS(Node root, List<int> output){
	if(root == null){
		return;
	}

	foreach(Node node in root.children){
		this.DFS(node, output);
	}

	output.Add(root.val);
}

Using Iteration

public IList<int> Postorder(Node root) {
	List<int> output = new List<int>();

	if(root == null){
		return output;
	}

	Stack<Node> st = new Stack<Node>();
	st.Push(root);

	while(st.Count > 0){
		Node tempNode = st.Pop();

		output.Add(tempNode.val);

		foreach(Node node in tempNode.children){
			st.Push(node);
		}
	}

	output.Reverse();
	return output;
}

Longest Substring Without Repeating Characters

Link - Longest Substring Without Repeating Characters

Using Sliding Window

public int LengthOfLongestSubstring(string s) {
	int n = s.Length;

	if(n <= 1){
		return n;
	}

	HashSet<char> set = new HashSet<char>();

	int startIndex = 0;
	int endIndex = 0;
	int count = 0;

	while(startIndex < n && endIndex < n){
		// If the set doesn't contain the character then add it to the set and update the end index. Update the count as well.
		if(!set.Contains(s[endIndex])){
			set.Add(s[endIndex]);
			endIndex++;
			count = Math.Max(count, set.Count);
		}
		// If set already contains the character then remove all the characters from the set until the first appearance of that repeated character.
		else{
			while(s[startIndex] != s[endIndex]){
				set.Remove(s[startIndex]);
				startIndex++;
			}
			set.Remove(s[startIndex]);
			startIndex++;
		}
	}

	return count;
}
var lengthOfLongestSubstring = function (s) {
  let N = s.length;

  if (N < 2) {
    return N;
  }

  let sIndex = 0;
  let eIndex = 0;

  let set = new Set();

  let output = 0;

  while (sIndex < N && eIndex < N) {
    if (set.has(s[eIndex])) {
      set.delete(s[sIndex]);
      sIndex++;
    } else {
      set.add(s[eIndex]);
      output = Math.max(output, set.size);
      eIndex++;
    }
  }

  return output;
};

Link - Print FooBar Alternately

Using AutoResetEvent

using System.Threading;

public class FooBar {
    private int n;

    AutoResetEvent event1;
    AutoResetEvent event2;

    public FooBar(int n) {
        this.n = n;

        event1 = new AutoResetEvent(true);
        event2 = new AutoResetEvent(false);
    }

    public void Foo(Action printFoo) {
        for (int i = 0; i < n; i++) {
            event1.WaitOne();
        	printFoo();
            event2.Set();
        }
    }

    public void Bar(Action printBar) {
        for (int i = 0; i < n; i++) {
            event2.WaitOne();
        	printBar();
            event1.Set();
        }
    }
}

Cousins in Binary Tree

Link - Cousins in Binary Tree

Using Breadth First Search

public bool IsCousins(TreeNode root, int x, int y) {
	Queue<TreeNode> bfsQueue = new Queue<TreeNode>();
	bfsQueue.Enqueue(root);

	while(bfsQueue.Count > 0){
		int count = bfsQueue.Count;

		bool xExists = false;
		bool yExists = false;

		for(int i = 0; i < count; i++){
			TreeNode temp = bfsQueue.Dequeue();

			if(temp.val == x){
				xExists = true;
			}
			if(temp.val == y){
				yExists = true;
			}

			if(temp.left != null && temp.right != null){
				if(temp.left.val == x && temp.right.val == y){
					return false;
				}
				if(temp.right.val == x && temp.left.val == y){
					return false;
				}
			}

			if(temp.left != null){
				bfsQueue.Enqueue(temp.left);
			}
			if(temp.right != null){
				bfsQueue.Enqueue(temp.right);
			}
		}

		if(xExists && yExists){
			return true;
		}
	}

	return false;
}

Link - Word Search

Using Depth First Search

public bool Exist(char[][] board, string word) {
	int rows = board.Length;

	if(rows == 0){
		return false;
	}

	int cols = board[0].Length;

	bool[,] visited = new bool[rows, cols];

	int[][] directions = new int[4][];
	directions[0] = new int[]{1,0};
	directions[1] = new int[]{0,1};
	directions[2] = new int[]{0,-1};
	directions[3] = new int[]{-1,0};

	for(int i = 0; i < rows; i++){
		for(int j = 0; j < cols; j++){
			bool flag = this.Search(board, word, visited, 0, i, j, rows, cols, directions);
			if(flag == true){
				return true;
			}
		}
	}

	return false;
}

public bool Search(char[][] board, string word, bool[,] visited, int charIndex, int rowIndex, int colIndex, int rows, int cols, int[][] directions){
	if(charIndex >= word.Length || rowIndex >= rows || colIndex >= cols || rowIndex < 0 || colIndex < 0 || visited[rowIndex,colIndex] == true){
		return false;
	}

	if(board[rowIndex][colIndex] != word[charIndex]){
		return false;
	}
	else{
		if(charIndex == word.Length - 1){
			return true;
		}
	}

	visited[rowIndex,colIndex] = true;

	// Recurse through all the four directions of board for the current element
	for(int i = 0; i < directions.Length; i++){
		bool flag = this.Search(board, word, visited, charIndex+1, rowIndex + directions[i][0], colIndex + directions[i][1], rows, cols, directions);

		if(flag == true){
			return true;
		}
	}

	// Backtracking
	visited[rowIndex,colIndex] = false;

	return false;
}

Climbing Stairs

Link - Climbing Stairs

Recursion

Recursively call the ClimbStairs function to get the result.

public int ClimbStairs(int n) {
    // if n is negative integer then return 0
    if(n < 0){
        return 0;
    }
    // if n is 0 or 1 then there is one way to achieve
    else if (n <= 1){
        return 1;
    }
    else{
        return this.ClimbStairs(n - 1) + this.ClimbStairs(n-2);
    }
}

The above solution works but is not feasible for large numbers since the above solution has a space complexity of O(2^n).

Recursion with Memoization

This is similar to the above solution except we use a dictionary to cache the values whose values are already found.

Dictionary<int,int> memoize = new Dictionary<int,int>();

public int ClimbStairs(int n) {
    if(memoize.ContainsKey(n)){
        return memoize[n];
    }

    if(n < 0){
        return 0;
    }
    else if (n <= 1){
        return 1;
    }
    else{
        memoize[n] = this.ClimbStairs(n - 1) + this.ClimbStairs(n-2);
        return memoize[n];
    }
}

Dynamic Programming

Similar to the above solution but using dynamic programming.

public int ClimbStairs(int n) {
    if(n <= 1){
        return 1;
    }
    int[] dp = new int[n];

    dp[0] = 1; // 0th index is the number of ways for 1 step
    dp[1] = 2; // 1st index is the number of ways for 2 steps

    for(int i = 2; i < n; i++){
        dp[i] = dp[i-1] + dp[i-2];
    }

    return dp[n-1];
}

Fibonnaci Sequence

The solution to this question is the nth number in the Fibonacci sequence.

ApproachTime ComplexitySpace Complexity
RecursionO(n^2)O(1)
Recursion with MemoizationO(n)O(n)
Dynamic ProgrammingO(n)O(n)
Fibonnaci SequenceO(n)O(1)

Shortest Path in Binary Matrix

Link - Shortest Path in Binary Matrix

Using DFS (Time Limit Exceeded)

public int ShortestPathBinaryMatrix(int[][] grid) {
	int[] dirR = new int[] {-1, -1, -1, 0, 1, 1,  1,  0};
	int[] dirC = new int[] {-1,  0,  1, 1, 1, 0, -1, -1};

	int rows = grid.Length;

	if(rows == 0){
		return 0;
	}

	int cols = grid[0].Length;

	int val = this.DFS(grid, dirR, dirC, rows-1, cols-1, new HashSet<string>(), rows, cols);

	return val >= 10000 ? -1 : val;
}

public int DFS(int[][] grid, int[] dirR, int[] dirC, int i, int j, HashSet<string> visited, int rows, int cols){
	string path = i.ToString() + "%" + j.ToString();

	if(i < 0 || j < 0 || i >= rows || j >= cols || visited.Contains(path) || grid[i][j] == 1){
		return 10000;
	}

	if(i == 0 && j == 0){
		return 1;
	}

	visited.Add(path);

	int minVal = 10000;
	for(int a = 0; a < 8; a++){
		int newI = i + dirR[a];
		int newJ = j + dirC[a];

		minVal = Math.Min(minVal, this.DFS(grid, dirR, dirC, newI, newJ, visited, rows, cols));
	}

	visited.Remove(path);

	return 1 + minVal;
}

Using BFS

public int ShortestPathBinaryMatrix(int[][] grid) {
	int[] dirR = new int[] {-1, -1, -1, 0, 1, 1,  1,  0};
	int[] dirC = new int[] {-1,  0,  1, 1, 1, 0, -1, -1};

	int rows = grid.Length;

	if(rows == 0){
		return 0;
	}

	int cols = grid[0].Length;

	if(grid[0][0] == 1 || grid[rows-1][cols-1] == 1){
		return -1;
	}

	bool[,] visited = new bool[rows,cols];
	visited[0,0] = true;

	Queue<int[]> bfsQueue = new Queue<int[]>();
	bfsQueue.Enqueue(new int[]{0,0});

	int output = 0;

	while(bfsQueue.Count > 0){
		int count = bfsQueue.Count;

		for(int i = 0; i < count; i++){
			int[] curr = bfsQueue.Dequeue();

			if(curr[0] == rows-1 && curr[1] == cols-1){
				return output + 1;
			}

			for(int a = 0; a < 8; a++){
				int newI = curr[0] + dirR[a];
				int newJ = curr[1] + dirC[a];

				if(newI >= 0 && newJ >= 0 && newI < rows && newJ < cols && !visited[newI,newJ] && grid[newI][newJ] == 0){
					bfsQueue.Enqueue(new int[] {newI, newJ});
					visited[newI,newJ] = true;
				}

			}
		}
		output++;
	}

	return -1;
}

Swap Nodes in Pairs

Link - Swap Nodes in Pairs

Recursion

public ListNode SwapPairs(ListNode head) {
	if(head == null || head.next == null){
		return head;
	}

	ListNode newHead = head.next;
	head.next = this.SwapPairs(head.next.next);
	newHead.next = head;
	return newHead;
}

Unique Email Addresses

Link - Unique Email Addresses

For a given email split it by @ character. The second part of the split will give you the domain name. Now again split first part of the previous split by + character. Now the first part of the split will have the name of the email address. Now replace the . character of this substring with an empty character. This will give you the required email. Now push this name along with the domain to a HashSet. Repeat this process on all the emails. Now the size of the HashSet is the required solution.

    public int NumUniqueEmails(string[] emails) {
        HashSet<string> output = new HashSet<string>();

        foreach(string email in emails){
            string[] split = email.Split('@');
            string[] name = split[0].Split('+');
            string subname = name[0].Replace(".", "");
            string domain = split[1];
            output.Add(subname + '@'+ domain);
        }

        return output.Count;
    }

Minimum Domino Rotations For Equal Row

Link - Minimum Domino Rotations For Equal Row

Linear Time, Constant Space

public int MinDominoRotations(int[] A, int[] B) {
	int[] ACount = new int[6];
	int[] BCount = new int[6];

	int N = A.Length;

	for(int i = 0; i < A.Length; i++){
		ACount[A[i]-1]++;
		BCount[B[i]-1]++;
	}

	int posNum = -1;

	for(int i = 0; i < 6; i++){
		if(ACount[i] + BCount[i] >= N){
			posNum = i+1;
			continue;
		}
	}

	if(posNum == -1){
		return -1;
	}

	int repNums = 0; // Number of times A and B have same values(posNum) at a index
	int count = 0;

	for(int i = 0; i < A.Length; i++){
		if(A[i] == posNum && B[i] == posNum){
			repNums++;
		}
		else if(A[i] == posNum || B[i] == posNum){
			if(A[i] == posNum){
				count++;
			}
		}
		else{
			return -1;
		}
	}

	return Math.Min(count, N - count - repNums);
}

Linear Time, Constant Space - Improved

public int MinDominoRotations(int[] A, int[] B) {
	int[] ACount = new int[6];
	int[] BCount = new int[6];
	int[] SameCount = new int[6];

	int N = A.Length;

	for(int i = 0; i < A.Length; i++){
		ACount[A[i]-1]++;
		BCount[B[i]-1]++;

		if(A[i] == B[i]){
			SameCount[A[i]-1]++;
		}

	}

	int posNum = -1;

	for(int i = 0; i < 6; i++){
		if(ACount[i] + BCount[i] - SameCount[i] == N){
			return N - Math.Max(ACount[i], BCount[i]);
		}
	}

	return -1;
}

Valid Anagram

Link - Valid Anagram

Using Sorting

The naive approach to this problem would be to sort both the strings first and then check if they match by each characters.

This solution has a time complexity of O(nlogn) and space complexity of O(1)

Using Dictionary

Create a dictionary first. Take one of the string and start adding them to the dictionary along with the count of number of times a specific character has occured. Then take the second string and remove the key one by one. And the end, if the dictionary doesn’t have any item then it’s a valid anagram.

public bool IsAnagram(string s, string t) {
    // Check if both strings are of equal length
    if(s.Length != t.Length){
        return false;
    }

    Dictionary<char,int> temp = new Dictionary<char,int>();

    // Add all the characters of the string to the dictionary
    foreach(char item in s){
        if(temp.ContainsKey(item)){
            temp[item] += 1;
        }
        else{
            temp[item] = 1;
        }
    }

    // Remmoving characters from the string
    foreach(char item in t){
        if(temp.ContainsKey(item)){
            // If value of key is 1 then remvoe the key else reduce the value by 1
            if(temp[item] == 1){
                temp.Remove(item);
            }
            else{
                temp[item] -= 1;
            }
        }
        else{
            return false;
        }
    }

    // If the dictionary is empty then it is a valid anagram
    return temp.Count == 0;
}

The above solution has a time complexity of O(n) and space complexity of O(n).

ApproachTime ComplexitySpace Complexity
Using SortingO(nlogn)O(1)
Using DictionaryO(n)O(n)

Maximal Network Rank

Link - Maximal Network Rank

N^2 Solution

public class Solution {
    public int MaximalNetworkRank(int n, int[][] roads) {
        HashSet<int>[] graph = new HashSet<int>[n];

        Node[] inDegrees = new Node[n];

        for(int i = 0; i < n; i++){
            graph[i] = new HashSet<int>();
            inDegrees[i] = new Node(i);
        }

        foreach(int[] road in roads){
            inDegrees[road[0]].degree++;
            inDegrees[road[1]].degree++;

            graph[road[0]].Add(road[1]);
            graph[road[1]].Add(road[0]);
        }

        int maxSoFar = 0;

        for(int i = 0; i < inDegrees.Length; i++){
            for(int j = i + 1; j < inDegrees.Length; j++){

                int currSum = inDegrees[i].degree + inDegrees[j].degree;

                if(this.AreConnected(graph, inDegrees[i], inDegrees[j])){
                    currSum -= 1;
                }

                maxSoFar = Math.Max(maxSoFar, currSum);
            }
        }

        return maxSoFar;
    }

    public bool AreConnected(HashSet<int>[] graph, Node a, Node b){
        if(graph[a.node].Contains(b.node)){
            return true;
        }

        return false;
    }
}

public class Node{
    public int node;
    public int degree;

    public Node(int node){
        this.node = node;
    }
}

Space Optimized N^2 Solution

public class Solution {
    public int MaximalNetworkRank(int n, int[][] roads) {
        HashSet<int>[] graph = new HashSet<int>[n];

        Node[] inDegrees = new Node[n];

        for(int i = 0; i < n; i++){
            graph[i] = new HashSet<int>();
            inDegrees[i] = new Node(i);
        }

        foreach(int[] road in roads){
            inDegrees[road[0]].degree++;
            inDegrees[road[1]].degree++;

            graph[road[0]].Add(road[1]);
            graph[road[1]].Add(road[0]);
        }

        Array.Sort(inDegrees, (a,b) => b.degree - a.degree);

        List<Node> tempList = new List<Node>();

        tempList.Add(inDegrees[0]);
        tempList.Add(inDegrees[1]);

        int tempDegree = inDegrees[1].degree;

        for(int i = 2; i < n; i++){
            if(tempDegree == inDegrees[i].degree){
                tempList.Add(inDegrees[i]);
            }
            else{
                break;
            }
        }

        int maxSoFar = 0;

        for(int i = 0; i < tempList.Count; i++){
            for(int j = i + 1; j < tempList.Count; j++){

                int currSum = tempList[i].degree + tempList[j].degree;

                if(this.AreConnected(graph, tempList[i], tempList[j])){
                    currSum -= 1;
                }

                maxSoFar = Math.Max(currSum, maxSoFar);

            }
        }

        return maxSoFar;
    }

    public bool AreConnected(HashSet<int>[] graph, Node a, Node b){
        if(graph[a.node].Contains(b.node)){
            return true;
        }

        return false;
    }
}

public class Node{
    public int node;
    public int degree;

    public Node(int node){
        this.node = node;
    }
}

Subarray Product Less Than K

Link - Subarray Product Less Than K

Using Two Pointer Approach

public int NumSubarrayProductLessThanK(int[] nums, int k) {
	if(k <= 1){
		return 0;
	}

	int N = nums.Length;

	int count = 0;

	int sIndex = 0;
	int eIndex = 0;

	int currProd = 1;

	while(eIndex < N){
		currProd *= nums[eIndex];

		while(currProd >= k){
			currProd /= nums[sIndex];
			sIndex++;
		}

		count += eIndex - sIndex + 1;

		eIndex++;
	}

	return count;
}

Minimum Deletions to Make Character Frequencies Unique

Link - Minimum Deletions to Make Character Frequencies Unique

Greedy Solution

public int MinDeletions(string s) {
	Dictionary<char,int> map = new Dictionary<char,int>();

	foreach(char c in s){
		if(!map.ContainsKey(c)){
			map[c] = 0;
		}

		map[c]++;
	}

	HashSet<int> set = new HashSet<int>();

	int result = 0;

	foreach(var item in map){
		int count = item.Value;

		while(set.Contains(count)){
			count--;
			result++;
		}

		if(count != 0){
			set.Add(count);
		}
	}

	return result;
}

Unique Binary Search Trees

Link - Unique Binary Search Trees

Using Dynamic Programming

public int NumTrees(int n) {
	int[] memo = new int[n+1];
	memo[0] = 1;
	memo[1] = 1;

	for(int i = 2; i <=n; i++){
		for(int j = 1; j <= i; j++){
			memo[i] += memo[i-j] * memo[j-1];
		}
	}

	return memo[n];
}

Using Catalan Number

Use this formula to get the result - (2n)!/((n+1)!*n!)

Ugly Number II

Link - Ugly Number II

Using Dynamic programming

public int NthUglyNumber(int n) {
	int[] dp = new int[n];

	dp[0] = 1;

	int index2 = 0;
	int index3 = 0;
	int index5 = 0;

	int factor2 = 2;
	int factor3 = 3;
	int factor5 = 5;

	dp[0] = 1;

	for(int i = 1; i < n; i++){
		dp[i] = Math.Min(Math.Min(factor2, factor3), factor5);

		if(factor2 == dp[i]){
			index2++;
			factor2 = 2*dp[index2];
		}

		if(factor3 == dp[i]){
			index3++;
			factor3 = 3*dp[index3];
		}

		if(factor5 == dp[i]){
			index5++;
			factor5 = 5*dp[index5];
		}
	}

	return dp[n-1];
}

Reduce Array Size to The Half

updated: “2021-08-07”

Link - Reduce Array Size to The Half

Greedy Approach

public int MinSetSize(int[] arr) {
	Dictionary<int, int> freq = new Dictionary<int,int>();

	foreach(int item in arr){
		if(!freq.ContainsKey(item)){
			freq[item] = 0;
		}

		freq[item] += 1;
	}

	int[] counts = new int[freq.Count];

	int i = 0;
	foreach(var item in freq){
		counts[i] = item.Value;
		i++;
	}

	Array.Sort(counts, (a,b) => b-a);

	int reqWeight = arr.Length / 2;
	int count = 0;

	while(reqWeight > 0){
		reqWeight -= counts[count];
		count++;
	}

	return count;
 }

Using Priority Queue

  • Find the frequency of numbers in the given input array and store it in map.
  • Put these frequencies in a priority queue.
  • Pop the frequencies in the order of highest to lowest from the priority queue. Repeat it until reaching half the size of the input array size.
class Solution {
public:
    int minSetSize(vector<int>& arr) {
        int N = arr.size();

        unordered_map<int, int> freq;
        priority_queue<int> pq;

        for(int i : arr){
            freq[i]++;
        }

        for(auto i : freq){
            pq.push(i.second);
        }

        int output = 0;
        int removed = 0;

        while(removed < N/2){
            removed += pq.top();
            pq.pop();
            output++;
        }

        return output;
    }
};

Design Parking System

Link - Design Parking System

Naive Approach

public class ParkingSystem {

    int big = 0;
    int medium = 0;
    int small = 0;

    public ParkingSystem(int big, int medium, int small) {
        this.big = big;
        this.medium = medium;
        this.small = small;
    }

    public bool AddCar(int carType) {
        if(carType == 1){
            big--;
            return big < 0 ? false : true;
        }
        else if(carType == 2){
            medium--;
            return medium < 0 ? false : true;
        }
        else if(carType == 3){
            small--;
            return small < 0 ? false : true;
        }

        return false;
    }
}

Range Sum Query 2D - Immutable

Link - Range Sum Query 2D - Immutable

Using Bottom Up Approach

int[,] range;
public NumMatrix(int[][] matrix) {
	int rows = matrix.Length;
	if(rows == 0){
		range = new int[0,0];
		return;
	}

	int cols = matrix[0].Length;

	range = new int[rows+1, cols+1];

	for(int i = 1; i <= rows; i++){
		for(int j = 1; j <= cols; j++){
			range[i,j] = matrix[i-1][j-1] + range[i-1,j] + range[i,j-1] - range[i-1,j-1];
		}
	}

}

public int SumRegion(int row1, int col1, int row2, int col2) {
	return range[row2+1, col2+1] - range[row2+1,col1] - range[row1,col2+1] + range[row1,col1];
}

Smallest Integer Divisible by K

Link - Smallest Integer Divisible by K

Using Stack

public int SmallestRepunitDivByK(int K) {
	int rem = 0;

	for(int N = 1; N <= K; N++){
		rem = ((rem * 10) + 1) % K;

		if(rem == 0){
			return N;
		}
	}

	return -1;
}

Add Binary

Link - Add Binary

Firstly make the lengths of the both input string equal by prepending 0 to them. Now loop through the given strings starting from the last bit and perform XOR operation for sum and AND operation for the carry flag. Keep doing this to get the desired result.

public string AddBinary(string a, string b) {
    int aLen = a.Length;
    int bLen = b.Length;

    // Make the lengths of both the inputs equal
    if(aLen > bLen){
        for(int i = 0; i < aLen-bLen; i++){
            b = "0" + b;
        }
    }
    else if(bLen > aLen){
        for(int i = 0; i < bLen-aLen; i++){
            a = "0" + a;
        }
    }

    char[] output = new char[a.Length];
    char carry = '0';

    Dictionary<char, bool> MAP = new Dictionary<char, bool>();
    MAP['1'] = true;
    MAP['0'] = false;

    Dictionary<bool, char> REVERSEMAP = new Dictionary<bool, char>();
    REVERSEMAP[true] = '1';
    REVERSEMAP[false] = '0';

    for(int i = a.Length - 1; i >= 0; i--){
        Console.WriteLine(i.ToString());
        output[i] = REVERSEMAP[MAP[a[i]] ^ MAP[b[i]] ^ MAP[carry]];
        carry = REVERSEMAP[(MAP[a[i]] && MAP[b[i]]) || (MAP[b[i]] && MAP[carry]) || (MAP[carry] && MAP[a[i]])];
    }

    if(carry == '1'){
        return '1' + new string(output);
    }
    else{
        return new string(output);
    }
}

Sort Array By Parity

Link - Sort Array By Parity

Naive Approach

Th naive approach would be to loop through the array add even elements to a new array. Then loop again from start to end of the array and add odd elements of the array.

This has a time complexity of O(n) and space complexity of O(n).

Two Pointer Approach (In-place)

Here we use two pointers to achieve the solution. Start pointer points to even numbers and end pointer points to odd numbers. Increment the start pointer and check for an odd number. When you find an odd number stop the loop. Do the same with the end pointer also. So, now the start pointer is pointing to an odd number and the end pointer is pointing to an even number. Swap the values in the start and end pointer. Repeat this until both the pointers meet to get the solution.

public int[] SortArrayByParity(int[] A) {
    if(A.Length < 2){
        return A;
    }

    int start = 0;
    int end = A.Length - 1;

    while(start <= end){
        while(start <= end && A[start] % 2 == 0){
            start++;
        }

        while(start <= end && A[end] % 2 != 0){
            end--;
        }

        if(start <= end){
            int temp = A[start];
            A[start] = A[end];
            A[end] = temp;

            start++;
            end--;
        }
    }

    return A;
}

This solution has a time complexity of O(n) and space complexity of O(1).

ApproachTime ComplexitySpace Complexity
Naive ApproachO(n)O(n)
Two Pointer ApproachO(n)O(1)

Diameter of Binary Tree

Link - Diameter of Binary Tree

Depth First Search

public class Solution {
    int diameter = 0;
    public int DiameterOfBinaryTree(TreeNode root) {
        this.Depth(root);

        return diameter;
    }

    public int Depth(TreeNode node){
        if(node == null){
            return 0;
        }

        int lDepth = this.Depth(node.left);
        int rDepth = this.Depth(node.right);

        diameter = Math.Max(diameter, lDepth + rDepth);

        return Math.Max(lDepth, rDepth) + 1;
    }
}

Shuffle the Array

Link - Shuffle the Array

Two Pointer Approach

public int[] Shuffle(int[] nums, int n) {
	int fPointer = 0;
	int sPointer = n;

	int[] output = new int[2*n];

	int i = 0;
	while(fPointer < n){
		output[i] = nums[fPointer];
		output[i+1] = nums[sPointer];
		i += 2;
		fPointer++;
		sPointer++;
	}

	return output;
}

Maximize Distance to Closest Person

Link - Maximize Distance to Closest Person

One Pass Approach

public int MaxDistToClosest(int[] seats) {
	int N = seats.Length;

	int start = -1;
	int result = 0;

	for(int i = 0; i < N; i++){
		if(seats[i] == 1){
			if(start == -1){
				result = Math.Max(result, i);
			}

			result = Math.Max(result, (i - start)/2);
			start = i;
		}
	}

	result = Math.Max(result, N - start - 1);

	return result;
}

Merge k Sorted Lists

Link - Merge k Sorted Lists

Using Recursion

public ListNode MergeKLists(ListNode[] lists) {
	ListNode dummy = new ListNode(0);

	this.Merge(lists, dummy);

	return dummy.next;
}

public void Merge(ListNode[] lists, ListNode output){
	ListNode minNode = new ListNode(Int32.MaxValue);

	int listIndex = -1;
	for(int i = 0; i < lists.Length; i++){
		if(lists[i] != null && lists[i].val < minNode.val){
			minNode = lists[i];
			listIndex = i;
		}
	}

	if(minNode.val == Int32.MaxValue){
		return;
	}

	output.next = minNode;
	output = output.next;

	lists[listIndex] = lists[listIndex].next;

	this.Merge(lists, output);
	return;
}

Valid Palindrome

Link - Valid Palindrome

Using Two Pointer Technique

public bool IsPalindrome(string s) {
	int start = 0;
	int end = s.Length - 1;

	while(start < end){
		if(!char.IsLetterOrDigit(s[start])){
			start++;
			continue;
		}

		if(!char.IsLetterOrDigit(s[end])){
			end--;
			continue;
		}

		if(char.ToLower(s[start]) != char.ToLower(s[end])){
			return false;
		}
		start++;
		end--;
	}

	return true;
}

Count of substrings with only 1s

Problem Statement

Given a binary string (consisting of 0s and 1s), return the count of substrings with only 1s.

Using patterns

1 => one 1’s => 1 = 1 2 => two 1’s, one 2’s => 2 + 1 = 3 3 => three 1’s, two 2’s, one 3’s => 3 + 2 + 1 = 6 4 => four 1’s, three 2’s, two 3’s, one 4’s => 4 + 3 + 2 + 1 = 10

Looking at the above patterns we can generalize that for n subsequent ones, the substring count will be n(n+1)/2.

Apply this method throughout the given string array to get the desired result.

public int SubstringCount(string s)
{
	int count = 0;

	int tempCount = 0;
	foreach(char c in s)
	{
		if(c == '1')
		{
			tempCount++;
		}
		else
		{
			count += this.Count(tempCount);
			tempCount = 0;
		}
	}

	count += this.Count(tempCount);

	return count;
}

public int Count(int n)
{
	if(n == 0)
	{
		return 0;
	}
	return (n * (n + 1))/ 2;
}

TestCases

  • 1 // 1
  • 11 // 3
  • 111 // 6
  • 01110 // 6
  • 0011100110 // 9
  • 001111110 // 21

Valid Square

Link - Valid Square

Using HashSet

public bool ValidSquare(int[] p1, int[] p2, int[] p3, int[] p4) {
	HashSet<int> set = new HashSet<int>();

	set.Add(this.Dist(p1, p2));
	set.Add(this.Dist(p1, p3));
	set.Add(this.Dist(p1, p4));
	set.Add(this.Dist(p2, p3));
	set.Add(this.Dist(p2, p4));
	set.Add(this.Dist(p3, p4));

	// there should be 4 equal sides and 2 equal diagonals
	return set.Count == 2 && !set.Contains(0);
}

public int Dist(int[] pt1, int[] pt2){
	return (int)Math.Pow(pt1[0] - pt2[0], 2) + (int)Math.Pow(pt1[1] - pt2[1], 2);
}

Remove Nth Node From End of List

Link - Remove Nth Node From End of List

Two-Pass Approach

One way to solve this problem would be, finding the length of the Linked list first. Once you get the length traverse through the linked list again length - n times. Then change the next pointer of that node to point to the next to next node. This will return you the required result. See the below code for the solution.

public ListNode RemoveNthFromEnd(ListNode head, int n) {
    int length = this.Count(head) - n;

    ListNode dummy = new ListNode(0);
    dummy.next = head;
    head = dummy;
    int i = 0;
    while(i < length){
        head = head.next;
        i++;
    }

    head.next = head.next.next;
    return dummy.next;
}

public int Count(ListNode head){
    int count = 1;
    while(head.next != null){
        head = head.next;
        count++;
    }
    return count;
}

You might have seen I am using a dummy node and then pointing the dummy node to the head. This is done to prevent certain edge cases where the linked list has only 1 node.

The time complexity if O(1) and space complexity if O(1) for the above approach.

The above solution traverses through the linked list twice. Can we solve it in one pass? See the next approach to see how it can be done.

One Pass Solution (2 Pointer Approach)

In this approach, we move the first pointer n steps ahead. Then we move both the pointers until the first pointer reaches the end. At this point the first pointer is pointing to the node that has to be removed, so change the next pointer of the second pointer to point to the next to next node. This will give you the required result.

public ListNode RemoveNthFromEnd(ListNode head, int n) {
    ListNode dummy = new ListNode(0);
    dummy.next = head;

    ListNode fPointer = dummy;
    ListNode sPointer = dummy;

    for(int i = 0; i < n; i++){
        fPointer = fPointer.next;
    }

    while(fPointer.next != null){
        fPointer = fPointer.next;
        sPointer = sPointer.next;
    }

    sPointer.next = sPointer.next.next;

    return dummy.next;
}

Space and time complexity of this approach is the same as the previous method. The only benefit with this approach is that the list will be traversed only once.

Clone Graph

Link - Clone Graph

Using Depth First Search

public Node CloneGraph(Node node) {
	Dictionary<Node, Node> map = new Dictionary<Node, Node>();

	return this.Clone(node, map);
}

public Node Clone(Node node, Dictionary<Node, Node> map){
	if (node == null){
		return null;
	}

	if(map.ContainsKey(node)){
		return map[node];
	}

	Node tempNode = new Node(node.val);
	map[node] = tempNode;

	List<Node> vertices = new List<Node>();
	foreach(Node item in node.neighbors){
		vertices.Add(this.Clone(item, map));
	}
	tempNode.neighbors = vertices;

	return tempNode;
}

Construct K Palindrome Strings

Link - Construct K Palindrome Strings

Greedy Approach

public bool CanConstruct(string s, int k) {
	int maxSplits = s.Length;

	// If k is greater than the length of the string then we cannot divide since the question explicitly mentions that there cannot be any palindromes with length 0
	if(k > maxSplits){
		return false;
	}

	Dictionary<char,int> map = new Dictionary<char,int>();

	foreach(char c in s){
		if(!map.ContainsKey(c)){
			map[c] = 0;
		}
		map[c] += 1;
	}

	// This variable holds the minimum number of splits that will have to be made to construct the palindromic strings.
	// If k is greater than minSplits then we can construct else we cannot.
	int minSplits = 0;

	// minSplits basically calculates the number of characters which have odd frequency count, since odd numbered characters have to be a separate palindrome
	foreach(var item in map){
		if(item.Value % 2 == 1){
			minSplits++;
		}
	}

	// If the minspits is 0 then there will be atleat one palindrome with all characters
	if(minSplits == 0){
		minSplits = 1;
	}

	if(k >= minSplits){
		return true;
	}
	else{
		return false;
	}
}

Alert Using Same Key-Card Three or More Times in a One Hour Period

Link - Alert Using Same Key-Card Three or More Times in a One Hour Period

Using Sorted Set + Dictionary

public IList<string> AlertNames(string[] keyName, string[] keyTime) {
	Dictionary<string, SortedSet<int>> map = new Dictionary<string, SortedSet<int>>();

	for(int i = 0; i < keyName.Length; i++){
		if(!map.ContainsKey(keyName[i])){
			map[keyName[i]] = new SortedSet<int>();
		}

		map[keyName[i]].Add(this.ConvertTime(keyTime[i]));
	}

	HashSet<string> result = new HashSet<string>();

	foreach(var item in map){
		List<int> tempList = new List<int>(item.Value.ToList());
		for(int i = 2; i < tempList.Count; i++){
			if(this.IsSameTimeGroup(tempList[i-2], tempList[i-1]) && this.IsSameTimeGroup(tempList[i-2], tempList[i])){
				result.Add(item.Key);
			}
		}

	}

	List<string> output = result.ToList();

	output.Sort();
	return output;
}

public int ConvertTime(string time){
	string[] timeSplit = time.Split(':');
	int min = Convert.ToInt32(timeSplit[0]);
	int sec = Convert.ToInt32(timeSplit[1]);

	return min * 60 + sec;
}

public bool IsSameTimeGroup(int time1, int time2){
	if(Math.Abs(time1 - time2) <= 60){
		return true;
	}

	return false;
}

Min Stack

Link - Min Stack

Using Two Stacks

public class MinStack {
    Stack<int> mainStack = new Stack<int>();
    Stack<int> minStack = new Stack<int>();

    public MinStack() {
        mainStack = new Stack<int>();
        minStack = new Stack<int>();
    }

    public void Push(int x) {
        mainStack.Push(x);

        // Whenever the value to pushed is less than the top element then push the new element. Else push the new element.
        if(minStack.Count == 0 || x < minStack.Peek()){
            minStack.Push(x);
        }
        else{
            minStack.Push(minStack.Peek());
        }
    }

    public void Pop() {
        mainStack.Pop();
        minStack.Pop();
    }

    public int Top() {
        return mainStack.Peek();
    }

    public int GetMin() {
        return minStack.Peek();
    }
}

Combination Sum III

Link - Combination Sum III

Using Backtracking

public IList<IList<int>> CombinationSum3(int k, int n) {
	IList<IList<int>> output = new List<IList<int>>();

	this.Combinations(output, new List<int>(), 1, k, n);

	return output;
}

public void Combinations(IList<IList<int>> output, List<int> tempOutput, int currIndex, int remSize, int remSum){
	if(remSize == 0 && remSum == 0){
		List<int> temp = new List<int>();
		temp.AddRange(tempOutput);

		output.Add(temp);
		return;
	}

	for(int i = currIndex; i <= 9; i++){
		tempOutput.Add(i);
		this.Combinations(output, tempOutput, i+1, remSize-1, remSum-i);
		tempOutput.RemoveAt(tempOutput.Count - 1);
	}
}

Using Backtracking (Improved Version)

public IList<IList<int>> CombinationSum3(int k, int n) {
	IList<IList<int>> output = new List<IList<int>>();

	this.Combinations(output, new List<int>(), 1, k, n);

	return output;
}

public void Combinations(IList<IList<int>> output, List<int> tempOutput, int currIndex, int remSize, int remSum){
	if(remSize < 0){
		return;
	}

	if(remSize == 0 && remSum == 0){
		List<int> temp = new List<int>();
		temp.AddRange(tempOutput);

		output.Add(temp);
		return;
	}

	for(int i = currIndex; i <= 9; i++){
		if(remSum - i < 0){
			continue;
		}

		tempOutput.Add(i);
		this.Combinations(output, tempOutput, i+1, remSize-1, remSum-i);
		tempOutput.RemoveAt(tempOutput.Count - 1);
	}
}

Maximum Sum Circular Subarray

Link - Maximum Sum Circular Subarray

Using Kadane’s Algorithm

public int MaxSubarraySumCircular(int[] A) {
	// Edge case when array length is 1
	if(A.Length == 1){
		return A[0];
	}

	int totalSum = A[0];
	int currMaxSum = A[0];
	int currMinSum = A[0];
	int maxSum = A[0];
	int minSum = A[1];

	for(int i = 1; i < A.Length; i++){
		totalSum += A[i];
		currMaxSum = Math.Max(currMaxSum + A[i], A[i]);
		currMinSum = Math.Min(currMinSum + A[i], A[i]);

		maxSum = Math.Max(maxSum, currMaxSum);
		minSum = Math.Min(minSum, currMinSum);
	}

	int output = Math.Max(maxSum, totalSum - minSum);

	// Case when all the elements of the array are negative, the output will be zero.
	if(output == 0){
		return maxSum;
	}

	return output;
}

Robot Return to Origin

Link - Robot Return to Origin

Maintain two counters one for vertical and another for horizontal moves. Iterate over all the moves in the given input. Increment or decrement these counter values as you iterate through the moves array. In the end, if both the counter values are zero then it means that the robot has returned to the origin.

public bool JudgeCircle(string moves) {
    int xCount = 0;
    int yCount = 0;

    for(int i = 0; i < moves.Length; i++){
        switch(moves[i]){
            case 'L':
                xCount -= 1; break;
            case 'R':
                xCount += 1; break;
            case 'U':
                yCount -= 1; break;
            case 'D':
                yCount += 1; break;
        }
    }

    return xCount == 0 && yCount == 0;
}

Short Encoding of Words

Link - Short Encoding of Words

Using Trie

public class Solution {
    public int MinimumLengthEncoding(string[] words) {
        TrieNode root = new TrieNode();

        Dictionary<TrieNode, int> map = new Dictionary<TrieNode, int>();

        for(int i = 0; i < words.Length; i++){
            string currWord = words[i];
            TrieNode temp = root;

            for(int j = currWord.Length-1; j >= 0; j--){
                temp = temp.Get(currWord[j]);
            }

            map[temp] = i;
        }

        int output = 0;

        foreach(var item in map){
            TrieNode tempNode = item.Key;

            if(tempNode.count == 0){
                output += words[map[tempNode]].Length + 1;
            }
        }

        return output;
    }
}

public class TrieNode{
    TrieNode[] children;
    public int count;

    public TrieNode(){
        children = new TrieNode[26];
        count = 0;
    }

    public TrieNode Get(char c){
        if(children[c - 'a'] == null){
            children[c - 'a'] = new TrieNode();
            count++;
        }

        return children[c - 'a'];
    }
}

Last Moment Before All Ants Fall Out of a Plank

Link - Last Moment Before All Ants Fall Out of a Plank

Using Naive approach

When two ants meet at a point, it’s same as these ants continuing to move in the same direction. So the time units required for all ants to fall off the plank will be the max of the left ants moving towards the start(i.e. 0) and right ants moving towards the end of the plank(i.e. n).

public int GetLastMoment(int n, int[] left, int[] right) {
	int max = 0;

	foreach(int item in left){
		max = Math.Max(max, item);
	}

	foreach(int item in right){
		max = Math.Max(max, n - item);
	}

	return max;
}

Intersection of Two Arrays

Link - Intersection of Two Arrays

Naive Approach

Initially push the contents of both the arrays to two different HashSets. Now loop through all the contents of one HashSet and check if the second HashSet has that value. If yes, then add those values to a new list. This should give you the required result.

public int[] Intersection(int[] nums1, int[] nums2) {
    HashSet<int> set1 = new HashSet<int>();
    HashSet<int> set2 = new HashSet<int>();

    for(int i = 0; i < nums1.Length; i++){
        set1.Add(nums1[i]);
    }

    for(int i = 0; i < nums2.Length; i++){
        set2.Add(nums2[i]);
    }

    List<int> output = new List<int>();

    foreach(int key in set1){
        if(set2.Contains(key)){
            output.Add(key);
        }
    }

    return output.ToArray();
}

This solution has a time complexity of O(m + n) and space complexity of O(m + n) where m and n are the sizes of both the arrays.

4Sum II

Link - 4Sum II

Quadratic Time and Space Complexity

public int FourSumCount(int[] A, int[] B, int[] C, int[] D) {
	Dictionary<int,int> map = new Dictionary<int,int>();

	for(int i = 0; i < A.Length; i++){
		for(int j = 0; j < B.Length; j++){
			int sum = A[i] + B[j];

			if(!map.ContainsKey(sum)){
				map[sum] = 0;
			}
			map[sum]++;
		}
	}

	int output = 0;

	for(int i = 0; i < C.Length; i++){
		for(int j = 0; j < D.Length; j++){
			int sum = -1 * (C[i] + D[j]);

			if(map.ContainsKey(sum)){
				output += map[sum];
			}
		}
	}

	return output;
}

Valid Sudoku

Link - Valid Sudoku

Naive Approach

Loop through each row and check if the number is repeated using a HashSet. Clear the `HashSet and repeat the same on the next row. When looping through each row, check if the HashSet already contains the number, if yes it’s not valid sudoku.

Repeat the same process on each column as well as each 3x3 rows. This return the required result. If it passes all three of these validations, then it’s valid sudoku.

public bool IsValidSudoku(char[][] board) {
    HashSet<int> tempSet = new HashSet<int>();

    for(int i = 0; i < 9; i++){
        tempSet.Clear();
        for(int j = 0; j < 9; j++){
            if(board[i][j] != '.' && tempSet.Contains(board[i][j])){
                return false;
            }
            tempSet.Add(board[i][j]);
        }
    }

    for(int i = 0; i < 9; i++){
        tempSet.Clear();
        for(int j = 0; j < 9; j++){
            if(board[j][i] != '.' && tempSet.Contains(board[j][i])){
                return false;
            }
            tempSet.Add(board[j][i]);
        }
    }

    for(int i = 0; i < 3; i++){
        for(int j = 0; j < 3; j++){
            tempSet.Clear();
            for(int a = 0; a < 3; a++){
                for(int b = 0; b < 3; b++){
                    if(board[(3 * i) + a][(3 * j) + b] != '.' && tempSet.Contains(board[(3 * i) + a][(3 * j) + b])){
                        return false;
                    }
                    tempSet.Add(board[(3 * i) + a][(3 * j) + b]);
                }
            }
        }
    }

    return true;
}

Check If a String Contains All Binary Codes of Size K

Link - Check If a String Contains All Binary Codes of Size K

Using Sliding Window

public bool HasAllCodes(string s, int k) {
	HashSet<string> codes = new HashSet<string>();

	int maxCombos = (int)Math.Pow(2, k);

	for(int i = 0; i <= s.Length - k; i++){
		codes.Add(s.Substring(i, k));
	}

	return maxCombos == codes.Count;
}

Distribute Candies to People

Link - Distribute Candies to People

Intuitive Approach

public int[] DistributeCandies(int candies, int num_people) {
	int[] output = new int[num_people];

	int size = 1;
	int index = 0;
	while(candies >= size){
		output[index] += size;
		candies -= size;
		size++;
		index++;

		if(index == num_people){
			index = 0;
		}
	}

	output[index] += candies;

	return output;
}

3Sum

Link - 3Sum

Using Sorting & Binary Search

public IList<IList<int>> ThreeSum(int[] nums) {
	IList<IList<int>> output = new List<IList<int>>();

	Array.Sort(nums);

	int N = nums.Length;

	for(int i = 0; i < N-2; i++){
		int target = -nums[i];

		int low = i+1;
		int high = N - 1;

		// Perform a binary search to get the target sum
		while(low < high){
			int sum = nums[low] + nums[high];

			if(sum < target){
				low++;
			}
			else if(sum > target){
				high--;
			}
			else{
				int num1 = nums[i];
				int num2 = nums[low];
				int num3 = nums[high];

				output.Add(new List<int>(){num1, num2, num3});

				// This skips the numbers so that duplicate triplets groups are prevented
				while(low < high && nums[low] == num2){
					low++;
				}

				while(low < high && nums[high] == num3){
					high--;
				}

			}

			// This skips the numbers so that duplicate triplets groups are prevented
			while(i+1 < N && nums[i+1] == nums[i]){
				i++;
			}
		}
	}

	return output;
}

Network Delay Time

Link - Network Delay Time

Using Floyd-Warshall Algorithm (N^3 time complexity, N^2 space complexity)

public int NetworkDelayTime(int[][] times, int N, int K) {
	int[,] memo = new int[N+1,N+1];

	// Set the max value of all the edges to infinity
	for(int i = 1; i <= N; i++){
		for(int j = 1; j <=N; j++){
			memo[i,j] = Int32.MaxValue;
		}
	}

	// Update the edge weights
	foreach(int[] edge in times){
		int f = edge[0];
		int t = edge[1];
		int w = edge[2];

		memo[f,t] = w;
	}

	for(int i = 1; i <= N; i++){
		for(int j = 1; j <= N; j++){
			for(int k = 1; k <= N; k++){
				if(memo[j,i] != Int32.MaxValue && memo[i,k] != Int32.MaxValue){
					memo[j,k] = Math.Min(memo[j,k], memo[j,i] + memo[i,k]);
				}
			}
		}
	}

	int maxTime = 0;

	// Finding out the max time to reach all the nodes
	for(int i = 1; i <= N; i++){
		if(i == K){
			continue;
		}

		// validate if there's any path from K to other nodes. If the weight is infinity then it means there's no path.
		if(memo[K, i] == Int32.MaxValue){
			return -1;
		}
		else{
			maxTime = Math.Max(maxTime, memo[K,i]);
		}

	}

	return maxTime;
}

Bellman-Ford Algorithm (VE time complexity, V space complexity)

public int NetworkDelayTime(int[][] times, int N, int K) {
	Dictionary<int, List<Tuple<int,int>>> graph = new Dictionary<int, List<Tuple<int,int>>>();

	// Initialize the graph
	for(int i = 1; i <= N; i++){
		graph[i] = new List<Tuple<int,int>>();
	}

	// Build the graph
	foreach(int[] edge in times){
		int f = edge[0];
		int t = edge[1];
		int w = edge[2];

		graph[f].Add(new Tuple<int,int>(t,w));
	}

	Queue<Node> bfsQueue = new Queue<Node>();
	bfsQueue.Enqueue(new Node(K, 0));

	int[] dist = new int[N+1]; // this array holds the smallest distance to a node from K
	Array.Fill(dist, 101);
	dist[K] = 0; // Distance from start node to start node is 0

	while(bfsQueue.Count > 0){
		Node tempNode = bfsQueue.Dequeue();
		int currNode = tempNode.node;
		int currWeight = tempNode.weight;

		foreach(var edge in graph[currNode]){
			if(dist[edge.Item1] > currWeight + edge.Item2){
				dist[edge.Item1] = currWeight + edge.Item2;
				bfsQueue.Enqueue(new Node(edge.Item1, dist[edge.Item1]));
			}
		}
	}

	// Finding the max time
	int maxTime = 0;
	for(int i = 1; i <= N; i++){
		if(dist[i] == 101){
			return -1;
		}
		else{
			maxTime = Math.Max(maxTime, dist[i]);
		}
	}

	return maxTime;
}

Dijkstra’s Algorithm (Using Min Heaps) (VElogV time complexity)

Changes compared to Bellman-Ford algorithm are

  • Instead of a Queue, Priority Queue with Min Heaps is used.
  • visited array is used track the nodes which are visited.
public int NetworkDelayTime(int[][] times, int N, int K) {
	Dictionary<int, List<Tuple<int,int>>> graph = new Dictionary<int, List<Tuple<int,int>>>();

	// Initialize the graph
	for(int i = 1; i <= N; i++){
		graph[i] = new List<Tuple<int,int>>();
	}

	// Build the graph
	foreach(int[] edge in times){
		int f = edge[0];
		int t = edge[1];
		int w = edge[2];

		graph[f].Add(new Tuple<int,int>(t,w));
	}

	PriorityQueue<Node> pq = new PriorityQueue<Node>();
	pq.Enqueue(0, new Node(K, 0));

	int[] dist = new int[N+1]; // this array holds the smallest distance to a node from K
	bool[] visited = new bool[N+1];
	Array.Fill(dist, 101);
	dist[K] = 0; // Distance from start node to start node is 0

	while(pq.Count > 0){
		Node tempNode = pq.Dequeue();
		int currNode = tempNode.node;
		int currWeight = tempNode.weight;

		if(visited[currNode]){
			continue;
		}

		visited[currNode] = true;

		foreach(var edge in graph[currNode]){
			if(dist[edge.Item1] > currWeight + edge.Item2){
				dist[edge.Item1] = currWeight + edge.Item2;
				pq.Enqueue(dist[edge.Item1], new Node(edge.Item1, dist[edge.Item1]));
			}
		}
	}

	// Finding the max time
	int maxTime = 0;
	for(int i = 1; i <= N; i++){
		if(dist[i] == 101){
			return -1;
		}
		else{
			maxTime = Math.Max(maxTime, dist[i]);
		}
	}

	return maxTime;
}

Binary Tree Zigzag Level Order Traversal

Link - Binary Tree Zigzag Level Order Traversal

Best Approach

This problem is exactly similar to Binary Tree Level Order Traversal. The only difference here is that we print odd depth levels of the tree in the reverse order. In order to add this, simply maintain another variable called depth which is incremented on every level traversal. At the end of traversal of a level, check if the depth is odd. If it is odd then reverse the list of that level.

public IList<IList<int>> ZigzagLevelOrder(TreeNode root) {
    IList<IList<int>> output = new List<IList<int>>();

    if(root == null){
        return output;
    }

    int depth = 0;
    Queue<TreeNode> bfsQueue = new Queue<TreeNode>();
    bfsQueue.Enqueue(root);

    while(bfsQueue.Count != 0){
        int count = bfsQueue.Count;
        List<int> temp = new List<int>();
        for(int i = 0; i < count; i++){
            TreeNode node = bfsQueue.Dequeue();

            if(node.left != null){
                bfsQueue.Enqueue(node.left);
            }

            if(node.right != null){
                bfsQueue.Enqueue(node.right);
            }

            temp.Add(node.val);
        }

        if(depth % 2 != 0){
            temp.Reverse();
        }

        depth++;
        output.Add(temp);
    }

    return output;
}

Min Cost to Connect All Points

Link - Min Cost to Connect All Points

Prim’s Algorithm using Priority Queue (Min Heap)

public class Solution {
    public int MinCostConnectPoints(int[][] points) {
        int N = points.Length;

        HashSet<int> unVisitedNodes = new HashSet<int>();

        for(int i = 0; i < N; i++){
            unVisitedNodes.Add(i);
        }

        PriorityQueue<Node> pq = new PriorityQueue<Node>();
        pq.Enqueue(0, new Node(0, 0));

        int minCost = 0;

        while(pq.Count > 0){
            Node tempNode = pq.Dequeue();
            int currNode = tempNode.node;
            int currWeight = tempNode.weight;

            if(!unVisitedNodes.Contains(currNode)){
                continue;
            }

            minCost += currWeight;

            unVisitedNodes.Remove(currNode);

            foreach(int nextNode in unVisitedNodes){
                int nextWeight = this.MinCost(points, currNode, nextNode);
                pq.Enqueue(nextWeight, new Node(nextNode, nextWeight));
            }
        }

        return minCost;
    }

    public int MinCost(int[][] points, int i, int j){
        int min = Math.Abs(points[i][0] - points[j][0]) + Math.Abs(points[i][1] - points[j][1]);

        return min;
    }
}

public class Node{
    public int node;
    public int weight;

    public Node(int _node, int _weight){
        this.node = _node;
        this.weight = _weight;
    }
}

// Implementation of Priority Queue using Min Heaps

public class KeyValuePair<T>
{
	public int Key;
	public T Value;

	public KeyValuePair(int _key, T _value)
	{
		this.Key = _key;
		this.Value = _value;
	}
}

public class PriorityQueue<T>
{
	List<KeyValuePair<T>> queue = null;
	int heapSize = -1;
	public int Count { get { return queue.Count; } }

	public PriorityQueue()
	{
		queue = new List<KeyValuePair<T>>();
	}

	private int LeftChild(int i)
	{
		return (i * 2) + 1;
	}

	private int RightChild(int i)
	{
		return (i * 2) + 2;
	}

	private void Swap(int i, int j)
	{
		KeyValuePair<T> temp = queue[i];
		queue[i] = queue[j];
		queue[j] = temp;
	}

	private void MinHeapify(int i)
    {
        int left = this.LeftChild(i);
        int right = this.RightChild(i);

        int lowest = i;

        if (left <= heapSize && queue[lowest].Key > queue[left].Key)
        {
            lowest = left;
        }

        if (right <= heapSize && queue[lowest].Key > queue[right].Key)
        {
            lowest = right;
        }

        if (lowest != i)
        {
            this.Swap(lowest, i);
            this.MinHeapify(lowest);
        }
    }

	private void BuildMinHeap(int i)
    {
        while (i >= 0 && queue[(i - 1) / 2].Key > queue[i].Key)
        {
            this.Swap(i, (i - 1) / 2);
            i = (i - 1) / 2;
        }
    }

	public void Enqueue(int _key, T _value)
	{
		KeyValuePair<T> temp = new KeyValuePair<T>(_key, _value);
		queue.Add(temp);
		heapSize++;
		this.BuildMinHeap(heapSize);
	}

	public T Dequeue()
	{
		if (heapSize > -1)
		{
			T returnVal = queue[0].Value;
			queue[0] = queue[heapSize];
			queue.RemoveAt(heapSize);
			heapSize--;
			this.MinHeapify(0);
			return returnVal;
		}
		else
		{
			throw new Exception("Queue is empty");
		}
	}
}

Determine if Two Strings Are Close

Link - Determine if Two Strings Are Close

Using Frequency Count

public bool CloseStrings(string word1, string word2) {
	if(word1.Length != word2.Length){
		return false;
	}

	int[] word1Count = new int[26];
	int[] word2Count = new int[26];

	for(int i = 0; i < word1.Length; i++){
		word1Count[word1[i] - 'a']++;
		word2Count[word2[i] - 'a']++;
	}

	for(int i = 0; i < 26; i++){
		if((word1Count[i] > 0 && word2Count[i] == 0) || (word1Count[i] == 0 && word2Count[i] > 0)){
			return false;
		}
	}

	Array.Sort(word1Count);
	Array.Sort(word2Count);

	for(int i = 0; i < 26; i++){
		if(word1Count[i] != word2Count[i]){
			return false;
		}
	}

	return true;
}

Count and Say

Link - Count and Say

Using Iteration

public string CountAndSay(int n) {
	if(n <= 0){
		return "-1";
	}

	string output = "1";

	for(int i = 1; i < n; i++){
		output = this.Builder(output);
	}

	return output;
}

public string Builder(string input){
	StringBuilder output = new StringBuilder();

	int i = 1;
	int count = 1;

	while(i < input.Length){
		if(input[i-1] == input[i]){
			count++;
		}
		else{
			output.Append(count.ToString() + input[i-1]);
			count = 1;
		}
		i++;
	}

	output.Append(count.ToString() + input[i-1]);

	return output.ToString();
}

Evaluate Division

Link - Evaluate Division

Depth First Search

public double[] CalcEquation(IList<IList<string>> equations, double[] values, IList<IList<string>> queries) {

	Dictionary<string, List<string>> graph = new Dictionary<string, List<string>>();
	Dictionary<string, List<double>> graphValues = new Dictionary<string, List<double>>();

	for(int i = 0; i < equations.Count; i++){
		string[] equation = equations[i].ToArray();

		if(!graph.ContainsKey(equation[0])){
			graph[equation[0]] = new List<string>();
			graphValues[equation[0]] = new List<double>();
		}

		if(!graph.ContainsKey(equation[1])){
			graph[equation[1]] = new List<string>();
			graphValues[equation[1]] = new List<double>();
		}

		graph[equation[0]].Add(equation[1]);
		graph[equation[1]].Add(equation[0]);

		graphValues[equation[0]].Add(values[i]);
		graphValues[equation[1]].Add(1/values[i]);

	}

	double[] output = new double[queries.Count];

	for(int j = 0; j < queries.Count; j++){
		string[] query = queries[j].ToArray();

		output[j] = this.DFS(graph, graphValues, query[0], query[1], new HashSet<string>(), 1.0);

		if(output[j] == 0.0){
			output[j] = -1;
		}
	}

	return output;
}

public double DFS(Dictionary<string, List<string>> graph, Dictionary<string, List<double>> graphValues, string sNode, string eNode, HashSet<string> visited, double val){

	if(visited.Contains(sNode)){
		return 0.0;
	}

	if(!graph.ContainsKey(sNode)){
		return 0.0;
	}

	if(sNode == eNode){
		return val;
	}

	visited.Add(sNode);

	List<string> nodes = graph[sNode];
	List<double> nodeValues = graphValues[sNode];

	double temp = 0.0;

	for(int i = 0; i < nodes.Count; i++){
		temp = this.DFS(graph, graphValues, nodes[i], eNode, visited, val * nodeValues[i]);

		if(temp != 0.0){
			break;
		}
	}

	visited.Remove(sNode);

	return temp;
}

Insert Interval

Link - Insert Interval

Straight Forward Approach

public int[][] Insert(int[][] intervals, int[] newInterval) {
	List<int[]> output = new List<int[]>();

	foreach(int[] interval in intervals){

		if(newInterval[0] > interval[1]){
			output.Add(interval);
		}
		else if(newInterval[1] < interval[0]){
			output.Add(newInterval);
			newInterval = interval;
		}
		else{
			newInterval[0] = Math.Min(interval[0], newInterval[0]);
			newInterval[1] = Math.Max(interval[1], newInterval[1]);
		}
	}

	output.Add(newInterval);

	return output.ToArray();
}

Diagonal Traverse

Link - Diagonal Traverse

Simulation

public int[] FindDiagonalOrder(int[][] matrix) {
	int rows = matrix.Length;

	if(rows == 0){
		return new int[]{};
	}

	int cols = matrix[0].Length;

	List<int> output = new List<int>();

	int i = 0;
	int j = 0;

	int direction = 1;

	while(i < rows && j < cols){
		output.Add(matrix[i][j]);

		int newRow = i + (direction == 1 ? -1 : 1);
		int newCol = j + (direction == 1 ? 1 : -1);

		if(newRow < 0 || newRow == rows || newCol < 0 || newCol == cols){
			if(direction == 1){
				i += (j == cols-1 ? 1 : 0);
				j += (j < cols-1 ? 1 : 0);
			}
			else{
				j += (i == rows-1 ? 1 : 0);
				i += (i < rows-1 ? 1 : 0);
			}

			direction = 1-direction;
		}
		else{
			i = newRow;
			j = newCol;
		}
	}

	return output.ToArray();
}

Is Subsequence

Link - Is Subsequence

Two-Pointer Approach

public bool IsSubsequence(string s, string t) {
	int sIndex = 0;
	int tIndex = 0;
	while(sIndex < s.Length && tIndex < t.Length){
		if(s[sIndex] == t[tIndex]){
			sIndex++;
			tIndex++;
		}
		else{
			tIndex++;
		}
	}

	if(sIndex == s.Length){
		return true;
	}

	return false;
}

Thousand Separator

Link - Thousand Separator

Naive Approach

public string ThousandSeparator(int n) {
	string output = "";

	while(n > 999){
		output = "." + (n%1000).ToString("D3") + output;
		n /= 1000;
	}

	return n.ToString() + output;
}

Wiggle Subsequence

Link - Wiggle Subsequence

Dynamic Programming

public int WiggleMaxLength(int[] nums) {
	if(nums.Length <= 1){
		return nums.Length;
	}

	int up = 1;
	int down = 1;

	for(int i = 0; i < nums.Length - 1; i++){
		if(nums[i] < nums[i+1]){ // uphill
			up = down + 1;
		}
		else if(nums[i] > nums[i+1]){
			down = up + 1;
		}
	}

	return Math.Max(up, down);
}

Largest Component Size by Common Factor

Link - Largest Component Size by Common Factor

Adjacency List + DFS (Time Limit Exceeded error)

public int LargestComponentSize(int[] A) {
	int n = A.Length;

	Dictionary<int, HashSet<int>> graph = new Dictionary<int, HashSet<int>>();

	foreach(int node in A){
		graph[node] = new HashSet<int>();
	}

	for(int i = 0; i < n; i++){
		for(int j = i+1; j < n; j++){
			if(this.GCD(A[i], A[j]) > 1){
				graph[A[i]].Add(A[j]);
				graph[A[j]].Add(A[i]);
			}
		}
	}

	HashSet<int> visited = new HashSet<int>();

	int maxSoFar = 0;

	foreach(var node in graph){
		if(!visited.Contains(node.Key)){
			maxSoFar = Math.Max(maxSoFar, this.DFS(graph, visited, node.Key));
		}
	}

	return maxSoFar;
}

public int GCD(int a, int b){
	if (b == 0){
	  return a;
	}
	return this.GCD(b, a % b);
}

public int DFS(Dictionary<int, HashSet<int>> graph, HashSet<int> visited, int node){
	if(visited.Contains(node)){
		return 0;
	}

	visited.Add(node);

	int count = 1;

	foreach(int currNode in graph[node]){
		count += this.DFS(graph, visited, currNode);
	}

	return count;
}

Using Union Find

public int LargestComponentSize(int[] A) {
	Dictionary<int, int> parent = new Dictionary<int,int>();

	foreach(int num in A){
		for(int factor = 2; factor*factor <= num; factor++){
			if(num % factor == 0){
				this.Unify(num, factor, parent);
				this.Unify(num, num/factor, parent);
			}
		}
	}

	Dictionary<int,int> freqCount = new Dictionary<int,int>();
	int maxSoFar = 1;
	foreach(int item in A){
		int p = this.Find(item, parent);
		if(freqCount.ContainsKey(p)){
			freqCount[p] += 1;
			maxSoFar = Math.Max(maxSoFar, freqCount[p]);
		}
		else{
			freqCount[p] = 1;
		}
	}

	return maxSoFar;
}

public void Unify(int a, int b, Dictionary<int,int> parents){
	int aParent = this.Find(a, parents);
	int bParent = this.Find(b, parents);

	if(aParent < bParent){
		parents[bParent] = aParent;
	}
	else{
		parents[aParent] = bParent;
	}
}

public int Find(int i, Dictionary<int,int> parent){
	if(!parent.ContainsKey(i)){
		parent[i] = i;
	}

	while(i != parent[i]){
		i = parent[i];
	}

	return i;
}

Deepest Leaves Sum

Link - Deepest Leaves Sum

Level Order Traversal

Perform a level order traversal on the given root node. As each level of the tree is traversed, add the node values and store it in a variable. Clear the sum when traversing through the next level. Keep repeating it until the last level to get the required result.

public int DeepestLeavesSum(TreeNode root) {
    Queue<TreeNode> bfsQueue = new Queue<TreeNode>();

    if(root == null){
        return 0;
    }

    bfsQueue.Enqueue(root);

    int output = 0;

    while(bfsQueue.Count != 0){
        int count = bfsQueue.Count;
        output = 0;
        for(int i = 0; i < count; i++){
            TreeNode temp = bfsQueue.Dequeue();
            output += temp.val;

            if(temp.left != null){
                bfsQueue.Enqueue(temp.left);
            }

            if(temp.right != null){
                bfsQueue.Enqueue(temp.right);
            }
        }
    }

    return output;
}

Gas Station

Link - Gas Station

Best Approach

In this problem, we need to find two things

  • Find if the available gas is greater or equal to the total cost of gas across all the stations
  • Finding the starting point of the station which gives us the solution

First one can be found by summing up both the cost and gas arrays. If the total cost is less than total gas then it means that there is no solution, so return -1;

For the second one, start from the zeroeth index and check if the tank capacity goes below 0 by finding the difference between gas value and cost value at a given station. Keep repeating this until the end of the array. If at any point if the tank capacity goes below the 0 then it means the car cannot go to the next station, so update the start pointer by incrementing it by 1. This should give you the required solution.

public int CanCompleteCircuit(int[] gas, int[] cost) {

    int N = gas.Length;
    int gasSum = 0;
    int costSum = 0;
    int tank = 0;
    int start = 0;

    for(int i = 0; i < N; i++){
        gasSum += gas[i];
        costSum += cost[i];

        tank += gas[i] - cost[i];

        if(tank < 0){
            tank = 0;
            start = i + 1;
        }
    }

    if(gasSum >= costSum){
        return start;
    }

    return -1;
}

This solution has a time complexity of O(n) and space complexity of O(1).

Friend Circles

Link - Friend Circles

Using Depth First Search

public int FindCircleNum(int[][] M) {
	int count = 0;
	int N = M.Length;

	for(int i = 0; i < N; i++){
		for(int j = 0; j < N; j++){
			if(M[i][j] == 1){
				this.DFS(M, N, i, j);
				count++;
			}
		}
	}

	return count;
}

public void DFS(int[][] M, int N, int i, int j){
	if(M[i][j] == 0){
		return;
	}

	M[i][j] = 0;

	for(int a = 0; a < N; a++){
		if(M[a][j] == 1){
			this.DFS(M, N, a, j);
		}

		if(M[i][a] == 1){
			this.DFS(M, N, i, a);
		}
	}
}

Score of Parentheses

Link - Score of Parentheses

Using Stack

public int ScoreOfParentheses(string S) {
	Stack<int> stack = new Stack<int>();
	int current = 0;

	foreach(char c in S){
		if(c == '('){
			stack.Push(current);
			current = 0;
		}
		else{
			current = stack.Pop() + Math.Max(current * 2, 1);
		}
	}

	return current;
}

Implement Trie (Prefix Tree)

Link - Implement Trie (Prefix Tree)

Trie Data Structure

public class Trie {

    public class Node{
        public bool isEnd;
        public Node[] values;

        public Node(bool _isEnd){
            this.isEnd = _isEnd;
            this.values = new Node[26];
        }
    }

    /** Initialize your data structure here. */

    Node root;

    public Trie() {
        root = new Node(false);
    }

    /** Inserts a word into the trie. */
    public void Insert(string word) {
        Node temp = root;
        for(int i = 0; i < word.Length; i++){
            int value = Convert.ToInt32(word[i]) - 97;

            if(temp.values[value] == null){
                temp.values[value] = new Node(false);
            }

            temp = temp.values[value];
        }
        temp.isEnd = true;
    }

    /** Returns if the word is in the trie. */
    public bool Search(string word) {
        Node temp = root;

        for(int i = 0; i < word.Length; i++){
            int value = Convert.ToInt32(word[i]) - 97;

            if(temp == null || temp.values[value] == null){
                return false;
            }

            temp = temp.values[value];
        }

        return temp.isEnd;
    }

    /** Returns if there is any word in the trie that starts with the given prefix. */
    public bool StartsWith(string prefix) {
        Node temp = root;

        for(int i = 0; i < prefix.Length; i++){
            int value = Convert.ToInt32(prefix[i]) - 97;

            if(temp == null || temp.values[value] == null){
                return false;
            }

            temp = temp.values[value];
        }

        return true;
    }
}

Add and Search Word - Data structure design

Link - Add and Search Word - Data structure design

Using Depth First Approach

public class WordDictionary {
    public TrieNode rootNode;

    public WordDictionary() {
        rootNode = new TrieNode(true);
    }

    public void AddWord(string word) {
        TrieNode temp = rootNode;

        for(int i = 0; i < word.Length; i++){
            int asciiValue = Convert.ToInt32(word[i]) - 96;

            if(temp.nodes[asciiValue] == null){
                temp.nodes[asciiValue] = new TrieNode(false);
            }
            temp = temp.nodes[asciiValue];
        }
        temp.val = true;
    }

    public bool Search(string word) {
        TrieNode temp = rootNode;
        return this.Search(word, 0, temp);
    }

    private bool Search(string word, int currIndex, TrieNode temp){
        if(currIndex == word.Length){
            return temp.val;
        }

        int asciiValue = Convert.ToInt32(word[currIndex]) - 96;
        if(word[currIndex] != '.'){
            return temp.nodes[asciiValue] != null && this.Search(word, currIndex + 1, temp.nodes[asciiValue]);
        }
        else{
            for(int i = 0; i < temp.nodes.Length; i++){
                if(temp.nodes[i] != null && this.Search(word, currIndex + 1, temp.nodes[i])){
                    return true;
                }
            }
        }

        return false;
    }
}

public class TrieNode{
    public bool val;
    public TrieNode[] nodes;

    public TrieNode(bool _val){
        this.val = _val;
        this.nodes = new TrieNode[27];
    }
}

Using Breadth First Approach

public class WordDictionary {

    Trie root;
    public WordDictionary() {
        root = new Trie();
    }

    public void AddWord(string word) {
        Trie tempNode = root;
        foreach(char c in word){
            int charValue = c - 'a';

            if(tempNode.child[charValue] == null){
                tempNode.child[charValue] = new Trie();
            }
            tempNode = tempNode.child[charValue];
        }
        tempNode.isWord = true;
    }

    public bool Search(string word) {
        Queue<Trie> bfsQueue = new Queue<Trie>();
        bfsQueue.Enqueue(root);

        foreach(char c in word){
            int count = bfsQueue.Count;

            if(c == '.'){
                for(int i = 0; i < count; i++){
                    Trie tempNode = bfsQueue.Dequeue();

                    for(int j = 0; j < tempNode.child.Length; j++){
                        if(tempNode.child[j] != null){
                            bfsQueue.Enqueue(tempNode.child[j]);
                        }
                    }

                }
            }
            else{
                for(int i = 0; i < count; i++){
                    int charValue = c - 'a';
                    Trie tempNode = bfsQueue.Dequeue();

                    if(tempNode.child[charValue] != null){
                        bfsQueue.Enqueue(tempNode.child[charValue]);
                    }

                }
            }
        }

        while(bfsQueue.Count > 0){
            if(bfsQueue.Dequeue().isWord == true){
                return true;
            }
        }

        return false;
    }
}

public class Trie{
    public Trie[] child;
    public bool isWord;

    public Trie(){
        child = new Trie[26];
        isWord = false;
    }
}

Destination City

Link - Destination City

Dictionary Approach

Push all the from and to of a city path to a dictionary. Then iterate through the dictionary items and check if the value of a key is also a key in the dictionary. The value which isn’t a key in the dictionary is the solution to this problem.

public string DestCity(IList<IList<string>> paths) {
	Dictionary<string,string> map = new Dictionary<string, string>();

	for(int i = 0; i < paths.Count; i++){
		map[paths[i][0]] = paths[i][1];
	}

	foreach(var item in map){
		string Key = item.Key;

		while(true){
			if(map.ContainsKey(Key)){
				Key = map[Key];
			}
			else{
				return Key;
			}
		}
	}

	return "";
}

Reverse Linked List

Link - Reverse Linked List

Iterative Approach

Iteratively update the next pointer of all the nodes to the previous node until the end of the list to get the reversed list.

public ListNode ReverseList(ListNode head) {
	ListNode prev = null;
	ListNode curr = head;
	ListNode next = null;

	while(curr != null){
		next = curr.next;
		curr.next = prev;
		prev = curr;
		curr = next;
	}

	return prev;
}

Maximum Sum Obtained of Any Permutation

Link - Maximum Sum Obtained of Any Permutation

Using Frequency Count and Sorting (Time Limit Exceeded)

This method uses nested loops to find the frequency count of ranges.

public int MaxSumRangeQuery(int[] nums, int[][] requests) {
	int N = nums.Length;

	int[] freq = new int[N];

	int M = 1000000007;

	foreach(int[] request in requests){
		for(int i = request[0]; i <= request[1]; i++){
			freq[i]++;
		}
	}

	Array.Sort(nums);
	Array.Sort(freq);

	long result = 0;

	for(int i = 0; i < N; i++){
		result = result + (nums[i] * freq[i]);
	}

	return (int)(result % M);
}

Using Frequency Count and Sorting (Improved)

This method uses prefix sum approach to find the frequency count of the ranges.

public int MaxSumRangeQuery(int[] nums, int[][] requests) {
	int N = nums.Length;

	int[] freq = new int[N];

	int M = 1000000007;

	for(int i = 0; i < requests.Length; i++){
		int start = requests[i][0];
		int end = requests[i][1];

		freq[start]++;

		if(end+1 < N){
			freq[end+1]--;
		}
	}

	int[] prefixSum = new int[N];
	int tempSum = 0;

	for(int i = 0; i < N; i++){
		tempSum += freq[i];
		prefixSum[i] = tempSum;
	}

	Array.Sort(nums);
	Array.Sort(prefixSum);

	long result = 0;

	for(int i = 0; i < N; i++){
		result = result + (nums[i] * prefixSum[i]);
	}

	return (int)(result % M);
}

Palindrome Number

Link - Palindrome Number

Naive Approach

The naive way to solve this problem would be to convert the given number to a string and then reverse it. Then compare if the original string and the reversed strings are the same.

See the next approach which doesn’t involve converting the number to a string.

Better Approach

First, check if the number is negative since a negative number cannot be a palindrome. Now, get the mod 10 of the number, this gives your the last digit, then multiply it by 10. Divide the input number by 10 which will remove the last digit. Keep repeating this. In the end, check if the input number and the reversed one are the same.

public bool IsPalindrome(int x) {
    // Checks if the given number is negative
    if(x < 0){
        return false;
    }

    int original = x;
    int reverse = 0;
    while(x != 0){
        reverse = (x % 10) + (reverse * 10);
        x /= 10;
    }

    if(original == reverse){
        return true;
    }
    return false;
}

Set Matrix Zeroes

Link - Set Matrix Zeroes

Not the Optimal Solution (Fails in certain edge cases)

public void SetZeroes(int[][] matrix) {
	int row = matrix.Length;
	if(row == 0){
		return;
	}

	int col = matrix[0].Length;

	for(int i = 0; i < row; i++){
		for(int j = 0; j < col; j++){
			if(matrix[i][j] == 0){
				for(int a = 0; a < col; a++){
					if(matrix[i][a] != 0){
						matrix[i][a] = Int32.MinValue;
					}
				}

				for(int b = 0; b < row; b++){
					if(matrix[b][j] != 0){
						matrix[b][j] = Int32.MinValue;
					}
				}
			}
		}
	}

	for(int i = 0; i < row; i++){
		for(int j = 0; j < col; j++){
			if(matrix[i][j] == Int32.MinValue){
				matrix[i][j] = 0;
			}
		}
	}

}

Better Solution

public void SetZeroes(int[][] matrix) {
	int row = matrix.Length;
	if(row == 0){
		return;
	}

	int col = matrix[0].Length;

	bool isFirstRow = false;
	bool isFirstCol = false;

	// Set isFirstRow as true if there is any 0 in the first row of the matrix
	for(int i = 0; i < col; i++){
		if(matrix[0][i] == 0){
			isFirstRow = true;
			break;
		}
	}

	// Set isFirstCol as true if there is any 0 in the first column of the matrix
	for(int i = 0; i < row; i++){
		if(matrix[i][0] == 0){
			isFirstCol = true;
			break;
		}
	}

	// Iterate through the array and if a 0 is found then set the first row and column of that index to zero
	for(int i = 1; i < row; i++){
		for(int j = 1; j < col; j++){
			if(matrix[i][j] == 0){
				matrix[i][0] = 0;
				matrix[0][j] = 0;
			}
		}
	}

	// Iterate though the matrix again and if the first row or the column of the that element is 0 then update the current element to 0
	for(int i = 1; i < row; i++){
		for(int j = 1; j < col; j++){
			if(matrix[i][0] == 0 || matrix[0][j] == 0){
				matrix[i][j] = 0;
			}
		}
	}

	// If isFirstRow is true then set all the elements of the first row to zero
	if(isFirstRow){
		for(int i = 0; i < col; i++){
			matrix[0][i] = 0;
		}
	}

	// If isFirstCol is true then set all the elements of the first column to zero
	if(isFirstCol){
		for(int i = 0; i < row; i++){
			matrix[i][0] = 0;
		}
	}

}

| Approach | Time Complexity | Space Complexity | | | :-: | :—: | | Not the Optimal Solution | O((mn)(m+n)) | O(1) | | Better Solution | O(m*n) | O(1) |

Sequential Digits

Link - Sequential Digits

Using Queue

public IList<int> SequentialDigits(int low, int high) {
	List<int> output = new List<int>();

	Queue<int> que = new Queue<int>();

	for(int i = 1; i <= 9; i++){
		que.Enqueue(i);
	}

	while(que.Count > 0){
		int currNum = que.Dequeue();

		if(currNum >= low && currNum <= high){
			output.Add(currNum);
		}

		int lastDigit = currNum % 10;
		int newNum = currNum * 10 + lastDigit + 1;

		if(lastDigit < 9 && newNum <= high){
			que.Enqueue(newNum);
		}
	}

	return output;
}

Sort Characters By Frequency

Link - Sort Characters By Frequency

Using Dictionary and Priority Queue

Iterate through the given array and store the count in a dictionary. Now enqueue all the values of the dictionary into a Priority Queue. Now dequeue all the items from the queue and return the output.

    public string FrequencySort(string s) {
        Dictionary<char, int> map = new Dictionary<char, int>();

        for(int i = 0; i < s.Length; i++){
            if(map.ContainsKey(s[i])){
                map[s[i]] += 1;
            }
            else{
                map[s[i]] = 1;
            }
        }

        PriorityQueue<char> pq = new PriorityQueue<char>();

        foreach(var item in map){
            pq.Enqueue(item.Value, item.Key);
        }

        char[] output = new char[s.Length];
        int pointer = 0;

        for(int i = 0; i < map.Count; i++){
            char key = pq.Dequeue();
            int val = map[key];

            for(int j = 0; j < val; j++){
                output[pointer] = key;
                pointer++;
            }
        }

        return new string(output);
    }

Merge Sorted Array

Link - Merge Sorted Array

Naive Approach

Start two pointers from the end of both the lists and push the value which is higher than the other. Keep doing this until one of the pointers reaches 0.

public void Merge(int[] nums1, int m, int[] nums2, int n) {
    int mPointer = nums1.Length - 1;
    int fPointer = m - 1;
    int sPointer = n - 1;

    while(fPointer >= 0 && sPointer >= 0){
        if(nums2[sPointer] >= nums1[fPointer]){
            nums1[mPointer] = nums2[sPointer];
            sPointer--;
        }
        else{
            nums1[mPointer] = nums1[fPointer];
            fPointer--;
        }

        mPointer--;
    }

    // This is required in certain edge cases when fPointer has reached 0th index and sPointer has still some elements that are to be moved to the num1 array
    while(sPointer >= 0){
        nums1[mPointer] = nums2[sPointer];
        sPointer--;
        mPointer--;
    }
}

Number of Operations to Make Network Connected

Link - Number of Operations to Make Network Connected

Using Depth First Search

Find the number of connected components in the given graph. count - 1 will be the number of edges that will have to be moved to make the network connected. Perform DFS on all the nodes. When a node is traversed mark it as visited. This should give us the result

public int MakeConnected(int n, int[][] connections) {
	//There should be atleat n-1 edges in the graph to make the network connected
	if(connections.Length < n-1){
		return -1;
	}

	Dictionary<int, HashSet<int>> graph = new Dictionary<int, HashSet<int>>();

	for(int i = 0; i < n; i++){
		graph[i] = new HashSet<int>();
	}

	foreach(int[] edge in connections){
		int f = edge[0];
		int t = edge[1];

		graph[f].Add(t);
		graph[t].Add(f);
	}

	int connectedComponents = 0;

	bool[] visited = new bool[n];

	for(int i = 0; i < n; i++){
		if(visited[i]){
			continue;
		}

		connectedComponents++;

		this.DFS(graph, visited, i);
	}

	return connectedComponents - 1;
}

public void DFS(Dictionary<int, HashSet<int>> graph, bool[] visited, int currNode){
	if(visited[currNode]){
		return;
	}

	visited[currNode] = true;

	foreach(int toNode in graph[currNode]){
		this.DFS(graph, visited, toNode);
	}
}

Union Find (with Path Compression)

public class Solution {
    public int MakeConnected(int n, int[][] connections) {
        //There should be atleat n-1 edges in the graph to make the network connected
        if(connections.Length < n-1){
            return -1;
        }

        UnionFind uf = new UnionFind(n);

        foreach(int[] edge in connections){
            uf.Unionize(edge[0], edge[1]);
        }

        return uf.connectedComponents - 1;
    }
}

public class UnionFind{

    int[] weight;
    int[] parent;

    public int connectedComponents;

    public UnionFind(int n){
        weight = new int[n];
        parent = new int[n];

        Array.Fill(weight, 1);

        for(int i = 0; i < n; i++){
            parent[i] = i;
        }

        connectedComponents = n;
    }

    public void Unionize(int a, int b){
        int parentA = this.Find(a);
        int parentB = this.Find(b);

        if(parentA == parentB){
            return;
        }

        if(weight[parentA] >= weight[parentB]){
            parent[parentB] = parentA;
            weight[parentA] += weight[parentB];
        }
        else{
            parent[parentA] = parentB;
            weight[parentB] += weight[parentA];
        }

        connectedComponents--;
    }

    public int Find(int a){

        int rootNode = a;

        while(rootNode != parent[rootNode]){
            rootNode = parent[rootNode];
        }

        // Applying path compression
        while(a != rootNode){
            int tempNode = parent[a];
            parent[a] = rootNode;
            a = tempNode;
        }

        return rootNode;
    }
}

Best Time to Buy and Sell Stock II

Link - Best Time to Buy and Sell Stock II

Peak Valley Approach

For the given array iteratively check until the price goes to the lowest value, that is the valley and it is the buying price. Next, iteratively check until the price goes to the peak value which will be the peak and it is selling price. The difference between the selling price and buying price will be the profit. Keep repeating this process until the end of the array to get the maximum profit.

public int MaxProfit(int[] prices) {
	int n = prices.Length;

	if(n == 0){
		return 0;
	}

	int maxProfit = 0;

	int i = 0;

	while(i < n - 1){
		while(i < n - 1 && prices[i + 1] <= prices[i]){
			i++;
		}
		int valley = prices[i];
		while(i < n - 1 && prices[i + 1] >= prices[i]){
			i++;
		}
		int peak = prices[i];

		maxProfit += peak - valley;
	}

	return maxProfit;
}

Kth Smallest Element in a BST

Link - Kth Smallest Element in a BST

In Order Traversal

Simply perform in-order traversal on the given tree and store it in a list. The kth element of that list in the required solution.

    public int KthSmallest(TreeNode root, int k) {
        List<int> temp = new List<int>();

        this.InOrderTraversal(root, temp);
        return temp[k-1];
    }

    public void InOrderTraversal(TreeNode root, List<int> temp){
        if(root.left != null){
            this.InOrderTraversal(root.left, temp);
        }

        temp.Add(root.val);

        if(root.right != null){
            this.InOrderTraversal(root.right, temp);
        }
    }

This solution has a time complexity of O(n) and space complexity of O(n).

We need not traverse the whole tree to get the result. Just traverse through k nodes of the tree to get better time and space complexity. See the next solution for this approach.

In Order Traversal (Better)

This approach is similar to the previous one, except that we need two reference variables count and output. Increment the count variable on each traversal and whenever the count matches the value k, set the value of that node to the output variable and return.

public int KthSmallest(TreeNode root, int k) {
    int ans = 0;
    int count = 0;

    this.KthSmallest(root, k, ref ans, ref count);

    return ans;
}

public void KthSmallest(TreeNode root, int k, ref int ans, ref int count){
    if(root == null){
        return;
    }

    this.KthSmallest(root.left, k, ref ans, ref count);

    count++;
    if(k == count){
        ans = root.val;
        return;
    }

    this.KthSmallest(root.right, k, ref ans, ref count);
}

This solution has a time complexity of O(k) and space complexity of O(k).

| Approach | Time Complexity | Space Complexity | | | :-: | :—: | | In Order Traversal | O(n) | O(n) | | In Order Traversal (Better) | O(k) | O(k) |

Design Browser History

Link - Design Browser History

Using Dynamic List

public class BrowserHistory {
    List<string> UrlList;
    int pointer;
    int maxForwardPointer;

    public BrowserHistory(string homepage) {
        UrlList = new List<string>();
        pointer = 0;
        maxForwardPointer = 0;
        UrlList.Add(homepage);
    }

    public void Visit(string url) {
        pointer++;
        maxForwardPointer = pointer;
        if(UrlList.Count > pointer){
            UrlList[pointer] = url;
        }
        else{
            UrlList.Add(url);
        }
    }

    public string Back(int steps) {
        pointer = Math.Max(0, pointer - steps);
        return UrlList[pointer];
    }

    public string Forward(int steps) {
        pointer = Math.Min(maxForwardPointer, pointer + steps);
        return UrlList[pointer];
    }
}

Search a 2D Matrix II

Link - Search a 2D Matrix II

Using Divide and Conquer (Time Limit exceeded)

public bool SearchMatrix(int[,] matrix, int target) {
	int rows = matrix.GetLength(0);
	int cols = matrix.GetLength(1);

	if(rows == 0 && cols == 0){
		return false;
	}

	return this.DivideAndConquer(matrix, 0, rows-1, 0, cols-1, target);
}

public bool DivideAndConquer(int[,] matrix, int rStart, int rEnd, int cStart, int cEnd, int target){
	if(rStart > rEnd || cStart > cEnd){
		return false;
	}

	if(rStart == rEnd && cStart == cEnd){
		return target == matrix[rStart,cStart] ? true : false;
	}

	int rMid = (rStart + rEnd)/2;
	int cMid = (cStart + cEnd)/2;

	return this.DivideAndConquer(matrix, rStart, rMid, cStart, cMid, target)
		|| this.DivideAndConquer(matrix, rMid+1, rEnd, cStart, cMid, target)
		|| this.DivideAndConquer(matrix, rStart, rMid, cMid+1, cEnd, target)
		|| this.DivideAndConquer(matrix, rMid+1, rEnd, cMid+1, cEnd, target);
}

Using (m+n) solution

public bool SearchMatrix(int[,] matrix, int target) {
	int row = 0;
	int col = matrix.GetLength(1)-1;

	while(col >= 0 && row < matrix.GetLength(0)){
		if(target == matrix[row,col]){
			return true;
		}
		else if(target < matrix[row,col]){
			col--;
		}
		else{
			row++;
		}
	}

	return false;
}

Using Binary Seach (mlogn or nlogm solution)

This method performs binary seach on each row(or column) to find the element.

public bool SearchMatrix(int[,] matrix, int target) {
	int rows = matrix.GetLength(0);
	int cols = matrix.GetLength(1);

	if(rows == 0  && cols == 0){
		return false;
	}

	for(int i = 0; i < rows; i++){

		int low = 0;
		int high = cols-1;

		while(low <= high){
			int mid = (low+high)/2;

			if(target == matrix[i,mid]){
				return true;
			}
			else if(target < matrix[i,mid]){
				high = mid-1;
			}
			else{
				low = mid+1;
			}
		}
	}

	return false;
}

Minimum Number of Arrows to Burst Balloons

Link - Minimum Number of Arrows to Burst Balloons

Greedy Approach

public int FindMinArrowShots(int[][] points) {
	if(points.Length == 0){
		return 0;
	}

	Array.Sort(points, (a,b)=>a[1]-b[1]);

	int currPoint = points[0][1];
	int count = 1;

	for(int i = 1; i < points.Length; i++){
		if(currPoint >= points[i][0]){
			continue;
		}

		count++;
		currPoint = points[i][1];
	}

	return count;
}

Check if the Sentence Is Pangram

Link - Check if the Sentence Is Pangram

Using HashSet

public bool CheckIfPangram(string sentence) {
	HashSet<char> set = new HashSet<char>();

	foreach(char c in sentence){
		set.Add(c);
	}

	return set.Count == 26;
}

Maximum Number of Coins You Can Get

Link - Maximum Number of Coins You Can Get

Greedy Approach

public int MaxCoins(int[] piles) {
	int n = piles.Length / 3;

	Array.Sort(piles);
	Array.Reverse(piles);

	int maxCoins = 0;

	for(int i = 0; i < n; i++){
		maxCoins += piles[(i*2) + 1];
	}

	return maxCoins;
}

Average of Levels in Binary Tree

Link - Average of Levels in Binary Tree

This problem can be solved using both Breadth First Search and Depth First Search. The most appropriate approach to solve this problem would be to use BFS, since we need an average of nodes at each level of depth, and BFS traverses at each depth level.

See the following code which enqueues the root node first into the Queue, then enqueues the left and right nodes of the root node. Keep doing this recursively until all nodes are traversed. As and when the Queue is empty find their average and store it in a list. See the below code for the solution.

public IList<double> AverageOfLevels(TreeNode root) {
    Queue<TreeNode> BFS = new Queue<TreeNode>();
    List<double> output = new List<double>();

    BFS.Enqueue(root);

    while(BFS.Count != 0){
        int levelSize = BFS.Count;
        double heightSum = 0;

        for(int i = 0; i < levelSize; i++){
            TreeNode currentNode = BFS.Dequeue();
            if(currentNode.left != null){
                BFS.Enqueue(currentNode.left);
            }
            if(currentNode.right != null){
                BFS.Enqueue(currentNode.right);
            }
            heightSum += currentNode.val;
        }
        output.Add(heightSum/levelSize);
    }
    return output.ToArray();
}

Angle Between Hands of a Clock

Link - Angle Between Hands of a Clock

Using Recursion

public double AngleClock(int hour, int minutes) {
	hour = hour % 12;
	minutes = minutes % 60;

	double hourDegrees = ((hour * 30) + (minutes * 0.5));
	double minDegrees = minutes * 6;

	double angle = Math.Abs(hourDegrees - minDegrees);

	if(angle > 180){
		return 360 - angle;
	}
	return angle;
}

Invert a binary tree

Link - Invert Binary Tree

Recursive Approach

One of the approach to invert a binary tree to use Recursive approach. In this approach a function calls itself recursively until a certain condition is met. See the following solution.

public TreeNode InvertTree(TreeNode root) {
    // This is the condition used to end recursion
    if (root == null){
        return null;
    }

    // Recursively call the InvertTree function on left and right nodes
    TreeNode left = this.InvertTree(root.left);
    TreeNode right = this.InvertTree(root.right);

    // Assign left node to right and vice versa
    root.left = right;
    root.right = left;

    return root;
}

The above solution works for a smaller binary tree. What if the tree is really large, there is a possibility of getting stack overflow error. The above solution is not really scalable.

Let’s use the iterative approach to solve the above problem.

Iterative Approach

Check if the root is null initially. Create a stack to store left and right nodes of a given node. To invert the tree pop an element from stack and exchange the left and right nodes. Also push the left and right nodes of the popped item again into the stack. Keep looping until the stack is empty. At the end you will have an inverted array.

public TreeNode InvertTree(TreeNode root) {
    if (root == null){
        return null;
    }

    Stack<TreeNode> temp = new Stack<TreeNode>();
    // Push the root element first to start the inversion of binary tree
    temp.Push(root);

    // Keep looping until the stack is empty
    while(temp.Count > 0){
        TreeNode current = temp.Pop();
        if(current != null){
            // Push the left and right nodes again to the stack
            temp.Push(current.left);
            temp.Push(current.right);
            // Typical data exchange pattern
            TreeNode temp2 = current.left;
            current.left = current.right;
            current.right = temp2;
        }
    }
    return root;
}

Questions to be asked before solving

  • Is the binary tree small. Do you want a scalable solution? This is get clarity on stack over flow we get with the first solution.
  • Am I allowed to modify the existing binary tree or do you want the original tree as well. Both the approaches above modify the original binary tree.

Count Complete Tree Nodes

Link - Count Complete Tree Nodes

Using Breadth First Search Approach

public int CountNodes(TreeNode root) {
	if(root == null){
		return 0;
	}

	Queue<TreeNode> bfsQueue = new Queue<TreeNode>();
	bfsQueue.Enqueue(root);
	int nodeCount = 0;

	while(bfsQueue.Count > 0){
		int count = bfsQueue.Count;
		nodeCount += count;

		for(int i = 0; i < count; i++){
			TreeNode tempNode = bfsQueue.Dequeue();

			if(tempNode.left != null){
				bfsQueue.Enqueue(tempNode.left);
			}

			if(tempNode.right != null){
				bfsQueue.Enqueue(tempNode.right);
			}
		}

	}

	return nodeCount;
}

Matrix Diagonal Sum

Link - Matrix Diagonal Sum

Linear Approach

public int DiagonalSum(int[][] mat) {
	int n = mat.Length;

	if(n == 0){
		return 0;
	}

	int sum = 0;

	for(int i = 0; i < n; i++){
		sum += mat[i][i] + mat[i][n-i-1];
	}

	if(n % 2 == 1){
		sum -= mat[n/2][n/2];
	}

	return sum;
}

Sum of Left Leaves

Link - Sum of Left Leaves

Using Recursion

public int SumOfLeftLeaves(TreeNode root) {
	if(root == null){
		return 0;
	}

	return this.SumOfLeftLeaves(root, false);
}

public int SumOfLeftLeaves(TreeNode root, bool isLeft){
	if(root == null){
		return 0;
	}

	if(root.left == null && root.right == null){
		return isLeft ? root.val : 0;
	}

	return this.SumOfLeftLeaves(root.left, true) + this.SumOfLeftLeaves(root.right, false);
}

Reverse Bits

Link - Reverse Bits

Using Bitwise Operations

public uint reverseBits(uint n) {
	uint output = 0;

	for(int i = 0; i < 32; i++){
		output <<= 1;
		if((n&1) == 1){
			output++;
		}
		n >>= 1;
	}

	return output;
}

Remove Duplicates from Sorted Array II

Link - Binary Search Tree Iterator

Fast and Slow pointer

public int RemoveDuplicates(int[] nums) {
	int N = nums.Length;

	if(N < 3){
		return N;
	}

	int slow = 2;
	int fast = 2;

	while(fast < N){
		if(nums[slow-2] != nums[fast]){
			nums[slow] = nums[fast];
			slow++;
		}
		fast++;
	}

	return slow;
}

Binary Tree Level Order Traversal

Link - Binary Tree Level Order Traversal

Breadth First Search

Simply performing a Breadth-First Search on the given root element will give you the required result.

public IList<IList<int>> LevelOrder(TreeNode root) {
    IList<IList<int>> output = new List<IList<int>>();

    if(root == null){
        return output;
    }

    Queue<TreeNode> bfsQueue = new Queue<TreeNode>();
    bfsQueue.Enqueue(root);

    while(bfsQueue.Count != 0){
        int levelSize = bfsQueue.Count;

        List<int> temp = new List<int>();

        for(int i = 0; i < levelSize; i++){
            TreeNode item = bfsQueue.Dequeue();
            temp.Add(item.val);

            if(item.left != null){
                bfsQueue.Enqueue(item.left);
            }

            if(item.right != null){
                bfsQueue.Enqueue(item.right);
            }
        }
        output.Add(temp);
    }
    return output;
}

Rotate Array

Link - Rotate Array

This problem can be solved in three different ways.

Brute Force Method

The brute force way of solving is to swap all the number in the array k times. The time-complexity for this solution will be O(n * k) (where n is the size of the array). Since the swapping is done in-place the space complexity is linear O(1). See the next approach which reduces the time-complexity significantly.

public void Rotate(int[] nums, int k) {
    for(int i = 0; i < k; i++){
        int temp = nums[nums.Length - 1];
        for(int j = nums.Length - 1; j > 0; j--){
            nums[j] = nums[j-1];
        }
        nums[0] = temp;
    }
}

Stack/Queue based approach

In this approach, you could use a stack, queue or a new array. Following code uses the Queue data structure. Start queueing elements from (n-k) till the end of the array. Then enqueue starting from 0 to n-k-1. The resulting queue will have the data rotated k times. Dequeue all the elements into the original array.

This approach will fail when the k is greater or equal to n. Setting the k value to k mod n will fix that. This is because rotating k times is same as rotating k mod n times.

public void Rotate(int[] nums, int k) {
    Queue<int> temp = new Queue<int>();
    k = k % nums.Length;
    int startIndex = nums.Length - k;

    for(int i = startIndex; i < nums.Length; i++){
        temp.Enqueue(nums[i]);
    }

    for(int i = 0; i < startIndex; i++){
        temp.Enqueue(nums[i]);
    }

    for(int i = 0; i < nums.Length; i++){
        nums[i] = temp.Dequeue();
    }
}

This solution has a time complexity of O(n) and space complexity of O(n) (since we need a queue or stack or array of size n). Can we do better than this? See the next method which solves it in O(1) space complexity.

Best Approach

This approach is the fastest among them all. First, it reverses the whole array then reverses the first k-1 elements then reverses the last k elements which give you the result. There is a separate method called ArraySwapper which takes start and end index along with the array. This method reverses the array based on the provided indexes.

public void Rotate(int[] nums, int k) {
    k = k % nums.Length;
    this.ArraySwapper(nums, 0, nums.Length-1);
    this.ArraySwapper(nums, 0, k - 1);
    this.ArraySwapper(nums, k, nums.Length-1);
}

public void ArraySwapper(int[] nums, int startIndex, int endIndex){
    while(startIndex < endIndex){
        int temp = nums[startIndex];
        nums[startIndex] = nums[endIndex];
        nums[endIndex] = temp;
        startIndex++;
        endIndex--;
    }
}

The time complexity is O(n) and space complexity is O(1) since everything happens in-memory.

Minimum Difference Between Largest and Smallest Value in Three Moves

Link - Minimum Difference Between Largest and Smallest Value in Three Moves

public int MinDifference(int[] nums) {
	int N = nums.Length;

	// For arrays of size less than or equal to 4, all elements can be made identical in 3 moves, so the min difference will always be zero.
	if(N <= 4){
		return 0;
	}

	Array.Sort(nums);

	int minValue = Int32.MaxValue;

	int totalMoves = 3;
	int currMove = 0;

	while(currMove <= totalMoves){
		minValue = Math.Min(minValue, nums[N-totalMoves+currMove-1] - nums[currMove]);

		currMove++;
	}

	return minValue;
}

Maximum Nesting Depth of the Parentheses

Link - Maximum Nesting Depth of the Parentheses

Naive Solution

public int MaxDepth(string s) {
	int maxSoFar = 0;
	int curr = 0;

	foreach(char c in s){
		if(c == '('){
			curr++;
		}
		else if(c == ')'){
			curr--;
		}
		maxSoFar = Math.Max(curr, maxSoFar);
	}

	return maxSoFar;
}

Count Sorted Vowel Strings

Link - Count Sorted Vowel Strings

Using Top Down Approach

public int CountVowelStrings(int n) {
	int[] vowels = new int[] {1,2,3,4,5};

	int count = 0;

	for(int i = 0; i < 5; i++){
		count += this.DP(vowels, i, n-1);
	}

	return count;

}

public int DP(int[] vowels, int currIndex, int remNum){
	if(remNum == 0){
		return 1;
	}

	if(currIndex >= vowels.Length){
		return 0;
	}

	int count = 0;

	for(int a = currIndex; a < vowels.Length; a++){
		count += this.DP(vowels, a, remNum-1);
	}

	return count;
}

Verifying an Alien Dictionary

Link - Verifying an Alien Dictionary

Using Dictionary

public bool IsAlienSorted(string[] words, string order) {
	if(words.Length <= 1){
		return true;
	}

	Dictionary<char, int> map = new Dictionary<char, int>();

	for(int i = 0; i < order.Length; i++){
		map[order[i]] = i;
	}

	for(int i = 1; i < words.Length; i++){
		int index = Math.Min(words[i-1].Length, words[i].Length);

		int flag = 0;
		for(int j = 0; j < index; j++){
			if(map[words[i-1][j]] > map[words[i][j]]){
				return false;
			}
			else if(map[words[i-1][j]] < map[words[i][j]]){
				break;
			}

			if(words[i-1][j] == words[i][j]){
				flag++;
			}
		}

		if(flag == index && words[i-1].Length > words[i].Length){
			return false;
		}
	}

	return true;
}

Linked List Random Node

Link - Linked List Random Node

Fixed Range Sampling

List<int> array = new List<int>();
Random rnd = new Random();

public Solution(ListNode head) {
	while(head != null){
		array.Add(head.val);
		head = head.next;
	}
}

/** Returns a random node's value. */
public int GetRandom() {
	int index = rnd.Next(array.Count);
	return array[index];
}

Reservoir Sampling

ListNode head;
Random rnd = new Random();

public Solution(ListNode head) {
	this.head = head;
}

/** Returns a random node's value. */
public int GetRandom() {
	int size = 1;
	int returnVal = 0;

	ListNode curr = head;

	while(curr != null){
		int index = rnd.Next(size);

		if(index == 0){
			returnVal = curr.val;
		}

		curr = curr.next;
		size++;
	}

	return returnVal;
}

Champagne Tower

Link - Champagne Tower

Using Simulation

public double ChampagneTower(int poured, int query_row, int query_glass) {
	double[,] output = new double[102,102];
	output[0,0] = (double)poured;

	for(int r = 0; r <= query_row; r++){
		for(int c = 0; c <= r; c++){
			double q = (output[r,c] - 1.0) / 2.0;

			if(q > 0){
				output[r+1,c] += q;
				output[r+1,c+1] += q;
			}
		}
	}

	return Math.Min(1, output[query_row,query_glass]);
}

Check Array Formation Through Concatenation

Link - Check Array Formation Through Concatenation

Using Dictionary

public bool CanFormArray(int[] arr, int[][] pieces) {
	Dictionary<int,int> map = new Dictionary<int,int>();

	for(int k = 0; k < pieces.Length; k++){
		map[pieces[k][0]] = k;
	}

	int i = 0;
	while(i < arr.Length){
		if(!map.ContainsKey(arr[i])){
			return false;
		}

		int pieceIndex = map[arr[i]];
		int size = pieces[pieceIndex].Length;

		int a = 0;

		while(a < size){
			if(arr[i] != pieces[pieceIndex][a]){
				return false;
			}
			a++;
			i++;
		}

	}

	return true;
}

Hamming Distance

Link - Hamming Distance

Using Bitwise Operations

Performing an XOR operation between the given two numbers will return you a new binary number with the bits set to 1 wherever the corresponding bits are different. Now take this number and AND it with 1. This will give you 1 if the LSB is 1 else it will return you zero. Add this number to a count variable. Now right shift the number by 1 bit and repeat this operation. Keep doing this until the number is zero. In the end, the count value is the Hamming Distance of the given two numbers.

public int HammingDistance(int x, int y) {
	int z = x ^ y;

	int count = 0;
	while(z != 0){
		count += z & 1;
		z = z >> 1;
	}

	return count;
}

FSorting the Sentence

Link - Sorting the Sentence

Naive Approach

public string SortSentence(string s) {
    string[] words = s.Split(' ');
    Array.Sort(words, (a,b) => a[a.Length-1] - b[b.Length-1]);

    string output = "";
    for(int i = 0; i < words.Length; i++){
        output += words[i].Substring(0, words[i].Length-1);
        if(i < words.Length-1){
            output += " ";
        }
    }

    return output;
}

Making File Names Unique

Link - Making File Names Unique

Using Dictionary

public string[] GetFolderNames(string[] names) {
	int N = names.Length;

	string[] output = new string[N];

	Dictionary<string, int> map = new Dictionary<string,int>();

	for(int i = 0; i < N; i++){
		string currName = names[i];

		if(map.ContainsKey(names[i])){
			string tempName = currName + "(" + map[currName] + ")";

			while(map.ContainsKey(tempName)){
				map[currName]++;
				tempName = currName + "(" + map[currName] + ")";
			}

			output[i] = tempName;
		}
		else{
			output[i] = currName;
			map[currName] = 1;
		}

		map[output[i]] = 1;
	}

	return output;
}

Kth Missing Positive Number

Link - Kth Missing Positive Number

Naive Solution

public int FindKthPositive(int[] arr, int k) {
	int i = 1;
	int index = 0;
	while(k > 0 && index < arr.Length){
		if(i == arr[index]){
			index++;
		}
		else{
			k--;
		}
		i++;
	}

	return i + k - 1;
}

Simplified Fractions

Link - Simplified Fractions

Using HashSet Data Structure

public IList<string> SimplifiedFractions(int n) {
	List<string> output = new List<string>();

	HashSet<double> set = new HashSet<double>();

	if(n < 2){
		return output;
	}

	for(int i = 2; i <= n; i++){
		for(int j = 1; j < i; j++){
			if(!set.Contains((double)j/(double)i)){
				set.Add((double)j/(double)i);
				output.Add(j.ToString() + "/" + i.ToString());
			}
		}
	}

	return output;
}

Usin GCD (Faster Approach)

public IList<string> SimplifiedFractions(int n) {
	List<string> output = new List<string>();

	for(int i = 2; i <= n; i++){
		for(int j = 1; j < i; j++){
			if(this.GCD(j,i) == 1){
				output.Add(j.ToString() + "/" + i.ToString());
			}
		}
	}

	return output;
}

public int GCD(int x, int y){
	if(x == 0){
		return y;
	}
	else{
		return this.GCD(y % x, x);
	}
}

Odd Even Linked List

Link - Odd Even Linked List

Use odd and even pointers starting from the head node, and update the next pointers of these nodes until the end of the list. This will give you the required result.

public ListNode OddEvenList(ListNode head) {
    if(head == null){
        return head;
    }
    if(head.next == null){
        return head;
    }

    ListNode oddHead = head;
    ListNode evenHead = head.next;
    ListNode even = head.next;

    while(evenHead != null && evenHead.next != null){
        oddHead.next = evenHead.next;
        oddHead = oddHead.next;
        evenHead.next = oddHead.next;
        evenHead = evenHead.next;
    }
    oddHead.next = even;

    return head;
}

This solution has a time complexity of O(n) and space complexity of O(1).

Running Sum of 1d Array

Link - Running Sum of 1d Array

Using Prefix Sum

public int[] RunningSum(int[] nums) {
	int N = nums.Length;

	int[] output = new int[N];

	if(N == 0){
		return output;
	}

	output[0] = nums[0];

	for(int i = 1; i < N; i++){
		output[i] = output[i-1] + nums[i];
	}

	return output;
}

Link - Binary Search

Using Binary Search

public int Search(int[] nums, int target) {
	int low = 0;
	int high = nums.Length - 1;

	while(low <= high){
		int mid = low + ((high - low)/2);

		if(nums[mid] == target){
			return mid;
		}
		else if(nums[mid] < target){
			low = mid + 1;
		}
		else{
			high = mid - 1;
		}
	}

	return -1;
}

Split Linked List in Parts

Link - Split Linked List in Parts

public ListNode[] SplitListToParts(ListNode root, int k) {
	ListNode[] output = new ListNode[k];

	if(root == null){
		return output;
	}

	ListNode temp = root;
	int length = this.Length(temp);

	int i = 0;

	ListNode prevNode = new ListNode(0);

	while(k > 0){
		int currSize = (int)Math.Ceiling(length / (double)k);
		length = length - currSize;
		k--;

		output[i] = root;

		for(int a = 0; a < currSize; a++){
			prevNode = root;
			root = root.next;
		}

		prevNode.next = null;

		i++;
	}

	return output;
}

public int Length(ListNode head){
	int count = 0;

	while(head != null){
		head = head.next;
		count++;
	}

	return count;
}

Sorting Algorithm - Insertion Sort

Problem Statement

Sort integer array using Insertion Sort

Solution

In this sorting technique, you take an element at ith index and check if that fits between 0 to i-1th index and place it where it fits. Repeat this from throughout the array to sort the array.

public static void InsertionSort(int[] arr){
	int N = arr.Length;

	for(int i = 1; i < N; i++){
		int key = arr[i];
		int j = i - 1;

		while(j >= 0 && arr[j] > key){
			arr[j+1] = arr[j];
			j--;
		}
		arr[j+1] = key;
	}
}

The above sorting technique has quadratic time complexity and constant space requirement. This sorting is useful when the input array is almost sorted, only a few elements are misplaced.

Even or Odd using Bitwise Operators

Problem Statement

Find if a given number is odd or even using bitwise operators

Solution

Simply perform an XOR operation between the given number and 1 to get the desired result. The last bit of an odd number will be one and XORing it with 1 will reduce the number by 1. And XORing an even number with 1 will increase the number by 1. Use this technique to get the required result

public bool IsEven(int num)
{
    if ((num ^ 1) == (num + 1))
    {
        return true;
    }
    else
    {
        return false;
    }
}

Minimum Height Trees

Link - Minimum Height Trees

Topological Sorting

public IList<int> FindMinHeightTrees(int n, int[][] edges) {
	List<int> output = new List<int>();

	if(n == 1){
		output.Add(0);
		return output;
	}

	int[] degree = new int[n];

	Dictionary<int, List<int>> map = new Dictionary<int, List<int>>();

	// Building graph using adjacency list
	foreach(int[] edge in edges){
		if(!map.ContainsKey(edge[0])){
			map[edge[0]] = new List<int>();
		}

		if(!map.ContainsKey(edge[1])){
			map[edge[1]] = new List<int>();
		}

		map[edge[0]].Add(edge[1]);
		map[edge[1]].Add(edge[0]);

		degree[edge[0]]++;
		degree[edge[1]]++;
	}

	Queue<int> bfsQueue = new Queue<int>();

	for(int i = 0; i < n; i++){
		if(degree[i] == 1){
			bfsQueue.Enqueue(i);
		}
	}

	while(bfsQueue.Count > 0){
		List<int> temp = new List<int>();
		int size = bfsQueue.Count;

		for(int i = 0; i < size; i++){
			int node = bfsQueue.Dequeue();
			temp.Add(node);

			foreach(int child in map[node]){
				degree[child]--;
				if(degree[child] == 1){
					bfsQueue.Enqueue(child);
				}
			}

			output = temp;
		}
	}

	return output;
}

Find the Duplicate Number

Link - Find the Duplicate Number

Using Dictionary Approach (could use Set also)

This method uses a dictionary to store the numbers. Every time a new key/value pair is to be added, check if the key already exists. If the key already exists then that itself is the duplicate number.

public int FindDuplicate(int[] nums) {
    Dictionary<int,Boolean> temp = new Dictionary<int,Boolean>();

    for(int i = 0; i < nums.Length; i++){
        if(temp.ContainsKey(nums[i])){
            return nums[i];
        }
        else{
            temp[nums[i]] = true;
        }
    }
    return -1;
}

The above method had a time complexity of O(n) and space complexity of O(n).

Using Sorting Approach

In this approach, the given number array is first sorted and then check if two adjacent items are the same.

public int FindDuplicate(int[] nums) {
    Array.Sort(nums);

    for(int i = 1; i < nums.Length; i++){
        if(nums[i] == nums[i-1]){
            return nums[i];
        }
    }

    return -1;
}

The above method has a time complexity of O(nlogn) and space complexity of O(1).

Tortoise and Hare Approach

Here we use two pointers, one moves one step at a time and the other at 2 steps at a time. Keep doing this until the values match.

public int FindDuplicate(int[] nums) {
    int slow = nums[0];
    int fast = nums[0];

    do {
        slow = nums[slow];
        fast = nums[nums[fast]];
    } while(slow != fast);

    int pt1 = nums[0];
    int pt2 = slow;

    while(pt1 != pt2){
        pt1 = nums[pt1];
        pt2 = nums[pt2];
    }

    return pt1;
}

This method has a time complexity of O(n) and space complexity of O(1).

Time and Space Complexities

ApproachTime ComplexitySpace Complexity
Dictionary ApproachO(n)O(n)
Sorting ApproachO(nlogn)O(1)
Tortoise and Hare ApproachO(n)O(1)

Minimum Depth of Binary Tree

Link - Minimum Depth of Binary Tree

Using Breadth First Search

public int MinDepth(TreeNode root) {
	if(root == null){
		return 0;
	}

	Queue<TreeNode> bfsQueue = new Queue<TreeNode>();
	bfsQueue.Enqueue(root);

	int depthCount = 0;

	while(bfsQueue.Count > 0){
		int count = bfsQueue.Count;
		depthCount++;

		for(int i = 0; i < count; i++){
			TreeNode tempNode = bfsQueue.Dequeue();

			if(tempNode.left == null && tempNode.right == null){
				return depthCount;
			}

			if(tempNode.left != null){
				bfsQueue.Enqueue(tempNode.left);
			}

			if(tempNode.right != null){
				bfsQueue.Enqueue(tempNode.right);
			}
		}
	}

	return depthCount;
}

Using Depth First Search

public int MinDepth(TreeNode root) {
	if(root == null){
		return 0;
	}

	int left = this.MinDepth(root.left);
	int right = this.MinDepth(root.right);

	if(left == 0){
		return 1 + right;
	}
	else if (right == 0){
		return 1 + left;
	}
	else{
		return 1 + Math.Min(left, right);
	}
}

Binary Search Tree Iterator

Link - Binary Search Tree Iterator

Naive Approach

List<int> nums;
int index;

public BSTIterator(TreeNode root) {
	index = 0;
	nums = new List<int>();
	this.InOrder(root);
}

private void InOrder(TreeNode root){
	if(root == null){
		return;
	}

	this.InOrder(root.left);
	this.nums.Add(root.val);
	this.InOrder(root.right);
}

public int Next() {
	index++;
	return nums[index-1];
}

public bool HasNext() {
	return index < nums.Count ? true : false;
}

Using Stack

Stack<TreeNode> st;

public BSTIterator(TreeNode root) {
	st = new Stack<TreeNode>();

	this.LeftMostInOrder(root);
}

private void LeftMostInOrder(TreeNode root){
	while(root != null){
		this.st.Push(root);
		root = root.left;
	}
}

public int Next() {
	TreeNode temp = this.st.Pop();

	if(temp.right != null){
		this.LeftMostInOrder(temp.right);
	}

	return temp.val;
}

public bool HasNext() {
	return st.Count > 0 ? true : false;
}

Recover Binary Search Tree

Link - Recover Binary Search Tree

Using InOrder Traversal

public void RecoverTree(TreeNode root) {

	TreeNode prevNode = null;
	TreeNode firstNode = null;
	TreeNode secondNode = null;

	this.InOrder(root, ref prevNode, ref firstNode, ref secondNode);

	int temp = firstNode.val;
	firstNode.val = secondNode.val;
	secondNode.val = temp;

}

public void InOrder(TreeNode currNode, ref TreeNode prevNode, ref TreeNode firstNode, ref TreeNode secondNode){
	if(currNode == null){
		return;
	}

	this.InOrder(currNode.left, ref prevNode, ref firstNode, ref secondNode);

	if(prevNode != null){
		if(prevNode.val > currNode.val){
			if(firstNode == null){
				firstNode = prevNode;
			}
			secondNode = currNode;
		}
	}

	prevNode = currNode;

	this.InOrder(currNode.right, ref prevNode, ref firstNode, ref secondNode);
}

Merge Intervals

Link - Merge Intervals

Using Sorting (Uses extra space. Using IComparable interface)

public int[][] Merge(int[][] intervals) {
	if(intervals.Length <= 1){
		return intervals;
	}

	List<Range> input = new List<Range>();
	for(int i = 0; i < intervals.Length; i++){
		input.Add(new Range(intervals[i][0], intervals[i][1]));
	}

	input.Sort();

	List<int[]> output = new List<int[]>();

	int start = input[0].start;
	int end = input[0].end;

	foreach(Range item in input){
		if(item.start <= end){
			end = Math.Max(item.end, end);
		}
		else{
			output.Add(new int[]{start, end});
			start = item.start;
			end = item.end;
		}
	}

	output.Add(new int[]{start, end});

	return output.ToArray();
}

public class Range: IComparable<Range>{
	public int start;
	public int end;

	public Range(int _start, int _end){
		this.start = _start;
		this.end = _end;
	}

	public int CompareTo(Range other){
		if(this.start < other.start){
			return -1;
		}
		else if(this.start > other.start){
			return 1;
		}
		else{
			return 0;
		}
	}
}

Using Sorting (Without extra space. Using IComparer interface)

public int[][] Merge(int[][] intervals) {
	if(intervals.Length <= 1){
		return intervals;
	}

	Array.Sort(intervals, new IntervalSort());

	List<int[]> output = new List<int[]>();

	int start = intervals[0][0];
	int end = intervals[0][1];

	foreach(int[] item in intervals){
		if(item[0] <= end){
			end = Math.Max(item[1], end);
		}
		else{
			output.Add(new int[]{start, end});
			start = item[0];
			end = item[1];
		}
	}

	output.Add(new int[]{start, end});

	return output.ToArray();
}

public class IntervalSort: IComparer<int[]>{
	public int Compare(int[] interval1, int[] interval2){
		if(interval1[0] < interval2[0]){
			return -1;
		}
		else if(interval1[0] > interval2[0]){
			return 1;
		}
		else{
			return 0;
		}
	}
}

Clean Solution

public int[][] Merge(int[][] intervals) {
	if(intervals.Length == 0){
		return intervals;
	}

	Array.Sort(intervals, (a,b) => a[0] - b[0]);

	List<int[]> result = new List<int[]>();

	int[] past = intervals[0];

	for(int i = 1; i < intervals.Length; i++){
		int[] curr = intervals[i];

		if(curr[0] <= past[1]){
			past[1] = Math.Max(curr[1], past[1]);
		}
		else{
			result.Add(past);
			past = curr;
		}
	}

	result.Add(past);

	return result.ToArray();
}

Valid Mountain Array

Link - Valid Mountain Array

Naive Approach

public bool ValidMountainArray(int[] arr) {
	if(arr.Length < 3){
		return false;
	}

	int i = 1;

	while(i < arr.Length){
		if(arr[i-1] < arr[i]){
			i++;
			continue;
		}
		else if(arr[i-1] == arr[i]){
			return false;
		}
		else{
			break;
		}

	}

	if(i == arr.Length || i == 1){
		return false;
	}

	while(i < arr.Length){
		if(arr[i-1] > arr[i]){
			i++;
			continue;
		}
		else{
			return false;
		}
	}

	return true;
}

Final Prices With a Special Discount in a Shop

Link - Final Prices With a Special Discount in a Shop

Using Brute Force Solution

public int[] FinalPrices(int[] prices) {
	int N = prices.Length;

	int[] output = new int[N];

	for(int i = 0; i < N; i++){
		output[i] = prices[i];
	}

	for(int i = 0; i < N; i++){
		for(int j = i+1; j < N; j++){
			if(prices[j] <= prices[i]){
				output[i] = prices[i] - prices[j];
				break;
			}
		}
	}

	return output;
}

Using Monotone Stack

public int[] FinalPrices(int[] prices) {
	int N = prices.Length;

	int[] output = new int[N];

	Stack<int> st = new Stack<int>();

	for(int i = N - 1; i >= 0; i--){
		while(st.Count > 0 && prices[i] < st.Peek()){
			st.Pop();
		}

		if(st.Count == 0){
			output[i] = prices[i];
		}
		else{
			output[i] = prices[i] - st.Peek();
		}

		st.Push(prices[i]);
	}

	return output;
}

This solution has a time complexity of O(n) and space complexity of O(1).

ApproachTime ComplexitySpace Complexity
Using Brute ForceO(n^2)O(1)
Using Monotone StackO(n)O(n)

Basic Calculator

Link - Basic Calculator

Using Stack

public class Solution {
    public int Calculate(string s) {
        Stack<string> stack = new Stack<string>();

        int i = 0;
        while(i < s.Length){
            if(s[i] == ' '){
                i++;
                continue;
            }

            if(s[i] != ')'){
                stack.Push(s[i].ToString());
            }
            else{
                List<string> eval = new List<string>();

                while(stack.Peek() != "("){
                    eval.Insert(0, stack.Pop());
                }

                stack.Pop();
                stack.Push(this.Evaluate(eval));
            }

            i++;
        }

        List<string> eval2 = new List<string>();
        while(stack.Count > 0){
            if(stack.Peek() == "("){
                stack.Pop();
                continue;
            }

            eval2.Insert(0, stack.Pop());
        }

        return Convert.ToInt32(this.Evaluate(eval2));
    }

    public string Evaluate(List<string> s){
        int tempEval = 0;
        bool isPlus = true;

        string nextNum = "";

        for(int i = 0; i < s.Count; i++){
            if(s[i] == "+" || s[i] == "-"){
                tempEval = isPlus ? tempEval + Convert.ToInt32(nextNum) : tempEval - Convert.ToInt32(nextNum);
                isPlus = s[i] == "+" ? true : false;

                nextNum = "";
            }
            else{
                nextNum += s[i];
            }
        }

        tempEval = isPlus ? tempEval + Convert.ToInt32(nextNum) : tempEval - Convert.ToInt32(nextNum);

        return tempEval.ToString();
    }
}

Burst Balloons

Link - Burst Balloons

Dynamic Programming

public int MaxCoins(int[] nums) {
	List<int> numList = new List<int>();
	numList.Add(1);
	numList.AddRange(nums.ToList());
	numList.Add(1);

	int N = numList.Count;

	int[,] dp = new int[N,N];

	for(int i = 1; i <= N-2; i++){
		for(int left = 1; left < N - i; left++){
			int right = left + i - 1;

			for(int j = left; j <= right; j++){
				int res = numList[left-1] * numList[j] * numList[right+1] + dp[left, j-1] + dp[j+1, right];
				dp[left, right] = Math.Max(dp[left, right], res);
			}
		}
	}

	return dp[1, N-2];
}

Longest Valid Parentheses

Link - Longest Valid Parentheses

Using Stack

public int LongestValidParentheses(string s) {
	Stack<int> stack = new Stack<int>();
	stack.Push(-1);

	int result = 0;

	for(int i = 0; i < s.Length; i++){
		if(s[i] == '('){
			stack.Push(i);
		}
		else{
			stack.Pop();
			if(stack.Count == 0){
				stack.Push(i);
			}
			else{
				result = Math.Max(result, i - stack.Peek());
			}
		}
	}

	return result;
}

Letter Case Permutation

Link - Letter Case Permutation

Breadth First Approach

public IList<string> LetterCasePermutation(string S) {
	Queue<char[]> bfsQueue = new Queue<char[]>();
	bfsQueue.Enqueue(new char[S.Length]);

	for(int i = 0; i < S.Length; i++){
		int count = bfsQueue.Count;

		for(int j = 0; j < count; j++){
			char[] temp = bfsQueue.Dequeue();

			if(Char.IsDigit(S[i])){
				temp[i] = S[i];
				bfsQueue.Enqueue(temp);
			}
			else{
				temp[i] = Char.ToLower(S[i]);
				bfsQueue.Enqueue(temp);

				char[] temp2 = (char[])temp.Clone();
				temp2[i] = Char.ToUpper(S[i]);
				bfsQueue.Enqueue(temp2);
			}
		}
	}

	List<string> output = new List<string>();

	while(bfsQueue.Count > 0){
		output.Add(new string(bfsQueue.Dequeue()));
	}

	return output;
}

Recursion (Depth First Approach)

public IList<string> LetterCasePermutation(string S) {
	List<string> output = new List<string>();

	this.LetterPerms(S, output, 0, new char[S.Length]);

	return output;
}

public void LetterPerms(string S, List<string> output, int currIndex, char[] constructed){
	if(currIndex > S.Length){
		return;
	}

	if(currIndex == S.Length){
		output.Add(new string(constructed));
		return;
	}

	if(Char.IsDigit(S[currIndex])){
		constructed[currIndex] = S[currIndex];
		this.LetterPerms(S, output, currIndex + 1, constructed);
	}
	else{
		constructed[currIndex] = Char.ToUpper(S[currIndex]);
		this.LetterPerms(S, output, currIndex + 1, constructed);
		constructed[currIndex] = Char.ToLower(S[currIndex]);
		this.LetterPerms(S, output, currIndex + 1, constructed);
	}

	return;
}

Uncrossed Lines

Link - Uncrossed Lines

Using Bottom Up Appraoch

public int MaxUncrossedLines(int[] A, int[] B) {
	int a = A.Length;
	int b = B.Length;

	int[,] dp = new int[a+1, b+1];

	for(int i = 1; i <= a; i++){
		for(int j = 1; j <= b; j++){
			if(A[i-1] == B[j-1]){
				dp[i,j] = 1 + dp[i-1,j-1];
			}
			else{
				dp[i,j] = Math.Max(dp[i,j-1], dp[i-1,j]);
			}
		}
	}

	return dp[a,b];
}

Using Bottom Up Approach (Space Optimised)

public int MaxUncrossedLines(int[] A, int[] B) {
	int a = A.Length;
	int b = B.Length;

	int[] dp = new int[b+1];

	for(int i = 1; i <= a; i++){
		int prev = 0;
		for(int j = 1; j <= b; j++){
			int curr = dp[j];
			if(A[i-1] == B[j-1]){
				dp[j] = 1 + prev;
			}
			else{
				dp[j] = Math.Max(dp[j], dp[j-1]);
			}
			prev = curr;
		}
	}

	return dp[b];
}

Valid Parentheses

Link - Valid Parentheses

This problem can be solved using the Stack data structure. Start by looping through each character in the string array. Push it to the stack when there is an opening parenthesis and pop an element from the stack and see if it a matching parenthesis. Keep repeating this to get the result.

public bool IsValid(string s) {
    Stack<char> chars = new Stack<char>();

    // Dictionary to store the opening and closing parenthesis groups
    Dictionary<char,char> dict = new Dictionary<char,char>();
    dict['{'] = '}';
    dict['('] = ')';
    dict['['] = ']';

    for(int i = 0; i< s.Length; i++){
        if(s[i] == '(' || s[i] == '[' || s[i] == '{'){
            chars.Push(s[i]);
        }
        else{
            // Checks if the stack is empty before popping an element out of the stack
            if(chars.Count == 0){
                return false;
            }
            char temp = chars.Pop();
            if(dict[temp] != s[i]){
                return false;
            }
        }
    }
    return chars.Count == 0;
}

The above solution has time complexity and space complexity of O(n).

Asteroid Collision

Link - Asteroid Collision

Using Stack

public int[] AsteroidCollision(int[] asteroids) {
	Stack<int> st = new Stack<int>();

	foreach(int curr in asteroids){
		if(curr > 0){
			st.Push(curr);
		}
		else{
			while(st.Count > 0 && st.Peek() > 0 && st.Peek() < -curr){
				st.Pop();
			}

			if(st.Count > 0 && st.Peek() == -curr){
				st.Pop();
			}
			else if(st.Count == 0 || st.Peek() < 0){
				st.Push(curr);
			}

		}
	}

	int[] result = (int[])st.ToArray();
	Array.Reverse(result);

	return result;
}

Partition List

Link - Partition List

One Pass Solution

public ListNode Partition(ListNode head, int x) {
	if(head == null){
		return head;
	}

	ListNode first = new ListNode(0);
	ListNode second = new ListNode(0);

	ListNode rootFirst = first;
	ListNode rootSecond = second;

	while(head != null){
		if(head.val < x){
			first.next = head;
			first = first.next;
		}
		else{
			second.next = head;
			second = second.next;
		}

		head = head.next;
	}

	second.next = null; // Not setting this to null will to TLE error
	first.next = rootSecond.next;

	return rootFirst.next;
}

Excel Sheet Column Title

Link - Excel Sheet Column Title

public string ConvertToTitle(int n) {
	string output = "";

	while(n > 0){
		n--;
		int val = n % 26;
		output = Convert.ToChar(65 + val) + output;
		n /= 26;
	}

	return output;
}

Course Schedule

Link - Course Schedule

Using DFS

public bool CanFinish(int numCourses, int[][] prerequisites)
{
	Dictionary<int, List<int>> map = new Dictionary<int, List<int>>();

	// Initialising the adjacency list
	for (int i = 0; i < numCourses; i++)
	{
		map[i] = new List<int>();
	}

	// Adding all the courses and it's prerequisites to the dictionary
	foreach (int[] item in prerequisites)
	{
		map[item[0]].Add(item[1]);
	}

	HashSet<int> track = new HashSet<int>();

	foreach (var item in map)
	{
		bool flag = this.DFS(map, track, item.Key);

		if (flag == false)
		{
			return false;
		}
	}

	return true;
}

public bool DFS(Dictionary<int, List<int>> map, HashSet<int> track, int courseId)
{
	// If the course id already exists then return false, since there is a circular dependency of courses
	if (track.Contains(courseId))
	{
		return false;
	}

	track.Add(courseId);

	// Recursively call the DFS function on all prerequisites of course.
	foreach (int item in map[courseId])
	{
		bool flag = this.DFS(map, track, item);

		if (flag == false)
		{
			return false;
		}
	}

	// Remove te course id from the HaseSet to backtrack
	track.Remove(courseId);

	return true;
}

Using Topological Sort (BFS) - O(V + E) time complexity

public bool CanFinish(int numCourses, int[][] prerequisites) {
	HashSet<int>[] graph = new HashSet<int>[numCourses];
	int[] inDegree = new int[numCourses];

	for(int i = 0; i < numCourses; i++){
		graph[i] = new HashSet<int>();
	}

	// Build Graph
	foreach(int[] edge in prerequisites){
		int to = edge[0];
		int from = edge[1];

		graph[from].Add(to);
		inDegree[to]++;
	}

	Queue<int> bfsQueue = new Queue<int>();
	int count = 0;

	// Add all the nodes with zero indegrees since those are courses which donot have any dependency
	for(int i = 0; i < numCourses; i++){
		if(inDegree[i] == 0){
			bfsQueue.Enqueue(i);
		}
	}

	// Perform BFS and reduce the indegree as and when the node is visited. If at a point indegree of a node reduces to zero then add it to the queue.
	while(bfsQueue.Count > 0){
		int tempNode = bfsQueue.Dequeue();

		count++;
		foreach(int subNode in graph[tempNode]){
			inDegree[subNode]--;
			if(inDegree[subNode] == 0){
				bfsQueue.Enqueue(subNode);
			}
		}
	}

	return count == numCourses;
}

Design HashMap

Link - Design HashMap

Using Array

public class MyHashMap {

    int[] store;

    public MyHashMap() {
        store = new int[1000001];
        Array.Fill(store, -1);
    }

    public void Put(int key, int value) {
        store[key] = value;
    }

    public int Get(int key) {
        return store[key];
    }

    public void Remove(int key) {
        store[key] = -1;
    }
}

Reverse String

Link - Reverse String

Using Two Pointer Technique

public void ReverseString(char[] s) {
	int startIndex = 0;
	int endIndex = s.Length - 1;

	while(startIndex < endIndex){
		char temp = s[startIndex];
		s[startIndex] = s[endIndex];
		s[endIndex] = temp;
		startIndex++;
		endIndex--;
	}
}

Product of Array Except Self

Link - Product of Array Except Self

Left and Right Product Approach (With Additional Space)

Find the product of array elements starting from left to right and store it in left array. Repeat the same from right to left and save it in the right array. Now find the product every item from left and right, that will be the required result.

public int[] ProductExceptSelf(int[] nums) {
    int N = nums.Length;

    int[] left = new int[N];
    int[] right = new int[N];
    int[] output = new int[N];

    left[0] = 1;

    for(int i = 1; i < N; i++){
        left[i] = nums[i-1] * left[i-1];
    }

    right[N-1] = 1;

    for(int i = N-2; i >= 0; i--){
        right[i] = nums[i+1] * right[i+1];
    }

    for(int i = 0; i < N; i++){
        output[i] = left[i] * right[i];
    }
    return output;
}

This solution has a time and space complexity of O(N).

Left and Right Product Approach (Without Additional Space)

This solution is similar to the previous approach except that it doesn’t require additional space two arrays.

public int[] ProductExceptSelf(int[] nums) {
        int N = nums.Length;

        int[] output = new int[N];

        output[0] = 1;

        for(int i = 1; i < N; i++){
            output[i] = nums[i-1] * output[i-1];
        }

        int right = 1;

        for(int i = N-1; i >= 0; i--){
            output[i] = output[i] * right;
            right *= nums[i];
        }

        return output;
    }

Even this solution has linear time and space complexity. The only difference between this and the previous approach is that this method doesn’t use additional space to store two different arrays.

Serialize and Deserialize BST

Link - Serialize and Deserialize BST

Using Recursion

public class Codec {
    // Encodes a tree to a single string.
    public string serialize(TreeNode root) {
        StringBuilder sb = new StringBuilder();

        this.serialize(root, sb);

        return sb.ToString();
    }

	// Each node is separated by | and empty nodes are represented as #

    public void serialize(TreeNode root, StringBuilder sb){
        if(root == null){
            sb.Append("#");
            sb.Append("|");
            return;
        }

        sb.Append(root.val);
        sb.Append("|");

        this.serialize(root.left, sb);
        this.serialize(root.right, sb);
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(string data) {
        if(data.Length == 0){
            return null;
        }

        Queue<string> que = new Queue<string>(data.Split("|"));

        return deserialize(que);
    }

    public TreeNode deserialize(Queue<string> queue){
        string str = queue.Dequeue();

        if(str == "#"){
            return null;
        }

        TreeNode root = new TreeNode(Convert.ToInt32(str));
        root.left = this.deserialize(queue);
        root.right = this.deserialize(queue);

        return root;
    }
}

Binary Tree Level Order Traversal II

Link - Binary Tree Level Order Traversal II

Using Level Order Traversal

public IList<IList<int>> LevelOrderBottom(TreeNode root) {
	IList<IList<int>> output = new List<IList<int>>();

	if(root == null){
		return output;
	}

	Queue<TreeNode> bfsQueue = new Queue<TreeNode>();
	bfsQueue.Enqueue(root);

	while(bfsQueue.Count > 0){
		int count = bfsQueue.Count;

		List<int> tempList = new List<int>();
		for(int i = 0; i < count; i++){
			TreeNode tempNode = bfsQueue.Dequeue();

			tempList.Add(tempNode.val);

			if(tempNode.left != null){
				bfsQueue.Enqueue(tempNode.left);
			}

			if(tempNode.right != null){
				bfsQueue.Enqueue(tempNode.right);
			}

		}
		output.Insert(0,tempList);
	}

	return output;
}

Flipping an Image

Link - Flipping an Image

Naive Approach

public int[][] FlipAndInvertImage(int[][] A) {
	int rows = A.Length;

	if(rows == 0){
		return A;
	}

	int cols = A[0].Length;

	for(int i = 0; i < rows; i++){
		for(int j = 0; j < (cols+1)/2; j++){
			int temp = this.Inv(A[i][j]);
			A[i][j] = this.Inv(A[i][cols-j-1]);
			A[i][cols-j-1] = temp;
		}
	}

	return A;
}

public int Inv(int a){
	return a == 1 ? 0 : 1; // Can use return a ^ 1 also
}

Check Completeness of a Binary Tree

Link - Check Completeness of a Binary Tree

Level Order Traversal

Perform a Level Order Traversal on the given binary tree. Break the loop when you find an empty node. All the nodes after that should be empty. If you find a non-empty node then it is not a complete binary tree.

public bool IsCompleteTree(TreeNode root) {
    if(root == null){
        return true;
    }

    Queue<TreeNode> bfsQueue = new Queue<TreeNode>();
    bfsQueue.Enqueue(root);

    while(bfsQueue.Count != 0){
        TreeNode tempNode = bfsQueue.Dequeue();

        if(tempNode == null){
            break;
        }
        bfsQueue.Enqueue(tempNode.left);
        bfsQueue.Enqueue(tempNode.right);
    }

    while(bfsQueue.Count != 0){
        TreeNode tempNode = bfsQueue.Dequeue();
        if(tempNode != null){
            return false;
        }
    }

    return true;
}

N-ary Tree Preorder Traversal

Link - N-ary Tree Preorder Traversal

Recursive Approach

public IList<int> Preorder(Node root) {
    List<int> output = new List<int>();

    this.Traverse(root, output);

    return output;
}

public void Traverse(Node root, List<int> output){
    if(root != null){
        output.Add(root.val);

        foreach(Node temp in root.children){
            this.Traverse(temp, output);
        }
    }
    return;
}

Can Make Arithmetic Progression From Sequence

Link - Can Make Arithmetic Progression From Sequence

Using Sorting (nlogn time)

    public bool CanMakeArithmeticProgression(int[] arr) {
        Array.Sort(arr);

        if(arr.Length <= 2){
            return true;
        }

        int diff = arr[1] - arr[0];

        for(int i = 1; i < arr.Length; i++){
            if(diff != arr[i] - arr[i-1]){
                return false;
            }
        }

        return true;
    }

Using Min and Max (linear time and space)

public bool CanMakeArithmeticProgression(int[] arr) {
	int N = arr.Length;

	if(N <= 2){
		return true;
	}

	int min = Int32.MaxValue;
	int max = Int32.MinValue;

	foreach(int item in arr){
		min = Math.Min(item, min);
		max = Math.Max(item, max);
	}

	// When all the numbers in the progression are same
	if(max == min){
		return true;
	}

	if((max-min) % (N-1) != 0){
		return false;
	}

	int d = (max - min) / (N-1);

	HashSet<int> set = new HashSet<int>();

	foreach(int item in arr){
		if(set.Contains(item) || (item-min)%d != 0){
			return false;
		}
		set.Add(item);
	}

	return true;
}

Ambiguous Coordinates

Link - Ambiguous Coordinates

Using Brute Force Approach

public IList<string> AmbiguousCoordinates(string s) {
	List<string> output = new List<string>();

	for(int i = 2; i < s.Length-1; i++){
	    foreach(string left in this.Helper(s, 1, i)){
		foreach(string right in this.Helper(s, i, s.Length-1)){
		    output.Add("(" + left + ", " + right + ")");
		}
	    }
	}

	return output;
}

public List<string> Helper(string s, int i, int j){
	List<string> output = new List<string>();

	for(int d = 1; d <= j-i; d++){
		string left = s.Substring(i, d);
		Console.WriteLine(i.ToString() + "-" + j.ToString() + "-" + d.ToString());
		string right = s.Substring(i+d, j-i-d);

		if((!left.StartsWith("0") || left.Equals("0")) && (!right.EndsWith("0"))){
		    output.Add(left + (d < j-i ? "." : "") + right);
		}
	}

	return output;
}

Find leap year

Problem Statement

Find if a given year is a leap year.

Solution

Following conditions should be met for a leap year

  • If the year is a multiple of 400
  • If the year is a multiple of 4 and not a multiple of 100
public static bool IsLeapYear(int year)
{
    if(year % 400 == 0)
    {
        return true;
    }
    else if(year % 100 == 0)
    {
        return false;
    }
    else if(year % 4 == 0)
    {
        return true;
    }
    else
    {
        return false;
    }
}

Find a Corresponding Node of a Binary Tree in a Clone of That Tree

Link - Find a Corresponding Node of a Binary Tree in a Clone of That Tree

Breadth First Search Approach

public TreeNode GetTargetCopy(TreeNode original, TreeNode cloned, TreeNode target) {

	Queue<Tuple<TreeNode, TreeNode>> bfsQueue = new Queue<Tuple<TreeNode, TreeNode>>();
	bfsQueue.Enqueue(new Tuple<TreeNode, TreeNode>(original, cloned));

	while(bfsQueue.Count > 0){
		int count = bfsQueue.Count;

		for(int i = 0; i < count; i++){
			Tuple<TreeNode, TreeNode> temp = bfsQueue.Dequeue();

			if(temp.Item1 == target){
				return temp.Item2;
			}

			if(temp.Item1.left != null){
				bfsQueue.Enqueue(new Tuple<TreeNode, TreeNode>(temp.Item1.left, temp.Item2.left));
			}

			if(temp.Item1.right != null){
				bfsQueue.Enqueue(new Tuple<TreeNode, TreeNode>(temp.Item1.right, temp.Item2.right));
			}
		}
	}

	return null;
}

Delete Operation for Two Strings

Link - Delete Operation for Two Strings

Top Down Approach (Time Limit Exceeded)

public int MinDistance(string word1, string word2) {
	int m = word1.Length;
	int n = word2.Length;

	if(m == 0 || n == 0){
		return Math.Max(m,n);
	}

	return m + n - 2 * this.MinSteps(word1, word2, m-1, n-1);
}

public int MinSteps(string word1, string word2, int m, int n){
	if(m < 0 || n < 0){
		return 0;
	}

	if(word1[m] == word2[n]){
		return 1 + this.MinSteps(word1, word2, m-1, n-1);
	}
	else{
		return Math.Max(this.MinSteps(word1, word2, m-1, n), this.MinSteps(word1, word2, m, n-1));
	}
}

Top Down Approach (using Memoization) (Time Limit Exceeded)

public int MinDistance(string word1, string word2) {
	int m = word1.Length;
	int n = word2.Length;

	if(m == 0 || n == 0){
		return Math.Max(m,n);
	}

	int[,] memo = new int[m,n];

	return m + n - 2 * this.MinSteps(word1, word2, m-1, n-1, memo);
}

public int MinSteps(string word1, string word2, int m, int n, int[,] memo){
	if(m < 0 || n < 0){
		return 0;
	}

	if(word1[m] == word2[n]){
		memo[m,n] = 1 + this.MinSteps(word1, word2, m-1, n-1, memo);
	}
	else{
		memo[m,n] = Math.Max(this.MinSteps(word1, word2, m-1, n, memo), this.MinSteps(word1, word2, m, n-1, memo));
	}

	return memo[m,n];
}

Bottom Up Approach

public int MinDistance(string word1, string word2) {
	int m = word1.Length;
	int n = word2.Length;

	int[,] dp = new int[m+1,n+1];

	for(int i = 1; i <= m; i++){
		for(int j = 1; j <= n; j++){
			if(word1[i-1] == word2[j-1]){
				dp[i,j] = 1 + dp[i-1,j-1];
			}
			else{
				dp[i,j] = Math.Max(dp[i-1,j], dp[i,j-1]);
			}
		}
	}

	return m + n - 2 * dp[m,n];
}

Bottom Up Approach (Space Optimised)

public int MinDistance(string word1, string word2) {
	int m = word1.Length;
	int n = word2.Length;

	int[] dp = new int[n+1];
	int[] prev = new int[n+1];

	for(int i = 1; i <= m; i++){
		for(int j = 1; j <= n; j++){
			if(word1[i-1] == word2[j-1]){
				dp[j] = 1 + prev[j-1];
			}
			else{
				dp[j] = Math.Max(prev[j], dp[j-1]);
			}
		}
		prev = (int[])dp.Clone();
	}

	return m + n - 2 * dp[n];
}

Last Stone Weight

Link - Last Stone Weight

Using Priority Queue

At first, enqueue all the stone weights into a Priority Queue. Now dequeue twice from the queue (which will return the top two weights from the queue). Find the difference between the two dequeued items and put it back to the queue. If the difference is zero, then it means the weights cancelled each other and that need not be enqueued. Keep iterating on this until the queue count goes below two. Now if the queue has zero items, then it means that all the stones cancelled each other and the result is zero. If not then dequeue the last element from the queue to get the required result.

public int LastStoneWeight(int[] stones) {
    PriorityQueue queue = new PriorityQueue();

    for(int i = 0; i < stones.Length; i++){
        queue.Enqueue(stones[i]);
    }

    while(queue.Count > 1){
        int num1 = queue.Dequeue();
        int num2 = queue.Dequeue();

        int diff = num1 - num2;

        if(diff != 0){
            queue.Enqueue(diff);
        }
    }

    if(queue.Count == 0){
        return 0;
    }
    else{
        return queue.Dequeue();
    }
}

Implement Stack using Linked List (using Generics)

Following are the list of abstract methods and properties associated with Stack Data Structure

  • Push() - Adds an item to the stack
  • Peek() - Returns the item at the top of the stack without removing it from the stack
  • Pop() - Returns the item at the top of the stack and removes it from the stack
  • Count - Returns the number of items in the stack

Implementation

This implementation uses the inbuilt LinkedList class available in .NET framework

Push

Adds the item at the last of the linked list

public void Push(T value)
{
	list.AddLast(value);
	stackSize++;
}

Peek

Last property of LinkedList class returns the last element in the list

public T Peek()
{
	return list.Last.Value;
}

Pop

Removes the last item in the list and returns it

public T Pop()
{
	if(stackSize != 0)
	{
		T temp = list.Last.Value;
		list.RemoveLast();
		stackSize--;

		return temp;
	}
	else
	{
		throw new Exception("Stack is Empty");
	}
}

Combine all these functions into a single class to get Stack. See the below code for the complete implementation.

public class CustomStack<T>
{
	private LinkedList<T> list;
	private int stackSize;

	public int Count { get { return stackSize; } }

	public CustomStack()
	{
		list = new LinkedList<T>();
		stackSize = 0;
	}

	public void Push(T value)
	{
		list.AddLast(value);
		stackSize++;
	}

	public T Peek()
	{
		return list.Last.Value;
	}

	public T Pop()
	{
		if(stackSize != 0)
		{
			T temp = list.Last.Value;
			list.RemoveLast();
			stackSize--;

			return temp;
		}
		else
		{
			throw new Exception("Stack is Empty");
		}
	}
}

See the following lines of code which uses the above implementation.

CustomStack<int> stack = new CustomStack<int>();

stack.Push(1);
stack.Push(2);
stack.Push(3);
stack.Pop();    // 3
stack.Peek();   // 2
stack.Pop();    // 2
stack.Pop();    // 1

Find Servers That Handled Most Number of Requests

Link - Find Servers That Handled Most Number of Requests

Naive Approach

public IList<int> BusiestServers(int k, int[] arrival, int[] load) {
	int[] busyCount = new int[k];

	int[] busyTill = new int[k];

	for(int i = 0; i < arrival.Length; i++){

		for(int j = 0; j < k; j++){
			if(arrival[i] >= busyTill[(i%k + j)%k]){
				busyTill[(i%k + j)%k] = arrival[i] + load[i];
				busyCount[(i%k + j)%k]++;
				break;
			}
		}

	}

	int maxSoFar = 0;

	foreach(int item in busyCount){
		maxSoFar = Math.Max(maxSoFar, item);
	}

	List<int> output = new List<int>();

	for(int i = 0; i < busyCount.Length; i++){
		if(busyCount[i] == maxSoFar){
			output.Add(i);
		}
	}

	return output;
}

Sliding Window Maximum

Link - Sliding Window Maximum

Using Deque (Linked List)

public int[] MaxSlidingWindow(int[] nums, int k) {
	int[] output = new int[nums.Length - k + 1];

	LinkedList<int> deque = new LinkedList<int>(); // Stores the indices of the nums array

	for(int i = 0; i < nums.Length; i++){

		// Remove if deque contains an index which is not part of the window
		if(deque.Count > 0 && deque.First.Value + k <= i){
			deque.RemoveFirst();
		}

		// Remove indices whose value is less than the current number
		while(deque.Count > 0 && nums[deque.Last.Value] <= nums[i]){
			deque.RemoveLast();
		}

		// Adds the new index as the window slides
		deque.AddLast(i);

		if(i + 1 - k >= 0){
			output[i + 1 - k] = nums[deque.First.Value];
		}
	}

	return output;
}

The kth Factor of n

Link - The kth Factor of n

Using Math

Check all the factors of n starting from 1. Whenever a factor is found decrment k. Repeat this until k is 0 to get the required result.

public int KthFactor(int n, int k) {
	int i = 1;

	while(k != 0 & i <= n){
		if(n % i == 0){
			k--;
		}
		i++;
	}

	return k == 0 ? i-1 : -1;
}

N-th Tribonacci Number

Link - N-th Tribonacci Number

Using DP

public int Tribonacci(int n) {
	int[] memo = new int[n+1];

	if(n == 0){
		return 0;
	}

	if(n <= 2){
		return 1;
	}

	memo[0] = 0;
	memo[1] = 1;
	memo[2] = 1;

	for(int i = 3; i <= n; i++){
		memo[i] = memo[i-1] + memo[i-2] + memo[i-3];
	}

	return memo[n];
}

Jump Game

Link - Possible Bipartition

Greedy Approach

public bool CanJump(int[] nums) {
	int last = nums.Length - 1;

	for(int i = nums.Length - 1; i >= 0; i--){
		if(i + nums[i] >= last){
			last = i;
		}
	}

	return last == 0;
}

Angle between the hour and minute hands

Note: This problem was taken from Daily Coding Problem #303

Problem Statement

Given a clock time in hh:mm format, determine, to the nearest degree, the angle between the hour and the minute hands.

Solution

The given input is a string so first format the data into an hour and minute hand values.

To find the hour angle first mod the hour value by 12 (this is to be done to convert the hours into 12-hour format). THe hour hand moves by 30 degrees every hour and 0.5 degrees every minute. Multiply hours and minutes to get the hour angle.

Do the same with hour values as well. Mod the hour value by 60 then multiply it by 6(minute hand moves 6 degrees every minute). This gives you the angle of the minute hand.

Difference between the hour and the minute hand gives you the angle between the two hands.

The question says find the nearest degree, so here what we do is, if the angle is greater than 180 return 360 - angle.

public static int FindAngle(string time){
    string[] formattedTime = time.Split(":");
    int hour = Int32.Parse(formattedTime[0]) % 12;
    int min = Int32.Parse(formattedTime[1]) % 60;

    double hourDegrees = ((hour * 30) + (min * 0.5));
    double minDegrees = min * 6;

    double angle = Math.Abs(hourDegrees - minDegrees);

    if(angle > 180){
        return 360 - angle;
    }
    return angle;
}

Jump Game III

Link - Jump Game III

Using DFS

public bool CanReach(int[] arr, int start) {
	HashSet<int> visited = new HashSet<int>();

	return this.DFS(arr, start, visited);
}

public bool DFS(int[] arr, int index, HashSet<int> visited){
	if(index < 0 || index >= arr.Length || visited.Contains(index)){
		return false;
	}

	if(arr[index] == 0){
		return true;
	}

	visited.Add(index);

	return this.DFS(arr, index + arr[index], visited) || this.DFS(arr, index - arr[index], visited);
}

Random Pick with Weight

Link - Random Pick with Weight

Using Cumulative Sum

Random random;
int[] cumWeight;
int N;
public Solution(int[] w) {
	N = w.Length;
	random = new Random();
	cumWeight = new int[N];
	cumWeight[0] = w[0];

	for(int i = 1; i < N; i++){
		cumWeight[i] = cumWeight[i-1] + w[i];
	}
}

public int PickIndex() {
	int index = random.Next(0, this.cumWeight[N-1]) + 1;
	int res = Array.BinarySearch(this.cumWeight, index);
	return res >= 0 ? res : -res-1;
}

Check If It Is a Straight Line

Link - Check If It Is a Straight Line

To find if a given set of points on a plane form a straight line use the standard equation, i.e y = mx + c where x, y are the coordinates, m is the slope of the line and c is constant. Applying this on two coordinates we get m = (y2 - y1)/(x2 - x1) which is m = dy/dx. For a set of points to form a straight line, the slope should remain the same so, apply this condition to all the remaining coordinates to see if the slope is the same. Use this equation (y - y1)/(x - x1) = dy/dx which is dx(y - y1) = dy(x - x1).

public bool CheckStraightLine(int[][] coordinates) {
    if(coordinates.Length <= 2){
        return true;
    }

    int x1 = coordinates[0][0];
    int y1 = coordinates[0][1];
    int x2 = coordinates[1][0];
    int y2 = coordinates[1][1];

    int dx = x2 - x1;
    int dy = y2 - y1;

    for(int i = 2; i < coordinates.Length; i++){
        int x = coordinates[i][0];
        int y = coordinates[i][1];

        if(dx * (y - y1) != dy * (x - x1)){
            return false;
        }
    }

    return true;
}

Move Zeros

Link - Move Zeros

Loop through all the elements of the array, if any element is non zero then swap it with the second pointer.

public void MoveZeroes(int[] nums) {
    int pointer = 0;

    for(int i = 0; i< nums.Length; i++){
        if(nums[i] != 0){
            int temp = nums[pointer];
            nums[pointer] = nums[i];
            nums[i] = temp;
            pointer++;
        }
    }
}

This solution has a time complexity of O(n) and space complexity of O(1).

Convert Sorted Array to Binary Search Tree

Link - Convert Sorted Array to Binary Search Tree

Using Depth First Search

public TreeNode SortedArrayToBST(int[] nums) {
	if(nums.Length == 0){
		return null;
	}

	return this.DFS(nums, 0, nums.Length - 1);
}

public TreeNode DFS(int[] nums, int startIndex, int endIndex){
	if(startIndex > endIndex){
		return null;
	}

	int midIndex = startIndex + ((endIndex - startIndex)/2);

	TreeNode tempNode = new TreeNode(nums[midIndex]);
	tempNode.left = this.DFS(nums, startIndex, midIndex-1);
	tempNode.right = this.DFS(nums, midIndex+1, endIndex);

	return tempNode;
}

Top K Frequent Elements

Link - Top K Frequent Elements

Using Dictionary and Priority Queue

Iterate through the given array and store the count in a dictionary. Now enqueue all the values of the dictionary into a Priority Queue. Now dequeue the first K elements from the queue to get the top K frequent elements.

Note: Priority Queue Implementation can be found here

public int[] TopKFrequent(int[] nums, int k) {
	Dictionary<int,int> map = new Dictionary<int,int>();

	for(int i = 0; i < nums.Length; i++){
		if(map.ContainsKey(nums[i])){
			map[nums[i]] += 1;
		}
		else{
			map[nums[i]] = 1;
		}
	}

	PriorityQueue<int> pq = new PriorityQueue<int>();

	foreach(var item in map){
		pq.Enqueue(item.Value, item.Key);
	}

	int[] output = new int[k];

	for(int i = 0; i < k; i++){
		output[i] = pq.Dequeue();
	}

	return output;
}

Maximum Points You Can Obtain from Cards

Link - Maximum Points You Can Obtain from Cards

Dynamic Programming (Time Limit Exceeded)

public int MaxScore(int[] cardPoints, int k) {
	Dictionary<int,int> memo = new Dictionary<int,int>();
	return this.MaxScore(cardPoints, 0, cardPoints.Length - 1, k, memo);
}

public int MaxScore(int[] cardPoints, int start, int end, int k, Dictionary<int,int> memo){
	if(k == 0 || start > end){
		return 0;
	}

	int memoLoc = (cardPoints.Length * start) + end;
	if(memo.ContainsKey(memoLoc)){
		return memo[memoLoc];
	}

	memo[memoLoc] = Math.Max(
		cardPoints[start] + this.MaxScore(cardPoints, start+1, end, k-1, memo),
		cardPoints[end] + this.MaxScore(cardPoints, start, end-1, k-1, memo)
	);

	return memo[memoLoc];
}

Two Pointer Approach

public int MaxScore(int[] cardPoints, int k) {
	int startPointer = -1;
	int endPointer = cardPoints.Length - k - 1;

	int sum = 0;

	for(int i = cardPoints.Length - k; i < cardPoints.Length; i++){
		sum += cardPoints[i];
	}

	int maxSum = sum;

	if(cardPoints.Length == k){
		return maxSum;
	}

	for(int i = 0; i < k; i++){
		startPointer++;
		endPointer++;

		sum = sum + cardPoints[startPointer] - cardPoints[endPointer];
		maxSum = Math.Max(maxSum, sum);
	}
	return maxSum;
}

Uncommon Words from Two Sentences

Link - Uncommon Words from Two Sentences

Using Dictionary

Split both the strings based on space. And then add it to the dictionary. This dictionary will have the required result.

public string[] UncommonFromSentences(string A, string B) {
	string[] splitA = A.Split(' ');
	string[] splitB = B.Split(' ');

	Dictionary<string, bool> output = new Dictionary<string, bool>();

	for(int i = 0; i < splitA.Length; i++){
		if(output.ContainsKey(splitA[i])){
			output[splitA[i]] = false;
		}
		else{
			output[splitA[i]] = true;
		}
	}

	for(int i = 0; i < splitB.Length; i++){
		if(output.ContainsKey(splitB[i])){
			output[splitB[i]] = false;
		}
		else{
			output[splitB[i]] = true;
		}
	}

	List<string> output2 = new List<string>();

	foreach(var item  in output){
		if(item.Value == true){
			output2.Add(item.Key);
		}
	}

	return output2.ToArray();
}

Sorting Algorithm - Bubble Sort

Problem Statement

Sort integer array using Bubble Sort

Solution

public void BubbleSort(int[] arr)
{
	for(int i = 0; i < arr.Length; i++)
	{
		for(int j = i+1; j < arr.Length; j++)
		{
			if(arr[i] > arr[j])
			{
				int temp = arr[i];
				arr[i] = arr[j];
				arr[j] = temp;
			}
		}
	}
}

The above sorting technique has quadratic time complexity

Arithmetic Subarrays

Link - Arithmetic Subarrays

Naive Approach

public IList<bool> CheckArithmeticSubarrays(int[] nums, int[] l, int[] r) {
	List<bool> output = new List<bool>();

	for(int i = 0; i < l.Length; i++){
		int[] newArray = this.SubArray(nums, l[i], r[i]);
		output.Add(this.IsArithmetic(newArray));
	}

	return output;
}

public int[] SubArray(int[] nums, int l, int r){
	int[] result = new int[r-l+1];

	Array.Copy(nums, l, result, 0, r-l+1);

	return result;
}

public bool IsArithmetic(int[] arr){
	if(arr.Length <= 2){
		return true;
	}

	Array.Sort(arr);

	int d = arr[1] - arr[0];

	for(int i = 2; i < arr.Length; i++){
		if(arr[i] - arr[i-1] != d){
			return false;
		}
	}

	return true;
}

Compare Version Numbers

Link - Compare Version Numbers

Naive Approach

public int CompareVersion(string version1, string version2) {
	List<int> ver1 = this.ConvertVersion(version1);
	List<int> ver2 = this.ConvertVersion(version2);

	if(ver1.Count > ver2.Count){
		int diff = ver1.Count - ver2.Count;
		for(int i = 0; i < diff; i++){
			ver2.Add(0);
		}
	}
	else if(ver2.Count > ver1.Count){
		int diff = ver2.Count - ver1.Count;
		for(int i = 0; i < diff; i++){
			ver1.Add(0);
		}
	}

	for(int i = 0; i < ver1.Count; i++){
		if(ver1[i] == ver2[i]){
			continue;
		}
		else if(ver1[i] > ver2[i]){
			return 1;
		}
		else{
			return -1;
		}
	}

	return 0;
}

public List<int> ConvertVersion(string version){
	string[] splits = version.Split(".");
	List<int> output = new List<int>();

	for(int i = 0; i < splits.Length; i++){
		output.Add(Convert.ToInt32(splits[i]));
	}

	return output;
}

Increasing Order Search Tree

Link - Increasing Order Search Tree

Using Recursion

TreeNode head;
public TreeNode IncreasingBST(TreeNode root) {
	TreeNode output = new TreeNode(0);
	head = output;
	this.InOrder(root);
	return output.right;
}

public void InOrder(TreeNode root){
	if(root == null){
		return;
	}

	this.InOrder(root.left);

	root.left = null;
	head.right = root;
	head = root;

	this.InOrder(root.right);
}

Jewels and Stones

Link - Jewels and Stones

Using HashSet

First, push all the jewels into a HashSet. Then loop through the stones and check if the stone exists in the HashSet. Increment the count on finding a Jewel. This count is the required result.

public int NumJewelsInStones(string J, string S) {
    HashSet<char> Jewels = new HashSet<char>();

    for(int i = 0; i < J.Length; i++){
        Jewels.Add(J[i]);
    }

    int count = 0;

    for(int i = 0; i < S.Length; i++){
        if(Jewels.Contains(S[i])){
            count++;
        }
    }

    return count;
}

Pairs of Songs With Total Durations Divisible by 60

Link - Pairs of Songs With Total Durations Divisible by 60

Two Sum Approach

public int NumPairsDivisibleBy60(int[] time) {
	int[] mod = new int[60];

	int count = 0;

	foreach(int t in time){
		int sec = t % 60;
		count += mod[(60 - sec) % 60];
		mod[sec]++;
	}

	return count;
}

Furthest Building You Can Reach

Link - Furthest Building You Can Reach

Using Top Down Approach (Time Limit Exceeded Error)

public int FurthestBuilding(int[] heights, int bricks, int ladders) {
	if(heights.Length == 1){
		return 1;
	}

	return this.Furthest(heights, 1, bricks, ladders);
}

public int Furthest(int[] heights, int currIndex, int remBricks, int remLadders){
	if(remBricks < 0 || remLadders < 0){
		return currIndex-2;
	}

	if(currIndex >= heights.Length){
		if(remBricks >= 0 && remLadders >= 0){
			return heights.Length - 1;
		}
		else{
			return 0;
		}
	}

	if(heights[currIndex] <= heights[currIndex - 1]){
		return this.Furthest(heights, currIndex+1, remBricks, remLadders);
	}
	else{
		return Math.Max(
			this.Furthest(heights, currIndex+1, remBricks - Math.Abs(heights[currIndex]-heights[currIndex-1]), remLadders),
			this.Furthest(heights, currIndex+1, remBricks, remLadders-1)
		);
	}

}

Using Priority Queue

public int FurthestBuilding(int[] heights, int bricks, int ladders)
{
	SortedSet<Tuple<int,int>> pq = new SortedSet<Tuple<int,int>>();

	for(int i = 0; i < heights.Length - 1; i++){
		int d = heights[i+1] - heights[i];

		if(d <= 0){
			continue;
		}

		pq.Add(new Tuple<int,int>(d, i));

		if(pq.Count > ladders){
			bricks -= pq.Min.Item1;
			pq.Remove(pq.Min);
		}

		if(bricks < 0){
			return i;
		}

	}

	return heights.Length - 1;
}

Partition Equal Subset Sum

Link - Partition Equal Subset Sum

Using Top-Down Approach (Time limit exceeded)

First, find the sum of the given array. If the sum is odd then the array cannot be partitioned. If the sum is even, recursively check if you can get the half of the sum using 0/1 knapsack technique.

public bool CanPartition(int[] nums) {
	int sum = 0;

	for(int i = 0; i < nums.Length; i++){
		sum += nums[i];
	}

	if(sum % 2 != 0){
		return false;
	}

	return this.CanPartition(nums, sum/2, 0);
}

public bool CanPartition(int[] nums, int remainingW, int currIndex){
	if(remainingW == 0 && currIndex == nums.Length){
		return true;
	}

	if (currIndex >= nums.Length){
		return false;
	}

	return this.CanPartition(nums, remainingW, currIndex + 1) || this.CanPartition(nums, remainingW - nums[currIndex], currIndex + 1);
}

Using Memoization

Making a few changes to the above code using memoization will significantly improve the speed of the above code.

Using Bottom-Up Approach

public bool CanPartition(int[] nums) {
	int sum = 0;

	for(int i = 0; i < nums.Length; i++){
		sum += nums[i];
	}

	if(sum % 2 != 0){
		return false;
	}

	int reqSum = sum / 2;

	int n = nums.Length;

	bool[,] memo = new bool[n + 1,reqSum + 1];

	for(int i = 0; i < n+1; i++){
		memo[i,0] = true;
	}

	for(int w = 1; w < reqSum + 1; w++){
		memo[0,w] = false;
	}

	for(int i = 1; i < n+1; i++){
		for(int w = 1; w < reqSum + 1; w++){
			if (nums[i-1] > w){
				memo[i,w] = memo[i-1,w];
			}
			else{
				memo[i,w] = memo[i-1,w] || memo[i-1,w-nums[i-1]];
			}
		}
	}

	return memo[n, reqSum];
}

Using Bottom Up (Space Optimized)

public bool CanPartition(int[] nums) {
	int sum = 0;

	for(int i = 0; i < nums.Length; i++){
		sum += nums[i];
	}

	if(sum % 2 != 0){
		return false;
	}

	int reqSum = sum / 2;

	int n = nums.Length;

	bool[] memo = new bool[reqSum + 1];

	memo[0] = true;

	for(int i = 0; i < n; i++){
		for(int w = reqSum; w > 0; w--){
			if(w >= nums[i]){
				memo[w] = memo[w] || memo[w - nums[i]];
			}
		}
	}

	return memo[reqSum];
}

XOR Operation in an Array

Link - XOR Operation in an Array

Using Linear Solution

public int XorOperation(int n, int start) {
	int output = start;

	for(int i = 1; i < n; i++){
		start += 2;
		output ^= start;
	}

	return output;
}

Maximum Number of Vowels in a Substring of Given Length

Link - Maximum Number of Vowels in a Substring of Given Length

Using Sliding Window

public int MaxVowels(string s, int k) {
	if(k > s.Length){
		return 0;
	}

	int maxCount = 0;
	int currCount = 0;

	for(int i = 0; i < k; i++){
		currCount += this.IsVowel(s, i);
	}

	maxCount = currCount;

	int index = k;

	while(index < s.Length){
		currCount += this.IsVowel(s, index) - this.IsVowel(s, index - k);
		maxCount = Math.Max(maxCount, currCount);
		index++;
	}

	return maxCount;
}

public int IsVowel(string s, int i){
	if(s[i] == 'a' || s[i] == 'e' || s[i] == 'i' || s[i] == 'o' || s[i] == 'u'){
		return 1;
	}

	return 0;
}

Dungeon Game

Link - Dungeon Game

Using Binary Search

For the given problem, the health can range between the values 1 and 10^9. So performing binary search for this given range can give us the minimum health required to save the pricess. But this solution may not be optimum. Check the next solution which uses dynamic programming.

Using Bottom Up Approach

public int CalculateMinimumHP(int[][] dungeon) {
	int rows = dungeon.Length;

	if(rows == 0){
		return 0;
	}

	int cols = dungeon[0].Length;

	int[,] dp = new int[rows, cols];

	for(int i = rows-1; i >= 0; i--){
		for(int j = cols-1; j >= 0; j--){
			// Bottom-right element
			if(i == rows-1 && j == cols - 1){
				// Min of 0 is considered whenever the health is positive, since a positive health in the future path of the knight cannot be used before that.
				dp[i,j] = Math.Min(0, dungeon[i][j]);
			}
			// Last row
			else if(i == rows-1){
				dp[i,j] = Math.Min(0, dungeon[i][j] + dp[i,j+1]);
			}
			// Last column
			else if(j == cols-1){
				dp[i,j] = Math.Min(0, dungeon[i][j] + dp[i+1,j]);
			}
			else{
				dp[i,j] = Math.Min(0, dungeon[i][j] + Math.Max(dp[i,j+1], dp[i+1,j]));
			}
		}
	}

	// plus one is added to the health since we need atleast one unit greater than the min health to survive
	return Math.Abs(dp[0,0]) + 1;
}

Pseudo-Palindromic Paths in a Binary Tree

Link - Pseudo-Palindromic Paths in a Binary Tree

Using Depth First Search

public int PseudoPalindromicPaths (TreeNode root) {
	int count = 0;
	int[] nodeValues = new int[9];

	this.DFS(root, nodeValues, ref count);
	return count;
}

public void DFS(TreeNode root, int[] nodeValues, ref int count){
	if(root == null){
		return;
	}

	nodeValues[root.val - 1]++;

	if(root.left == null && root.right == null){
		if(this.IsPseudoPalindromic(nodeValues)){
			count++;
		}
	}

	this.DFS(root.left, nodeValues, ref count);
	this.DFS(root.right, nodeValues, ref count);

	nodeValues[root.val - 1]--; // Backtracking to previous value
}

public bool IsPseudoPalindromic(int[] nodeValues){
	bool flag = true;

	for(int i = 0; i < nodeValues.Length; i++){
		if(nodeValues[i] == 0){
			continue;
		}

		if(nodeValues[i] % 2 != 0){
			if(flag == true){
				flag = false;
			}
			else{
				return false;
			}
		}
	}

	return true;
}

Knight Dialer

Link - Knight Dialer

Using Depth First Search (Time Limit Exceeded Error)

public int KnightDialer(int n) {
	int[] moveX = new int[] {1, 2, 2, 1, -1, -2, -2, -1};
	int[] moveY = new int[] {2, 1, -1, -2, -2, -1, 1, 2};

	int[,] dialer = new int[,]{{1,2,3}, {4,5,6}, {7,8,9}, {-1, 0,-1}};

	int count = 0;

	for(int i = 0; i < 4; i++){
		for(int j = 0; j < 3; j++){
			if(dialer[i,j] == -1){
				continue;
			}

			count += this.KD(i, j, n-1, moveX, moveY, dialer);
		}
	}

	return count;
}

public int KD(int i, int j, int remN, int[] moveX, int[] moveY, int[,] dialer){
	if(i < 0 || j < 0 || i >= 4 || j >= 3 || dialer[i,j] == -1){
		return 0;
	}

	if(remN == 0){
		return 1;
	}

	int count = 0;

	for(int a = 0; a < 8; a++){
		int newX = i + moveX[a];
		int newY = j + moveY[a];

		count += this.KD(newX, newY, remN-1, moveX, moveY, dialer);
	}

	return count;
}

Remove Duplicates from Sorted List II

Link - Remove Duplicates from Sorted List II

Iterative Approach

public ListNode DeleteDuplicates(ListNode head) {
	if(head == null){
		return head;
	}
	ListNode dummy = new ListNode(0);
	ListNode prevNode = dummy;
	ListNode currNode = head;
	prevNode.next = currNode;

	while(currNode != null){
		while(currNode.next != null && currNode.val == currNode.next.val){
			currNode = currNode.next;
		}

		if(prevNode.next == currNode){
			prevNode = prevNode.next;
		}
		else{
			prevNode.next = currNode.next;
		}
		currNode = currNode.next;
	}

	return dummy.next;
}

Maximum Difference Between Node and Ancestor

Link - Maximum Difference Between Node and Ancestor

Using Recursion

public class Solution {

    int maxSoFar = 0;
    public int MaxAncestorDiff(TreeNode root) {
        if(root == null){
            return 0;
        }

        this.Recurse(root, root.val, root.val);

        return maxSoFar;
    }

    public void Recurse(TreeNode root, int min, int max){
        if(root == null){
            return;
        }

        maxSoFar = Math.Max(maxSoFar, Math.Max(Math.Abs(root.val - min), Math.Abs(root.val - max)));

        int currMin = Math.Min(min, root.val);
        int currMax = Math.Max(max, root.val);

        this.Recurse(root.left, currMin, currMax);
        this.Recurse(root.right, currMin, currMax);
    }
}

Add Digits

Link - Add Digits

Naive Approach

public int AddDigits(int num) {
	int output = num;

	while(output > 9){
		output = this.FindSum(output);
	}

	return output;
}

public int FindSum(int num){
	int output = 0;

	while(num != 0){
		output += num % 10;
		num /= 10;
	}

	return output;
}

Using Congruence Formula

public int AddDigits(int num) {
	return 1 + (num - 1) % 9;
}

Sum of All Odd Length Subarrays

Link - Sum of All Odd Length Subarrays

Using Prefix Sum

public int SumOddLengthSubarrays(int[] arr) {
	int N = arr.Length;

	int[] prefixSum = new int[N+1];

	for(int i = 1; i <= N; i++){
		prefixSum[i] = prefixSum[i-1] + arr[i-1];
	}

	int result = 0;

	for(int i = 0; i < N; i++){
		for(int j = i+1; j <= N; j+=2){
			result += prefixSum[j] - prefixSum[i];
		}
	}

	return result;
}

Design Underground System

Link - Design Underground System

public class UndergroundSystem {

    Dictionary<int, Tuple<string, int>> checkInDict;
    Dictionary<string, Tuple<int, int>> checkOutDict;

    public UndergroundSystem() {
        checkInDict = new Dictionary<int, Tuple<string, int>>();
        checkOutDict = new Dictionary<string, Tuple<int, int>>();
    }

    public void CheckIn(int id, string stationName, int t) {
        checkInDict[id] = new Tuple<string, int>(stationName, t);
    }

    public void CheckOut(int id, string stationName, int t) {
        Tuple<string, int> temp = checkInDict[id];
        string route = temp.Item1 + "$" + stationName;
        int totalTime = t - temp.Item2;

        Tuple<int, int> checkOut;
        if(!checkOutDict.ContainsKey(route)){
            checkOutDict[route] = new Tuple<int, int>(0, 0);
        }

        int routeTotalTime = checkOutDict[route].Item1 + totalTime;
        int totalRoutes = checkOutDict[route].Item2 + 1;

        checkOutDict[route] = new Tuple<int, int>(routeTotalTime, totalRoutes);
    }

    public double GetAverageTime(string startStation, string endStation) {
        string route = startStation + "$" + endStation;
        Tuple<int, int> checkout = checkOutDict[route];
        return (double) checkout.Item1 / (double)checkout.Item2;
    }
}

Find All Duplicates in an Array

Link - Find All Duplicates in an Array

Approach 1

In this approach, first, we try to move all the numbers to their respective indices except for the repeated indices. Consider the following example:

[4,3,2,7,8,2,3,1]

Moving the numbers to respective indices will turn the array into:

[1,2,3,4,2,3,7,8]

Once this is constructed now iterate through this array and check if the index and their value match(here considering the index of an array starts from 1). If they don’t match then put those values in a separate array and return it.

public IList<int> FindDuplicates(int[] nums) {
    List<int> output = new List<int>();
    int n = nums.Length;

    int i = 0;

    while(i < n){
        if(nums[nums[i] - 1] != nums[i] && nums[i] - 1 != i){
            this.Swap(nums, nums[i] - 1, i);
        }
        else{
            i++;
        }
    }

    for(int j = 0; j < n; j++){
        if(nums[j] - 1 != j){
            output.Add(nums[j]);
        }
    }

    return output;
}

public void Swap(int[] arr, int i, int j){
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

Approach 2

Iterate through the given array and negate the value at the index where the current value is pointing. If you find a value which is already negated, it means that the number is repeated. Put that number in a separate list. Repeat this throughout the array to get the repeated numbers in the given array.

public IList<int> FindDuplicates(int[] nums) {
    List<int> output = new List<int>();
    int n = nums.Length;

    for(int i = 0 ; i < n; i++){
        int index = Math.Abs(nums[i]);

        if(nums[index - 1] > 0){
            nums[index - 1] = -nums[index - 1];
        }
        else{
            output.Add(index);
        }
    }

    return output;
}

The given question says that the input values are in the range 1 <= a[i] <= n. And the index of these values will be in the range 0 <= i < n. We use the properties of array indices to find the solution in the above two approaches.

Mean of Array After Removing Some Elements

Link - Mean of Array After Removing Some Elements

Naive Solution

public double TrimMean(int[] arr) {
	Array.Sort(arr);

	int size = arr.Length / 20;

	int sum = 0;
	int n = 0;

	for(int i = size; i < arr.Length - size; i++){
		sum += arr[i];
		n++;
	}

	double mean = (double)sum / (double)n;
	return mean;
}

Number of Ways Where Square of Number Is Equal to Product of Two Numbers

Link - Number of Ways Where Square of Number Is Equal to Product of Two Numbers

Using Dictionary

public int NumTriplets(int[] nums1, int[] nums2) {
	Dictionary<long, int> num1Squares = new Dictionary<long,int>();
	Dictionary<long, int> num2Squares = new Dictionary<long,int>();

	foreach(int num in nums1){
		long square = (long)num * (long)num;
		if(!num1Squares.ContainsKey(square)){
			num1Squares[square] = 0;
		}

		num1Squares[square] += 1;
	}

	foreach(int num in nums2){
		long square = (long)num * (long)num;
		if(!num2Squares.ContainsKey(square)){
			num2Squares[square] = 0;
		}

		num2Squares[square] += 1;
	}

	int count = 0;

	for(int i = 0; i < nums1.Length; i++){
		for(int j = i+1; j < nums1.Length; j++){
			long prod = (long)nums1[i] * (long)nums1[j];
			if(num2Squares.ContainsKey(prod)){
				count += num2Squares[prod];
			}
		}
	}

	for(int i = 0; i < nums2.Length; i++){
		for(int j = i+1; j < nums2.Length; j++){
			long prod = (long)nums2[i] * (long)nums2[j];
			if(num1Squares.ContainsKey(prod)){
				count += num1Squares[prod];
			}
		}
	}

	return count;
}

Range Sum of BST

Link - Range Sum of BST

Depth First Search

public int RangeSumBST(TreeNode root, int L, int R) {
	if(root == null){
		return 0;
	}

	if(root.val >= L && root.val <= R){
		return root.val + this.RangeSumBST(root.left, L, R) + this.RangeSumBST(root.right, L, R);
	}
	else{
		return this.RangeSumBST(root.left, L, R) + this.RangeSumBST(root.right, L, R);
	}
}

Surrounded Regions

Link - Surrounded Regions

Using Depth First Search

public void Solve(char[][] board) {
	int rows = board.Length;

	if(rows == 0){
		return;
	}

	int cols = board[0].Length;

	bool[,] visited = new bool[rows,cols];

	// Direction vectors for the DFS to traverse
	int[] dirR = new int[]{0,0,1,-1};
	int[] dirC = new int[]{1,-1,0,0};

	// Search through all the O's on the border of the matrix and perform DFS on all the borders where it is O
	for(int i = 0; i < rows; i++){
		for(int j = 0; j < cols; j++){
			if(i == 0 || j == 0 || i == rows-1 || j == cols-1){
				if(board[i][j] == 'O' && visited[i,j] == false){
					this.BorderDFS(board, visited, i, j, rows, cols, dirR, dirC);
				}
			}
		}
	}

	// Turn all the O's to X which are not visited
	for(int i = 0; i < rows; i++){
		for(int j = 0; j < cols; j++){
			if(i > 0 && j > 0 && i < rows && j < cols){
				if(board[i][j] == 'O' && visited[i,j] == false){
					board[i][j] = 'X';
				}
			}
		}
	}
}

public void BorderDFS(char[][] board, bool[,] visited, int i, int j, int rows, int cols, int[] dirR, int[] dirC){
	if(i < 0 || j < 0 || i >= rows || j >= cols || visited[i,j] == true || board[i][j] == 'X'){
		return;
	}

	visited[i,j] = true;

	for(int a = 0; a < dirR.Length; a++){
		this.BorderDFS(board, visited, i + dirR[a], j + dirC[a], rows, cols, dirR, dirC);
	}
}

Pascal’s Triangle II

Link - Pascal’s Triangle II

public IList<int> GetRow(int rowIndex) {
	if(rowIndex < 1){
		return new int[]{1};
	}

	int[] output = new int[rowIndex+1];
	int[] temp = new int[rowIndex+1];

	output[0] = 1;
	output[1] = 1;

	for(int i = 1; i < rowIndex; i++){
		int index = 0;
		int maxIndex = i;
		Array.Copy(output, temp, rowIndex+1);

		output[0] = 1;
		output[maxIndex+1] = 1;

		while(maxIndex > 0){
			output[maxIndex] = temp[maxIndex] + temp[maxIndex-1];
			maxIndex--;
		}
	}

	return output.ToList();
}

Find the range of a logical expression

Problem Statement

Given an expression containing only ’<’, ’>’ comparison operators and ‘and’ and ‘or’ logical operators. Find the range of numbers that are valid for the given logical expression.

Solution

public class Program
{
	static void Main(string[] args)
	{
		// Given input expression that is to be solved
		string input = "x>0 and x<5";

		string[] inputSplits = input.Split(' ');

		List<Range> expressions = new List<Range>();
		List<string> operations = new List<string>();

		// Even indexes will have the expression and odd indices will have the operand type
		for(int i = 0; i < inputSplits.Length; i++)
		{
			if(i%2 == 0)
			{
				expressions.Add(new Range(inputSplits[i]));
			}
			else
			{
				operations.Add(inputSplits[i]);
			}
		}

		Range currentRange = expressions[0];

		for(int i = 0; i < operations.Count; i++)
		{
			if(operations[i] == "and")
			{
				currentRange = SolveExpression.OperationAND(currentRange, expressions[i + 1]);
			}
			else if(operations[i] == "or")
			{
				currentRange = SolveExpression.OperationOR(currentRange, expressions[i + 1]);
			}
		}

		Console.WriteLine(currentRange.RangeStart.ToString() + " to " + currentRange.RangeEnd.ToString());
	}
}

public class Range
{
	int rangeStart;
	int rangeEnd;

	public int RangeStart {
		get { return rangeStart; }
	}
	public int RangeEnd
	{
		get { return rangeEnd; }
	}

	public Range(string expression)
	{
		// Extracting operand type and the number
		int indexOfLogicalOperator = Math.Max(expression.IndexOf('<'), expression.IndexOf('>'));
		char expType = expression[indexOfLogicalOperator];
		int num = Convert.ToInt32(expression.Substring(indexOfLogicalOperator + 1, expression.Length - indexOfLogicalOperator - 1));

		if(expType == '<')
		{
			this.rangeStart = Int32.MinValue;
			this.rangeEnd = num - 1;

		}
		else if (expType == '>')
		{
			this.rangeStart = num + 1;
			this.rangeEnd = Int32.MaxValue;
		}
	}

	public Range(int start, int end)
	{
		this.rangeStart = start;
		this.rangeEnd = end;
	}
}

public static class SolveExpression
{
	public static Range OperationAND(Range range1, Range range2)
	{
		int tempStart = Math.Max(range1.RangeStart, range2.RangeStart);
		int tempEnd = Math.Min(range1.RangeEnd, range2.RangeEnd);

		if(tempStart <= tempEnd)
		{
			return new Range(tempStart, tempEnd);
		}

		return new Range(0,0);
	}

	public static Range OperationOR(Range range1, Range range2)
	{
		int tempStart = Math.Min(range1.RangeStart, range2.RangeStart);
		int tempEnd = Math.Max(range1.RangeEnd, range2.RangeEnd);

		if (tempStart <= tempEnd)
		{
			return new Range(tempStart, tempEnd);
		}

		return new Range(0, 0);
	}
}

Rotate Image

Link - Rotate Image

Transpose and Rotate Approach

In this approach find the transpose of the matrix first and then mirror image the matrix vertically.

public void Rotate(int[][] matrix) {
    int N = matrix.Length;

    // Transpose the matrix
    for(int i = 0; i < N; i++){
        for(int j = i; j < N; j++){
            int temp = matrix[i][j];
            matrix[i][j] = matrix[j][i];
            matrix[j][i] = temp;
        }
    }

    // Swap the matrix vertically
    for(int i = 0; i < N; i++){
        for(int j = 0; j < N/2; j++){
            int temp = matrix[i][j];
            matrix[i][j] = matrix[i][N - j - 1];
            matrix[i][N - j - 1] = temp;
        }
    }
}

In-Place Rotation Method

Here we start swapping elements starting from the outer most layer. Keep doing this till the center, to get the rotated image.

public void Rotate(int[][] matrix) {
    int N = matrix.Length;

    for(int i = 0; i < N / 2; i++){
        for(int j = i; j < N - i - 1; j++){
            int temp = matrix[N-j-1][i];
            matrix[N-j-1][i] = matrix[N-i-1][N-j-1];
            matrix[N-i-1][N-j-1] = matrix[j][N-i-1];
            matrix[j][N-i-1] = matrix[i][j];
            matrix[i][j] = temp;
        }
    }
}

Minimum Time Visiting All Points

Link - Minimum Time Visiting All Points

Using Iteration

public int MinTimeToVisitAllPoints(int[][] points) {
	if(points.Length == 0){
		return 0;
	}

	int output = 0;

	for(int i = 1; i < points.Length; i++){
		output += Math.Max(Math.Abs(points[i][0] - points[i-1][0]), Math.Abs(points[i][1] - points[i-1][1]));
	}

	return output;
}

Implement Priority Queue with Generics (Max Heap)

This article only has the code to the generic implementation of the Priority Queue using Max Heap. Read this article here which explains how the priority queue is implemented from the ground up.

public class KeyValuePair<T>
{
	public int Key;
	public T Value;

	public KeyValuePair(int _key, T _value)
	{
		this.Key = _key;
		this.Value = _value;
	}
}

public class PriorityQueue<T>
{
	List<KeyValuePair<T>> queue = null;
	int heapSize = -1;
	public int Count { get { return queue.Count; } }

	public PriorityQueue()
	{
		queue = new List<KeyValuePair<T>>();
	}

	private int LeftChild(int i)
	{
		return (i * 2) + 1;
	}

	private int RightChild(int i)
	{
		return (i * 2) + 2;
	}

	private void Swap(int i, int j)
	{
		KeyValuePair<T> temp = queue[i];
		queue[i] = queue[j];
		queue[j] = temp;
	}

	private void MaxHeapify(int i)
	{
		int left = this.LeftChild(i);
		int right = this.RightChild(i);

		int highest = i;

		if (left <= heapSize && queue[highest].Key < queue[left].Key)
		{
			highest = left;
		}

		if (right <= heapSize && queue[highest].Key < queue[right].Key)
		{
			highest = right;
		}

		if (highest != i)
		{
			this.Swap(highest, i);
			this.MaxHeapify(highest);
		}
	}

	private void BuildMaxHeap(int i)
	{
		while (i >= 0 && queue[(i - 1) / 2].Key < queue[i].Key)
		{
			this.Swap(i, (i - 1) / 2);
			i = (i - 1) / 2;
		}
	}

	public void Enqueue(int _key, T _value)
	{
		KeyValuePair<T> temp = new KeyValuePair<T>(_key, _value);
		queue.Add(temp);
		heapSize++;
		this.BuildMaxHeap(heapSize);
	}

	public T Dequeue()
	{
		if (heapSize > -1)
		{
			T returnVal = queue[0].Value;
			queue[0] = queue[heapSize];
			queue.RemoveAt(heapSize);
			heapSize--;
			this.MaxHeapify(0);
			return returnVal;
		}
		else
		{
			throw new Exception("Queue is empty");
		}
	}
}

See the following lines of code which uses the above implementation.

PriorityQueue<string> queue = new PriorityQueue<string>();
queue.Enqueue(5, "a");
queue.Enqueue(3, "abcd");
queue.Enqueue(12, "xyz");
queue.Enqueue(1, "q");
queue.Dequeue(); // xyz
queue.Dequeue(); // a
queue.Dequeue(); // abcd
queue.Dequeue(); // q

Additional Notes

The above implemenation is using Max Heap. For Min heap implementation following are the functions that will have to be changed.

MinHeapify

private void MinHeapify(int i)
{
	int left = this.LeftChild(i);
	int right = this.RightChild(i);

	int lowest = i;

	if (left <= heapSize && queue[lowest].Key > queue[left].Key)
	{
		lowest = left;
	}

	if (right <= heapSize && queue[lowest].Key > queue[right].Key)
	{
		lowest = right;
	}

	if (lowest != i)
	{
		this.Swap(lowest, i);
		this.MinHeapify(lowest);
	}
}

Build Min Heap

private void BuildMinHeap(int i)
{
	while (i >= 0 && queue[(i - 1) / 2].Key > queue[i].Key)
	{
		this.Swap(i, (i - 1) / 2);
		i = (i - 1) / 2;
	}
}

Jump Game IV

Link - Jump Game IV

Breadth First Search

public int MinJumps(int[] arr) {
	Dictionary<int, List<int>> graph = new Dictionary<int, List<int>>();

	for(int i = arr.Length - 1; i >= 0; i--){
		int x = arr[i];
		if(!graph.ContainsKey(x)){
			graph[x] = new List<int>();
		}
		graph[x].Add(i);
	}

	bool[] visited = new bool[arr.Length];

	Queue<int> bfsQueue = new Queue<int>();
	bfsQueue.Enqueue(0);
	int depth = 0;

	while(bfsQueue.Count > 0){
		int count = bfsQueue.Count;

		for(int i = 0; i < count; i++){
			int temp = bfsQueue.Dequeue();

			if(temp == arr.Length - 1){
				return depth;
			}

			if(visited[temp] == true){
				continue;
			}

			visited[temp] = true;

			List<int> next = graph[arr[temp]];

			if(temp - 1 >= 0){
				next.Add(temp - 1);
			}

			if(temp + 1 < arr.Length){
				next.Add(temp + 1);
			}

			foreach(int item in graph[arr[temp]]){
				bfsQueue.Enqueue(item);
			}
			next.Clear();
		}

		depth++;
	}

	return arr.Length - 1;
}

Smallest Subtree with all the Deepest Nodes

Link - Smallest Subtree with all the Deepest Nodes

Recursion

public class Solution {
    public TreeNode SubtreeWithAllDeepest(TreeNode root) {
        if(root == null){
            return null;
        }

        return this.FindDepth(root, 0).key;
    }

    private Pair FindDepth(TreeNode root, int depth){
        if(root == null){
            return new Pair(null, depth);
        }

        Pair left = this.FindDepth(root.left, depth+1);
        Pair right = this.FindDepth(root.right, depth+1);

        if(left.value > right.value){
            return left;
        }
        else if(left.value < right.value){
            return right;
        }
        else{
            return new Pair(root, left.value);
        }
    }
}

public class Pair{
    public TreeNode key;
    public int value;

    public Pair(TreeNode key, int value){
        this.key = key;
        this.value = value;
    }
}

Maximum Depth of Binary Tree

Link - Maximum Depth of Binary Tree

Breadth First Search

In breadth-first technique, a queue is maintained which stores all the nodes of a particular depth at one point. Iteratively loop through each depth levels of the binary tree. Keep doing this until the queue is empty(i.e. all the nodes of that depth do not have any child nodes).

public int MaxDepth(TreeNode root) {
    if(root == null){
        return 0;
    }

    Queue<TreeNode> temp = new Queue<TreeNode>();
    temp.Enqueue(root);

    int depth = 0;

    while(temp.Count != 0){
        depth++;
        int size = temp.Count;
        for(int i = 0; i < size; i++){
            TreeNode node = temp.Dequeue();
            if(node.left != null){
                temp.Enqueue(node.left);
            }
            if(node.right != null){
                temp.Enqueue(node.right);
            }
        }
    }

    return depth;
}

Depth First Search

Recursively call the MaxDepth method on the left and right nodes of a node. Keep adding the 1 during this process. This will return the maximum depth of the given binary tree.

public int MaxDepth(TreeNode root) {
    if(root != null){
        return 1 + Math.Max(this.MaxDepth(root.left), this.MaxDepth(root.right));
    }
    else{
        return 0;
    }
}

Maximum Profit of Operating a Centennial Wheel

Link - Maximum Profit of Operating a Centennial Wheel

Greedy Approach

public int MinOperationsMaxProfit(int[] customers, int boardingCost, int runningCost) {
	int N = customers.Length;

	int roundCount = 1;
	int maxProfit = 0;
	int result = -1;

	int remCustomers = 0;
	int totalCustomers = 0;

	foreach(int cust in customers){
		remCustomers += cust;

		int currCustomer = Math.Min(remCustomers, 4);

		remCustomers -= currCustomer;
		totalCustomers += currCustomer;

		int currProfit = (boardingCost * totalCustomers) - (runningCost * roundCount);

		if(currProfit > maxProfit){
			maxProfit = currProfit;
			result = roundCount;
		}

		roundCount++;
	}

	while(remCustomers > 0){
		int currCustomer = Math.Min(remCustomers, 4);

		remCustomers -= currCustomer;
		totalCustomers += currCustomer;

		int currProfit = (boardingCost * totalCustomers) - (runningCost * roundCount);

		if(currProfit > maxProfit){
			maxProfit = currProfit;
			result = roundCount;
		}

		roundCount++;
	}

	return result;
}

Greddy Approach (Improved Code)

public int MinOperationsMaxProfit(int[] customers, int boardingCost, int runningCost) {
	int N = customers.Length;

	int roundCount = 1;
	int maxProfit = 0;
	int result = -1;

	int remCustomers = 0;
	int totalCustomers = 0;

	int i = 0;

	while(i < N || remCustomers > 0){
		if(i < N){
			remCustomers += customers[i];
			i++;
		}

		int currCustomer = Math.Min(remCustomers, 4);

		remCustomers -= currCustomer;
		totalCustomers += currCustomer;

		int currProfit = (boardingCost * totalCustomers) - (runningCost * roundCount);

		if(currProfit > maxProfit){
			maxProfit = currProfit;
			result = roundCount;
		}

		roundCount++;
	}

	return result;
}

Find intersection of two unsorted arrays

Link - Intersection of two arrays -2

Naive Approach

The brute force way to solve this problem is loop through every element of the first array and check if it exists in the second array.

public int[] Intersect(int[] nums1, int[] nums2) {
      List<int> intersection = new List<int>();

      for (int i = 0 ; i < nums1.Length ; i++){
          if(nums2.Contains(nums1[i])){
              intersection.Add(nums1[i]);
          }
      }

      return intersection.ToArray();
  }

Time complexity - O(m * n) where m and n are size of the two arrays

The above solution works in most of the cases, but fails during some of the cases. Suppose the arrays are [1,1] and [1] then the expected output is [1] but the above code returns [1,1].

Lets try a more time-efficient and fool proof solution.

Two Pointer Approach

In this approach we will have two pointers (i.e. array index pointers) and loop through the sorted arrays.

public int[] Intersect(int[] nums1, int[] nums2) {
    List<int> intersection = new List<int>();

    // Initialize both the pointers to zero
    int pt1 = 0;
    int pt2 = 0;

    // Sort both the arrays
    Array.Sort(nums1); // O(nlogn)
    Array.Sort(nums2); // O(mlogm)

    while(true){ // O(m+n)
        // breaks the while loop if the any one of the array pointer exceeds the array boundary
        if(pt1 > nums1.Length - 1 || pt2 > nums2.Length - 1){
            break;
        }

        if(nums1[pt1] < nums2[pt2]){
            pt1++;
        }
        else if (nums1[pt1] > nums2[pt2]){
            pt2++;
        }
        else{
            intersection.Add(nums1[pt1]);
            pt1++;
            pt2++;
        }
    }
    return intersection.ToArray();
}

In the above solution, I am sorting both the arrays first and then using the two-pointer technique to find the intersection. Total time complexity is O(nlogn) + O(mlogm) + O(m+n)

Dictionary Approach (Best Solution)

In this approach, loop through the one of the array and add them to a dictionary along with the count. Then loop again through the second array along with decrementing the count. At the end tou will get the intersection of the two arrays.

public int[] Intersect(int[] nums1, int[] nums2) {
    Dictionary<int, int> count = new Dictionary<int,int>();
    List<int> intersection = new List<int>();

    for (int i = 0; i < nums1.Length; i++){
        // If the number already exists then increment it by one
        if (count.ContainsKey(nums1[i])){

            count[nums1[i]] += 1;
        }
        else{
            count[nums1[i]] = 1;
        }
    }

    for (int i = 0 ; i < nums2.Length; i++){
      // If the key exists then decrement the count and add it to the intersection array
        if (count.ContainsKey(nums2[i]) && count[nums2[i]] > 0){
            intersection.Add(nums2[i]);
            count[nums2[i]] -= 1;
        }
    }

    return intersection.ToArray();
}

The dictionary based approach is the fastest among the all, since this approach doesn’t require the arrays to be sorted. The total time complexity of this approach is O(m + n) where m and n are the size of the arrays.

Queue Reconstruction by Height

Link - Queue Reconstruction by Height

Using Custom Sort

First, sort the given array in the decreasing order of their heights. If two have same heights then sort them in the ascending order of k. After sorting, iterate through the array and insert them in the correct order based on their index value. This should give you the required result.

public class Solution {
    public int[][] ReconstructQueue(int[][] people) {
        Array.Sort(people, new CustomComparator());

        List<int[]> output = new List<int[]>();

        foreach(int[] item in people){
            output.Insert(item[1], item);
        }

        return output.ToArray();
    }
}

public class CustomComparator: IComparer<int[]> {
    public int Compare(int[] index1, int[] index2){
        if(index1[0] == index2[0]){
            return index1[1] - index2[1];
        }
        else{
            return index2[0] - index1[0];
        }
    }
}

Redundant Connection

Link - Redundant Connection

Using DFS (Detecting cycles in the graph)

Before adding an edge to the graph, perform a DFS and check if there is a cycle in the graph. If there is a cycle save that edge in a temporary variable. Repeat this for all the edges to get the required solution.

public int[] FindRedundantConnection(int[][] edges) {
	Dictionary<int, HashSet<int>> graph = new Dictionary<int, HashSet<int>>();

	int N = edges.Length; // Number of nodes

	for(int i = 1; i <= N; i++){
		graph[i] = new HashSet<int>();
	}

	int[] output = new int[2];

	foreach(int[] edge in edges){
		int u = edge[0];
		int v = edge[1];

		if(this.DFS(graph, u, v, new HashSet<int>())){
			output = edge;
		}

		graph[u].Add(v);
		graph[v].Add(u);
	}

	return output;
}

public bool DFS(Dictionary<int, HashSet<int>> graph, int u, int v, HashSet<int> visited){
	if(u == v){
		return true;
	}

	if(visited.Contains(u)){
		return false;
	}

	visited.Add(u);

	foreach(int node in graph[u]){
		bool flag = this.DFS(graph, node, v, visited);

		if(flag){
			return true;
		}
	}

	return false;
}

Using Union Find (with Path Compression)

public class Solution {
    public int[] FindRedundantConnection(int[][] edges) {
        int nodes = edges.Length;

        UnionFind uf = new UnionFind(nodes);

        foreach(int[] edge in edges){
            int u = edge[0] - 1; // Adjusting to 0-indexing
            int v = edge[1] - 1;

            if(!uf.Unionize(u, v)){
                return edge;
            }
        }

        return new int[]{0, 0};
    }
}

public class UnionFind{

    int[] weight;
    int[] parent;

    public int connectedComponents;

    public UnionFind(int n){
        weight = new int[n];
        parent = new int[n];

        Array.Fill(weight, 1);

        for(int i = 0; i < n; i++){
            parent[i] = i;
        }

        connectedComponents = n;
    }

    public bool Unionize(int a, int b){
        int parentA = this.Find(a);
        int parentB = this.Find(b);

        if(parentA == parentB){
            return false;
        }

        if(weight[parentA] >= weight[parentB]){
            parent[parentB] = parentA;
            weight[parentA] += weight[parentB];
        }
        else{
            parent[parentA] = parentB;
            weight[parentB] += weight[parentA];
        }

        connectedComponents--;

        return true;
    }

    public int Find(int a){

        int rootNode = a;

        while(rootNode != parent[rootNode]){
            rootNode = parent[rootNode];
        }

        // Applying path compression
        while(a != rootNode){
            int tempNode = parent[a];
            parent[a] = rootNode;
            a = tempNode;
        }

        return rootNode;
    }
}

Rearrange Words in a Sentence

Link - Rearrange Words in a Sentence

Naive Method

    public string ArrangeWords(string text) {
        if(text.Length == 0){
            return "";
        }

        Dictionary<int, List<string>> map = new Dictionary<int, List<string>>();
        List<int> temp = new List<int>();

        string[] words = text.ToLower().Split(' ');

        foreach(string word in words){
            int len = word.Length;
            if(!map.ContainsKey(len)){
                map[len] = new List<string>();
                temp.Add(len);
            }
            map[len].Add(word);
        }

        int[] lengths = temp.ToArray();
        Array.Sort(lengths);

        StringBuilder output = new StringBuilder();

        foreach(int length in lengths){
            for(int i = 0; i < map[length].Count; i++){
                output.Append(map[length][i] + " ");
            }
        }

        output.Remove(output.Length - 1, 1);

        char c = Char.ToUpper(output[0]);
        output.Remove(0,1);
        output.Insert(0, c);

        return output.ToString();
    }

Permutations

Link - Permutations

Using Backtracking

public IList<IList<int>> Permute(int[] nums) {
	IList<IList<int>> output = new List<IList<int>>();

	List<int> tempList = new List<int>();

	this.Backtrack(output, nums, tempList);

	return output;
}

public void Backtrack(IList<IList<int>> output, int[] nums, List<int> tempList){
	if(tempList.Count == nums.Length){
		output.Add(new List<int>(tempList));
		return;
	}

	for(int i = 0; i < nums.Length; i++){
		if(tempList.Contains(nums[i])){
			continue;
		}

		tempList.Add(nums[i]);
		this.Backtrack(output, nums, tempList);
		tempList.RemoveAt(tempList.Count - 1);
	}
}

Using Backtracking + HashSet (Time improved)

public IList<IList<int>> Permute(int[] nums) {
	IList<IList<int>> output = new List<IList<int>>();

	List<int> tempList = new List<int>();
	HashSet<int> used = new HashSet<int>();

	this.Backtrack(output, nums, tempList, used);

	return output;
}

public void Backtrack(IList<IList<int>> output, int[] nums, List<int> tempList, HashSet<int> used){
	if(tempList.Count == nums.Length){
		output.Add(new List<int>(tempList));
		return;
	}

	for(int i = 0; i < nums.Length; i++){
		if(used.Contains(nums[i])){
			continue;
		}

		tempList.Add(nums[i]);
		used.Add(nums[i]);

		this.Backtrack(output, nums, tempList, used);

		tempList.RemoveAt(tempList.Count - 1);
		used.Remove(nums[i]);
	}
}

Largest Perimeter Triangle

Link - Largest Perimeter Triangle

For a triangle to be valid, the value of the longest side of the triangle must be less than the sum of the other two sides of the triangle i.e, c < a + b. Use this condition to find the triangle with the highest perimeter.

First, sort the given input, then starting from the end of the array, check if the last three values of the array satisfy the above condition. Keep doing this until you find the numbers which satisfy the condition.

public int LargestPerimeter(int[] A) {

    if(A.Length < 3){
        return 0;
    }

    Array.Sort(A);

    for(int i = A.Length - 1; i >= 2; i-- ){
        if(A[i-1] + A[i-2] > A[i]){
            return A[i] + A[i-1] + A[i-2];
        }
    }

    return 0;
}

Find XOR Sum of All Pairs Bitwise AND

Link - Find XOR Sum of All Pairs Bitwise AND

Using Distributive Property

public int GetXORSum(int[] arr1, int[] arr2) {
	int xorA = 0;
	int xorB = 0;

	foreach(int num in arr1){
		xorA ^= num;
	}

	foreach(int num in arr2){
		xorB ^= num;
	}

	return xorA & xorB;
}

Add Two Numbers II

Link - Add Two Numbers II

Using Stacks

First, traverse through the list and push all the values into the stacks. Then, pop the elements from both the stacks and add them. If there’s a carry push it to the next node. Repeat this until both the stacks go empty to get the desired result.

public ListNode AddTwoNumbers(ListNode l1, ListNode l2) {
	Stack<int> s1 = new Stack<int>();
	Stack<int> s2 = new Stack<int>();

	while(l1 != null){
		s1.Push(l1.val);
		l1 = l1.next;
	}

	while(l2 != null){
		s2.Push(l2.val);
		l2 = l2.next;
	}

	ListNode list = new ListNode(0);
	int sum = 0;
	while(s1.Count > 0 || s2.Count > 0){
		if(s1.Count > 0){
			sum += s1.Pop();
		}
		if(s2.Count > 0){
			sum += s2.Pop();
		}

		list.val = sum % 10;
		ListNode head = new ListNode(sum / 10);
		head.next = list;
		list = head;
		sum /= 10;
	}

	if(list.val == 0){
		return list.next;
	}
	else{
		return list;
	}
}

Generate Random Point in a Circle

Link - Generate Random Point in a Circle

public class Solution {
    double radius;
    double x_center;
    double y_center;
    Random rnd;

    public Solution(double radius, double x_center, double y_center) {
        this.radius = radius;
        this.x_center = x_center;
        this.y_center = y_center;

        rnd = new Random();
    }

    public double[] RandPoint() {
        double length = Math.Sqrt(rnd.NextDouble()) * this.radius;
        double degree = 2 * Math.PI * rnd.NextDouble();

        double newX = this.x_center + (length * Math.Cos(degree));
        double newY = this.y_center + (length * Math.Sin(degree));

        return new double[] {newX, newY};
    }
}

Same Tree

Link - Same Tree

Using Recursion

public bool IsSameTree(TreeNode p, TreeNode q) {
	if(p == null && q == null){
		return true;
	}

	if(p == null || q == null){
		return false;
	}

	if(p.val != q.val){
		return false;
	}

	return this.IsSameTree(p.left, q.left) && this.IsSameTree(p.right, q.right);
}

Spiral Matrix II

Link - Spiral Matrix II

Naive Solution

public int[][] GenerateMatrix(int n) {
	int[][] output = new int[n][];

	for(int i = 0; i < n; i++){
		output[i] = new int[n];
	}

	int num = 1;
	int top = 0;
	int bottom = n-1;
	int left = 0;
	int right = n-1;

	while(left <= right && top <= bottom){
		for(int i = left; i <= right && top <= bottom && left <= right; i++){
			output[top][i] = num;
			num++;
		}
		top++;

		for(int i = top; i <= bottom && top <= bottom && left <= right; i++){
			output[i][right] = num;
			num++;
		}
		right--;

		for(int i = right; i >= left && top <= bottom && left <= right; i--){
			output[bottom][i] = num;
			num++;
		}
		bottom--;

		for(int i = bottom; i >= top && top <= bottom && left <= right; i--){
			output[i][left] = num;
			num++;
		}
		left++;

	}

	return output;
}

Minimum Path Sum

Link - Minimum Path Sum

Recursion (Not efficient method)

public int MinPathSum(int[][] grid) {
	int row = grid.Length;
	int col = grid[0].Length;

	return this.Recursion(grid, row-1, col-1);
}

public int Recursion(int[][] grid, int row, int col){
	if(row ==0 &&  col == 0){
		return grid[0][0];
	}
	else if(row == 0){
		return grid[row][col] + this.Recursion(grid, row, col - 1);
	}
	else if(col == 0){
		return grid[row][col] + this.Recursion(grid, row - 1, col);
	}
	else{
		return grid[row][col] + Math.Min(this.Recursion(grid, row - 1, col), this.Recursion(grid, row, col - 1));
	}
}

Dynamic Programming

    public int MinPathSum(int[][] grid) {
        int row = grid.Length;

        if(row == 0){
            return 0;
        }

        int col = grid[0].Length;

        for(int i = 0; i < row; i++){
            for(int j = 0; j < col; j++){
                if(i == 0 && j == 0){
                    grid[0][0] = grid[0][0];
                }
                else if(i == 0){
                    grid[i][j] = grid[i][j] + grid[i][j-1];
                }
                else if(j == 0){
                    grid[i][j] = grid[i][j] + grid[i-1][j];
                }
                else{
                    grid[i][j] = grid[i][j] + Math.Min(grid[i-1][j], grid[i][j-1]);
                }
            }
        }

        return grid[row-1][col-1];
    }
ApproachTime ComplexitySpace Complexity
RecursionO(2^(m*n))stack memory is used
Dynamic ProgrammingO(m*n)O(m*n)

Decrypt String from Alphabet to Integer Mapping

Link - Decrypt String from Alphabet to Integer Mapping

Loop through each of the elements of the array and check if i+2th index is #. If yes then find the corresponding character for the value using the map dictionary. If not then it means that the character is lower than 10, so use the map dictionary to find the corresponding character. Repeat this until the end of the array to get the desired result.

public string FreqAlphabets(string s) {
    string output = "";

    Dictionary<string, string> map = new Dictionary<string, string>();
    map["1"] = "a";
    map["2"] = "b";
    map["3"] = "c";
    map["4"] = "d";
    map["5"] = "e";
    map["6"] = "f";
    map["7"] = "g";
    map["8"] = "h";
    map["9"] = "i";
    map["10"] = "j";
    map["11"] = "k";
    map["12"] = "l";
    map["13"] = "m";
    map["14"] = "n";
    map["15"] = "o";
    map["16"] = "p";
    map["17"] = "q";
    map["18"] = "r";
    map["19"] = "s";
    map["20"] = "t";
    map["21"] = "u";
    map["22"] = "v";
    map["23"] = "w";
    map["24"] = "x";
    map["25"] = "y";
    map["26"] = "z";

    int i = 0;

    while(i < s.Length){
        if(i+2 < s.Length && s[i+2] == '#'){
            output += map[s[i].ToString() + s[i+1].ToString()];
            i += 3;
        }
        else{
            output += map[s[i].ToString()];
            i += 1;
        }
    }

    return output;
}

Rotate List

Link - Rotate List

Naive Solution

public ListNode RotateRight(ListNode head, int k) {
	if(head == null){
		return head;
	}

	int length = this.ListLength(head);

	k = k % length;

	if(k == 0){
		return head;
	}

	k = length - k;

	ListNode newHead = head;
	ListNode prevNode = null;

	while(k > 0){
		prevNode = newHead;
		newHead = newHead.next;
		k--;
	}

	prevNode.next = null;

	prevNode = null;
	ListNode tempNode = newHead;

	while(tempNode != null){
		prevNode = tempNode;
		tempNode = tempNode.next;
	}

	prevNode.next = head;

	return newHead;
}

public int ListLength(ListNode root){
	int count = 0;

	while(root != null){
		count++;
		root = root.next;
	}

	return count;
}

Decoded String at Index

Link - Decoded String at Index

Naive Solution

public string DecodeAtIndex(string S, int K) {
	StringBuilder text = new StringBuilder();

	int i = 0;

	while(i < S.Length){
		if(char.IsDigit(S[i])){
			int count = Convert.ToInt32(S[i] - 49);

			string temp = text.ToString();
			while(count > 0){
				text.Append(temp);
				count--;
			}
		}
		else{
			text.Append(S[i]);
		}

		if(text.Length >= K){
			return text[K-1].ToString();
		}

		i++;
	}

	return text[K-1].ToString();
}

Better Solution

public string DecodeAtIndex(string S, int K) {
	long size = 0;
	int N = S.Length;

	for(int i = 0; i < N; i++){
		if(Char.IsDigit(S[i])){
			size *= (S[i] - '0');
		}
		else{
			size++;
		}
	}

	for(int i = N-1; i >= 0; i--){
		K = (int)(K % size);

		if(K == 0 && !Char.IsDigit(S[i])){
			return S[i].ToString();
		}

		if(Char.IsDigit(S[i])){
			size /= S[i] - '0';
		}
		else{
			size--;
		}
	}

	return "";
}

Minimum Operations to Make Array Equal

Link - Minimum Operations to Make Array Equal

Using Math

public int MinOperations(int n) {
	if(n % 2 == 0){
		int mid = n/2;
		return mid * mid;
	}
	else{
		int mid = (n-1)/2;
		return mid * (mid+1);
	}
}

Stone Game IV

Link - Stone Game IV

Bottom Up Approach

public bool WinnerSquareGame(int n) {
	bool[] dp = new bool[n+1];

	for(int i = 0; i < n+1; i++){
		for(int k = 1; k * k <= i; k++){
			if(dp[i - k*k] == false){
				dp[i] = true;
				break;
			}
		}
	}

	return dp[n];
}

Kids With the Greatest Number of Candies

Link - Kids With the Greatest Number of Candies

Solution

public IList<bool> KidsWithCandies(int[] candies, int extraCandies) {
	int max = Int32.MinValue;

	for(int i = 0; i < candies.Length; i++){
		max = Math.Max(max, candies[i]);
	}

	List<bool> output = new List<bool>();

	for(int i = 0; i < candies.Length; i++){
		if(extraCandies + candies[i] >= max){
			output.Add(true);
		}
		else{
			output.Add(false);
		}
	}

	return output;
}

Backspace String Compare

Link - Backspace String Compare

The first thing that comes to mind when you read the question is to use the Stack data structure. Read the below explanation.

Stack-based approach

Loop through string array, and Push it to the stack. Whenever you find a # character, check if the stack is empty and if not then Pop one element from the stack. Keep doing this until the end of the array. Repeat this same process on the second string array as well. Now take these two Stacks and Pop elements from both and check if they are same if they do not then return false.

public bool BackspaceCompare(string S, string T) {
    Stack<char> first = new Stack<char>();
    Stack<char> second = new Stack<char>();

    for(int i = 0; i < S.Length; i++){
        if(S[i] == '#'){
            if(first.Count != 0)
                first.Pop();
        }
        else{
            first.Push(S[i]);
        }
    }

    for(int i = 0; i < T.Length; i++){
        if(T[i] == '#'){
            if(second.Count != 0)
                second.Pop();
        }
        else{
            second.Push(T[i]);
        }
    }

    if(first.Count != second.Count){
        return false;
    }

    while(first.Count != 0){
        if(first.Pop() != second.Pop()){
            return false;
        }
    }

    return true;
}

The time-complexity of the above approach is O(S + T) (where S and T are the lengths of the strings) since we are looping through all the elements of both the arrays. The space-complexity will be O(S + T) since we need to stacks to store the characters of the string.

Can we do better than this? See the next solution which solves it in linear time-complexity.

Two Pointer Approach

Start looping from the end of the array, whenever # is found, increase the skip count. When a non # character is found, decrease the skip count. Keep decrementing the pointer during this process. Repeat the same for the second array also. Keep checking if the character pointed by both the arrays are matching. If they aren’t matching then return. Keep doing this until one of the pointers reaches 0. See the below code for this approach.

public bool BackspaceCompare(string S, string T) {
    int skipS = 0;
    int skipT = 0;

    int i = S.Length-1;
    int j = T.Length-1;

    while(i >= 0 || j >= 0){
        while(i >= 0){
            if(S[i] == '#'){
                skipS++;
                i--;
            }
            else if(skipS > 0){
                skipS--;
                i--;
            }
            else{
                break;
            }
        }

        while(j >= 0){
            if(T[j] == '#'){
                skipT++;
                j--;
            }
            else if(skipT > 0){
                skipT--;
                j--;
            }
            else{
                break;
            }
        }

        if (i >=0 && j>= 0 && S[i] != T[j] || ((i >= 0) != (j >= 0))){
            return false;
        }
        i--;
        j--;
    }
    return true;
}

The time-complexity is the same as the previous method, but the space-complexity has reduced to O(1).

Validate Binary Search Tree

Link - Validate Binary Search Tree

Using In-Order Traversal

Simply do an in-order traversal on tree starting from the root node and put node values in a list. Then check if the elements in the list are in ascending order. If yes then it is a valid Binary Search Tree.

public bool IsValidBST(TreeNode root) {
    if(root == null){
        return true;
    }

    List<int> temp = new List<int>();

    this.InOrderTraversal(root, temp);

    for(int i = 1; i < temp.Count; i++){
        if(temp[i-1] >= temp[i]){
            return false;
        }
    }
    return true;
}

public void InOrderTraversal(TreeNode node, List<int> temp){
    if(node.left != null){
        this.InOrderTraversal(node.left, temp);
    }
    temp.Add(node.val);
    if(node.right != null){
        this.InOrderTraversal(node.right, temp);
    }
}

Using Recursion

A min and max value integer is maintained for each node. Check if left and right nodes are in the low and high range. if any node violates the condition then it is not a valid BST. Call and check if the left and right nodes are invalid range recursively to get the required result.

public bool IsValidBST(TreeNode root) {
    return ValidBST(root, long.MinValue, long.MaxValue);
}

public bool ValidBST(TreeNode root, long minValue, long maxValue){
    if(root == null){
        return true;
    }

    if(root.val > minValue && root.val < maxValue){
        return this.ValidBST(root.left, minValue, root.val) && this.ValidBST(root.right, root.val, maxValue);
    }

    return false;
}

Replace Words

Link - Replace Words

Using Trie

public class Solution {
    public string ReplaceWords(IList<string> dictionary, string sentence) {
        Trie root = new Trie();

        // Build Trie
        foreach(string word in dictionary){
            Trie tempNode = root;

            foreach(char c in word){
                int charValue = c - 'a';

                if(tempNode.children[charValue] == null){
                    tempNode.children[charValue] = new Trie();
                }

                tempNode = tempNode.children[charValue];
            }

            tempNode.isWord = true;
        }

        string[] words = sentence.Split(' ');

        for(int i = 0; i < words.Length; i++){
            Trie tempNode = root;

            string appendString = "";

            foreach(char c in words[i]){
                int charValue = c - 'a';

                if(tempNode.isWord){
                    words[i] = appendString;
                    break;
                }

                if(tempNode.children[charValue] == null){
                    break;
                }

                tempNode = tempNode.children[charValue];

                appendString += c;
            }
        }

        return string.Join(" ", words);
    }
}

public class Trie{
    public Trie[] children;
    public bool isWord;

    public Trie(){
        children = new Trie[26];
        isWord = false;
    }
}

First Missing Positive

Link - First Missing Positive

O(n) time with O(1) space

public int FirstMissingPositive(int[] nums) {
	int N = nums.Length;

	for(int i = 0; i < N; i++){
		while(nums[i] > 0 && nums[i] <= N && nums[nums[i]-1] != nums[i]){
			this.Swap(nums, i, nums[i]-1);
		}
	}

	for(int i = 0; i < N; i++){
		if(nums[i] != i+1){
			return i+1;
		}
	}

	return N+1;
}

public void Swap(int[] nums, int a, int b){
	int temp = nums[a];
	nums[a] = nums[b];
	nums[b] = temp;
}

Sum Root to Leaf Numbers

Link - Sum Root to Leaf Numbers

Using Depth First Search

public int SumNumbers(TreeNode root) {
	int output = 0;
	this.DFS(root, 0, ref output);

	return output;
}

public void DFS(TreeNode root, int num, ref int sum){
	if(root == null){
		return;
	}

	int tempNum = (num * 10) + root.val;

	if(root.left == null && root.right == null){
		sum += tempNum;
		return;
	}

	this.DFS(root.left, tempNum, ref sum);
	this.DFS(root.right, tempNum, ref sum);
}

House Robber

Link - House Robber

Using Top Down Approach

public int Rob(int[] nums) {
	Dictionary<int,int> memo = new Dictionary<int,int>();
	return this.MaxRob(nums, 0, memo);
}

public int MaxRob(int[] nums, int index, Dictionary<int,int> memo){
	if(index >= nums.Length){
		return 0;
	}

	if(memo.ContainsKey(index)){
		return memo[index];
	}

	memo[index] = Math.Max(nums[index] + this.MaxRob(nums, index + 2, memo), this.MaxRob(nums, index + 1, memo));

	return memo[index];
}

Using Bottom Up Approach

public int Rob(int[] nums) {
	int N = nums.Length;

	if(N == 0){
		return 0;
	}

	int[] dp = new int[N+1];

	dp[0] = 0;
	dp[1] = nums[0];

	for(int i = 1; i < N; i++){
		dp[i+1] = Math.Max(dp[i], dp[i-1] + nums[i]);
	}

	return dp[N];
}

Boats to Save People

Link - Boats to Save People

Naive Approach

public int NumRescueBoats(int[] people, int limit) {
	Dictionary<int,int> map = new Dictionary<int,int>();

	foreach(int item in people){
		if(!map.ContainsKey(item)){
			map[item] = 0;
		}

		map[item] += 1;
	}

	HashSet<int> indices = new HashSet<int>();

	for(int j = 0; j < people.Length; j++){
		indices.Add(j);
	}

	int count = 0;

	int i = 0;
	while(indices.Count > 0){
		if(!indices.Contains(i)){
			i++;
			continue;
		}

		int weight = people[i];
		indices.Remove(i);

		int maxWeight = -1;
		int maxWeightIndex = -1;

		foreach(var index in indices){
			if(weight + people[index] == limit){
				maxWeight = people[index];
				maxWeightIndex = index;
				break;
			}
			else if(weight + people[index] <= limit && people[index] > maxWeight){
				maxWeight = people[index];
				maxWeightIndex = index;
			}
		}

		count++;
		i++;
		if(maxWeightIndex != -1){
			indices.Remove(maxWeightIndex);
		}

	}

	return count;
}

Greedy Approach (Two Pointer Solution)

public int NumRescueBoats(int[] people, int limit) {
	Array.Sort(people);

	int sIndex = 0;
	int eIndex = people.Length - 1;

	int count = 0;

	while(sIndex <= eIndex){
		if(people[sIndex] + people[eIndex] <= limit){
			count++;
			sIndex++;
			eIndex--;
		}
		else{
			count++;
			eIndex--;
		}

	}

	return count;
}

Reverse Integer

Link - Reverse Integer

This is a typical reverse a number problem. Simply mod the given number by 10. That gives you the last digit of the number then divide the input number by 10. That will remove the last digit of the number. Keep doing this to get the reversed number. To find if the number overflows, put the output in long datatype and check if the number is same as the int datatype.

public int Reverse(int x) {
    long output = 0;

    while(x != 0){
        int temp = x % 10;
        output = (output * 10) + temp;
        x /= 10;
    }

    if((int)output != output){
        return 0;
    }

    return (int)output;
}

Palindrome Linked List

Link - Palindrome Linked List

Naive Approach

In this approach, you go through all the nodes of the linked list and put the values in a new array. Then use the two-pointer technique one at the start and the other at the end to see if the numbers match.

public bool IsPalindrome(ListNode head) {
    List<int> temp = new List<int>();

    while(head != null){
        temp.Add(head.val);
        head = head.next;
    }

    for(int i = 0; i < temp.Count / 2; i++){
        if(temp[i] != temp[temp.Count - i - 1]){
            return false;
        }
    }
    return true;
}

This solution has a time complexity of O(n) and space complexity of O(n) (since an array of size n is used to store all the values).

See the next solution which solves this using lesser space.

Best Approach (Tortoise and Hare method)

Here we use two pointers, one pointer jumps one node at a time and the other jumps 2 nodes at a time. In the end, you will have a slow pointer in the middle of the list and the fast pointer at the end of the list. Now reverse the linked list starting from the slow pointer. Consider the following example

Input 1 -> 2 -> 3 -> 3 -> 2 -> 1 will turn into

1 <- 2 <- 3 (start pointer) <- 3 (end pointer) -> 2 -> 1

Start checking if the start and end pointer values are the same until the end of the list. This gives you the required result.

public bool IsPalindrome(ListNode head) {
    ListNode fast = head;
    ListNode slow = head;

    while(fast != null && fast.next != null){
        slow = slow.next;
        fast = fast.next.next;
    }

    // If there are an odd number of nodes in the list then move the slow pointer to the center of the list
    if(fast != null){
        slow = slow.next;
    }

    ListNode start = head;
    ListNode end = this.Reverse(slow);

    while(end != null){
        if(start.val != end.val){
            return false;
        }

        start = start.next;
        end = end.next;
    }
    return true;
}

public ListNode Reverse(ListNode head){
    ListNode prev = null;
    while(head != null){
        ListNode next = head.next;
        head.next = prev;
        prev = head;
        head = next;
    }
    return prev;
}

This solution has the same time complexity but has a O(1) space complexity since we all the operations are done in-place.

Remove All Adjacent Duplicates In String

Link - Remove All Adjacent Duplicates In String

Using Stack

    public string RemoveDuplicates(string S) {

        Stack<char> temp = new Stack<char>();

        for(int i = 0; i < S.Length; i++){
			// If stack is empty then push the character to the stack
            if(temp.Count == 0){
                temp.Push(S[i]);
            }
            else{
				// Peek and check if the current character is the same. If yes then Pop it out of the stack, else Push it to the stack
                if(temp.Peek() == S[i]){
                    temp.Pop();
                }
                else{
                    temp.Push(S[i]);
                }
            }
        }

        StringBuilder output = new StringBuilder();

        while(temp.Count > 0){
            output.Insert(0, temp.Pop());
        }

        return output.ToString();
    }

Bulb Switcher IV

Link - Bulb Switcher IV

Using greedy Approach

public int MinFlips(string target) {
	int flips = 0;

	char curr = '0';

	foreach(char c in target){
		if(curr != c){
			flips++;
			curr = c;
		}
	}

	return flips;
}

Reformat Date

Link - Reformat Date

Using Dictionary

public string ReformatDate(string date) {
	string[]splits = date.Split(" ");
	return splits[2] + "-" + this.GetMonth(splits[1]) + "-" + this.GetDate(splits[0]);
}

public string GetDate(string input){
	string num = "";
	if(input.Length == 3){
		return "0" + input.Substring(0,1);
	}
	else{
		return input.Substring(0,2);
	}
}

public string GetMonth(string input){
	Dictionary<string, string> map = new Dictionary<string, string>();
	map["Jan"] = "01";
	map["Feb"] = "02";
	map["Mar"] = "03";
	map["Apr"] = "04";
	map["May"] = "05";
	map["Jun"] = "06";
	map["Jul"] = "07";
	map["Aug"] = "08";
	map["Sep"] = "09";
	map["Oct"] = "10";
	map["Nov"] = "11";
	map["Dec"] = "12";

	return map[input];
}

Subrectangle Queries

Link - Subrectangle Queries

Basic Solution

public class SubrectangleQueries {
    int rows;
    int cols;
    int[,] matrix;

    public SubrectangleQueries(int[][] rectangle) {
        rows = rectangle.Length;
        if(rows == 0){
            cols = 0;
            matrix = new int[rows, cols];
            return;
        }

        cols = rectangle[0].Length;
        matrix = new int[rows,cols];

        for(int i = 0; i < rows; i++){
            for(int j = 0; j < cols; j++){
                matrix[i,j] = rectangle[i][j];
            }
        }
    }

    public void UpdateSubrectangle(int row1, int col1, int row2, int col2, int newValue) {
        for(int i = row1; i <= row2; i++){
            for(int j = col1; j <= col2; j++){
                matrix[i,j] = newValue;
            }
        }
    }

    public int GetValue(int row, int col) {
        if(row >= rows || col >= cols || row < 0 || col < 0){
            return 0;
        }

        return matrix[row,  col];
    }
}

Unique Morse Code Words

Link - Unique Morse Code Words

Using HashSet

public int UniqueMorseRepresentations(string[] words) {
	string[] MorseCode = new string[] {".-","-...","-.-.","-..",".","..-.","--.","....","..",".","-.-",".-..","--","-.","",".--.","--.-",".-.","...","-","..-","...-",".--","-..-","-.--","--.."};

	HashSet<string> seen = new HashSet<string>();

	foreach(string word in words){
		StringBuilder code = new StringBuilder();

		foreach(char c in word){
			code.Append(MorseCode[c - 'a']);
		}
		seen.Add(code.ToString());
	}

	return seen.Count;
}

Check If All 1’s Are at Least Length K Places Away

Link - Check If All 1’s Are at Least Length K Places Away

Linear Scan

Simply perform a linear scan on the array. Whenever a 1 is found check if the next k items are not 1. Repeat this throughout the array to get the result.

    public bool KLengthApart(int[] nums, int k) {
        int i = 0;
        while(i < nums.Length){
            if(nums[i] == 1){
                int temp = i;
                for(int j = 1; j <= k; j++){
                    if(i+j < nums.Length && nums[i+j] == 1){
                        return false;
                    }
                    temp = i + j;
                }
                i = temp;
            }
            i++;
        }

        return true;
    }

Sort List

Link - Sort List

Using Merge Sort

public ListNode SortList(ListNode head) {
	if(head == null || head.next == null){
		return head;
	}

	ListNode slow = head;
	ListNode fast = head;

	while(fast.next != null && fast.next.next != null){
		slow = slow.next;
		fast = fast.next.next;
	}

	ListNode mid = slow.next;
	slow.next = null;

	ListNode left = SortList(head);
	ListNode right = SortList(mid);
	ListNode dummy = new ListNode();
	ListNode current = dummy;
	while(left != null && right != null)
	{
		if(left.val < right.val)
		{
			current.next = left;
			current = current.next;
			left = left.next;
		}
		else
		{
			current.next = right;
			current = current.next;
			right = right.next;
		}
	}

	if(left != null)
		current.next = left;

	if(right != null)
		current.next = right;

	return dummy.next;
}

Path Crossing

Link - Path Crossing

Using HashSet

public bool IsPathCrossing(string path) {
	HashSet<string> set = new HashSet<string>();

	set.Add("0$0");

	int currX = 0;
	int currY = 0;

	foreach(char c in path){
		if(c == 'N'){
			currY++;
		}
		else if(c == 'S'){
			currY--;
		}
		else if(c == 'E'){
			currX++;
		}
		else{
			currX--;
		}

		string newLoc = currX + "$" + currY;

		if(set.Contains(newLoc)){
			return true;
		}

		set.Add(newLoc);
	}

	return false;
}

Insert into a Binary Search Tree

Link - Insert into a Binary Search Tree

Iterative Solution

public TreeNode InsertIntoBST(TreeNode root, int val) {
	if(root == null){
		return new TreeNode(val);
	}

	TreeNode tempNode = root;
	TreeNode parentNode = null;

	while(tempNode != null){
		parentNode = tempNode;

		if(tempNode.val < val){
			tempNode = tempNode.right;
		}
		else if(tempNode.val > val){
			tempNode = tempNode.left;
		}
	}

	if(val > parentNode.val){
		parentNode.right = new TreeNode(val);
	}
	else if(val < parentNode.val){
		parentNode.left = new TreeNode(val);
	}

	return root;
}

Recursive Solution

public TreeNode InsertIntoBST(TreeNode root, int val) {
	if(root == null){
		return new TreeNode(val);
	}

	if(val < root.val){
		root.left = this.InsertIntoBST(root.left, val);
	}
	else if(val > root.val){
		root.right = this.InsertIntoBST(root.right, val);
	}

	return root;
}

Maximum Depth of N-ary Tree

Link - Maximum Depth of N-ary Tree

BFS Approach

Perform a breadth-first search on the given root node. Maintain a variable depth to keep track of the number of levels that are traversed.

public int MaxDepth(Node root) {
    if(root == null){
        return 0;
    }

    Queue<Node> bfsQueue = new Queue<Node>();
    bfsQueue.Enqueue(root);
    int depth = 0;

    while(bfsQueue.Count != 0){
        int count = bfsQueue.Count;
        depth++;

        for(int i = 0; i < count; i++){
            Node tempNode = bfsQueue.Dequeue();
            foreach(Node temp in tempNode.children){
                bfsQueue.Enqueue(temp);
            }
        }
    }

    return depth;
}

DFS Search

public int MaxDepth(Node root) {
    if (root == null){
        return 0;
    }
    int depth = 0;
    foreach(Node temp in root.children){
        depth = Math.Max(depth, this.MaxDepth(temp));
    }
    return 1 + depth;
}

H-Index II

Link - H-Index II

Using Binary Search

public int HIndex(int[] citations) {
	int N = citations.Length;
	int low = 0;
	int high = N - 1;
	int mid = 0;

	while(low <= high){
		mid = low + (high-low)/2;

		if(citations[mid] >= (N-mid)){
			high = mid - 1;
		}
		else{
			low = mid + 1;
		}
	}

	return N-low;
}

Flatten Binary Tree to Linked List

Link - Flatten Binary Tree to Linked List

Using Recursion

public void Flatten(TreeNode root) {
	if(root == null){
		return;
	}

	TreeNode rightNode = root.right;

	this.Flatten(root.left);

	root.right = root.left;
	root.left = null;

	while(root.right != null){
		root = root.right;
	}

	this.Flatten(rightNode);

	root.right = rightNode;
}

Range Sum Query - Immutable

Link - Range Sum Query - Immutable

Using Cumulative Sum

int[] cumulativeSum;
public NumArray(int[] nums) {
	cumulativeSum = new int[nums.Length + 1];
	cumulativeSum[0] = 0;

	for(int i = 0; i < nums.Length; i++){
		cumulativeSum[i+1] = cumulativeSum[i] + nums[i];
	}
}

public int SumRange(int i, int j) {
	return cumulativeSum[j+1] - cumulativeSum[i];
}

Count Primes

Link - Count Primes

Naive Approach

public int CountPrimes(int n) {
	if(n <= 2){
		return 0;
	}

	int count = 1;

	for(int i = 3; i < n; i++){
		count += this.IsPrime(i) ? 1 : 0;
	}

	return count;
}

public bool IsPrime(int n){
	// The range to be cheked will be from 2 to sqrt(n) + 1
	for(int i = 2; i * i <= n; i++){
		if(n % i == 0){
			return false;
		}
	}

	return true;
}

Using Sieve of Erasthotenes

public int CountPrimes(int n) {
	bool[] arr = new bool[n];

	int count = 0;

	for(int i = 2; i < n; i++){
		if(arr[i] == false){
			count++;

			// Update all the multiples of a number to true, so that those numbers are not picked in the next sieve since they are not prime numbers
			int j = i + i;
			while(j < n){
				arr[j] = true;
				j += i;
			}
		}
	}

	return count;
}

Number of Substrings With Only 1s

Link - Number of Substrings With Only 1s

public int NumSub(string s) {
	int totalCount = 0;
	int tempCount = 0;

	for(int i = 0 ; i < s.Length; i++){
		if(s[i] == '1'){
			tempCount++;
		}
		else{
			totalCount += this.SubCount(tempCount);
			tempCount = 0;
		}

	}

	totalCount += this.SubCount(tempCount);

	return totalCount;
}

public int SubCount(int n){
	long temp = 0;
	for(int i = 0; i <= n; i++){
		temp += n;
		temp %= 1000000007;
	}

	return (int)temp/2;
}

Populating Next Right Pointers in Each Node

Link - Populating Next Right Pointers in Each Node

Using Breadth First Search

public Node Connect(Node root) {
	if(root == null){
		return root;
	}

	Queue<Node> bfs = new Queue<Node>();
	bfs.Enqueue(root);

	while(bfs.Count > 0){
		int count = bfs.Count;

		for(int i = 0; i < count; i++){
			Node tempNode = bfs.Dequeue();

			if(i != count - 1){
				tempNode.next = bfs.Peek();
			}

			if(tempNode.left != null){
				bfs.Enqueue(tempNode.left);
			}

			if(tempNode.right != null){
				bfs.Enqueue(tempNode.right);
			}
		}
	}

	return root;
}

Better Solution (Constant space)

public Node Connect(Node root) {
	if(root == null){
		return root;
	}

	Node prev = root;
	Node curr = null;

	while(prev.left != null){
		curr = prev;

		while(curr != null){
			curr.left.next = curr.right;
			if(curr.next != null){
				curr.right.next = curr.next.left;
			}
			curr = curr.next;
		}
		prev = prev.left;
	}

	return root;
}

Implement Union Find (Disjoint Set) Data Structure

Implementation of Union Find Data Structure

public class UnionFind{

	int count; // Holds the number of connected components in the graph
	int[] size;
	int[] id;

	public UnionFind(int N){
		this.count = N;

		size = new int[N];
		id = new int[N];

		// Initially the size of all the nodes will be 1
		for(int i = 0; i < N; i++){
			size[i] = 1;
			id[i] = i;
		}
	}

	public int Find(int node){
		int root = node;

		// Loop until you find the parent of a node to be same as parent node
		while(root != id[root]){
			root = id[root];
		}

		// Applying path compression for better time complexity
		while(node != root){
			int newNode = id[node];
			id[node] = root;
			node = newNode;
		}

		return root;
	}

	public void Unify(int a, int b){
		int aParent = this.Find(a);
		int bParent = this.Find(b);

		if(aParent == bParent){
			return;
		}

		if(size[aParent] < size[bParent]){
			id[aParent] = bParent;
			size[bParent] += size[aParent];
		}
		else{
			id[bParent] = aParent;
			size[aParent] += size[bParent];
		}

		count--;
	}

	public int NumberOfConnectedComponents(){
		return count;
	}
}

Intersection of Two Linked Lists

Link - Intersection of Two Linked Lists

Naive Approach

public ListNode GetIntersectionNode(ListNode headA, ListNode headB) {
	int sizeA = this.Size(headA);
	int sizeB = this.Size(headB);

	while(sizeA > sizeB){
		headA = headA.next;
		sizeA--;
	}

	while(sizeA < sizeB){
		headB = headB.next;
		sizeB--;
	}

	while(headA != headB){
		headA = headA.next;
		headB = headB.next;
	}

	return headA;
}

public int Size(ListNode root){
	int count = 0;

	while(root != null){
		root = root.next;
		count++;
	}

	return count;
}

Ransom Note

Link - Ransom Note

Dictionary Approach

public bool CanConstruct(string ransomNote, string magazine) {
	Dictionary<char, int> map = new Dictionary<char, int>();

	for(int i = 0; i < magazine.Length; i++){
		if(map.ContainsKey(magazine[i])){
			map[magazine[i]] += 1;
		}
		else{
			map[magazine[i]] = 1;
		}
	}

	for(int i = 0; i < ransomNote.Length; i++){
		if(map.ContainsKey(ransomNote[i]) && map[ransomNote[i]] > 0){
			map[ransomNote[i]] -= 1;
		}
		else{
			return false;
		}
	}

	return true;
}

Excel Sheet Column Number

Link - Excel Sheet Column Number

public int TitleToNumber(string s) {
	int power = 1;
	int output = 0;

	for(int i = s.Length-1; i >= 0; i--){
		int value = s[i] - 'A' + 1;

		output += (value * power);
		power *= 26;
	}

	return output;
}

Implement Rand10() Using Rand7()

Link - Implement Rand10() Using Rand7()

Using Rejection Sampling Technique

public int Rand10() {
	int row;
	int col;
	int val;

	do {
		row = this.Rand7();
		col = this.Rand7();

		// -1 is subtracted to convert it to 0th index
		val = (col-1) + (row-1) * 7;
	} while(val > 40);

	// +1 is added to to convert it back to 1th index
	return (val % 10) + 1;
}

Fizz Buzz

Link - Fizz Buzz

Naive Solution

public IList<string> FizzBuzz(int n) {
	List<string> output = new List<string>();

	for(int i = 1; i <= n; i++){
		if(i % 5 == 0 && i % 3 == 0){
			output.Add("FizzBuzz");
		}
		else if(i % 3 == 0){
			output.Add("Fizz");
		}
		else if(i % 5 == 0){
			output.Add("Buzz");
		}
		else{
			output.Add(i.ToString());
		}
	}

	return output;
}

Container With Most Water

Link - Container With Most Water

Brute Force Method

Brute force way to solve this problem would be to try every combination of input and check which one generates the maximum area.

The time complexity of this approach is O(n^2) and space complexity is O(1). Check the next solution which solves this problem in linear time.

Two pointer Method

In the Two Pointer approach, we use a start and an end pointer. Keep moving these pointers closer to the middle based on their heights. If one pointer is higher than the other then move the other pointer closer to the center. Keep repeating this to find get the desired result.

public int MaxArea(int[] height) {
    int max = 0;
    int start = 0;
    int end = height.Length - 1;

    while(start < end){
        int temp = Math.Min(height[start], height[end]) * (end - start);

        if(temp > max){
            max = temp;
        }

        if(height[start] > height[end]){
            end--;
        }
        else{
            start++;
        }
    }
    return max;
}

The above solution has a time complexity of O(n) and space complexity of O(1).

House Robber III

Link - House Robber III

Using Recursion

public int Rob(TreeNode root) {
	if(root == null){
		return 0;
	}

	int val = 0;

	if(root.left != null){
		val += this.Rob(root.left.left) + this.Rob(root.left.right);
	}

	if(root.right != null){
		val += this.Rob(root.right.left) + this.Rob(root.right.right);
	}

	return Math.Max(root.val + val, this.Rob(root.left) + this.Rob(root.right));
}

Using Recursion + Memoization

Dictionary<TreeNode, int> map = new Dictionary<TreeNode, int>();

public int Rob(TreeNode root) {
	if(root == null){
		return 0;
	}

	if(map.ContainsKey(root)){
		return map[root];
	}

	int val = 0;

	if(root.left != null){
		val += this.Rob(root.left.left) + this.Rob(root.left.right);
	}

	if(root.right != null){
		val += this.Rob(root.right.left) + this.Rob(root.right.right);
	}

	map[root] = Math.Max(root.val + val, this.Rob(root.left) + this.Rob(root.right));

	return map[root];
}

Using Recursion - Space Optimised

public int Rob(TreeNode root) {
	int[] output = this.Recurse(root);

	return Math.Max(output[0], output[1]);
}

public int[] Recurse(TreeNode root){
	if(root == null){
		return new int[2];
	}

	int[] left = this.Recurse(root.left);
	int[] right = this.Recurse(root.right);

	int[] output = new int[2];

	output[0] = Math.Max(left[0], left[1]) + Math.Max(right[0], right[1]);
	output[1] = root.val + left[0] + right[0];

	return output;
}

Path with Maximum Probability

Link - Path with Maximum Probability

Using Floys-Warshall Algorithm (Time Limit Exceeded error)

public double MaxProbability(int n, int[][] edges, double[] succProb, int start, int end) {
	double[,] probs = new double[n,n];

	for(int i = 0; i < edges.Length; i++){
		int a = edges[i][0];
		int b = edges[i][1];

		probs[a,b] = succProb[i];
		probs[b,a] = succProb[i];
	}


	for(int i = 0; i < n; i++){
		for(int j = 0; j < n; j++){
			for(int k = 0; k < n; k++){
				probs[j,k] = Math.Max(probs[j,k], probs[j,i]*probs[i,k]);
			}
		}
	}

	return probs[start, end];
}

Using DFS (Time Limit Exceeded error)

public double MaxProbability(int n, int[][] edges, double[] succProb, int start, int end) {
	Dictionary<int, List<Tuple<int, double>>> map = new Dictionary<int, List<Tuple<int, double>>>();

	for(int i = 0; i < n; i++){
		map[i] = new List<Tuple<int, double>>();
	}

	for(int i = 0; i < edges.Length; i++){
		int a = edges[i][0];
		int b = edges[i][1];
		double succ = succProb[i];

		map[a].Add(new Tuple<int,double>(b,succ));
		map[b].Add(new Tuple<int,double>(a,succ));
	}

	HashSet<int> visited = new HashSet<int>();

	double maxCount = 0;

	this.DFS(start, end, visited, map, ref maxCount, 1, succProb);

	return maxCount;
}

public void DFS(int start, int end, HashSet<int> visited,  Dictionary<int, List<Tuple<int, double>>> map, ref double maxCount, double currCount, double[] succProb){
	if(start == end){
		maxCount = Math.Max(maxCount, currCount);
	}

	if(currCount <= maxCount){
		return;
	}

	if(visited.Contains(start)){
		return;
	}

	visited.Add(start);

	foreach(var item in map[start]){
		double temp = currCount*item.Item2;
		if(temp <= maxCount || visited.Contains(item.Item1)){
			continue;
		}

		this.DFS(item.Item1, end, visited, map, ref maxCount, temp, succProb);
	}

	visited.Remove(start);
}

Using BFS (Bellman-Ford Algorithm)

public class Solution {
    public double MaxProbability(int n, int[][] edges, double[] succProb, int start, int end) {
        Dictionary<int, List<Tuple<int, double>>> map = new Dictionary<int, List<Tuple<int, double>>>();

        for(int i = 0; i < n; i++){
            map[i] = new List<Tuple<int, double>>();
        }

        for(int i = 0; i < edges.Length; i++){
            int a = edges[i][0];
            int b = edges[i][1];
            double succ = succProb[i];

            map[a].Add(new Tuple<int,double>(b,succ));
            map[b].Add(new Tuple<int,double>(a,succ));
        }

        Queue<Node> bfsQueue = new Queue<Node>();
        bfsQueue.Enqueue(new Node(start, 1.0));

        double[] probs = new double[n];

        while(bfsQueue.Count > 0){
            Node tempNode = bfsQueue.Dequeue();

            int currNode = tempNode.node;
            double currProb = tempNode.prob;

            foreach(Tuple<int, double> item in map[currNode]){
                if(probs[item.Item1] >= currProb * item.Item2){
                    continue;
                }

                bfsQueue.Enqueue(new Node(item.Item1, currProb * item.Item2));
                probs[item.Item1] = currProb * item.Item2;
            }

        }


        return probs[end];
    }
}

public class Node{
    public int node;
    public double prob;

    public Node(int _node, double _prob){
        node = _node;
        prob = _prob;
    }
}

Consecutive Characters

Link - Consecutive Characters

Linear Scan

public int MaxPower(string s) {
	if(s.Length < 2){
		return s.Length;
	}

	int maxCount = 1;
	int currCount = 1;
	for(int i = 1; i < s.Length; i++){
		if(s[i] == s[i-1]){
			currCount++;
		}
		else{
			maxCount = Math.Max(currCount, maxCount);
			currCount = 1;
		}
	}
	maxCount = Math.Max(currCount, maxCount);

	return maxCount;
}

Min Cost Climbing Stairs

Link - Min Cost Climbing Stairs

Dynamic Programming

public int MinCostClimbingStairs(int[] cost) {
    if(cost.Length == 0){
        return 0;
    }
    else if(cost.Length == 1){
        return cost[0];
    }

    int step1 = cost[0];
    int step2 = cost[1];

    for(int i = 2; i < cost.Length; i++){
        int temp = cost[i] + Math.Min(step1, step2);
        step1 = step2;
        step2 = temp;
    }

    return Math.Min(step1, step2);
}

Recursion

    public int MinCostClimbingStairs(int[] cost) {
        return Math.Min(MinCost(cost, 0), MinCost(cost, 1)); // It cannot be just MinCost(cost, 0); since that will always take the first element of the array, but the result start with the second element as well.
    }

    public int MinCost(int[] cost, int startIndex){
        if(startIndex >= cost.Length || startIndex < 0){
            return 0;
        }

        return cost[startIndex] + Math.Min(this.MinCost(cost, startIndex + 1), this.MinCost(cost, startIndex + 2));
    }

Recursion with Memoization

public int MinCostClimbingStairs(int[] cost) {
    int[] memo = new int[cost.Length];

    return Math.Min(MinCost(cost, 0, memo), MinCost(cost, 1, memo));
}

public int MinCost(int[] cost, int startIndex, int[] memo){
    if(startIndex >= cost.Length || startIndex < 0){
        return 0;
    }

    if(memo[startIndex] != 0){
        return memo[startIndex];
    }

    memo[startIndex] = cost[startIndex] +  Math.Min(this.MinCost(cost, startIndex + 1, memo), this.MinCost(cost, startIndex + 2, memo));

    return memo[startIndex];
}
ApproachTime ComplexitySpace Complexity
Dynamic ProgrammingO(n)O(1)
RecursionO(n^2)O(n) (recursive call uses stack memory)
Recursion with MemoizationO(n)O(n)

Build an Array With Stack Operations

Link - Build an Array With Stack Operations

Using Linear Scan

public IList<string> BuildArray(int[] target, int n) {
	List<string> output = new List<string>();
	int index = 0;

	for(int i = 1; i <= n; i++){
		if(index >= target.Length){
			break;
		}
		else if(target[index] == i){
			output.Add("Push");
			index++;
		}
		else{
			output.Add("Push");
			output.Add("Pop");
		}
	}

	return output;
}

Goat Latin

Link - Goat Latin

Naive Approach

public string ToGoatLatin(string S) {
	string[] words = S.Split(" ");

	string appendWord = "a";

	for(int i = 0; i < words.Length; i++){
		if(this.IsVowel(words[i])){
			words[i] += "ma";
		}
		else{
			words[i] = (words[i] + words[i][0] + "ma").Substring(1);
		}

		words[i] += appendWord;
		appendWord += "a";
	}

	return string.Join(" ", words);
}

private bool IsVowel(string word){
	if(word.Length == 0){
		return false;
	}

	char fChar = char.ToLower(word[0]);

	if(fChar == 'a' || fChar == 'e' || fChar == 'i' || fChar == 'o' || fChar == 'u'){
		return true;
	}

	return false;
}

Sorting Algorithm - Merge Sort

Problem Statement

Sort integer array using Merge Sort

Solution

In this sorting technique, you divide the given array into sub arrays and sort those sub arrays and merge them to get the sorted array


// Call this function to sort the array
this.MergeSort(arr, 0, arr.Length-1);

public static void MergeSort(int[] arr, int start, int end){
	if(start < end){
		int mid = (start + end) / 2;

		Sorting.MergeSort(arr, start, mid);
		Sorting.MergeSort(arr, mid+1, end);
		Sorting.Merge(arr, start, mid, end);
	}
}

// Using two pointer approach
public static void Merge(int[] arr, int start, int mid, int end){
	int[] tempArr = new int[end - start + 1];

	int fIndex = start;
	int sIndex = mid + 1;

	int i = 0;

	// Traverse both array groups and add smaller elements to the temp array
	while(fIndex <= mid && sIndex <= end){
		if(arr[fIndex] <= arr[sIndex]){
			tempArr[i] = arr[fIndex];
			fIndex++;
		}
		else{
			tempArr[i] = arr[sIndex];
			sIndex++;
		}
		i++;
	}

	// Copy remaining elements
	while(fIndex <= mid){
		tempArr[i] = arr[fIndex];
		fIndex++;
		i++;
	}

	// Copy remaining elements
	while(sIndex <= end){
		tempArr[i] = arr[sIndex];
		sIndex++;
		i++;
	}

	// Copy elements from temp array to original array
	for(int a = start; a <= end; a++){
		arr[a] = tempArr[a-start];
	}
}

The above sorting technique has nlogn time complexity and linear space requirement. This sorting method used Divide and Conquer approach.

Word Break

Link - Word Break

Brute Force (N^2 time complexity)

public bool WordBreak(string s, IList<string> wordDict) {
	HashSet<string> dict = new HashSet<string>(wordDict);

	return DFS(s, dict);
}

public bool DFS(string s, HashSet<string> dict){
	int len = s.Length;

	if(len == 0){
		return true;
	}

	for(int i = 1; i <= len; i++){
		if(dict.Contains(s.Substring(0, i)) && this.DFS(s.Substring(i), dict)){
			return true;
		}
	}

	return false;
}

Using Dynamic Programming

public bool WordBreak(string s, IList<string> wordDict) {
	HashSet<string> set = new HashSet<string>(wordDict);

	bool[] memo = new bool[s.Length + 1];

	memo[0] = true;

	for(int i = 1; i <= s.Length; i++){
		for(int j = 0; j < i; j++){
			if(memo[j] && set.Contains(s.Substring(j,i-j))){
				memo[i] = true;
				break;
			}
		}
	}

	return memo[s.Length];
}

Delete Node in a BST

Link - Delete Node in a BST

Using Recursion

There are 3 scenarios for deleting a case:

If the node to be deleted is:

  • Leaf Node - Delete that node.
  • Only one child - Replace the node to be deleted with its child node
  • Two children - Find the successor(of the node to be deleted by traversing all the way to the left most node of the that tree in the right subtree of the node to be deleted).
public TreeNode DeleteNode(TreeNode root, int key) {
	if(root == null){
		return root;
	}

	if(root.val > key){
		root.left = this.DeleteNode(root.left, key);
	}
	else if(root.val < key){
		root.right = this.DeleteNode(root.right, key);
	}
	else{
		if(root.left == null && root.right == null){
			return null;
		}
		else if(root.left == null){
			return root.right;
		}
		else if(root.right == null){
			return root.left;
		}
		else{
			TreeNode succ = this.GetSuccessor(root);
			root.val = succ.val;
			root.right = this.DeleteNode(root.right, succ.val);
		}
	}

	return root;
}

public TreeNode GetSuccessor(TreeNode root){
	TreeNode curr = root.right;

	while(curr != null && curr.left != null){
		curr = curr.left;
	}

	return curr;
}

Remove Duplicates from Sorted Array

Link - Remove Duplicates from Sorted Array

Using HashSet

This problem can be solved using Set. Simply loop through the array and add the values to the HashSet. Now loop through the Set and update those values back to the input array. The return value will be the size of the HashSet.

This solution has a time complexity of O(n) and space complexity of O(n).

Two Pointer Approach

Use two pointers, slow and fast to find the duplicates. The fast pointer checks if its value is the same as the slow pointer. If the values of slow and fast pointers match then increment only the fast pointer. If they differ, then increment the slow pointer and update its value with the value of the fast pointer. Keep doing this until the fast pointer reaches the end of the array.

    public int RemoveDuplicates(int[] nums) {
        // If the array length is 0 or 1 then return their length
        if(nums.Length < 2){
            return nums.Length;
        }

        int slowPointer = 0;
        int fastPointer = 1;

        while(fastPointer < nums.Length){
            if(nums[fastPointer] == nums[slowPointer]){
                fastPointer++;
            }
            else{
                slowPointer++;
                nums[slowPointer] = nums[fastPointer];
                fastPointer++;

            }
        }
        return slowPointer+1;
    }

This solution has a time complexity of O(n) and space complexity of O(1).

ApproachTime ComplexitySpace Complexity
Using HashSetO(n)O(n)
Two Pointer ApproachO(n)O(1)

Maximum Product Subarray

Link - Maximum Product Subarray

Greedy Approach

public int MaxProduct(int[] nums) {
	if(nums.Length == 0){
		return 0;
	}

	int min = nums[0];
	int max = nums[0];
	int maxSoFar = nums[0];

	for(int i = 1; i < nums.Length; i++){

		if(nums[i] < 0){
			int temp = min;
			min = max;
			max = temp;
		}

		min = Math.Min(nums[i], min * nums[i]);
		max = Math.Max(nums[i], max * nums[i]);

		maxSoFar = Math.Max(maxSoFar, max);
	}

	return maxSoFar;
}

Trapping Rain Water

Link - Trapping Rain Water

Dynamic Programming

Scan the given array starting from left by comparing its height with the previous elements. Store these values in an array. Repeat the same process by starting the scan from the right towards the left and store it in another array. Now using both these arrays find the max water that can be trapped. See the below solution.

public int Trap(int[] height) {
	int n = height.Length;

	if(n == 0){
		return 0;
	}

	int[] leftScan = new int[n];
	int[] rightScan = new int[n];

	leftScan[0] = height[0];
	rightScan[n-1] = height[n-1];

	for(int i = 1; i < n; i++){
		leftScan[i] = Math.Max(height[i], leftScan[i-1]);
	}

	for(int i = n-2; i >= 0; i--){
		rightScan[i] = Math.Max(height[i], rightScan[i+1]);
	}

	int maxWater = 0;

	for(int i = 0; i < n; i++){
		// Min of the two is taken since the water can stored until the level of the smallest wall. It is then subtracted with the height of the wall.
		maxWater += Math.Min(leftScan[i], rightScan[i]) - height[i];
	}

	return maxWater;
}

Two Pointer Approach

The previous solution can be improved by using two pointer approach. Start one pointer from 0th index and the other from the last index and move them to the center until the pointers overlap. See the below solution.

public int Trap(int[] height) {
	int leftIndex = 0;
	int rightIndex = height.Length - 1;

	int leftMax = 0;
	int rightMax = 0;

	int maxWater = 0;

	while(leftIndex <= rightIndex){

		if(height[leftIndex] <= height[rightIndex]){
			if(height[leftIndex] >= leftMax){
				leftMax = height[leftIndex];
			}
			else{
				maxWater += leftMax - height[leftIndex];
			}
			leftIndex++;
		}
		else{
			if(height[rightIndex] >= rightMax){
				rightMax = height[rightIndex];
			}
			else{
				maxWater += rightMax - height[rightIndex];
			}
			rightIndex--;
		}
	}
	return maxWater;
}
ApproachTime ComplexitySpace Complexity
Dynamic ProgrammingO(n)O(n)
Two Pointer ApproachO(n)O(1)

N-ary Tree Level Order Traversal

Link - N-ary Tree Level Order Traversal

Breadth-First Search Approach

The solution to this problem is a typical BFS approach, where you enqueue all the nodes of a level to a Queue. And when dequeuing the nodes, you enqueue the children of those to the Queue. Keep repeating this process until the Queue goes empty.

public IList<IList<int>> LevelOrder(Node root) {
    IList<IList<int>> output = new List<IList<int>>();

    if(root == null){
        return output;
    }

    Queue<Node> bfsQueue = new Queue<Node>();
    bfsQueue.Enqueue(root);

    while(bfsQueue.Count != 0){
        int count = bfsQueue.Count;

        List<int> temp = new List<int>();

        for(int i = 0; i < count; i++){
            Node tempNode = bfsQueue.Dequeue();

            temp.Add(tempNode.val);
            foreach(Node item in tempNode.children){
                bfsQueue.Enqueue(item);
            }
        }
        output.Add(temp);
    }

    return output;
}

Replace Elements with Greatest Element on Right Side

Link - Replace Elements with Greatest Element on Right Side

Scan from Right to Left

Scan from the rightmost element of the array towards left. Store the maximum number in a variable as you scan through the array. As you scan if a new maxNum is found then replace it. Keep doing this until the 0th index of the array.

public int[] ReplaceElements(int[] arr) {
    int[] output = new int[arr.Length];

    output[arr.Length - 1] = -1;
    int maxNum = arr[arr.Length - 1];

    for(int i = arr.Length - 2; i >= 0; i--){
        output[i] = maxNum;
        if(arr[i] > maxNum){
            maxNum = arr[i];
        }
    }

    return output;
}

Pancake Sorting

Link - Pancake Sorting

Intuitive Approach

public IList<int> PancakeSort(int[] A) {
	List<int> output = new List<int>();

	for(int i = A.Length; i > 0; i--){
		int currIndex = this.Find(A, i);

		// If the current number is in the current position then it need not be flipped
		if(currIndex == i-1){
			continue;
		}

		this.Flip(A, currIndex);
		this.Flip(A, i-1);

		output.Add(currIndex+1);
		output.Add(i);
	}

	return output;
}

public int Find(int[] A, int num){
	for(int i = 0; i < A.Length; i++){
		if(A[i] == num){
			return i;
		}
	}

	return -1;
}

public void Flip(int[] A, int eIndex){
	int sIndex = 0;

	while(sIndex < eIndex){
		int temp = A[sIndex];
		A[sIndex] = A[eIndex];
		A[eIndex] = temp;
		sIndex++;
		eIndex--;
	}
}

Keys and Rooms

Link - Keys and Rooms

Depth First Search

public bool CanVisitAllRooms(IList<IList<int>> rooms) {
	HashSet<int> set = new HashSet<int>();
	set.Add(0);

	Stack<int> stack = new Stack<int>();
	stack.Push(0);

	while(stack.Count > 0){
		int room = stack.Pop();

		foreach(int item in rooms[room]){
			if(!set.Contains(item)){
				set.Add(item);
				stack.Push(item);
			}
		}
	}

	return rooms.Count == set.Count;
}

Search a 2D Matrix

Link - Search a 2D Matrix

Naive Approach

The naive approach to solve this would be to go through every element in the array and check if the target number matches any one of them.

This solution has a time complexity of O(m*n) and space complexity of O(1).

Binary Search

Perform a simple binary search operation on the given 2D matrix iteratively to check if any number matches the given target number.

public bool SearchMatrix(int[][] matrix, int target) {
    int rows = matrix.Length;

    // Checks if the matrix is a 0 x 0 matrix
    if(rows == 0){
        return false;
    }

    int columns = matrix[0].Length;

    int start = 0;
    int end = (rows * columns) - 1;

    while(start <= end){
        int mid = (start + end) / 2;
        int midval = matrix[mid / columns][mid % columns];

        if(midval == target){
            return true;
        }
        else if (midval < target){
            start = mid + 1;
        }
        else{
            end = mid - 1;
        }
    }
    return false;
}

This solution has a time complexity of O(log(m*n)) and space complexity of O(1).

ApproachTime ComplexitySpace Complexity
Naive ApproachO(m*n)O(1)
Binary SearchO(log(m*n))O(1)

Remove Duplicate Letters

Link - Remove Duplicate Letters

Naive Solution

public string RemoveDuplicateLetters(string s) {
	if(s.Length == 0){
		return "";
	}

	int[] count = new int[26];

	for(int i = 0; i < s.Length; i++){
		count[s[i] - 'a']++;
	}

	StringBuilder sb = new StringBuilder();
	HashSet<char> set = new HashSet<char>();

	for(int i = 0; i < s.Length; i++){
		count[s[i] - 'a']--;

		if(set.Contains(s[i])){
			continue;
		}

		while(sb.Length > 0 && sb[sb.Length - 1] > s[i] && count[sb[sb.Length - 1] - 'a'] > 0){
			set.Remove(sb[sb.Length - 1]);
			sb.Length--;
		}

		set.Add(s[i]);
		sb.Append(s[i]);
	}

	return sb.ToString();
}

Split a String in Balanced Strings

Link - Split a String in Balanced Strings

Greedy Approach

public int BalancedStringSplit(string s) {
	int count = 0;

	int balance = 0;

	foreach(char c in s){
		if(c == 'R'){
			balance++;
		}
		else if(c == 'L'){
			balance--;
		}

		if(balance == 0){
			count++;
		}

	}

	return count;
}

Fizz Buzz Multithreaded

Link - Fizz Buzz Multithreaded

Using lock

public class FizzBuzz {
    private int n;
    private int i;
    private object sync;

    public FizzBuzz(int n) {
        this.n = n;
        i = 1;
        sync = new object();
    }

    // printFizz() outputs "fizz".
    public void Fizz(Action printFizz) {
        while(i<=n){
            lock(sync){
                if(i<=n && i%3==0 && i%5!=0)
                {
                        printFizz();
                        i++;
                }
            }
        }
    }

    // printBuzzz() outputs "buzz".
    public void Buzz(Action printBuzz) {
        while(i<=n){
            lock(sync){
                if(i<=n && i%3!=0 && i%5==0)
                {
                        printBuzz();
                        i++;
                }
            }
        }
    }

    // printFizzBuzz() outputs "fizzbuzz".
    public void Fizzbuzz(Action printFizzBuzz) {
        while(i<=n){
            lock(sync){
                if(i<=n && i%3==0 && i%5==0)
                {
                        printFizzBuzz();
                        i++;
                }
            }
        }
    }

    // printNumber(x) outputs "x", where x is an integer.
    public void Number(Action<int> printNumber) {
        while(i<=n){
            lock(sync){
                if(i<=n && i%3!=0 && i%5!=0)
                {
                        printNumber(i);
                        i++;
                }
            }
        }
    }
}

X of a Kind in a Deck of Cards

Link - X of a Kind in a Deck of Cards

Dictionary Approach

The easiest and the fastest approach to solve this problem is to use dictionary to store the number of times a specific number has appeared in the array. So first, loop through the items in the array and put them in a dictionary (key being the number and value being number of times the number has repeated).

Consider an example array [1,1,2,2,3,3,4] then dictionary will have {1: 2, 2:‌2, 3:2: 4:1}

Now once this is done I could just loop through the items in the dictionary and see if the values are matching, something like the below code

foreach(int item in dict.Values){
  if (partitionSize != item){
    return true;
  }
}
return false;

The above code works, but it fails some times.

Consider the following input array [1,1,2,2,2,2], the dictionary will be {1:2, 2:4}. This above piece of code will return false since partition sizes are different. But it should have returned true since it can partitioned in to [1,1] [2,2] [2,2].

To pass the above scenario, we should avoid checking the partition size and check the GCD of all the values (combined GCD) in the dictionary. So we loop through the dictionary and find their combined GCD.

Also the problem statement has mentioned that the partition size has to be greater than 1. So just put a check at the end and see if the combined GCD is greater than one and return a boolean value based on that.

See the below code for the whole solution in C#.

public bool HasGroupsSizeX(int[] deck) {
        Dictionary<int, int> dict = new Dictionary<int, int>();

        for (int i = 0 ; i < deck.Length; i++){
            if (dict.ContainsKey(deck[i])){
                dict[deck[i]]++;
            }
            else{
                dict[deck[i]] = 1;
            }
        }

        int combinedGcd = -1;

        foreach(int item in dict.Values){
            if (combinedGcd == -1){
                combinedGcd = item;
            }
            combinedGcd = this.Gcd(combinedGcd, item);
        }

        if (combinedGcd < 2){
            return false;
        }
        return true;
    }

    public int Gcd(int a, int b){
        if (a == 0){
            return b;
        }
        return this.Gcd(b%a, a);
    }

Find All Anagrams in a String

Link - Find All Anagrams in a String

Using Sliding Window Technique

public IList<int> FindAnagrams(string s, string p) {
	List<int> output = new List<int>();

	if(s.Length == 0 || p.Length == 0){
		return output;
	}

	Dictionary<char, int> map = new Dictionary<char, int>();

	foreach(char c in p){
		if(map.ContainsKey(c)){
			map[c] += 1;
		}
		else{
			map[c] = 1;
		}
	}

	int start = 0;
	int end = 0;
	int count = map.Count;

	while(end < s.Length){
		char c = s[end];
		if(map.ContainsKey(c)){
			map[c] = map[c] - 1;
			if(map[c] == 0){
				count--;
			}
		}
		end++;

		while(count == 0){
			char c2 = s[start];
			if(map.ContainsKey(c2)){
				map[c2] = map[c2] + 1;
				if(map[c2] > 0){
					count++;
				}
			}

			if(end - start == p.Length){
				output.Add(start);
			}
			start++;
		}
	}

	return output;
}

Delete Node in a Linked List

Link - Delete Node in a Linked List

Updating next pointer

public void DeleteNode(ListNode node) {
	node.val = node.next.val;
	node.next = node.next.next;
}

Find Nth Fibonacci Number

Note: This problem was taken from the Daily Coding Problem #233

Problem Statement

This problem was asked by Apple.

Implement the function fib(n), which returns the nth number in the Fibonacci sequence, using only O(1) space.

public int fib(int N)
{
    if(N < 2)
    {
        return N;
    }
    int prev = 0;
    int curr = 1;

    for(int i = 0; i < N -1; i++)
    {
        int temp = prev + curr;
        prev = curr;
        curr = temp;
    }

    return curr;
}

Sorting Algorithm - Quick Sort

Problem Statement

Sort integer array using Quick Sort

Solution

In this sorting technique, you divide the given array into sub arrays and sort those sub arrays by selecting a pivot element.

// Call this function to sort the array  - Sorting.QuickSort(arr, 0, arr.Length - 1);

public static class Sorting
{
	public static void QuickSort(int[] arr, int start, int end){
		if(start < end){
			int pIndex = Sorting.Partition(arr, start, end);

			Sorting.QuickSort(arr, start, pIndex-1);
			Sorting.QuickSort(arr, pIndex+1, end);
		}
	}

	public static int Partition(int[] arr, int start, int end){
		int pivot = arr[end]; // Last element of the sub array is considered as pivot

		int i = start - 1;

		for(int j = start; j <= end-1; j++){
			if(arr[j] < pivot){
				i++;
				Sorting.Swap(arr, i, j);
			}
		}

		Sorting.Swap(arr, i+1, end);
		return i + 1;
	}

	public static void Swap(int[] arr, int i, int j){
		int temp = arr[i];
		arr[i] = arr[j];
		arr[j] = temp;
	}
}

The above sorting technique has nlogn average time complexity. This sorting method used Divide and Conquer approach. This sorting approach sorts array using in-place method compared to merge sort which creates a temporary array to save the sorted elements of a sub array.

Number of Students Doing Homework at a Given Time

Link - Number of Students Doing Homework at a Given Time

Linear Scan

public int BusyStudent(int[] startTime, int[] endTime, int queryTime) {
	int count = 0;

	for(int i = 0; i < startTime.Length; i++){
		if(startTime[i] <= queryTime && endTime[i] >= queryTime){
			count++;
		}
	}

	return count;
}

All Elements in Two Binary Search Trees

Link - All Elements in Two Binary Search Trees

Using InOrder Traversal + Queue

Perform In Order traversal on both the BST’s and add it to Queue. Now dequeue the values from both the Queues based on the values and push it to a new list.

public IList<int> GetAllElements(TreeNode root1, TreeNode root2) {
    Queue<int> list1 = new Queue<int>();
    Queue<int> list2 = new Queue<int>();

    this.InOrderTraversal(root1, list1);
    this.InOrderTraversal(root2, list2);

    List<int> output = new List<int>();

    while(list1.Count != 0 || list2.Count != 0){
        if(list1.Count == 0){
            output.Add(list2.Dequeue());
        }
        else if(list2.Count == 0){
            output.Add(list1.Dequeue());
        }
        else{
            if(list1.Peek() < list2.Peek()){
                output.Add(list1.Dequeue());
            }
            else{
                output.Add(list2.Dequeue());
            }
        }
    }

    return output;
}

public void InOrderTraversal(TreeNode root, Queue<int> output){
    if(root == null){
        return;
    }

    this.InOrderTraversal(root.left, output);
    output.Enqueue(root.val);
    this.InOrderTraversal(root.right, output);
}

Using InOrder Traversal + Merge

public IList<int> GetAllElements(TreeNode root1, TreeNode root2) {
	List<int> l1 = new List<int>();
	List<int> l2 = new List<int>();

	this.Traverse(root1, l1);
	this.Traverse(root2, l2);

	List<int> output = new List<int>();

	int l1Index = 0;
	int l2Index = 0;

	while(l1Index < l1.Count && l2Index < l2.Count){
		if(l1[l1Index] < l2[l2Index]){
			output.Add(l1[l1Index]);
			l1Index++;
		}
		else{
			output.Add(l2[l2Index]);
			l2Index++;
		}
	}

	if(l1Index == l1.Count){
		while(l2Index < l2.Count){
			output.Add(l2[l2Index]);
			l2Index++;
		}
	}
	else if(l2Index == l2.Count){
		while(l1Index < l1.Count){
			output.Add(l1[l1Index]);
			l1Index++;
		}
	}

	return output;
}

public void Traverse(TreeNode root, List<int> output){
	if(root == null){
		return;
	}

	this.Traverse(root.left, output);
	output.Add(root.val);
	this.Traverse(root.right, output);
}

Word Pattern

Link - Word Pattern

Using Two HashSets

minCost += (group sum of repeated characters) - (max number in that group)

public bool WordPattern(string pattern, string str) {
	Dictionary<char,string> c2str = new Dictionary<char, string>();
	Dictionary<string,char> str2c = new Dictionary<string,char>();
	string[] words = str.Split(" ");

	if(words.Length != pattern.Length){
		return false;
	}

	for(int i = 0; i < pattern.Length; i++){
		if(c2str.ContainsKey(pattern[i]) && str2c.ContainsKey(words[i])){
			if(c2str[pattern[i]] != words[i] || str2c[words[i]] != pattern[i]){
				return false;
			}
		}
		else if((c2str.ContainsKey(pattern[i]) && !str2c.ContainsKey(words[i])) || (!c2str.ContainsKey(pattern[i]) && str2c.ContainsKey(words[i]))){
			return false;
		}
		else{
			c2str[pattern[i]] = words[i];
			str2c[words[i]] = pattern[i];
		}
	}

	return true;
}

Common System Design Interview Questions

type: interview-questions

Note: Following list was curated by going through various articles and blog posts

  • Design a URL shortening Service (tinyURL)

  • Design Pastebin

  • Design a chat service (Whatsapp)

  • Design a Video Streaming Service (Youtube, Netflix)

  • Desing a Social Network (Facebook, Twitter)

  • Design a File Storage Service (Dropbox, Google Drive)

  • Design a Ride Sharing Service (Uber)

  • Design a Type Ahead Suggestion

  • Design a API Rate Limiter

Links

Number of Islands

Link - Number of Islands

Depth First Search

Perform a DFS on all the nodes in the given grid. Each time a node is visited update the node value to V as in visited. This is done to prevent the islands from being counted twice.

public int NumIslands(char[][] grid) {
	int maxRow = grid.Length;

	// Edge case to check if the grid is of size 0x0
	if(maxRow == 0){
		return 0;
	}

	int maxCol = grid[0].Length;

	int count = 0;

	for(int i = 0; i < maxRow; i++){
		for(int j = 0; j < maxCol; j++){
			if(grid[i][j] == '1'){
				this.DFS(grid, i, j, maxRow, maxCol);
				count++;
			}
		}
	}

	return count;
}

public void DFS(char[][] grid, int row, int col, int maxRow, int maxCol){
	// Validating if the given row and col indexes are in the grid range
	if(row < 0 || row >= maxRow || col < 0 || col >= maxCol || grid[row][col] != '1'){
		return;
	}

	grid[row][col] = 'V';

	// For a row and column index recursively call the DFS function on the neighbouring nodes i.e. (-1, 0), (0, -1), (1, 0), (0, 1)
	this.DFS(grid, row+1, col, maxRow, maxCol);
	this.DFS(grid, row-1, col, maxRow, maxCol);
	this.DFS(grid, row, col+1, maxRow, maxCol);
	this.DFS(grid, row, col-1, maxRow, maxCol);
}

Basic Calculator II

Link - Basic Calculator II

Using Stack

public int Calculate(string s) {
	Stack<int> st = new Stack<int>();

	int currNum = 0;
	char operation = '+';

	for(int i = 0; i < s.Length; i++){
		if(char.IsDigit(s[i])){
			Console.WriteLine(Convert.ToInt32(s[i] - '0'));
			currNum = (currNum * 10) + Convert.ToInt32(s[i] - '0');
		}

		if((!char.IsDigit(s[i]) && s[i] != ' ') || i == s.Length - 1){
			if(operation == '+'){
				st.Push(currNum);
			}
			else if(operation == '-'){
				st.Push(-currNum);
			}
			else if(operation == '*'){
				int prev = st.Pop();
				st.Push(prev * currNum);
			}
			else if(operation == '/'){
				int prev = st.Pop();
				st.Push(prev / currNum);
			}

			operation = s[i];
			currNum = 0;
		}
	}

	int output = 0;

	while(st.Count > 0){
		output += st.Pop();
	}

	return output;
}

Count Odd Numbers in an Interval Range

Link - Count Odd Numbers in an Interval Range

Logical Approach

public int CountOdds(int low, int high) {
	int lowMod = low%2;
	int highMod = high%2;

	if(lowMod == 0 && highMod == 0){
		return (high-low)/2;
	}
	else{
		return (high-low)/2 + 1;
	}
}

Insertion Sort List

Link - Insertion Sort List

Using Insertion Sort

public ListNode InsertionSortList(ListNode head) {
	if(head == null){
		return head;
	}

	ListNode dummy = new ListNode(0);

	ListNode prev = dummy;
	ListNode curr = head;
	ListNode next = null;

	while(curr != null){
		next = curr.next;

		while(prev.next != null && prev.next.val < curr.val){
			prev = prev.next;
		}

		curr.next = prev.next;
		prev.next = curr;
		prev = dummy;
		curr = next;
	}

	return dummy.next;
}

Best Time to Buy and Sell Stock with Transaction Fee

Link - Best Time to Buy and Sell Stock with Transaction Fee

Dynamic Programming

public int MaxProfit(int[] prices, int fee) {
	int cash = 0;
	int hold = -prices[0];

	for(int i = 1; i < prices.Length; i++){
		cash = Math.Max(cash, hold + prices[i] - fee);
		hold = Math.Max(hold, cash - prices[i]);
	}

	return cash;
}

Defuse the Bomb

Link - Defuse the Bomb

Straight Forward Solution

public int[] Decrypt(int[] code, int k) {

	int N = code.Length;

	int[] output = new int[N];

	if(k == 0){
		return output;
	}
	else if(k < 0){
		for(int i = 0; i < N; i++){
			int count = 0;
			while(count < Math.Abs(k)){
				output[i] += code[(i - count - 1 + N)%N];
				count++;
			}
		}
	}
	else{
		for(int i = 0; i < N; i++){
			int count = 0;
			while(count < k){
				output[i] += code[(i + count + 1)%N];
				count++;
			}
		}
	}

	return output;
}

Coordinate With Maximum Network Quality

Link - Coordinate With Maximum Network Quality

Brute Force Solution

public int[] BestCoordinate(int[][] towers, int radius) {
	int N = towers.Length;

	int[] netQuality = new int[N];

	Dictionary<string, int> map = new Dictionary<string, int>();

	double rSquare = radius * radius;

	int maxSoFar = 0;

	for(int i = 0; i < N; i++){
		for(int j = 0; j < N; j++){
			double dist = Math.Pow(towers[i][0] - towers[j][0], 2) + Math.Pow(towers[i][1] - towers[j][1], 2);

			if(dist > rSquare){
				continue;
			}

			netQuality[i] += (int)Math.Floor(towers[j][2] / (1 + Math.Sqrt(dist)));
		}

		maxSoFar = Math.Max(maxSoFar, netQuality[i]);
	}

	int[] result = new int[]{Int32.MaxValue, Int32.MaxValue};

	for(int i = 0; i < N; i++){
		if(maxSoFar == netQuality[i]){

			if(towers[i][0] < result[0]){
				result[0] = towers[i][0];
				result[1] = towers[i][1];
			}
			else if(towers[i][0] == result[0]){
				if(towers[i][1] < result[1]){
					result[0] = towers[i][0];
					result[1] = towers[i][1];
				}
			}

		}
	}

	return result;
}

Most Visited Sector in a Circular Track

Link - Most Visited Sector in a Circular Track

Naive Approach

public IList<int> MostVisited(int n, int[] rounds) {
	if(rounds.Length == 0){
		return new List<int>();
	}

	Dictionary<int, int> map = new Dictionary<int, int>();

	for(int i = 1; i <= n; i++){
		map[i] = 0;
	}

	int currSector = rounds[0];
	map[currSector] = 1;
	for(int i = 1; i < rounds.Length; i++){
		this.AddCounts(map, currSector, rounds[i], n);
		currSector = rounds[i];
	}

	int maxCount = 0;
	foreach(var item in map){
		maxCount = Math.Max(maxCount, item.Value);
	}

	List<int> output = new List<int>();
	foreach(var item in map){
		if(item.Value == maxCount){
			output.Add(item.Key);
		}
	}

	return output;
}

public void AddCounts(Dictionary<int,int> map, int start, int end, int n){
	if(start <= end){
		for(int i = start+1; i<=end; i++){
			map[i] += 1;
		}
	}
	else{
		for(int i = start+1; i <= n; i++){
			map[i] += 1;
		}
		for(int i = 1; i <= end; i++){
			map[i] += 1;
		}
	}
}

Lowest Common Ancestor of a Binary Search Tree

Link - Lowest Common Ancestor of a Binary Search Tree

Using Level Order Traversal

Iteratively check if both search nodes belong to the same child (left or right child) of a node. If they belong to the same child nodes, then repeat this to the next level of the tree. If the search nodes belong to different child nodes then that is the ancestor node.

public TreeNode LowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
	TreeNode output = root;

	while(root != null){
		if(p.val < root.val && q.val < root.val){
			root = root.left;
		}
		else if(p.val > root.val && q.val > root.val){
			root = root.right;
		}
		else{
			output = root;
			break;
		}
	}

	return root;
}

Insert Delete GetRandom O(1)

Link - Insert Delete GetRandom O(1)

Using Recursive Approach

Random rnd;
Dictionary<int, int> dict;
List<int> lst;

public RandomizedSet() {
	rnd = new Random();
	lst = new List<int>();
	dict = new Dictionary<int,int>();
}

public bool Insert(int val) {
	if(dict.ContainsKey(val)){
		return false;
	}

	lst.Add(val);
	int index = lst.Count - 1;

	dict[val] = index;

	return true;
}

public bool Remove(int val) {
	if(!dict.ContainsKey(val)){
		return false;
	}

	// Swap the last item in the list with the given numbers' index value
	int index = dict[val];
	lst[index] = lst[lst.Count-1];
	dict[lst[index]] = index;

	// Remove the last item from thst list
	lst.RemoveAt(lst.Count - 1);
	dict.Remove(val);
	return true;
}

public int GetRandom() {
	int randomIndex = rnd.Next(0,lst.Count);
	return lst[randomIndex];
}

How Many Numbers Are Smaller Than the Current Number

Link - How Many Numbers Are Smaller Than the Current Number

Brute Force Approach

Check every element in the input array with the remaining elements and increase the counter on finding a smaller number.

public int[] SmallerNumbersThanCurrent(int[] nums) {
    int n = nums.Length;

    int[] output = new int[n];

    for(int i = 0; i < n; i++){
        for(int j = 0; j < n; j++){
            if(nums[i] > nums[j]){
                output[i]++;
            }
        }
    }

    return output;
}

Sorting Approach

Initially sort the given array. Once sorted, iterate through the array and find the numbers smaller than the current number and put those values in a dictionary. Now construct the output using the dictionary values.

public int[] SmallerNumbersThanCurrent(int[] nums) {
    int n = nums.Length;

    int[] output = new int[n];

    // The array needs to be cloned since the output has to be returned in the input array format
    int[] numsClone = (int[])nums.Clone();

    Array.Sort(numsClone);

    Dictionary<int,int> tempDict = new Dictionary<int,int>();
    tempDict[numsClone[0]] = 0;

    for(int i = 1; i < n; i++){
        // If the numbers are same then they need not be aaded to the dictionary
        if(numsClone[i] != numsClone[i-1]){
            // Index will give you the count of numbers smaller than the current number
            tempDict[numsClone[i]] = i;
        }
    }

    for(int i = 0; i < n; i++){
        output[i] = tempDict[nums[i]];
    }

    return output;
}
ApproachTime ComplexitySpace Complexity
Brute ForceO(n^2)O(n)
Sorting & DictionaryO(nlogn)O(n)

Robot Bounded In Circle

Link - Robot Bounded In Circle

Naive Approach

public bool IsRobotBounded(string instructions) {
	int x = 0;
	int y = 0;
	int i = 0;
	int[,] dirs = new int[,]{{0, 1}, {1, 0}, {0, -1}, {-1, 0}};

	foreach(char c in instructions)
		if (c == 'R')
			i = (i + 1) % 4;
		else if (c == 'L')
			i = (i + 3) % 4;
		else {
			x += dirs[i,0];
			y += dirs[i,1];
		}
	return x == 0 && y == 0 || i > 0;
}

Summary Ranges

Link - Summary Ranges

Naive Approach

public IList<string> SummaryRanges(int[] nums) {
	List<string> output = new List<string>();

	if(nums.Length == 0){
		return output;
	}

	int start = nums[0];
	int prev = nums[0];
	int i = 0;

	while(i < nums.Length){
		if((long)nums[i] - (long)prev > 1){
			if(start == prev){
				output.Add(start.ToString());
			}
			else{
				output.Add(start.ToString() + "->" + prev.ToString());
			}

			start = nums[i];
			prev = nums[i];
		}
		else{
			prev = nums[i];
			i++;
		}
	}

	if(start == prev){
		output.Add(start.ToString());
	}
	else{
		output.Add(start.ToString() + "->" + prev.ToString());
	}

	return output;
}

Naive Approach (Code Improved)

public IList<string> SummaryRanges(int[] nums) {
	List<string> output = new List<string>();

	if(nums.Length == 0){
		return output;
	}

	int start = nums[0];
	int prev = nums[0];
	int i = 0;

	while(i <= nums.Length){
		if(i == nums.Length || (long)nums[i] - (long)prev > 1){
			if(start == prev){
				output.Add(start.ToString());
			}
			else{
				output.Add(start.ToString() + "->" + prev.ToString());
			}

			if(i == nums.Length){
				break;
			}

			start = nums[i];
			prev = nums[i];
		}
		else{
			prev = nums[i];
			i++;
		}
	}

	return output;
}

Middle of the Linked List

Link - Middle of the Linked List

Two-Pass Approach

First, find the length of the list by traversing through the list. Now find the midpoint of the list based on this length. Now traverse the list starting from the head until the midpoint.

public ListNode MiddleNode(ListNode head) {
    if(head == null){
        return head;
    }

    int count = this.Length(head);
    int mid = count / 2;

    for(int i = 0; i < mid; i++){
        head = head.next;
    }

    return head;
}

public int Length(ListNode head){
    ListNode dummy = new ListNode(0);
    dummy.next = head;

    int count = 0;

    while(dummy.next != null){
        count++;
        dummy = dummy.next;
    }

    return count;
}

Slow and Fast Pointer Approach (Tortoise and Hare Approach)

This is a slow and pointer problem where you maintain two pointers. The slow pointer jumps one node at a time while the fast pointer jumps two nodes at a time. Keep doing this until the fast pointer reaches the end. When the fast pointer reaches the end, the slow pointer will be pointing to the centre node of the list.

public ListNode MiddleNode(ListNode head) {
    if(head == null){
        return head;
    }

    ListNode slow = head;
    ListNode fast = head;

    while(fast != null && fast.next != null){
        slow = slow.next;
        fast = fast.next.next;
    }

    return slow;
}

Repeated Substring Pattern

Link - Repeated Substring Pattern

Using Window Approach

public bool RepeatedSubstringPattern(string s) {
	if(s.Length == 0){
		return true;
	}
	int winSize = 1;

	while(winSize <= (s.Length/2)){

		// Checking if this window size can fit in the given string. If not, then skip checking for that window size.
		if(s.Length % winSize != 0){
			winSize++;
			continue;
		}

		int windows = s.Length / winSize;

		bool flag = true;

		for(int i = 1; i < windows; i++){
			if(s.Substring(0, winSize) != s.Substring(i*winSize, winSize)){
				flag = false;
				break;
			}
		}

		if(flag == true){
			return true;
		}

		winSize++;
	}

	return false;
}

Approach 2

public bool RepeatedSubstringPattern(string s) {
	string newStr = s + s;
	return newStr.Substring(1, newStr.Length-2).Contains(s);
}

Lowest Common Ancestor of a Binary Tree

Link - Lowest Common Ancestor of a Binary Tree

Using Depth First Search

public TreeNode LowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
	List<TreeNode> path1 = new List<TreeNode>();
	List<TreeNode> path2 = new List<TreeNode>();

	this.FindNode(root, path1, p);
	this.FindNode(root, path2, q);

	int min = Math.Min(path1.Count, path2.Count);

	TreeNode output = null;

	for(int i = 0; i < min; i++){
		if(path1[i] == path2[i]){
			output = path1[i];
		}
		else{
			break;
		}
	}

	return output;
}

public bool FindNode(TreeNode root, List<TreeNode> path, TreeNode searchNode){
	if(root == null){
		return false;
	}

	path.Add(root);

	if(root == searchNode){
		return true;
	}

	bool flag = this.FindNode(root.left, path, searchNode) || this.FindNode(root.right, path, searchNode);

	if(flag == true){
		return true;
	}
	else{
		// Backtrack
		path.RemoveAt(path.Count - 1);
		return false;
	}
}

Two Sum

Link - Two Sum

Brute Force Method

Loop through every element in the array and check if the target - num number exists.

This solution has a time complexity of O(n^2) and space complexity of O(1). This solution has quadratic time complexity. See the next approach which solves it in linear time.

Dictionary Approach

Loop through the array and add the number and its index to a dictionary. Before you add it to the dictionary check if target - current number already exists in the dictionary. If it exists, then that’s the solution.

public int[] TwoSum(int[] nums, int target) {
    Dictionary<int,int> temp = new Dictionary<int,int>();

    for(int i = 0; i < nums.Length; i++){
        if(temp.ContainsKey(target - nums[i])){
            return new int[]{i, temp[target - nums[i]]};
        }
        temp[nums[i]] = i;
    }

    return new int[]{};
}

The above solution has a time complexity of O(n) and space complexity of O(n).

ApproachTime ComplexitySpace Complexity
Brute ForceO(n^2)O(1)
Dictionary ApproachO(n)O(n)

Maximum Level Sum of a Binary Tree

Link - Maximum Level Sum of a Binary Tree

Breadth First Search

public int MaxLevelSum(TreeNode root) {
    if(root == null){
        return 0;
    }

    Queue<TreeNode> bfsQueue = new Queue<TreeNode>();
    bfsQueue.Enqueue(root);

    int max = Int32.MinValue;
    int minLevel = 0;
    int depth = 0;

    while(bfsQueue.Count > 0){
        int count = bfsQueue.Count;
        int sum = 0;
        depth++;

        for(int i = 0; i < count; i++){
            TreeNode tempNode = bfsQueue.Dequeue();

            sum += tempNode.val;

            if(tempNode.left != null){
                bfsQueue.Enqueue(tempNode.left);
            }

            if(tempNode.right != null){
                bfsQueue.Enqueue(tempNode.right);
            }
        }

        if(sum > max){
            max = sum;
            minLevel = depth;
        }
    }

    return minLevel;
}

Kth Largest Element in an Array

Link - Kth Largest Element in an Array

Using Sorting

Sort the given array in-place. Now the Kth element from the last will have the Kth largest element in the given array.

public int FindKthLargest(int[] nums, int k) {
    Array.Sort(nums);
    int n = nums.Length;
    return nums[n - k];
}

Using Priority Queue

Enqueue all the items in the array to a Priority Queue. Now, dequeue items from the priority queue K number of times to get the desired result.

public int FindKthLargest(int[] nums, int k) {
    PriorityQueue items = new PriorityQueue();

    for(int i = 0; i < nums.Length; i++){
        items.Enqueue(nums[i]);
    }

    int j = 1;
    while(j < k){
        items.Dequeue();
        j++;
    }

    return items.Dequeue();
}
ApproachTime ComplexitySpace Complexity
Using SortingO(nlogn)O(1)
Using Priority QueueO(nlogn + nlogk)O(n)

Looking at the above time complexities we can see that the sorting technique has better time and space complexities. Seems like that, butn ot really. In the above code solution I am creating a new list and pushing all the items and building max heap. But ideally I can use the given input array and build max heap on it in-place. This will give us constant space complexity. Even the time complextiy is bad in case of priority queues, as per the above table. But consider a sitaution where you have a stream (large amounts of data that cannot be stored in memory at one point in time) of data coming in. In that case, sorting technique cannot be used, but the priority queue implementation will have a time complexity of nlogk.

Combination Sum

Link - Combination Sum

Using Backtracking

public IList<IList<int>> CombinationSum(int[] candidates, int target) {
	IList<IList<int>> output = new List<IList<int>>();

	Array.Sort(candidates);

	this.Combis(output, candidates, 0, target, new List<int>());

	return output;
}

public void Combis(IList<IList<int>> output, int[] candidates, int start, int target, List<int> tempList){
	if(target < 0){
		return;
	}
	else if(target == 0){
		List<int> temp = new List<int>();
		temp.AddRange(tempList);
		output.Add(temp);
	}
	else{
		for(int i = start; i < candidates.Length; i++){
			tempList.Add(candidates[i]);
			this.Combis(output, candidates, i, target - candidates[i], tempList);
			tempList.RemoveAt(tempList.Count - 1);
		}
	}

}

Car Pooling

Link - Car Pooling

Using Prefix Sum

This method uses nested loops to find the frequency count of ranges.

public bool CarPooling(int[][] trips, int capacity) {
	int N = trips.Length;

	int[] locs = new int[1001];

	for(int i = 0; i < N; i++){
		int start = trips[i][1];
		int end = trips[i][2];
		int size = trips[i][0];

		locs[start] += size;

		if(end < 1001){
			locs[end] -= size;
		}
	}

	int sum = 0;
	for(int i = 0; i < 1001; i++){
		locs[i] += sum;
		sum = locs[i];

		if(locs[i] > capacity){
			return false;
		}
	}

	return true;
}

Binary Tree Right Side View

Link - Binary Tree Right Side View

Breadth-First Search Approach

public IList<int> RightSideView(TreeNode root) {
    List<int> output = new List<int>();

    if(root == null){
        return output;
    }

    Queue<TreeNode> bfsQueue = new Queue<TreeNode>();
    bfsQueue.Enqueue(root);

    while(bfsQueue.Count > 0){
        int count = bfsQueue.Count;

        int last = 0;

        for(int i = 0; i < count; i++){
            TreeNode tempNode = bfsQueue.Dequeue();

            last = tempNode.val;

            if(tempNode.left != null){
                bfsQueue.Enqueue(tempNode.left);
            }

            if(tempNode.right != null){
                bfsQueue.Enqueue(tempNode.right);
            }
        }

        output.Add(last);
    }

    return output.ToArray();
}

Find Pivot Index

Link - Find Pivot Index

Prefix Sum

public int PivotIndex(int[] nums) {
	int N = nums.Length;

	if(N == 0){
		return -1;
	}

	int[] leftSum = new int[N];
	int[] rightSum = new int[N];

	leftSum[0] = nums[0];
	rightSum[N-1] = nums[N-1];

	for(int i = 1; i < N; i++){
		leftSum[i] = leftSum[i-1] + nums[i];
	}

	for(int i = N-2; i >= 0; i--){
		rightSum[i] = rightSum[i+1] + nums[i];
	}

	for(int i = 0; i < N; i++){
		if(leftSum[i] == rightSum[i]){
			return i;
		}
	}

	return -1;
}

Course Schedule II

Link - Course Schedule II

Topological Sorting using Kahn’s Algorithm

public int[] FindOrder(int numCourses, int[][] prerequisites) {
	List<HashSet<int>> map =  new List<HashSet<int>>();
	int[] inDegrees = new int[numCourses]; // This array will have the prerequisite count of all the courses

	for(int i = 0; i < numCourses; i++){
		map.Add(new HashSet<int>());
	}

	foreach(int[] item in prerequisites){
		int course = item[0];
		int depend = item[1];
		inDegrees[course]++;

		map[depend].Add(course);
	}

	Queue<int> bfsQueue = new Queue<int>();

	// The order of courses can be started from courses where the inDegrees count is 0
	for(int i = 0; i < numCourses; i++){
		if(inDegrees[i] == 0){
			bfsQueue.Enqueue(i);
		}
	}

	List<int> output = new List<int>();

	while(bfsQueue.Count > 0){
		int from = bfsQueue.Dequeue();

		output.Add(from);

		foreach(int to in map[from]){
			// Only add the course to the queue which have 0 in-degree count.
			// Reduce the count each time that course is taken
			inDegrees[to]--;
			if(inDegrees[to] == 0){
				bfsQueue.Enqueue(to);
			}
		}

	}

	return output.Count == numCourses ? output.ToArray() : new int[0];
}

Palindrome Partitioning

Link - Palindrome Partitioning

Depth First Search

public IList<IList<string>> Partition(string s) {
	IList<IList<string>> output = new List<IList<string>>();

	this.DFS(0, output, new List<string>(), s);

	return output;
}

private void DFS(int start, IList<IList<string>> output, List<string> temp, string s){
	if(start >= s.Length){
		List<string> tempList = new List<string>(temp);
		output.Add(tempList);
		return;
	}

	for(int end = start; end < s.Length; end++){
		if(this.IsPalindrome(s, start, end)){
			temp.Add(s.Substring(start, end-start+1));
			this.DFS(end+1, output, temp, s);
			temp.RemoveAt(temp.Count-1);
		}
	}
}

private bool IsPalindrome(string s, int low, int high){
	while(low < high){

		if(s[low] != s[high]){
			return false;
		}
		low++;
		high--;
	}

	return true;
}

Edit Distance

Link - Edit Distance

Using Top Down Approach (Time Limit Exceeded error)

public int MinDistance(string word1, string word2) {
	return this.EditDistance(word1, word2, 0, 0);
}

public int EditDistance(string word1, string word2, int index1, int index2){
	if(index1 >= word1.Length && index2 >= word2.Length){
		return 0;
	}

	if(index1 == word1.Length){
		return word2.Length - index2;
	}

	if(index2 == word2.Length){
		return word1.Length - index1;
	}

	if(word1[index1] == word2[index2]){
		return this.EditDistance(word1, word2, index1 + 1, index2 + 1);
	}

	int insert = 1 + this.EditDistance(word1, word2, index1, index2 + 1);
	int delete = 1 + this.EditDistance(word1, word2, index1 + 1, index2);
	int replace = 1 + this.EditDistance(word1, word2, index1 + 1, index2 + 1);

	return Math.Min(insert, Math.Min(delete, replace));
}

The time complexity of the above solution can be improved using Memoization

Using Bottom Up Approach

public int MinDistance(string word1, string word2) {
	int m = word1.Length;
	int n = word2.Length;

	int[,] dp = new int[m+1, n+1];

	// If one of the word is of length 0, then the edit distance will be the length of the word.
	for(int i = 0; i <= m; i++){
		dp[i,0]= i;
	}

	for(int i = 0; i <= n; i++){
		dp[0,i]= i;
	}

	for(int i = 1; i <= m; i++){
		for(int j = 1; j <= n; j++){
			if(word1[i-1] == word2[j-1]){
				dp[i,j] = dp[i-1,j-1];
			}
			else{
				dp[i,j] = 1 + Math.Min(dp[i-1,j], Math.Min(dp[i, j-1], dp[i-1,j-1]));
			}
		}
	}

	return dp[m,n];
}

Maximum Product of Three Numbers

Link - Maximum Product of Three Numbers

Using Sorting

public int MaximumProduct(int[] nums) {
	if(nums.Length < 3){
		return 0;
	}

	Array.Sort(nums);
	int N = nums.Length;

	return Math.Max(nums[N-1] * nums[N-2] * nums[N-3], nums[0] * nums[1] * nums[N-1]);
}