Leetcode and other problems in C#
Table of Contents
- Slowest Key
- Subsets
- Example
- Minimum Initial Energy to Finish Tasks
- Coin Change 2
- 132 Pattern
- Throne Inheritance
- Special Array With X Elements Greater Than or Equal X
- Possible Bipartition
- Largest Number
- People Whose List of Favorite Companies Is Not a Subset of Another List
- Max Difference You Can Get From Changing an Integer
- Search in a Binary Search Tree
- Island Perimeter
- Number of Steps to Reduce a Number to Zero
- Increasing Triplet Subsequence
- Search in Rotated Sorted Array
- Find GCD and LCM of two or more numbers
- Letter Combinations of a Phone Number
- Coin Change
- Maximum Ice Cream Bars
- Divide Two Integers
- Stream of Characters
- Permutations II
- Distribute Candies
- Longest Palindrome
- Search Insert Position
- Unique Paths
- Count Good Nodes in Binary Tree
- Decode Ways
- Widest Vertical Area Between Two Points Containing No Points
- Unique Paths III
- Roman to Integer
- Maximum Average Subarray I
- First Unique Character in a String
- Maximum Swap
- Permutation in String
- To Lower Case
- Random Point in Non-overlapping Rectangles
- Partition Labels
- Max Dot Product of Two Subsequences
- Single Number
- Two Sum II - Input array is sorted
- Non-overlapping Intervals
- Lemonade Change
- Convert Binary Number in a Linked List to Integer
- Sort Array by Increasing Frequency
- Assign Cookies
- Find Valid Matrix Given Row and Column Sums
- Number of 1 Bits
- Max Consecutive Ones III
- 3Sum With Multiplicity
- Largest Substring Between Two Equal Characters
- Pascal’s Triangle
- Check If Two String Arrays are Equivalent
- Encode and Decode TinyURL
- Path Sum
- Squares of a Sorted Array
- Linked List Cycle II
- Missing Number
- Minimum Add to Make Parentheses Valid
- Maximum Frequency Stack
- First Bad Version
- Smallest String With A Given Numeric Value
- Shuffle String
- Repeated DNA Sequences
- Get Maximum in Generated Array
- The k Strongest Values in an Array
- Range Sum Query - Mutable
- Set Mismatch
- Teemo Attacking
- Race Car
- Water Bottles
- Best Time to Buy and Sell Stock
- Fancy Sequence
- Design a Stack With Increment Operation
- Binary Tree Paths
- Complement of Base 10 Integer
- Buddy Strings
- Maximum XOR of Two Numbers in an Array
- Longest Common Subsequence
- Implement Queue using Stacks
- LRU Cache
- Decrease Elements To Make Array Zigzag
- Swimming Championship
- Balanced Binary Tree
- Custom Sort method using IComparable and IComparer interface
- Merge Two Sorted Lists
- Counting Bits
- String Compression
- Linked List Cycle
- Count Square Submatrices with All Ones
- Invert Binary Tree
- Course Schedule III
- Remove Linked List Elements
- Maximal Square
- Even Odd Tree
- Find Largest Value in Each Tree Row
- Sort Colors
- Contiguous Array
- Subdomain Visit Count
- Prison Cells After N Days
- Find the Difference
- Detect Cycles in 2D Grid
- License Key Formatting
- Sqrt(x)
- Reconstruct Itinerary
- Jump Game II
- Power of Two
- Maximum Subarray
- Deletion Distance
- Minimum Deletions to Make String Balanced
- Remove Palindromic Subsequences
- Crawler Log Folder
- Add Two Numbers
- Sum of Root To Leaf Binary Numbers
- Rotting Oranges
- Longest Word in Dictionary through Deleting
- Reorder List
- Search Algorithms
- Find Peak Element
- Convert to Base -2
- Thread Safe Stack
- Guess Number Higher or Lower
- Remove Covered Intervals
- Number of Recent Calls
- Sorting Algorithm - Selection Sort
- Make Two Arrays Equal by Reversing Sub-arrays
- Common Operating Systems Questions
- Valid Perfect Square
- Range Sum of Sorted Subarray Sums
- H-Index
- Special Positions in a Binary Matrix
- Minimum Deletion Cost to Avoid Repeating Letters
- Design Linked List
- Find the sum of two integers without using + or - operators
- K Closest Points to Origin
- Implement Priority Queue (Max Heap)
- Design Circular Queue
- Game of Life
- Minimum Number of Vertices to Reach All Nodes (star)
- Cheapest Flights Within K Stops
- Russian Doll Envelopes
- Reach a Number
- Consecutive Numbers Sum
- Interval List Intersections
- Find Common Characters
- Online Stock Span
- Maximum Width of Binary Tree
- Arranging Coins
- Find Minimum in Rotated Sorted Array
- Word Search II
- Permutation Sequence
- Longest Common Prefix
- Number of Good Pairs
- Check If a Word Occurs As a Prefix of Any Word in a Sentence
- Find First and Last Position of Element in Sorted Array
- Maximize Sum Of Array After K Negations
- Can Place Flowers
- N-ary Tree Postorder Traversal
- Longest Substring Without Repeating Characters
- Print FooBar Alternately
- Cousins in Binary Tree
- Word Search
- Climbing Stairs
- Shortest Path in Binary Matrix
- Swap Nodes in Pairs
- Unique Email Addresses
- Minimum Domino Rotations For Equal Row
- Valid Anagram
- Maximal Network Rank
- Subarray Product Less Than K
- Minimum Deletions to Make Character Frequencies Unique
- Unique Binary Search Trees
- Ugly Number II
- Reduce Array Size to The Half
- Design Parking System
- Range Sum Query 2D - Immutable
- Smallest Integer Divisible by K
- Add Binary
- Sort Array By Parity
- Diameter of Binary Tree
- Shuffle the Array
- Maximize Distance to Closest Person
- Merge k Sorted Lists
- Valid Palindrome
- Count of substrings with only 1s
- Valid Square
- Remove Nth Node From End of List
- Clone Graph
- Construct K Palindrome Strings
- Alert Using Same Key-Card Three or More Times in a One Hour Period
- Min Stack
- Combination Sum III
- Maximum Sum Circular Subarray
- Robot Return to Origin
- Short Encoding of Words
- Last Moment Before All Ants Fall Out of a Plank
- Intersection of Two Arrays
- 4Sum II
- Valid Sudoku
- Check If a String Contains All Binary Codes of Size K
- Distribute Candies to People
- 3Sum
- Network Delay Time
- Binary Tree Zigzag Level Order Traversal
- Min Cost to Connect All Points
- Determine if Two Strings Are Close
- Count and Say
- Evaluate Division
- Insert Interval
- Diagonal Traverse
- Is Subsequence
- Thousand Separator
- Wiggle Subsequence
- Largest Component Size by Common Factor
- Deepest Leaves Sum
- Gas Station
- Friend Circles
- Score of Parentheses
- Implement Trie (Prefix Tree)
- Add and Search Word - Data structure design
- Destination City
- Reverse Linked List
- Maximum Sum Obtained of Any Permutation
- Palindrome Number
- Set Matrix Zeroes
- Sequential Digits
- Sort Characters By Frequency
- Merge Sorted Array
- Number of Operations to Make Network Connected
- Best Time to Buy and Sell Stock II
- Kth Smallest Element in a BST
- Design Browser History
- Search a 2D Matrix II
- Minimum Number of Arrows to Burst Balloons
- Check if the Sentence Is Pangram
- Maximum Number of Coins You Can Get
- Average of Levels in Binary Tree
- Angle Between Hands of a Clock
- Invert a binary tree
- Count Complete Tree Nodes
- Matrix Diagonal Sum
- Sum of Left Leaves
- Reverse Bits
- Remove Duplicates from Sorted Array II
- Binary Tree Level Order Traversal
- Rotate Array
- Minimum Difference Between Largest and Smallest Value in Three Moves
- Maximum Nesting Depth of the Parentheses
- Count Sorted Vowel Strings
- Verifying an Alien Dictionary
- Linked List Random Node
- Champagne Tower
- Check Array Formation Through Concatenation
- Hamming Distance
- FSorting the Sentence
- Making File Names Unique
- Kth Missing Positive Number
- Simplified Fractions
- Odd Even Linked List
- Running Sum of 1d Array
- Binary Search
- Split Linked List in Parts
- Sorting Algorithm - Insertion Sort
- Even or Odd using Bitwise Operators
- Minimum Height Trees
- Find the Duplicate Number
- Minimum Depth of Binary Tree
- Binary Search Tree Iterator
- Recover Binary Search Tree
- Merge Intervals
- Valid Mountain Array
- Final Prices With a Special Discount in a Shop
- Basic Calculator
- Burst Balloons
- Longest Valid Parentheses
- Letter Case Permutation
- Uncrossed Lines
- Valid Parentheses
- Asteroid Collision
- Partition List
- Excel Sheet Column Title
- Course Schedule
- Design HashMap
- Reverse String
- Product of Array Except Self
- Serialize and Deserialize BST
- Binary Tree Level Order Traversal II
- Flipping an Image
- Check Completeness of a Binary Tree
- N-ary Tree Preorder Traversal
- Can Make Arithmetic Progression From Sequence
- Ambiguous Coordinates
- Find leap year
- Find a Corresponding Node of a Binary Tree in a Clone of That Tree
- Delete Operation for Two Strings
- Last Stone Weight
- Implement Stack using Linked List (using Generics)
- Find Servers That Handled Most Number of Requests
- Sliding Window Maximum
- The kth Factor of n
- N-th Tribonacci Number
- Jump Game
- Angle between the hour and minute hands
- Problem Statement
- Jump Game III
- Random Pick with Weight
- Check If It Is a Straight Line
- Move Zeros
- Convert Sorted Array to Binary Search Tree
- Top K Frequent Elements
- Note: Priority Queue Implementation can be found here
- Maximum Points You Can Obtain from Cards
- Uncommon Words from Two Sentences
- Sorting Algorithm - Bubble Sort
- Arithmetic Subarrays
- Compare Version Numbers
- Increasing Order Search Tree
- Jewels and Stones
- Pairs of Songs With Total Durations Divisible by 60
- Furthest Building You Can Reach
- Partition Equal Subset Sum
- XOR Operation in an Array
- Maximum Number of Vowels in a Substring of Given Length
- Dungeon Game
- Pseudo-Palindromic Paths in a Binary Tree
- Knight Dialer
- Remove Duplicates from Sorted List II
- Maximum Difference Between Node and Ancestor
- Add Digits
- Sum of All Odd Length Subarrays
- Design Underground System
- Find All Duplicates in an Array
- Mean of Array After Removing Some Elements
- Number of Ways Where Square of Number Is Equal to Product of Two Numbers
- Range Sum of BST
- Surrounded Regions
- Pascal’s Triangle II
- Find the range of a logical expression
- Rotate Image
- Minimum Time Visiting All Points
- Implement Priority Queue with Generics (Max Heap)
- Jump Game IV
- Smallest Subtree with all the Deepest Nodes
- Maximum Depth of Binary Tree
- Maximum Profit of Operating a Centennial Wheel
- Find intersection of two unsorted arrays
- Queue Reconstruction by Height
- Redundant Connection
- Rearrange Words in a Sentence
- Permutations
- Largest Perimeter Triangle
- Find XOR Sum of All Pairs Bitwise AND
- Add Two Numbers II
- Generate Random Point in a Circle
- Same Tree
- Spiral Matrix II
- Minimum Path Sum
- Decrypt String from Alphabet to Integer Mapping
- Rotate List
- Decoded String at Index
- Minimum Operations to Make Array Equal
- Stone Game IV
- Kids With the Greatest Number of Candies
- Backspace String Compare
- Validate Binary Search Tree
- Replace Words
- First Missing Positive
- Sum Root to Leaf Numbers
- House Robber
- Boats to Save People
- Reverse Integer
- Palindrome Linked List
- Remove All Adjacent Duplicates In String
- Bulb Switcher IV
- Reformat Date
- Subrectangle Queries
- Unique Morse Code Words
- Check If All 1’s Are at Least Length K Places Away
- Sort List
- Path Crossing
- Insert into a Binary Search Tree
- Maximum Depth of N-ary Tree
- H-Index II
- Flatten Binary Tree to Linked List
- Range Sum Query - Immutable
- Count Primes
- Number of Substrings With Only 1s
- Populating Next Right Pointers in Each Node
- Implement Union Find (Disjoint Set) Data Structure
- Intersection of Two Linked Lists
- Ransom Note
- Excel Sheet Column Number
- Implement Rand10() Using Rand7()
- Fizz Buzz
- Container With Most Water
- House Robber III
- Path with Maximum Probability
- Consecutive Characters
- Min Cost Climbing Stairs
- Build an Array With Stack Operations
- Goat Latin
- Sorting Algorithm - Merge Sort
- Word Break
- Delete Node in a BST
- Remove Duplicates from Sorted Array
- Maximum Product Subarray
- Trapping Rain Water
- N-ary Tree Level Order Traversal
- Replace Elements with Greatest Element on Right Side
- Pancake Sorting
- Keys and Rooms
- Search a 2D Matrix
- Remove Duplicate Letters
- Split a String in Balanced Strings
- Fizz Buzz Multithreaded
- X of a Kind in a Deck of Cards
- Find All Anagrams in a String
- Delete Node in a Linked List
- Find Nth Fibonacci Number
- Sorting Algorithm - Quick Sort
- Number of Students Doing Homework at a Given Time
- All Elements in Two Binary Search Trees
- Word Pattern
- Common System Design Interview Questions
- Number of Islands
- Basic Calculator II
- Count Odd Numbers in an Interval Range
- Insertion Sort List
- Best Time to Buy and Sell Stock with Transaction Fee
- Defuse the Bomb
- Coordinate With Maximum Network Quality
- Most Visited Sector in a Circular Track
- Lowest Common Ancestor of a Binary Search Tree
- Insert Delete GetRandom O(1)
- How Many Numbers Are Smaller Than the Current Number
- Robot Bounded In Circle
- Summary Ranges
- Middle of the Linked List
- Repeated Substring Pattern
- Lowest Common Ancestor of a Binary Tree
- Two Sum
- Maximum Level Sum of a Binary Tree
- Kth Largest Element in an Array
- Combination Sum
- Car Pooling
- Binary Tree Right Side View
- Find Pivot Index
- Course Schedule II
- Palindrome Partitioning
- Edit Distance
- Maximum Product of Three Numbers
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
- HereV
(prevNum) is greater thanI
(currNum) so add it to the result.IV
- HereI
(prevNum) is lesser thanV
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
Approach | Time Complexity | Space Complexity |
---|---|---|
Hashtable or Set | O(n) | O(n) |
Bitwise XOR | O(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;
}
Algorithm | Input type | Time Complexity | Space Complexity |
---|---|---|---|
Linear Search | Data need not be sorted | O(n) | O(1) |
Binary Search | Sorted Data required | O(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;
}
Approach | Time Complexity | Space Complexity |
---|---|---|
Brute Force | O(n) | O(1) |
Binary Search | O(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;
};
Print FooBar Alternately
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;
}
Word Search
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.
Approach | Time Complexity | Space Complexity |
---|---|---|
Recursion | O(n^2) | O(1) |
Recursion with Memoization | O(n) | O(n) |
Dynamic Programming | O(n) | O(n) |
Fibonnaci Sequence | O(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).
Approach | Time Complexity | Space Complexity |
---|---|---|
Using Sorting | O(nlogn) | O(1) |
Using Dictionary | O(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).
Approach | Time Complexity | Space Complexity |
---|---|---|
Naive Approach | O(n) | O(n) |
Two Pointer Approach | O(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;
}
Binary Search
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 XOR
ing it with 1 will reduce the number by 1. And XOR
ing 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
Approach | Time Complexity | Space Complexity |
---|---|---|
Dictionary Approach | O(n) | O(n) |
Sorting Approach | O(nlogn) | O(1) |
Tortoise and Hare Approach | O(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).
Approach | Time Complexity | Space Complexity |
---|---|---|
Using Brute Force | O(n^2) | O(1) |
Using Monotone Stack | O(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];
}
Approach | Time Complexity | Space Complexity |
---|---|---|
Recursion | O(2^(m*n)) | stack memory is used |
Dynamic Programming | O(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+2
th 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];
}
Approach | Time Complexity | Space Complexity |
---|---|---|
Dynamic Programming | O(n) | O(1) |
Recursion | O(n^2) | O(n) (recursive call uses stack memory) |
Recursion with Memoization | O(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).
Approach | Time Complexity | Space Complexity |
---|---|---|
Using HashSet | O(n) | O(n) |
Two Pointer Approach | O(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;
}
Approach | Time Complexity | Space Complexity |
---|---|---|
Dynamic Programming | O(n) | O(n) |
Two Pointer Approach | O(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).
Approach | Time Complexity | Space Complexity |
---|---|---|
Naive Approach | O(m*n) | O(1) |
Binary Search | O(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;
}
Approach | Time Complexity | Space Complexity |
---|---|---|
Brute Force | O(n^2) | O(n) |
Sorting & Dictionary | O(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).
Approach | Time Complexity | Space Complexity |
---|---|---|
Brute Force | O(n^2) | O(1) |
Dictionary Approach | O(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();
}
Approach | Time Complexity | Space Complexity |
---|---|---|
Using Sorting | O(nlogn) | O(1) |
Using Priority Queue | O(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]);
}