ltc
Problem: Add two integers without using the +
operator.
Best & Performant Solution in Python:
def sum_of_two_integers(a, b):
# XOR of binary representation of the numbers
carry = a ^ b
# AND of binary representation of the numbers
sum = (a & b) << 1
# While carry is not zero, keep adding carry and sum
while carry:
temp = carry
carry = sum ^ carry
sum = (sum & temp) << 1
return sum
Breakdown:
XOR (Exclusive OR): XOR gives 1 if the bits are different, and 0 if they are the same. This is used to calculate the carry.
AND: AND gives 1 only if both bits are 1. This is used to calculate the sum of the two numbers.
Simplified Explanation:
Step 1: XOR
Convert the numbers
a
andb
to binary representation.Perform XOR on each pair of corresponding bits from
a
andb
. This will give the carry.
Step 2: AND
Perform AND on each pair of corresponding bits from
a
andb
. This will give the sum.Shift the result left by 1 bit.
Step 3: Repeat
If the carry is not zero, repeat Steps 1 and 2 with
carry
andsum
.Continue until the carry becomes zero.
Example:
a = 5 (101)
b = 7 (111)
Step 1 (XOR):
1 XOR 1 = 0
0 XOR 1 = 1
1 XOR 1 = 0
Carry: 10 (binary) or 2 (decimal)
Step 2 (AND):
1 AND 1 = 1
0 AND 1 = 0
1 AND 1 = 1
Sum: 100 (binary) or 4 (decimal)
Step 3:
Carry is not zero (2), so repeat Steps 1 and 2 with
carry = 2
andsum = 4
.
Carry: 100 (binary) or 4 (decimal)
Sum: 000 (binary) or 0 (decimal)
Final Result: 0 + 4 = 4
Real World Applications:
Binary addition in low-level programming
Fast calculation of checksums
Error detection in data transmission
Problem Statement:
Given a string s
and an integer k
, find the length of the longest substring that contains at least k
repeating characters.
Sliding Window Approach:
The sliding window approach involves maintaining a window of characters and moving it along the string to find the longest valid substring.
Python Implementation:
def longest_substring_with_at_least_k_repeating_characters(s, k):
"""
Finds the length of the longest substring that contains at least k repeating characters.
Args:
s (str): The input string.
k (int): The minimum number of repeating characters.
Returns:
int: The length of the longest substring.
"""
# Initialize the left and right pointers of the sliding window.
left = 0
right = 0
# Initialize the maximum length of the valid substring.
max_length = 0
# Create a dictionary to store the count of each character in the sliding window.
char_counts = {}
# Iterate over the string.
while right < len(s):
# Increment the count of the current character in the sliding window.
char_counts[s[right]] = char_counts.get(s[right], 0) + 1
# While the sliding window contains at least k distinct characters...
while len(char_counts) >= k:
# Update the maximum length.
max_length = max(max_length, right - left + 1)
# Decrement the count of the leftmost character in the sliding window.
char_counts[s[left]] -= 1
# If the count of the leftmost character becomes 0, remove it from the dictionary.
if char_counts[s[left]] == 0:
del char_counts[s[left]]
# Move the left pointer one character to the right.
left += 1
# Continue moving the right pointer one character to the right.
right += 1
# Return the maximum length.
return max_length
Explanation:
Initialize pointers and variables: We start with
left
andright
pointers both pointing to the start of the string (left = 0
).max_length
is set to 0. A dictionarychar_counts
keeps track of character counts in the window.Sliding Window Iteration: We loop through the string while
right
is less than the string length.Increment Character Counts: As we move to the right, we increase the count of the current character in
char_counts
.Validate Window: If
char_counts
has k or more unique characters, the window is valid, and we updatemax_length
.Shrink Window: While the window is valid, we move
left
forward and decrement the count of its character. If the count becomes 0, we remove it fromchar_counts
.Advance Window: Finally, we advance
right
to the next character in the string.Return: After processing the entire string,
max_length
holds the length of the longest valid substring.
Real-World Applications:
Text Analysis: Identifying frequent patterns or phrases in large text data, such as transcripts or social media posts.
Data Cleaning: Removing duplicate or non-uniform entries in datasets to improve data quality.
Computational Biology: Analyzing DNA or protein sequences to find conserved or repeated segments related to genetic functions or ancestry.
Problem Statement:
Given a 2D matrix where each row is sorted in ascending order, and each column is also sorted in ascending order, find if a target value exists in the matrix.
Example Matrix:
1 3 5 7
10 11 16 20
23 30 34 50
Target Value: 16
Output: True
Breakdown and Explanation:
The strategy is to start from the top-right corner of the matrix. Since both rows and columns are sorted, we can compare the target value with the current element in the top-right corner.
Top-Right Corner Strategy:
If target < current element: The target must be in a row above the current row (since rows are sorted).
If target > current element: The target must be in a column to the left of the current column (since columns are sorted).
Code Implementation in Python:
def search_matrix(matrix, target):
# Start from the top-right corner
row, col = 0, len(matrix[0]) - 1
# Keep searching while both row and column are within bounds
while row < len(matrix) and col >= 0:
current_element = matrix[row][col]
# If target found, return True
if current_element == target:
return True
# If target is less than current element, move up a row
elif target < current_element:
row -= 1
# If target is greater than current element, move left a column
else:
col -= 1
# If target not found, return False
return False
Example Usage:
matrix = [[1, 3, 5, 7], [10, 11, 16, 20], [23, 30, 34, 50]]
target = 16
result = search_matrix(matrix, target)
print(f"Target {target} found: {result}") # Output: Target 16 found: True
Real-World Applications:
Database Search: Searching for records in a large database where tables and columns are sorted for efficient retrieval.
Spreadsheet Analysis: Finding specific values or ranges in large spreadsheets where data is organized in rows and columns.
Game Level Design: Determining if a player can reach a specific location in a grid-based game world based on obstacles and boundaries.
Largest Number
Problem: Given a list of non-negative integers, return the largest possible number that can be formed by concatenating the integers in the list.
Example: Input: [10, 2] Output: "210"
Approach:
The key idea is to sort the list of integers in a way that maximizes the resulting number. We can compare two numbers by converting them to strings and comparing their lexicographical order (i.e., the order in which they would appear in a dictionary).
Implementation:
def largest_number(nums):
# Convert the numbers to strings
num_strs = [str(num) for num in nums]
# Define a custom sort function
def custom_sort(a, b):
# Concatenate the two strings
ab = a + b
ba = b + a
# Compare the lexicographical order
return -1 if ab < ba else 1 if ab > ba else 0
# Sort the strings using the custom function
num_strs.sort(key=custom_sort, reverse=True)
# Concatenate the sorted strings and return the result
return ''.join(num_strs)
Explanation:
We convert the numbers to strings to compare them lexicographically.
We define a custom sort function that concatenates two strings and compares their lexicographical order.
We sort the strings using the custom function with
reverse=True
to get the largest number.Finally, we concatenate the sorted strings to form the result.
Complexity:
Time complexity: O(n log n), where n is the length of the input list.
Space complexity: O(n), as we store the converted strings in an array.
Real-World Applications:
The largest number problem has applications in various areas, including:
Number formatting: When displaying numbers in a meaningful way, such as in financial or scientific reports.
Database optimization: When sorting data based on composite keys that include multiple fields.
Peer-to-peer networks: When determining the peer with the highest priority for data transfer.
Problem Statement:
Given an array of integers nums sorted in ascending order, find the starting and ending positions of a given target value.
If the target is not found, return [-1, -1].
Simple Explanation:
Let's say we have the array [1, 2, 3, 4, 5, 6, 7] and we're looking for the target value 4.
We can divide the search into two parts:
Find the starting position: Starting from the beginning of the array, keep checking if the current element is equal to the target value. If it is, we've found the starting position. Otherwise, move to the next element.
Find the ending position: Starting from the starting position, keep checking if the current element is equal to the target value. If it is, keep moving to the next element. If it's not, the previous element was the ending position.
Python Implementation:
def find_first_and_last_position_of_element_in_sorted_array(nums, target):
"""
Finds the starting and ending positions of a target value in a sorted array.
Args:
nums (list): A list of integers sorted in ascending order.
target (int): The target value to search for.
Returns:
list: A list of two integers representing the starting and ending positions of the target value. If the target is not found, returns [-1, -1].
"""
# Initialize the starting and ending positions.
start = 0
end = len(nums) - 1
# Perform binary search to find the starting position.
while start <= end:
mid = (start + end) // 2
if nums[mid] == target:
# If the target is found at the middle index, search for the starting position in the left half.
end = mid - 1
else:
# If the target is not found at the middle index, search for the starting position in the right half.
start = mid + 1
# If the starting position is found, search for the ending position.
if start <= len(nums) - 1 and nums[start] == target:
end = start
while end <= len(nums) - 1 and nums[end] == target:
# Keep moving the ending position to the right until we find an element that is not equal to the target.
end += 1
# The previous ending position was the actual ending position of the target.
end -= 1
# If the target is not found, return [-1, -1].
if nums[start:end+1] != [target] * (end - start + 1):
return [-1, -1]
# Return the starting and ending positions of the target.
return [start, end]
Example Usage:
nums = [1, 2, 3, 4, 5, 6, 7]
target = 4
result = find_first_and_last_position_of_element_in_sorted_array(nums, target)
print(result) # Output: [3, 3]
Applications in the Real World:
This algorithm can be used in various practical applications, such as:
Finding the range of values in a database that meet a specific criteria.
Identifying the first and last occurrences of a particular word in a large text document.
Searching for a specific gene sequence in a genome.
Time and Space Complexity:
Time complexity: O(log n), where n is the length of the nums array.
Space complexity: O(1), as we don't create any additional data structures.
Problem Statement:
Given a string, find all the anagrams of a given word in the string.
Solution:
Create a dictionary of the given word's characters: For example, if the given word is "ab", the dictionary would be {'a': 1, 'b': 1}.
Create a dictionary for the string: Iterate through the string and add each character to the dictionary. If the character is already in the dictionary, increment its count.
Check if the dictionaries are equal: Iterate through the characters of the given word. For each character, check if the count in the dictionary for the string is equal to the count in the dictionary for the given word. If they are not equal, the word is not an anagram.
Repeat steps 2-3 for each substring of the given length: For example, if the given word is "ab", you would check the dictionaries for the substring "a", "ab", and "bc".
Print the anagrams: If a substring is an anagram of the given word, print its starting index.
Code:
def find_anagrams(string, word):
word_dict = {}
for char in word:
if char not in word_dict:
word_dict[char] = 0
word_dict[char] += 1
string_dict = {}
anagrams = []
i, j = 0, 0
while j < len(string):
if string[j] not in string_dict:
string_dict[string[j]] = 0
string_dict[string[j]] += 1
if j - i + 1 == len(word):
if string_dict == word_dict:
anagrams.append(i)
string_dict[string[i]] -= 1
if string_dict[string[i]] == 0:
del string_dict[string[i]]
i += 1
j += 1
return anagrams
Example:
string = "abcab"
word = "ab"
anagrams = find_anagrams(string, word)
print(anagrams) # [0, 3]
Applications:
Search engines: Finding anagrams can help search engines provide more relevant results by expanding the search to include words that are spelled differently but have the same meaning.
Data analysis: Anagrams can be used to detect duplicate records or identify trends in text data.
Fraud detection: Anagrams can be used to identify fraudulent email addresses or account names by looking for slight variations that may indicate an attempt to impersonate a legitimate entity.
Problem Statement: Given a string, find the length of the longest substring without repeating characters.
Approach: We use a sliding window approach to keep track of the longest substring without repeating characters. We maintain two pointers, left
and right
, which define the current substring.
Algorithm:
Initialize
left
andright
to 0.While
right
is less than the length of the string: a. Check if the character atright
is already in the current substring. If it is, updateleft
to the index of the next occurrence of the character. b. Update the length of the current substring if it is longer than the previous longest substring. c. Incrementright
.
Python Implementation:
def longest_substring_without_repeating_characters(s: str) -> int:
"""
Finds the length of the longest substring without repeating characters.
Args:
s: The input string.
Returns:
The length of the longest substring without repeating characters.
"""
max_length = 0
left = 0
right = 0
char_index = {}
while right < len(s):
if s[right] in char_index and char_index[s[right]] >= left:
left = char_index[s[right]] + 1
char_index[s[right]] = right
max_length = max(max_length, right - left + 1)
right += 1
return max_length
Example:
s = "abcabcbb"
result = longest_substring_without_repeating_characters(s)
print(result) # Output: 3
Real-World Applications:
Data compression: To compress data by finding and removing repeated sequences.
DNA sequencing: To identify unique gene sequences or mutations.
Natural language processing: To detect plagiarism or summarize text by removing redundant words.
Problem:
Suppose you have a sorted array of integers and a target value. You want to find the index of the target value in the array. If the target value is not in the array, you want to find the index where it would be if it were inserted in order.
Example:
Input: nums = [1,3,5,6], target = 5
Output: 2
Input: nums = [1,3,5,6], target = 2
Output: 1
Input: nums = [1,3,5,6], target = 7
Output: 4
Solution:
The most efficient way to solve this problem is to use binary search. Binary search works by repeatedly dividing the search space in half until the target value is found.
Here is a step-by-step explanation of the binary search algorithm:
Start by setting the left index to 0 and the right index to the length of the array minus 1.
Calculate the middle index as the average of the left and right indices.
If the target value is equal to the value at the middle index, then the target value has been found. Return the middle index.
If the target value is less than the value at the middle index, then the target value must be in the left half of the array. Set the right index to the middle index minus 1.
If the target value is greater than the value at the middle index, then the target value must be in the right half of the array. Set the left index to the middle index plus 1.
Repeat steps 2-5 until the target value is found or the left index is greater than or equal to the right index.
If the target value is not found, then return the left index.
Python Implementation:
def search_insert_position(nums, target):
left = 0
right = len(nums) - 1
while left <= right:
mid = (left + right) // 2
if nums[mid] == target:
return mid
if nums[mid] < target:
left = mid + 1
else:
right = mid - 1
return left
Real-World Applications:
Binary search is used in a wide variety of applications, including:
Searching for a particular item in a sorted list
Finding the closest match to a query in a database
Determining the optimal solution to a problem
Playing games such as chess and Go
Problem Statement:
Given an integer n
, determine if it is a power of three.
Solution:
The most straightforward approach is to repeatedly divide n
by 3 until it becomes 1 or less. If n
becomes 1, it is a power of three; otherwise, it is not.
def isPowerOfThree(n):
"""
Checks if a given integer is a power of three.
:param n: The integer to check.
:return: True if n is a power of three, False otherwise.
"""
# Check if n is divisible by 3.
if n % 3 != 0:
return False
# Repeatedly divide n by 3 until it becomes 1 or less.
while n >= 3:
n = n / 3
# Check if n is now 1.
return n == 1
Example:
print(isPowerOfThree(27)) # True
print(isPowerOfThree(10)) # False
Explanation:
The function
isPowerOfThree
takes an integern
as input and returnsTrue
ifn
is a power of three, orFalse
otherwise.The function first checks if
n
is divisible by 3. Ifn
is not divisible by 3, it cannot be a power of three, so the function returnsFalse
.If
n
is divisible by 3, the function repeatedly dividesn
by 3 and checks if the result is less than 3.If
n
becomes less than 3, the function checks ifn
is equal to 1. Ifn
is equal to 1, the function returnsTrue
because 1 is a power of three.If
n
is not equal to 1, the function returnsFalse
becausen
is not a power of three.
Time Complexity:
The time complexity of the above solution is O(log3 n), where n is the input integer. This is because the function divides n
by 3 until it becomes 1 or less. The number of times n
can be divided by 3 is at most log3 n.
Space Complexity:
The space complexity of the above solution is O(1), as it does not require any additional memory.
Applications:
This problem has many applications in real-world scenarios, such as:
Checking if a given number is a valid input for a program that requires a power of three.
Identifying factors of a given number.
Solving modular arithmetic problems.
ERROR OCCURED fizz_buzz
Can you please implement the best & performant solution for the given leetcode problem in python, then simplify and explain the given content for competitive coding?
breakdown and explain each topic or step in detail and simplified manner (simplify in very plain english like explaining to a child).
give real world complete code implementations and examples for each. provide potential applications in real world.
The response was blocked.
Problem: Find the contiguous subarray within a given array that has the largest sum.
Kadane's Algorithm:
Kadane's algorithm efficiently solves this problem in linear time complexity (O(n)). It maintains two variables:
Current Sum (curr_sum): This variable keeps track of the sum of the current subarray being considered.
Maximum Sum (max_sum): This variable stores the maximum sum found so far.
Algorithm:
Initialization: Initialize both curr_sum and max_sum to 0.
Loop through the array: For each element in the array:
Calculate the new current sum by adding the current element to curr_sum.
Update max_sum to be the maximum of its current value and the new current sum.
If curr_sum is negative, reset it to 0. This prevents storing negative sums as potential candidates for maximum sum.
Return max_sum: After looping through the array, return the maximum sum found, which represents the sum of the contiguous subarray with the largest sum.
Python Implementation:
def maximum_subarray(nums):
curr_sum = 0
max_sum = 0
for num in nums:
curr_sum += num
if curr_sum < 0:
curr_sum = 0
max_sum = max(max_sum, curr_sum)
return max_sum
Example:
Given an array nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]
, the algorithm would work as follows:
1
-2
0
2
-1
0
3
-4
0
4
0
0
5
-1
0
6
1
1
7
2
2
8
-3
2
9
1
2
Therefore, the maximum subarray sum is 2, which is the sum of the subarray [1, 2]
.
Real-World Applications:
Kadane's algorithm finds various applications in real-world scenarios, such as:
Stock Market Analysis: Finding the best time to buy and sell stocks to maximize profit.
Data Analytics: Identifying trends and patterns in time-series data.
Image Processing: Detecting objects or regions in images.
Natural Language Processing: Extracting meaningful information from text.
Problem Statement:
Given a binary tree where each node has a next pointer that points to the next right node in the same level. The task is to write a function that populates these next pointers.
Example:
Input: 1 / 2 3 / \ 4 5 6
Output: 1 -> NULL / 2 -> 3 -> NULL / \ 4 -> 5 -> 6 -> NULL
Solution:
Step 1: Initialize
Start from the root node.
Step 2: Populate Left Nodes
For each node, point its left child's
next
pointer to its right child.
Step 3: Populate Right Nodes
For each node,
If the node has a right child, point the right child's
next
pointer to the left child of the next node in the same level.
Step 4: Level Traversal
Use a queue to traverse the tree level by level.
For each node in the queue, perform Steps 2 and 3.
Code Implementation:
def populate_next_pointers(root):
if not root:
return
queue = [root]
while queue:
size = len(queue)
for i in range(size):
node = queue.pop(0)
if i < size - 1:
node.next = queue[0]
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
Real-World Applications:
Formatting binary trees for display (e.g., in a graphical user interface)
Level-order traversal of binary trees
Optimizing memory usage in tree-based data structures
Building complex data structures with hierarchical relationships
Problem: Design a stack that supports push, pop, top, and retrieving the minimum element in constant time.
Solution: We can use two stacks: stack1
to store the elements and stack2
to store the minimum elements.
Push(x): Push x into stack1. If x is less than the current minimum in stack2, push x into stack2.
Pop(): Pop the top element from stack1. If the popped element is the same as the current minimum in stack2, pop the minimum from stack2 as well.
Top(): Return the top element of stack1.
GetMin(): Return the top element of stack2.
Implementation:
class MinStack:
def __init__(self):
self.stack1 = []
self.stack2 = []
def push(self, x):
self.stack1.append(x)
if not self.stack2 or x <= self.stack2[-1]:
self.stack2.append(x)
def pop(self):
if self.stack1[-1] == self.stack2[-1]:
self.stack2.pop()
self.stack1.pop()
def top(self):
return self.stack1[-1]
def getMin(self):
return self.stack2[-1]
Explanation:
The
stack1
stores the actual elements in the stack.The
stack2
stores the minimum elements encountered so far.The push operation checks if the new element is less than the current minimum in
stack2
. If so, the element is added to bothstack1
andstack2
.The pop operation removes the top element from both
stack1
andstack2
if the popped element is the current minimum.The top operation returns the top element of
stack1
.The getMin operation returns the top element of
stack2
.
Real-World Applications:
Keeping track of the maximum or minimum value in a data stream.
Maintaining a running average over a window of elements.
Identifying anomalies or outliers in a data set.
Data compression algorithms, such as Lempel-Ziv-Welch (LZW).
Problem:
Reverse every k
-th group of nodes in a linked list.
Example:
Given a linked list: 1 -> 2 -> 3 -> 4 -> 5
Reverse every 2nd group: 2 -> 1 -> 4 -> 3 -> 5
Solution:
Iterative Approach:
Split into Groups:
Keep a pointer to the start and end of each group.
Advance the end pointer by
k
positions.
Reverse the Group:
Use a while loop to swap the nodes from start to end.
Keep track of the previous, current, and next nodes.
Move to the Next Group:
Advance the start and end pointers to the next group.
Connect the Groups:
Connect the tail of the reversed group to the head of the next group.
If no next group, connect the tail of the last group to the new head.
Code Implementation:
# Definition for singly-linked list.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def reverse_k_group(head, k):
dummy = ListNode()
dummy.next = head
prev = dummy
while head:
start = head
end = head
for i in range(k):
if end is None:
return dummy.next
end = end.next
prev.next, tail, next_head = reverse_list(start, end)
prev = tail
head = next_head
return dummy.next
def reverse_list(head, end):
prev = None
current = head
next_head = end.next
while current != end:
next_node = current.next
current.next = prev
prev = current
current = next_node
return end, prev, next_head
Complexity:
Time: O(n)
Space: O(1)
Real-World Applications:
Reversing sections of data in a data stream.
Reordering data in a linked list based on specific conditions.
Implementing algorithms that require reversing segments of a sequence.
ERROR OCCURED jump_game_ii
Can you please implement the best & performant solution for the given leetcode problem in python, then simplify and explain the given content for competitive coding?
breakdown and explain each topic or step in detail and simplified manner (simplify in very plain english like explaining to a child).
give real world complete code implementations and examples for each. provide potential applications in real world.
The response was blocked.
Problem:
Given the head of a linked list, reverse the order of the nodes in the list.
Solution:
Iterative Approach:
Initialize three pointers:
prev
toNone
,curr
to the head, andnext
to the next node of the head.While
curr
is notNone
:Set
next
to the next node ofcurr
.Set
curr.next
toprev
.Set
prev
tocurr
.Set
curr
tonext
.
Return
prev
as the new head of the reversed linked list.
Recursive Approach:
Base Case: If
curr
isNone
, returnprev
as the new head.Recursively reverse the rest of the list by calling
reverse_linked_list(curr.next)
.Set
curr.next
toprev
.Set
prev
tocurr
.Return
prev
as the new head.
Code Implementations:
Iterative Approach:
def reverse_linked_list_iterative(head):
if not head:
return head
prev = None
curr = head
next = head.next
while curr:
next = curr.next
curr.next = prev
prev = curr
curr = next
return prev
Recursive Approach:
def reverse_linked_list_recursive(head):
if not head or not head.next:
return head
new_head = reverse_linked_list_recursive(head.next)
head.next.next = head
head.next = None
return new_head
Time Complexity:
Both iterative and recursive approaches have a time complexity of O(n), where n is the number of nodes in the linked list.
Space Complexity:
The iterative approach has a space complexity of O(1), while the recursive approach has a space complexity of O(n), due to the call stack.
Real-World Applications:
Reversing linked lists can be useful in various scenarios, such as:
Data Processing: Reversing a linked list can help in improving the efficiency of certain data processing algorithms.
Caching: A reversed linked list can be used as a cache, where recently accessed items are placed at the beginning for faster retrieval.
Undo/Redo Operations: Reversing a linked list can be used to implement undo or redo operations in editing systems.
Problem:
Find the median of a stream of integers that are continuously being added to the stream. The median is the middle value of a sorted list of integers.
Optimal Solution:
1. Balanced Binary Search Tree (BBST):
Insert each incoming integer into a BBST.
Maintain the height of the BBST balanced, typically using red-black trees or AVL trees.
The median is the integer at the middle position. If the BBST has an even number of nodes, the median is the average of the two middle values.
2. Heaps:
Use two heaps: a "min-heap" for integers less than the median and a "max-heap" for integers greater than the median.
Initially, both heaps are empty.
When a new integer is added:
If the min-heap is empty or the integer is less than the minimum value in the min-heap, add it to the min-heap.
Otherwise, add it to the max-heap.
As integers are added, the sizes of the heaps are balanced by moving integers from one heap to another.
The median is the minimum value in the min-heap if the min-heap has a larger size. Otherwise, it is the maximum value in the max-heap.
Simplified Explanation:
BBST Approach:
Think of a binary search tree as a sorted list of integers.
Each time you add an integer, you insert it into the correct position in the sorted list.
The median is always at the middle of the sorted list, or the average of the two middle values.
Heaps Approach:
Imagine two piles of integers, a "less-than-pile" and a "greater-than-pile".
The median is the integer that would separate these two piles if you merged them into a single sorted list.
By maintaining the balance between the two piles, you can efficiently find the median with each new integer.
Real-World Examples:
Online Statistics: Tracking the median of website traffic or social media data as it streams in.
Data Analysis: Finding the median of large datasets or financial transactions.
Streaming Analytics: Real-time analysis of sensor data or stock market prices using a median filtering technique.
Code Implementation in Python (Heaps Approach):
import heapq
class MedianFinder:
def __init__(self):
self.min_heap = []
self.max_heap = []
def addNum(self, num):
if not self.min_heap or num < -self.min_heap[0]:
heapq.heappush(self.min_heap, -num) # negate to make a max-heap
else:
heapq.heappush(self.max_heap, num)
self.balanceHeaps()
def balanceHeaps(self):
while len(self.min_heap) > len(self.max_heap):
heapq.heappush(self.max_heap, -heapq.heappop(self.min_heap))
while len(self.max_heap) > len(self.min_heap) + 1:
heapq.heappush(self.min_heap, -heapq.heappop(self.max_heap))
def findMedian(self):
if len(self.min_heap) == len(self.max_heap):
return (-self.min_heap[0] + self.max_heap[0]) / 2
else:
return -self.min_heap[0]
Problem Description
Given a string s, find the longest palindromic substring in it.
Implementation
Brute Force Solution
Breakdown:
Loop through all possible substrings of s.
Check if each substring is a palindrome.
Python Code:
def longest_palindrome_brute_force(s): max_length = 0 max_palindrome = "" for start in range(0, len(s)): for end in range(start+1, len(s)+1): substring = s[start:end] if len(substring) > max_length and is_palindrome(substring): max_length = len(substring) max_palindrome = substring return max_palindrome def is_palindrome(substring): return substring == substring[::-1]
Manacher's Algorithm
Breakdown:
Preprocess the string by inserting a special character between each pair of characters.
Construct a table that stores the length of the longest palindromic substring for each character.
Find the maximum length and corresponding palindromic substring.
Python Code:
def longest_palindrome_manacher(s): preprocessed_s = "#" + "#".join(s) + "#" p = [0] * len(preprocessed_s) center = 0 right = 0 max_length = 0 max_center = 0 for i in range(1, len(preprocessed_s)): mirror = 2 * center - i if i < right: p[i] = min(right - i, p[mirror]) while i - p[i] - 1 >= 0 and i + p[i] + 1 < len(preprocessed_s): if preprocessed_s[i - p[i] - 1] == preprocessed_s[i + p[i] + 1]: p[i] += 1 else: break if p[i] > max_length: max_length = p[i] max_center = i if i + p[i] > right: right = i + p[i] center = max_center return preprocessed_s[max_center - max_length:max_center + max_length + 1].replace("#", "")
Applications
Finding palindromes in text or DNA sequences
Detecting symmetries in images or other data structures
String compression and pattern recognition
Problem Statement
Given a string s, determine if it is a palindrome, which means it reads the same from left to right and from right to left.
Example 1:
Input: s = "racecar" Output: true
Example 2:
Input: s = "level" Output: true
Example 3:
Input: s = "a" Output: true
Example 4:
Input: s = "hello" Output: false
Brute-Force Approach (Naive Method)
The most straightforward approach is to iterate through the string and compare each character with its corresponding character from the other end. If any pair of characters does not match, the string is not a palindrome.
Python Implementation:
def is_palindrome_brute(s):
# Reverse the string and compare it with the original string
reversed_s = ''.join(reversed(s))
return s == reversed_s
Time Complexity: O(n), where n is the length of the string s.
Space Complexity: O(1), as only constant extra space is used.
Optimized Approach (Two-Pointer Method)
A more efficient approach is to use two pointers, one starting from the left and the other from the right. These pointers move towards each other and compare the corresponding characters. If a mismatch is found, the string is not a palindrome.
Python Implementation:
def is_palindrome_optimized(s):
# Use two pointers, one starting from the left and the other from the right
left, right = 0, len(s) - 1
# Iterate until the pointers meet
while left < right:
# Compare the characters at the current positions
if s[left] != s[right]:
return False
# Move the pointers towards each other
left += 1
right -= 1
# If the loop finishes without returning False, the string is a palindrome
return True
Time Complexity: O(n), where n is the length of the string s.
Space Complexity: O(1), as only constant extra space is used.
Applications in the Real World
Checking for palindromes has various applications in the real world, including:
DNA analysis: Identifying palindromic sequences in DNA can help scientists understand genetic mutations and disease mechanisms.
Data compression: Palindrome detection can be used to compress data by identifying repeated sequences that can be stored only once.
Cryptography: Palindromes are sometimes used in cryptography to create secure hashes and codes.
Text processing: Palindrome detection can be used to identify common words and phrases in text for language processing tasks.
Alien Dictionary
Problem Description
Given an array of words where each word consists of lowercase English letters, the task is to construct a new dictionary that's consistent with the given list of words. Return the ordered characters of this dictionary. Return "" if there is no valid dictionary. It is guaranteed that the words in the given list are all unique.
Approach
To construct an alien dictionary, we can use the following steps:
For each word, add all the characters to a set. This set will contain all the unique characters in the given list of words.
Construct a graph using the unique characters from step 1. The graph will have directed edges from one character to another if and only if one character appears before the other in at least one word in the given list.
Perform topological sort on the graph. Topological sort is a linear ordering of the vertices in a directed graph such that for every directed edge from vertex u to vertex v, u comes before v in the ordering. If the topological sort is not possible, then there is no valid dictionary.
Return the topological ordering as the alien dictionary.
Example
Input: {"baa", "abcd", "abca", "cab", "cad"}
Output: "abcd"
The valid alien dictionary can be "abcd" because:
'b' comes before 'a' in "baa".
'a' comes before 'b' in "abca".
'c' comes before 'a' in "cab".
'd' comes before 'a' in "cad".
Simplified Explanation - Breakdown
Create a character set: We create a set to store all the unique characters in the given list of words. For example, for the input {"baa", "abcd", "abca", "cab", "cad"}, the character set will be {'a', 'b', 'c', 'd'}.
Construct a graph: We construct a graph using the unique characters from the character set. The graph will have directed edges from one character to another if and only if one character appears before the other in at least one word in the given list. For example, the graph for the input {"baa", "abcd", "abca", "cab", "cad"} will be:
a
/ \
b c
\ /
d
Perform topological sort: We perform topological sort on the graph. Topological sort is a linear ordering of the vertices in a directed graph such that for every directed edge from vertex u to vertex v, u comes before v in the ordering. If the topological sort is not possible, then there is no valid dictionary. For the given graph, the topological sorting is "abcd".
Return the topological ordering: We return the topological ordering as the alien dictionary. For the input {"baa", "abcd", "abca", "cab", "cad"}, the alien dictionary is "abcd".
Applications in Real World
Alien dictionaries can be used in many real-world applications, such as:
Text processing: Alien dictionaries can be used to sort text data in a consistent manner. For example, in a dictionary application, the words can be sorted using an alien dictionary to ensure that the words are displayed in a consistent order.
Machine translation: Alien dictionaries can be used to translate text from one language to another. For example, an alien dictionary can be used to translate English text to Spanish text.
Natural language processing: Alien dictionaries can be used to process natural language text. For example, an alien dictionary can be used to identify the parts of speech in a sentence.
ERROR OCCURED count_primes
Can you please implement the best & performant solution for the given leetcode problem in python, then simplify and explain the given content for competitive coding?
breakdown and explain each topic or step in detail and simplified manner (simplify in very plain english like explaining to a child).
give real world complete code implementations and examples for each. provide potential applications in real world.
The response was blocked.
RegexPattern
a sequence of characters that helps you match or find other strings or sets of strings, using a specialized syntax held in a pattern.
for example, a regex pattern could be used to find all the phone numbers in a given text.
Problem: Given an input string (s) and a pattern (p), implement a function that matches the pattern to the string. The pattern can contain wildcard characters '*' and '.'.
Constraints:
0 <= s.length, p.length <= 20
s and p consist of only lowercase English letters and wildcard characters '*' and '.'.
Example:
Input: s = "aa", p = "a"
Output: false
Input: s = "aa", p = "a*"
Output: true
Input: s = "ab", p = ".*"
Output: true
Solution: We will use dynamic programming to solve this problem. We will create a 2D array dp, where dp[i][j] will represent whether the substring s[0:i] matches the pattern p[0:j].
We will initialize dp[0][0] to True, since an empty string always matches an empty pattern.
For each character in s, we will iterate over the characters in p. If the character in p is '.', then it matches any character in s, so we will set dp[i][j] to True if dp[i-1][j-1] is True.
If the character in p is '*', then it can match 0 or more occurrences of the previous character in p. We will set dp[i][j] to True if:
dp[i-1][j] is True, which means the previous character in p matches the current character in s.
dp[i][j-1] is True, which means the '*' matches 0 occurrences of the previous character in p.
Otherwise, we will set dp[i][j] to False.
After filling out the dp array, we will return dp[len(s)][len(p)].
Python Implementation:
def isMatch(s, p):
m, n = len(s), len(p)
dp = [[False] * (n + 1) for _ in range(m + 1)]
dp[0][0] = True
for i in range(1, m + 1):
for j in range(1, n + 1):
if p[j - 1] == '.' or p[j - 1] == s[i - 1]:
dp[i][j] = dp[i - 1][j - 1]
elif p[j - 1] == '*':
dp[i][j] = dp[i][j - 1] or dp[i - 1][j - 1]
if p[j - 2] == '.' or p[j - 2] == s[i - 1]:
dp[i][j] |= dp[i - 1][j]
else:
dp[i][j] = False
return dp[m][n]
Time Complexity: O(m * n), where m is the length of s and n is the length of p.
Space Complexity: O(m * n).
Applications: Regex patterns have a wide range of applications in real-world scenarios, including:
Data validation: ensuring that user input conforms to a specific format (e.g., email addresses, phone numbers).
Text processing: searching and replacing text based on specific patterns (e.g., finding all occurrences of a particular word in a document).
Data extraction: extracting specific information from unstructured text (e.g., parsing HTML code to extract website content).
Code analysis: identifying patterns in code to improve maintainability and readability.
Problem:
Given a non-empty array of digits representing a non-negative integer, plus one to the integer.
The digits are stored such that the most significant digit is at the head of the list, and each element in the array contains a single digit.
Solution:
Create a copy of the input array: We need to modify the input array in place, but we also want to preserve the original array. So, we create a copy of the input array using the
copy()
method.digits_copy = digits.copy()
Iterate through the array in reverse order: We start from the least significant digit (the last element in the array) and iterate towards the most significant digit (the first element).
Increment the current digit: For each digit, we increment its value by 1. If the result is greater than or equal to 10, it means that we need to carry over the extra digit to the next iteration.
for i in range(len(digits_copy) - 1, -1, -1): digits_copy[i] += 1 if digits_copy[i] >= 10: digits_copy[i] -= 10 if i > 0: digits_copy[i - 1] += 1
Handle overflow: If we increment the most significant digit (the first element) and it becomes 10, it means that the resulting number has one more digit than the original number. In this case, we need to create a new array with one more element and set the first element to 1.
if digits_copy[0] >= 10: digits_copy = [1] + digits_copy
Return the updated array: Finally, we return the updated array
digits_copy
which represents the incremented integer.
Time Complexity:
O(n), where n is the number of digits in the input array.
Space Complexity:
O(1) because we are modifying the input array in place.
Real-World Applications:
This problem is useful in various real-world applications, such as:
Number manipulation: Incrementing integers is a fundamental operation in many programming tasks.
Currency calculation: Adding one to a monetary value is commonly used in financial calculations.
Date and time handling: Incrementing dates and times is essential for calendar and clock applications.
Problem Statement:
Given a number of pairs of parentheses 'n', print all possible well-formed parentheses of length '2n'.
Example:
Input: n = 3
Output: ["((()))", "(()())", "(())()", "()(())", "()()()"]
Intuition:
We can generate all valid parentheses by adding a pair of parentheses to each existing valid parenthesis sequence. However, we need to ensure that the resulting sequence is still valid, meaning that the number of opening parentheses should always be greater than or equal to the number of closing parentheses.
Algorithm:
Initialize a list
result
to store the valid parentheses.Call the
generate_parentheses_helper
function with an empty string, 'n', 'n', andresult
.In the
generate_parentheses_helper
function:Check if the number of opening parentheses ('open') is equal to 'n':
If yes, add the current sequence to
result
.
Otherwise:
Recursively call
generate_parentheses_helper
with the current sequence plus an opening parenthesis.Check if the number of closing parentheses ('close') is less than 'open':
If yes, recursively call
generate_parentheses_helper
with the current sequence plus a closing parenthesis.
Python Implementation:
def generate_parentheses(n):
result = []
generate_parentheses_helper("", n, n, result)
return result
def generate_parentheses_helper(sequence, open, close, result):
if open == close == 0:
result.append(sequence)
if open > 0:
generate_parentheses_helper(sequence + "(", open - 1, close, result)
if close > open:
generate_parentheses_helper(sequence + ")", open, close - 1, result)
Time Complexity:
O(4^n / sqrt(n))
Space Complexity:
O(4^n / sqrt(n))
Real-World Applications:
This algorithm can be useful in computer science and linguistics for generating balanced sequences of parentheses, brackets, or braces. It can be applied in:
Parsing expressions or text
Compiling code
Natural language processing
Problem Statement: Given a binary tree, flatten it to a linked list in-place.
For example: Given the following binary tree:
1
/ \
2 5
/ \ \
3 4 6
The flattened tree should look like:
1
\
2
\
3
\
4
\
5
\
6
Approach: The idea is to use a recursive approach to flatten the binary tree. The function will take a node as input and return the tail of the flattened list.
The function will first check if the node is null. If it is, then the tail of the list is null.
If the node is not null, then the function will recursively flatten the left and right subtrees. The tail of the left subtree will be the new tail of the list. The tail of the right subtree will be the new tail of the list.
The function will then set the left and right subtrees of the node to null. This will effectively flatten the tree.
The function will then return the tail of the list.
Implementation:
def flatten_binary_tree_to_linked_list(node):
if node is None:
return None
left_tail = flatten_binary_tree_to_linked_list(node.left)
right_tail = flatten_binary_tree_to_linked_list(node.right)
if left_tail is not None:
left_tail.right = node.right
node.right = node.left
node.left = None
return right_tail or left_tail
Example: The following code will flatten the binary tree given in the example:
tree = TreeNode(1)
tree.left = TreeNode(2)
tree.right = TreeNode(5)
tree.left.left = TreeNode(3)
tree.left.right = TreeNode(4)
tree.right.right = TreeNode(6)
flatten_binary_tree_to_linked_list(tree)
print(tree)
The output of the code will be:
1
\
2
\
3
\
4
\
5
\
6
Potential Applications: This algorithm can be used to flatten a binary tree for any purpose. One potential application is to convert a binary tree to a linked list so that it can be processed more easily. Another potential application is to flatten a binary tree so that it can be stored in a database.
Understanding the N-Queens Problem
Imagine a chessboard with an N x N grid. You want to place N queens on the board such that no two queens threaten each other, i.e., they are not in the same row, column, or diagonal.
Recursive Backtracking Algorithm
We'll use recursion and backtracking to solve this problem:
Base Case: If all N queens have been placed, return the solution.
Try to Place a Queen: Iterate through each column in the current row.
Check for Threats: For each column, check if it's safe to place a queen by examining the row, column, and diagonals for any existing queens.
Place the Queen: If the column is safe, place a queen there.
Recurse: Call the function again with the next row and try to place queens.
Backtrack: If placing a queen in the current column leads to an invalid solution, remove the queen and try the next column.
Python Implementation
def n_queens(n):
def is_safe(board, row, col):
for i, j in zip(range(row, -1, -1), range(col, -1, -1)):
if board[i][j] == 'Q':
return False
for i, j in zip(range(row, -1, -1), range(col, n)):
if board[i][j] == 'Q':
return False
return True
def solve(board, row):
if row == n:
return board
for col in range(n):
if is_safe(board, row, col):
board[row][col] = 'Q'
solution = solve(board, row + 1)
if solution:
return solution
else:
board[row][col] = '.'
return None
board = [['.' for _ in range(n)] for _ in range(n)]
return solve(board, 0)
Real-World Applications
Scheduling: Assigning tasks to resources without conflicts or overlaps.
Resource Allocation: Determining the best allocation of resources among competing requests.
Graph Coloring: Assigning colors to nodes in a graph to avoid conflicts between adjacent nodes.
Problem: Serialize and Deserialize Binary Tree
Problem Statement:
Design an algorithm to serialize and deserialize a binary tree.
Solution Breakdown:
Serialization:
We will use a pre-order traversal to serialize the binary tree into a string. Pre-order traversal means we visit the root node first, then the left subtree, and finally the right subtree.
For each node:
If the node is not null, append its value to the string.
If the node is null, append "None" to the string to represent a null pointer.
Separate nodes with commas to make it easier to parse the string later.
Example:
Input:
1
/ \
2 3
/ \
4 5
Serialized String:
1,2,4,None,None,5,None,None,3,None,None
Deserialization:
To deserialize the binary tree from the serialized string, we will use a queue to keep track of the nodes we have yet to visit.
Create a queue and enqueue the root node.
While the queue is not empty:
Dequeue the first node from the queue.
If the node is not null:
Create two new nodes for its left and right children.
Enqueue the left child to the queue.
Enqueue the right child to the queue.
Return the root node.
Example:
Input Serialized String:
1,2,4,None,None,5,None,None,3,None,None
Deserialized Binary Tree:
1
/ \
2 3
/ \
4 5
Real-World Applications:
Storing binary trees in databases or files.
Sending binary trees over a network.
Caching binary trees to improve performance.
Simplified Python Implementation:
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def serialize(root):
result = []
def pre_order(node):
if node is None:
result.append("None")
else:
result.append(str(node.val))
pre_order(node.left)
pre_order(node.right)
pre_order(root)
return ",".join(result)
def deserialize(data):
queue = [None]
nodes = data.split(",")
root = TreeNode(int(nodes[0]))
queue.append(root)
index = 1
while queue:
node = queue.pop(0)
if nodes[index] != "None":
node.left = TreeNode(int(nodes[index]))
queue.append(node.left)
index += 1
if nodes[index] != "None":
node.right = TreeNode(int(nodes[index]))
queue.append(node.right)
index += 1
return root
Problem Statement
Given a binary tree, return the maximum path sum.
The path may start and end at any node in the tree.
Input:
1
/ \
2 3
Output:
6
Explanation:
The maximum path sum is the path from node 2 to node 3, which has a sum of 6.
Solution
The problem can be solved using a recursive approach. We can define a function max_path_sum(node)
that returns the maximum path sum starting from the given node.
The function max_path_sum(node)
will have the following cases:
If
node
isNone
, then the maximum path sum is 0.If
node.left
isNone
andnode.right
isNone
, then the maximum path sum is the value ofnode.val
.If
node.left
is notNone
andnode.right
isNone
, then the maximum path sum is the maximum of the following values:node.val
node.val + max_path_sum(node.left)
If
node.left
isNone
andnode.right
is notNone
, then the maximum path sum is the maximum of the following values:node.val
node.val + max_path_sum(node.right)
If
node.left
is notNone
andnode.right
is notNone
, then the maximum path sum is the maximum of the following values:node.val
node.val + max_path_sum(node.left)
node.val + max_path_sum(node.right)
max_path_sum(node.left) + max_path_sum(node.right)
The following code implements the solution:
def max_path_sum(node):
if node is None:
return 0
if node.left is None and node.right is None:
return node.val
if node.left is not None and node.right is None:
return max(node.val, node.val + max_path_sum(node.left))
if node.left is None and node.right is not None:
return max(node.val, node.val + max_path_sum(node.right))
return max(node.val, node.val + max_path_sum(node.left), node.val + max_path_sum(node.right), max_path_sum(node.left) + max_path_sum(node.right))
Time Complexity
The time complexity of the solution is O(n), where n is the number of nodes in the tree.
Space Complexity
The space complexity of the solution is O(h), where h is the height of the tree.
Applications
The problem has applications in finding the longest path in a graph.
Problem Statement:
Given a string of parentheses, determine if they are balanced.
Constraints:
String length: 1 to 104
Characters: Only open and close parentheses '()'
Examples:
Input: "()"
Output: True (Balanced parentheses)
Input: "(()"
Output: False (Unbalanced parentheses)
Solution Breakdown:
1. Stack Approach:
Initialize a stack to store the open parentheses.
Iterate through the string character by character.
If the current character is an open parenthesis '(', push it onto the stack.
If the current character is a close parenthesis ')', pop the last open parenthesis from the stack.
If the stack is empty after processing the entire string, the parentheses are balanced.
Python Implementation:
def valid_parentheses(string):
stack = []
for char in string:
if char == '(':
stack.append(char)
elif char == ')':
if not stack:
return False
stack.pop()
return not stack
2. Counter Approach:
Count the occurrences of open and close parentheses.
If the count of open and close parentheses is the same, the parentheses are balanced.
Python Implementation:
def valid_parentheses(string):
open_count = 0
close_count = 0
for char in string:
if char == '(':
open_count += 1
elif char == ')':
close_count += 1
return open_count == close_count
Real-World Applications:
Parser Validation: In compilers, parentheses are used to group expressions. Valid parentheses ensure proper code execution.
XML and JSON Parsing: XML and JSON documents use parentheses to define structure. Balanced parentheses help in correctly parsing the data.
Problem Statement
Design a data structure that supports the following operations:
insert(val)
: Inserts an integerval
into the data structure.delete(val)
: Deletes the integerval
from the data structure.getRandom()
: Returns a random element from the data structure.
Approach
We can use a combination of a HashMap
and a List
to implement this data structure efficiently.
HashMap Implementation
The HashMap
will store the elements as keys and their corresponding indices in the List
as values. This allows us to quickly insert, delete, and get the index of an element.
List Implementation
The List
will store the elements in the data structure. We will use the List
to generate random elements efficiently.
Implementation
import random
class RandomizedSet:
def __init__(self):
self.map = {}
self.list = []
def insert(self, val):
if val not in self.map:
self.map[val] = len(self.list)
self.list.append(val)
return True
return False
def delete(self, val):
if val in self.map:
index = self.map[val]
last_element = self.list[-1]
self.list[index], self.list[-1] = self.list[-1], self.list[index]
self.map[last_element] = index
self.list.pop()
del self.map[val]
return True
return False
def getRandom(self):
return random.choice(self.list)
Breakdown
Insertion: We check if the element is already in the
map
. If not, we add it to themap
with its index and append it to thelist
.Deletion: We check if the element is in the
map
. If it is, we find its index, swap it with the last element in thelist
, and update themap
accordingly. Then, we remove the element from themap
and thelist
.Getting a Random Element: We simply return a random element from the
list
.
Real-World Applications
Lottery: Selecting a random winner.
Shuffling a Deck of Cards: Randomizing the order of cards in a deck.
Sampling: Selecting a sample of data for analysis.
Problem Statement:
Given a binary tree, check if it is symmetric. A binary tree is symmetric if the left subtree is a mirror image of the right subtree.
Input: A binary tree.
Output: True if the tree is symmetric, False otherwise.
Solution:
We can use recursion to check if a binary tree is symmetric. Here's the Python code:
def is_symmetric(root):
if root is None:
return True
return is_symmetric_helper(root.left, root.right)
def is_symmetric_helper(left, right):
if left is None and right is None:
return True
if left is None or right is None:
return False
if left.val != right.val:
return False
return is_symmetric_helper(left.left, right.right) and is_symmetric_helper(left.right, right.left)
Explanation:
The is_symmetric
function checks if the given binary tree is symmetric. It starts by checking if the root is None
. If it is, then the tree is considered symmetric.
If the root is not None
, the function calls the is_symmetric_helper
function to check if the left subtree is a mirror image of the right subtree. The is_symmetric_helper
function recursively checks if the left and right subtrees are symmetric.
The base case of the is_symmetric_helper
function is when both the left and right subtrees are None
. In this case, the function returns True
.
If either the left or right subtree is None
, the function returns False
.
If the values of the root nodes of the left and right subtrees are not equal, the function returns False
.
If all of the above conditions are met, the function returns True
if the left and right subtrees are symmetric, and False
otherwise.
Real-World Applications:
Checking if a binary tree is symmetric has applications in computer graphics, where it can be used to create symmetrical images. It is also used in data structures, where it can be used to check if a binary tree is a complete binary tree.
Problem Statement: Reverse an integer.
Example:
Input: 123
Output: 321
Solution: There are several ways to reverse an integer in Python. One simple way is to convert the integer to a string, reverse the string, and then convert it back to an integer.
def reverse_integer(num):
"""Reverses an integer.
Args:
num: The integer to reverse.
Returns:
The reversed integer.
"""
# Convert the integer to a string.
num_str = str(num)
# Reverse the string.
reversed_num_str = num_str[::-1]
# Convert the reversed string back to an integer.
reversed_num = int(reversed_num_str)
# Return the reversed integer.
return reversed_num
Time Complexity: O(n), where n is the number of digits in the integer. Space Complexity: O(n).
Real-World Applications: Reversing an integer can be useful in various real-world applications, such as:
Converting a number from base 10 to another base.
Verifying if a number is a palindrome.
Implementing a calculator.
Majority Element
Problem Statement: Given an array of integers, find the element that appears more than half the size of the array.
Example:
Input: [3, 2, 3]
Output: 3
Optimal Solution (Boyer-Moore Voting Algorithm):
This algorithm works by iterating through the array and maintaining two variables:
count
: The count of occurrences of the majority element.majority
: The current candidate for the majority element.
The algorithm works as follows:
Initialize
count
to 0 andmajority
to None.Iterate through the array.
If
majority
is None orcount
is 0, setmajority
to the current element andcount
to 1.Otherwise, if the current element is equal to
majority
, incrementcount
by 1.Otherwise, decrement
count
by 1.
Once the iteration is complete, check if
count
is greater than 0. If it is, thenmajority
is the majority element.
Code:
def majority_element(nums):
count = 0
majority = None
for num in nums:
if count == 0 or majority == num:
majority = num
count += 1
else:
count -= 1
if count > 0:
return majority
else:
return None
Explanation:
Initialize the count to 0 to keep track of the number of times the majority element is encountered.
Initialize the majority to None to represent that no majority element has been identified yet.
Iterate over each element in the array.
If the majority is None or the count is 0, the current element becomes the majority candidate and the count is set to 1.
If the current element is the same as the majority candidate, the count is incremented by 1.
If the current element is different from the majority candidate, the count is decremented by 1.
After iterating through the entire array, if the count is greater than 0, the majority candidate is the majority element.
Otherwise, there is no majority element.
Applications: The Boyer-Moore Voting Algorithm can be used in various real-world applications, such as:
Finding the most common element in a dataset
Determining the majority opinion in a voting system
Identifying the most popular product in a store
Definition:
A palindrome linked list is a linked list that reads the same backward and forward.
Examples:
1->2->1 1->2->2->1 1->2->3->2->1
Approach:
There are two main approaches for solving this problem:
Recursive Approach: Divide the linked list into two halves, then check if the first half is the same as the second half. Repeat this process until you reach the middle of the linked list. This approach has a time complexity of O(n log n) and a space complexity of O(n).
Iterative Approach: Use two pointers, one to traverse the linked list forward, and the other to traverse the linked list backward. Compare the values of the two pointers at each step. If they are equal, continue. Otherwise, return false. This approach has a time complexity of O(n) and a space complexity of O(1).
Python Implementation:
# Definition for singly-linked list.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
# Check if the linked list is empty or has only one node
if head is None or head.next is None:
return True
# Find the middle of the linked list
slow = head
fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# Reverse the second half of the linked list
prev = None
while slow:
curr = slow
slow = slow.next
curr.next = prev
prev = curr
# Compare the first half and the second half of the linked list
while head and prev:
if head.val != prev.val:
return False
head = head.next
prev = prev.next
# If all the values match, return True
return True
Time Complexity:
The time complexity of the iterative approach is O(n), where n is the number of nodes in the linked list.
Space Complexity:
The space complexity of the iterative approach is O(1).
Applications:
Palindrome linked lists can be used in a variety of applications, such as:
Detecting palindromes in strings
Checking if a given sequence of characters is a palindrome
Verifying the integrity of data
Problem: Given an array and a number k, rotate the array k times to the right.
Solution: This problem can be solved using various algorithms, with varying complexities and performance characteristics. One efficient approach is to use the following steps:
Reverse the entire array: Reverse the elements of the entire array. This brings the elements that were k positions to the right to the first k positions.
Reverse the first k elements: Reverse the elements of the first k positions. This moves the elements that were initially at the end of the array to their correct positions.
Reverse the remaining elements: Reverse the elements from position k to the end of the array. This places the remaining elements in their final positions.
Simplified Implementation:
def rotate_array(nums, k):
n = len(nums)
k = k % n # Handle cases where k exceeds the array length
# Reverse the entire array
nums.reverse()
# Reverse the first k elements
nums[0:k] = reversed(nums[0:k])
# Reverse the remaining elements
nums[k:] = reversed(nums[k:])
return nums
Complexity Analysis:
Time Complexity: O(n), where n is the length of the array. The three reversal operations each take O(n) time, resulting in a total time complexity of O(n).
Space Complexity: O(1), as no additional memory is required beyond the original array.
Applications in the Real World:
Image rotation: Rotating an image involves moving each pixel to its new position, which can be achieved using this algorithm.
Log file processing: Analyzing log files often involves extracting specific events or data from the end of the file. This algorithm can be used to efficiently rotate the log file so that the latest events are accessible at the beginning.
Array manipulation for efficiency: In some cases, rotating an array can optimize certain operations or algorithms. For example, in a circular buffer, rotating the array allows for easy access to the oldest data.
Longest Common Subsequence
The longest common subsequence (LCS) problem is a classic dynamic programming problem in computer science. Given two sequences of characters, the goal is to find the longest subsequence that is common to both sequences.
Example:
sequence1 = "ABCDGH"
sequence2 = "AEDFHR"
The LCS of sequence1
and sequence2
is "ADH"
, which is the longest subsequence that appears in both sequences.
Dynamic Programming Solution:
To solve the LCS problem, we can use a dynamic programming approach. We define a matrix L
where L[i, j]
stores the length of the LCS of the first i
characters of sequence1
and the first j
characters of sequence2
.
We can compute L
iteratively using the following formula:
L[i, j] = L[i-1, j-1] + 1 if sequence1[i] == sequence2[j]
L[i, j] = max(L[i-1, j], L[i, j-1]) if sequence1[i] != sequence2[j]
where L[i-1, j-1]
is the length of the LCS of the first i-1
characters of sequence1
and the first j-1
characters of sequence2
, L[i-1, j]
is the length of the LCS of the first i-1
characters of sequence1
and the first j
characters of sequence2
, and L[i, j-1]
is the length of the LCS of the first i
characters of sequence1
and the first j-1
characters of sequence2
.
Python Implementation:
def longest_common_subsequence(sequence1, sequence2):
"""Finds the longest common subsequence of two sequences.
Args:
sequence1: The first sequence.
sequence2: The second sequence.
Returns:
The length of the longest common subsequence.
"""
# Create a matrix to store the length of the LCS of the first i characters of
# sequence1 and the first j characters of sequence2.
L = [[0 for _ in range(len(sequence2) + 1)] for _ in range(len(sequence1) + 1)]
# Compute the length of the LCS iteratively.
for i in range(1, len(sequence1) + 1):
for j in range(1, len(sequence2) + 1):
if sequence1[i - 1] == sequence2[j - 1]:
L[i][j] = L[i - 1][j - 1] + 1
else:
L[i][j] = max(L[i - 1][j], L[i][j - 1])
# Return the length of the LCS.
return L[len(sequence1)][len(sequence2)]
Applications in the Real World:
The LCS problem has numerous applications in the real world, including:
Diff tools: LCS is used to compare two files and find the differences between them.
Sequence alignment: LCS is used to align biological sequences to find regions of similarity.
Natural language processing: LCS is used to find the similarity between two strings of text.
Code plagiarism detection: LCS is used to detect similarities between two pieces of code.
Problem Statement:
Given an array of integers nums
, find the longest increasing triplet subsequence. A triplet is a subsequence of three elements.
Example:
For nums = [1,2,3,4,5]
, the longest increasing triplet subsequence is [1,2,3]
.
Approach:
The problem can be solved using dynamic programming. We create two arrays, l[i]
and r[i]
. l[i]
stores the length of the longest increasing subsequence ending at index i
, and r[i]
stores the length of the longest decreasing subsequence starting at index i
.
To compute l[i]
, we consider all possible pairs of elements (j,k)
such that j < i
and nums[j] < nums[i]
. We then take the maximum of l[j]
and l[i]
and add 1 to it.
To compute r[i]
, we consider all possible pairs of elements (j,k)
such that j > i
and nums[j] > nums[i]
. We then take the maximum of r[k]
and r[i]
and add 1 to it.
Finally, to find the longest increasing triplet subsequence, we find the index i
such that l[i] + r[i] - 1
is the maximum.
Python Implementation:
def increasing_triplet_subsequence(nums):
"""
Args:
nums (list): The array of integers.
Returns:
int: The length of the longest increasing triplet subsequence.
"""
l = [1] * len(nums)
r = [1] * len(nums)
for i in range(1, len(nums)):
for j in range(i):
if nums[j] < nums[i]:
l[i] = max(l[i], l[j] + 1)
for i in range(len(nums) - 2, -1, -1):
for j in range(i + 1, len(nums)):
if nums[j] > nums[i]:
r[i] = max(r[i], r[j] + 1)
max_length = 0
for i in range(len(nums)):
max_length = max(max_length, l[i] + r[i] - 1)
return max_length if max_length >= 3 else 0
Time Complexity: O(n^2), where n is the length of the array.
Space Complexity: O(n).
Real-World Applications:
The problem of finding the longest increasing triplet subsequence has applications in various real-world scenarios, such as:
Stock trading: Identifying the best time to buy and sell a stock to maximize profit.
Scheduling: Optimizing the order of tasks to minimize the total completion time.
Data analysis: Identifying trends and patterns in data.
Problem Statement:
You have an array of stock prices, where each element represents the price of a stock on a given day. You want to maximize your profit by buying and selling the stock multiple times.
Intuition:
The key to solving this problem is to find the local minima and maxima in the array. Buy the stock at a local minimum and sell it at a local maximum. Repeat this process as many times as possible.
Algorithm (Kadane's Algorithm):
Initialize two variables,
buy
andsell
, both pointing to the first element of the array.Iterate through the array.
While
sell
points to a lower element thanbuy
, updatesell
to point to the next element.If
sell
points to a higher element thanbuy
, buy the stock atbuy
, sell it atsell
, and updatebuy
to point to the next element.Repeat steps 3-4 until
buy
reaches the end of the array.
Python Implementation:
def max_profit(prices):
buy = sell = 0
profit = 0
for i in range(len(prices)):
if prices[i] < prices[sell]:
sell = i
else:
if i == len(prices) - 1 or prices[i + 1] < prices[i]:
profit += prices[sell] - prices[buy]
buy = sell = i
return profit
Real-World Application:
This problem is a classical problem in finance. It models the behavior of stock prices, which often fluctuate in the short term but tend to increase over the long term. Investors use this algorithm to find the best times to buy and sell stocks to maximize their profits.
Example:
prices = [7, 1, 5, 3, 6, 4]
result = max_profit(prices)
print(result) # Output: 7
Explanation:
Buy the stock at day 1 (price = 7).
Sell the stock at day 4 (price = 6).
Profit = 6 - 7 = -1.
Buy the stock at day 5 (price = 4).
Sell the stock at day 6 (price = 6).
Profit = 6 - 4 = 2.
Total profit = -1 + 2 = 7.
3Sum
Problem Statement:
Given an array of integers, find all unique triplets that sum to a given target value.
Simplified Explanation:
Imagine you have a list of numbers and you want to find three numbers that, when added together, equal a specific number (target). For example, if you have the numbers [1, 2, 3, 4, 5, 6, 7] and the target is 9, you would find the triplets (1, 2, 6) and (1, 3, 5) because both of these triplets add up to 9.
Efficient Solution in Python:
def three_sum(nums, target):
# Sort the array to make it easier to find triplets
nums.sort()
# Initialize an empty list to store the triplets
triplets = []
# Iterate over the array
for i in range(len(nums)):
# Check if the current number is less than the target
if nums[i] > target:
# If it is, then there are no more possible triplets
break
# Set the left and right pointers
left = i + 1
right = len(nums) - 1
# Find two numbers that add up to the target
while left < right:
current_sum = nums[i] + nums[left] + nums[right]
if current_sum < target:
# If the sum is less than the target, move the left pointer to the right
left += 1
elif current_sum > target:
# If the sum is greater than the target, move the right pointer to the left
right -= 1
else:
# If the sum is equal to the target, add the triplet to the list and move the pointers
triplets.append([nums[i], nums[left], nums[right]])
left += 1
right -= 1
# Return the list of triplets
return triplets
Simplification and Explanation:
Sorting: We first sort the array in ascending order. This makes it easier to find the triplets because we can iterate through the sorted array and only consider combinations of numbers that are in the correct order.
Iterating Over the Array: We iterate over the array from the beginning until we reach a number that is greater than the target. This is because any triplet containing a number that is greater than the target cannot sum to the target.
Setting Pointers: For each number in the array, we set two pointers: one that starts at the next index and one that starts at the last index. These pointers represent the left and right boundaries of the possible triplets that contain the current number.
Finding Two Numbers: We use these pointers to find two numbers that add up to the target. We do this by adding the current number to the number at the left pointer and the number at the right pointer. If the sum is less than the target, we move the left pointer to the right. If the sum is greater than the target, we move the right pointer to the left.
Adding Triplets to List: When we find two numbers that add up to the target, we add the triplet (the current number, the number at the left pointer, and the number at the right pointer) to the list of triplets. We then move both pointers to the next index so that we can find more triplets.
Returning the List of Triplets: Once we have iterated through the entire array, we return the list of triplets that we have found.
Real-World Applications:
The 3Sum problem has applications in various real-world problems, such as:
Machine learning: Finding patterns in data
Finance: Analyzing financial data
Optimization: Finding the best solution to a problem
Physics: Modeling complex systems
Problem: Find the first occurrence of a substring in a string
Input: Two strings: haystack
(the larger string) and needle
(the substring to find)
Output: The index of the first occurrence of needle
in haystack
, or -1 if needle
is not found
Example:
haystack = "hello"
needle = "ll"
result = 2 # The first occurrence of "ll" in "hello" is at index 2
Solution (Python):
def find_the_index_of_the_first_occurrence_in_a_string(haystack, needle):
"""
Finds the first occurrence of a substring in a string.
Parameters:
haystack (str): The larger string.
needle (str): The substring to find.
Returns:
int: The index of the first occurrence of `needle` in `haystack`, or -1 if `needle` is not found.
"""
# Check if the needle is empty. If it is, return 0.
if not needle:
return 0
# Iterate over the haystack string.
for i in range(len(haystack) - len(needle) + 1):
# Check if the substring at the current index matches the needle.
if haystack[i:i+len(needle)] == needle:
# If it does, return the index.
return i
# If the needle is not found, return -1.
return -1
Explanation:
We first check if the needle is empty. If it is, we return 0 because an empty string is always found at the beginning of any string.
We then iterate over the haystack string, starting from the beginning. For each index, we check if the substring at that index matches the needle. If it does, we return the index.
If we reach the end of the haystack string without finding the needle, we return -1 to indicate that the needle is not found.
Time Complexity:
The time complexity of this solution is O(n*m), where n is the length of the haystack string and m is the length of the needle string. This is because we iterate over the haystack string once and, for each index, we check if the substring at that index matches the needle.
Space Complexity:
The space complexity of this solution is O(1), as we do not allocate any additional memory.
Applications:
This solution can be used in a variety of applications, such as:
Searching for a substring in a text document
Finding the first occurrence of a pattern in a DNA sequence
Checking if a string contains a particular word
Problem Statement
A happy number is a number defined by the following process:
Starting with any positive integer, replace the number by the sum of the squares of its digits.
Repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1.
Those numbers for which this process ends in 1 are happy numbers, while those that do not are unhappy numbers (or sad numbers).
Write a function that determines whether a given number is a happy number.
Solution
One way to approach this problem is to use a set to keep track of the numbers that we have already seen. If we ever encounter a number that we have already seen, then we know that we are in a cycle and the number is not happy.
Here is the Python code for this solution:
def is_happy(num):
seen = set()
while num != 1:
if num in seen:
return False
seen.add(num)
num = sum(int(d) ** 2 for d in str(num))
return True
Example
The following code shows how to use the is_happy()
function to check if a number is happy:
print(is_happy(19)) # True
print(is_happy(2)) # False
Real-World Applications
Happy numbers have been used in various fields, including:
Cryptanalysis: Happy numbers can be used to generate pseudorandom numbers.
Computer science: Happy numbers can be used to test the randomness of a number generator.
Mathematics: Happy numbers have been studied for their mathematical properties.
Explanation
The key idea behind the solution is to use a set to keep track of the numbers that we have already seen. This allows us to quickly check if we are in a cycle.
The is_happy()
function takes a number as input and returns True
if the number is happy and False
otherwise. The function first initializes a set seen
to keep track of the numbers that we have already seen.
The function then enters a while loop that continues until the number is equal to 1. Inside the loop, the function checks if the number is in the seen
set. If it is, then we know that we are in a cycle and the number is not happy. In this case, the function returns False
.
If the number is not in the seen
set, then the function adds the number to the set and calculates the sum of the squares of its digits. The function then updates the number to the new value and continues the loop.
If the number ever reaches 1, then the function knows that the number is happy and returns True
.
Problem: Unique Paths
Explanation:
Imagine you are standing in the top-left corner of a grid with m
rows and n
columns. You want to reach the bottom-right corner of the grid. You can only move right or down at each step. How many unique paths are there to reach the bottom-right corner?
For example, if we have a grid with 3 rows and 7 columns, there are 28 unique paths to reach the bottom-right corner.

Recursive Solution:
One way to solve this problem is to use recursion. We can define a function unique_paths(i, j)
which returns the number of unique paths to reach the bottom-right corner of a grid starting from cell (i, j)
.
def unique_paths(i, j):
# Base cases:
if i == m - 1 and j == n - 1:
return 1
if i >= m or j >= n:
return 0
# Recursive cases:
return unique_paths(i + 1, j) + unique_paths(i, j + 1)
Dynamic Programming Solution:
The recursive solution above is inefficient because it computes the same subproblems multiple times. We can use dynamic programming to optimize this solution by storing the number of unique paths to reach each cell in a table.
def unique_paths(m, n):
# Create a table to store the number of unique paths to reach each cell
dp = [[0] * n for _ in range(m)]
# Initialize the first row and column of the table
for i in range(m):
dp[i][0] = 1
for j in range(n):
dp[0][j] = 1
# Fill in the rest of the table
for i in range(1, m):
for j in range(1, n):
dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
# Return the number of unique paths to reach the bottom-right corner
return dp[m - 1][n - 1]
Applications:
This problem has applications in a variety of fields, including:
Robotics: A robot moving through a grid can use this algorithm to find the shortest path to its destination.
Computer graphics: This algorithm can be used to generate realistic-looking 3D models.
Operations research: This algorithm can be used to optimize the flow of goods and services through a network.
The idea of the problem is to return the number of squared integers that are not grater than the integer n
.
def numSquares(n):
"""
:type n: int
:rtype: int
"""
if n <= 0:
return 0
# Create a dynamic programming table dp
dp = [float('inf') for _ in range(n+1)]
# Base case
dp[0] = 0
# Iterate over all the values of n
for i in range(1, n+1):
# Iterate over all the possible squares that can be added to i
for j in range(1, int(i ** 0.5) + 1):
# Update dp[i] with the minimum of the current value and the value of dp[i - j * j]
dp[i] = min(dp[i], dp[i - j * j] + 1)
# Return the value of dp[n]
return dp[n]
Here is a step-by-step explanation of the solution:
We start by creating a dynamic programming table
dp
of sizen+1
. This table will store the minimum number of perfect squares that sum up to each value from 0 ton
.We initialize the base case:
dp[0] = 0
. This means that we can represent 0 as the sum of 0 perfect squares.We then iterate over all the values of
n
from 1 ton+1
.For each value of
n
, we iterate over all the possible squares that can be added toi
. For example, ifn
is 10, we would iterate over the squares of 1, 2, 3, and 4.For each square
j
, we updatedp[i]
with the minimum of the current value and the value ofdp[i - j * j] + 1
. This means that we are considering the possibility of representingi
as the sum ofj * j
and some other perfect squares.After iterating over all the possible squares, we will have found the minimum number of perfect squares that sum up to
n
. We return this value as the answer.
Here is an example of how the solution works:
Let's say we want to find the minimum number of perfect squares that sum up to 10.
We start by initializing the dynamic programming table dp
:
dp = [float('inf') for _ in range(11)]
dp[0] = 0
We then iterate over all the values of n
from 1 to 10:
For
n = 1
, we iterate over the squares of 1. We find thatdp[1] = min(float('inf'), dp[1 - 1 * 1] + 1) = min(float('inf'), 1) = 1
.For
n = 2
, we iterate over the squares of 1 and 2. We find thatdp[2] = min(float('inf'), dp[2 - 1 * 1] + 1, dp[2 - 2 * 2] + 1) = min(float('inf'), 2, 1) = 1
.For
n = 3
, we iterate over the squares of 1, 2, and 3. We find thatdp[3] = min(float('inf'), dp[3 - 1 * 1] + 1, dp[3 - 2 * 2] + 1, dp[3 - 3 * 3] + 1) = min(float('inf'), 2, 2, 1) = 1
.We continue in this manner until we reach
n = 10
.
After iterating over all the values of n
, we will have the following values in the dynamic programming table:
dp = [0, 1, 1, 1, 2, 2, 3, 3, 4, 4, 3]
We return the value of dp[10]
, which is 3. This means that the minimum number of perfect squares that sum up to 10 is 3, which can be represented as 1 + 4 + 5.
The time complexity of the solution is O(n * sqrt(n)). The space complexity is O(n).
Definition for a binary tree node.
class TreeNode:
def init(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution: def rightSideView(self, root: TreeNode) -> List[int]:
result = []
# if root is none just return the empty list
if not root:
return result
queue = [root]
while queue:
levelSize = len(queue)
# Add value of the right most node from the current level
result.append(queue[levelSize - 1].val)
# Process each node in the current level
while levelSize > 0:
curr = queue.pop(0)
# Add left child to queue if it exists
if curr.left:
queue.append(curr.left)
# Add right child to queue if it exists
if curr.right:
queue.append(curr.right)
levelSize -= 1
return result
Leetcode Problem 139: Word Break
Problem Statement
Given a string s
and a dictionary of words wordDict
, determine if s
can be segmented into a space-separated sequence of one or more dictionary words.
Optimal Solution: Dynamic Programming
Algorithm:
This problem can be solved efficiently using dynamic programming.
Define a boolean array
dp
of lengthn+1
, wheren
is the length of the strings
.dp[i]
represents if the substrings[0:i]
can be segmented into dictionary words.
Initialize
dp[0]
toTrue
as the empty string can be segmented.Iterate through the string character by character:
For each character
s[i]
, iterate through all substringss[j:i+1]
for all0 <= j < i
.If
dp[j] == True
ands[j:i+1]
is a word in the dictionary, setdp[i+1] = True
.
Return
dp[n]
.
Explanation:
The substring
s[0:i]
can be segmented if it can be partitioned into two substringss[0:j]
ands[j:i]
, wheres[0:j]
is already segmented ands[j:i]
is a dictionary word.We start by initializing
dp[0]
toTrue
as the empty string is considered segmented.For each character
s[i]
, we consider all possible substringss[j:i+1]
and check if the substrings[0:j]
is already segmented ands[j:i+1]
is a dictionary word. If this is true, we setdp[i+1]
toTrue
.Finally, we return
dp[n]
to indicate if the entire strings
can be segmented.
Example:
Input:
s = "leetcode"
wordDict = ["leet", "code"]
Output:
True
Explanation: We can segment s
as "leet code".
Real-World Applications:
Word break is a fundamental technique in natural language processing. It is used in:
Text summarization: Breaking down text into smaller segments makes it easier to summarize the main points.
Machine translation: Word segmentation helps translate text more accurately by identifying word boundaries and grammatical structures.
Spell checking: Word break helps identify misspelled words by comparing words to a dictionary.
Search engines: Search engines use word break to index and retrieve web pages relevant to search queries.
Code Implementation:
def word_break(s, wordDict):
n = len(s)
dp = [False] * (n+1)
dp[0] = True
for i in range(1, n+1):
for j in range(i):
if dp[j] and s[j:i] in wordDict:
dp[i] = True
break
return dp[-1]
Problem Statement:
Given two linked lists, find the intersection of the two lists. The intersection is the first node where both lists have the same value.
Example:
Input:
n1 = 1 -> 2 -> 3 -> 4 -> 5 -> None
n2 = 6 -> 7 -> 3 -> 4 -> 5 -> None
Output:
Intersection point: 3
Solution 1: Brute Force
Iterate through the first list.
For each node in the first list, iterate through the second list.
If the current nodes in both lists are equal, return the current node.
If no intersection is found, return None.
Python Implementation:
def intersection_brute_force(head1, head2):
current1 = head1
while current1 is not None:
current2 = head2
while current2 is not None:
if current1.val == current2.val:
return current1
current2 = current2.next
current1 = current1.next
return None
Complexity Analysis:
Time complexity: O(mn), where m and n are the lengths of the two linked lists.
Space complexity: O(1).
Drawbacks:
Can be slow for large linked lists.
Requires iterating through both lists multiple times.
Solution 2: Hash Table
Create a hash table to store the values of the first linked list.
Iterate through the second linked list.
For each node in the second list, check if its value is in the hash table.
If the value is found, return the corresponding node.
If no intersection is found, return None.
Python Implementation:
def intersection_hash_table(head1, head2):
hash_table = set()
current1 = head1
while current1 is not None:
hash_table.add(current1.val)
current1 = current1.next
current2 = head2
while current2 is not None:
if current2.val in hash_table:
return current2
current2 = current2.next
return None
Complexity Analysis:
Time complexity: O(m + n), where m and n are the lengths of the two linked lists.
Space complexity: O(m).
Advantages:
Faster than the brute force approach for large linked lists.
Only requires iterating through the linked lists once.
Applications in Real World:
Finding duplicate elements in a data set.
Identifying common elements in different collections.
Checking for the presence of an item in a database.
Problem Statement:
Given two non-empty linked lists representing two non-negative integers, where each node contains a single digit, add the two numbers and return the sum as a linked list.
Example:
Input: l1 = [2,4,3], l2 = [5,6,4]
Output: [7,0,8]
Explanation: 342 + 465 = 807.
Implementation:
# Definition for singly-linked list.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
# Initialize the head of the result linked list to None
head = None
# Initialize the previous node in the result linked list to None
prev = None
# Carry is used to store the carry-over from the previous addition
carry = 0
# Iterate over both linked lists until both are empty
while l1 or l2:
# Get the values of the current nodes in both linked lists
val1 = l1.val if l1 else 0
val2 = l2.val if l2 else 0
# Calculate the sum of the current values and the carry-over
sum = val1 + val2 + carry
# Update the carry-over
carry = sum // 10
# Create a new node with the current sum
new_node = ListNode(sum % 10)
# If this is the first node in the result linked list, set the head to it
if not head:
head = new_node
# Otherwise, append the new node to the end of the result linked list
else:
prev.next = new_node
# Update the previous node to the current node
prev = new_node
# Advance both linked lists
l1 = l1.next if l1 else None
l2 = l2.next if l2 else None
# If there is any remaining carry-over, create a new node for it
if carry:
new_node = ListNode(carry)
prev.next = new_node
return head
Breakdown:
Initialize result linked list: We initialize the head and prev nodes of the result linked list to None. These will be used to keep track of the start and end of the result linked list.
Initialize carry: We initialize the carry variable to 0, which will be used to store any carry-over from the previous addition.
Iterate over linked lists: We use a while loop to iterate over both linked lists until both are empty.
Calculate sum and carry: We calculate the sum of the current values in both linked lists and the carry-over. We update the carry-over to be the integer division of the sum by 10.
Create new node: We create a new node with the value of the current sum modulo 10 (to get the digit).
Set or append node: If this is the first node in the result linked list, we set the head to the new node. Otherwise, we append the new node to the end of the result linked list.
Update previous node: We update the previous node to the current node, so that we can append to it in the next iteration.
Advance linked lists: We advance both linked lists by moving to the next node in each.
Add remaining carry: If there is any remaining carry-over after both linked lists are empty, we create a new node for it and append it to the end of the result linked list.
Return result: We return the head of the result linked list, which is the start of the sum of the two input linked lists.
Real-World Applications:
This algorithm can be used in a variety of real-world applications, such as:
Adding large numbers in financial calculations
Calculating distances in navigation systems
Computing checksums for data integrity
Problem Statement:
Given an array of non-negative integers representing the heights of a histogram, find the largest rectangular area that can be formed within the histogram.
Brute Force Solution:
The brute force approach is to consider all possible pairs of bars and calculate the area of the rectangle formed by them. The time complexity of this approach is O(n^2), where n is the number of bars. It can be improved using Stack based approach.
Stack Based Solution:
This approach uses a stack to keep track of the indices of the bars. It iterates through the array, and for each bar, it calculates the maximum width and height of the rectangle it can form with the previous bars in the stack. If the current bar is shorter than the previous bar, it pops the previous bar from the stack and calculates the area of the rectangle. The stack is updated with the index of the current bar. This approach has a time complexity of O(n).
Simplified Explanation:
Imagine a histogram as a series of vertical bars with varying heights. The problem is to find the largest rectangle that can be formed within the histogram.
The brute force approach is like trying out all possible combinations of bars to see which one gives the largest rectangle. It's like trying out all possible pairs of bricks to build the tallest tower, which is not very efficient.
The stack-based approach is like building a tower brick by brick. It starts with the first brick (bar) and, for each subsequent brick, it calculates the maximum height and width of the tower it can form with the previous bricks. If the current brick is shorter than the previous brick, it removes the previous brick and calculates the area of the tower. It continues this process until it reaches the end of the histogram.
Real World Implementation:
This problem has applications in histogram analysis, image processing, and data compression. For example, in image processing, it can be used to find the largest connected component in a binary image.
Example:
Input: [2,1,5,6,2,3] Output: 10 Explanation: The largest rectangle is formed by bars with heights 5, 6, and 3. Its width is 3 and height is 5, so the area is 3 * 5 = 10.
Python Code:
def largest_rectangle_in_histogram(heights):
stack = []
max_area = 0
for i, height in enumerate(heights):
while stack and height < heights[stack[-1]]:
h = heights[stack.pop()]
w = i if not stack else i - stack[-1] - 1
max_area = max(max_area, h * w)
stack.append(i)
while stack:
h = heights[stack.pop()]
w = len(heights) if not stack else len(heights) - stack[-1] - 1
max_area = max(max_area, h * w)
return max_area
Merge Sorted Arrays
Problem: Given two sorted arrays, merge them into a single sorted array.
Naive Solution:
Create a new array with size equal to the sum of sizes of both arrays.
Iterate through both arrays, comparing elements and adding the smaller element to the new array.
Return the new array.
Optimized Solution:
Initialize two pointers, one for each array, at the first element.
Compare the elements at the current pointers.
Add the smaller element to the merged array.
Increment the pointer of the array with the smaller element.
Repeat steps 2-4 until both pointers reach the end of their respective arrays.
Append the remaining elements from both arrays to the merged array.
Python Implementation:
def merge_sorted_arrays(arr1, arr2):
# Create a new array to store the merged array
merged_arr = []
# Initialize pointers for both arrays
i = 0
j = 0
# Compare elements and add smaller element to merged array
while i < len(arr1) and j < len(arr2):
if arr1[i] < arr2[j]:
merged_arr.append(arr1[i])
i += 1
else:
merged_arr.append(arr2[j])
j += 1
# Append remaining elements from both arrays
merged_arr.extend(arr1[i:])
merged_arr.extend(arr2[j:])
# Return the merged array
return merged_arr
Applications:
Merging data from multiple sources
Combining user preferences or recommendations
Creating sorted lists of items from different categories
Data analysis and visualization
Problem Statement:
Given an integer n
, return the number of 1 bits in its binary representation.
Example:
Input: n = 00000000000000000000000000001011
Output: 3
Explanation: The binary representation of 11 is 00000000000000000000000000001011, which has three 1s.
Brute Force Approach:
One approach is to iterate through each bit in n
and count the number of 1s. Here's a simple Python implementation:
def number_of_1_bits_brute_force(n):
count = 0
while n:
if n & 1:
count += 1
n >>= 1
return count
Time Complexity: O(log(n)), where n
is the input integer.
Optimized Approach (Bit Manipulation):
A more efficient approach is to use bit manipulation to count the number of 1s. We can use the &
(bitwise AND) and >>
(bitwise right shift) operators to perform this operation.
Here's how it works:
We perform a bitwise AND operation between
n
and 1 (binary 00000001). This operation returns 1 if the bit inn
is set, and 0 otherwise.We add the result of the AND operation to a counter variable.
We then bitwise right shift
n
by 1, which effectively removes the least significant bit.We repeat steps 1-3 until
n
becomes 0.
Here's a Python implementation:
def number_of_1_bits_optimized(n):
count = 0
while n:
count += n & 1
n >>= 1
return count
Time Complexity: O(1), since the number of iterations is independent of the input size.
Applications in the Real World:
Counting the number of set flags: In computer systems, flags are often used to indicate the status of certain operations or conditions. The number of 1 bits in a flag register can provide insight into which flags are currently active.
Checksum calculations: Checksum algorithms use bit manipulation to verify the integrity of data during transmission or storage. The number of 1 bits in a checksum can be used to detect errors in the data.
Data compression: Some data compression algorithms use bit manipulation to reduce the storage space required for data. The number of 1 bits in a compressed file can be used to measure the compression efficiency.
Problem Statement:
Given an n x n 2D matrix representing an image, rotate the image by 90 degrees clockwise.
Intuition:
To rotate an image by 90 degrees clockwise, we can imagine flipping the image over its diagonal and then mirroring it vertically.
Algorithm:
Transpose the Matrix (Flip over diagonal):
Iterate over the matrix and swap each element
matrix[i][j]
withmatrix[j][i]
.
Reverse Each Row (Mirror Vertically):
For each row
matrix[i]
, iterate from index 0 to the midpoint and swap each element with its mirror element at the opposite end of the row.
Python Implementation:
def rotate_image(matrix):
# Transpose the matrix
for i in range(len(matrix)):
for j in range(i):
matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
# Reverse each row
for row in matrix:
row.reverse()
return matrix
Example:
Input:
matrix = [
[1,2,3],
[4,5,6],
[7,8,9]
]
Output:
matrix = [
[7,4,1],
[8,5,2],
[9,6,3]
]
Explanation:
Transpose the matrix:
Swap
matrix[0][1]
withmatrix[1][0]
.Swap
matrix[0][2]
withmatrix[2][0]
.Swap
matrix[1][2]
withmatrix[2][1]
.
Reverse each row:
Reverse the first row:
[7, 4, 1]
.Reverse the second row:
[8, 5, 2]
.Reverse the third row:
[9, 6, 3]
.
Applications in Real World:
Image rotation is used in various applications, including:
Image processing and editing software
Rotating camera footage
Creating panoramas and virtual tours
Image stabilization in drones and self-driving cars
LRU Cache (Least Recently Used)
Problem: Design and implement a cache that stores a limited number of items and evicts the least recently used item when the cache is full.
Python Implementation:
from collections import OrderedDict
class LRUCache:
def __init__(self, capacity):
self.capacity = capacity
self.cache = OrderedDict()
def get(self, key):
if key in self.cache:
value = self.cache.pop(key)
self.cache[key] = value
return value
return None
def put(self, key, value):
if key in self.cache:
self.cache.pop(key)
self.cache[key] = value
if len(self.cache) > self.capacity:
self.cache.popitem(last=False)
**Breakdown:**
1. **Initialize Cache:** Create a cache with a specified capacity and an empty dictionary to store items. We use an `OrderedDict` to track insertion order for the LRU policy.
2. **Get Item:** If the item exists in the cache, move it to the end of the dictionary (most recently used). Return the value. If it doesn't exist, return `None`.
3. **Put Item:** If the item already exists, update its value and move it to the end. If it doesn't exist, add it to the end. If the cache is full, evict the oldest item (first item in the dictionary).
**Real World Applications:**
LRU caches are used in various applications, including:
* Browser caches: Store recently visited web pages for faster loading.
* Database caches: Speed up database queries by caching frequently used data.
* Operating system caches: Improve performance by caching commonly used files and processes.
* In-memory caching: Store data in memory for quick access, such as session data in web applications.
**Simplified Example:**
Imagine a cache with a capacity of 3. We add the following items:
* `put("a", 1)`
* `put("b", 2)`
* `put("c", 3)`
The cache looks like this:
{ "a": 1, "b": 2, "c": 3, }
* `get("a")` returns `1` and moves "a" to the end:
{ "b": 2, "c": 3, "a": 1, }
* `put("d", 4)` evicts "b" because it's the oldest:
{ "c": 3, "a": 1, "d": 4, }
---
**Problem Statement**
Suppose you are at a party with n people (labeled from 0 to n-1) and you want to find out who the celebrity is. A celebrity is a person who everyone knows, but they don't know anyone.
You can only make a single pass through the party and you can only ask a single person at a time, "Do you know person x?"
Based on the responses you receive, you need to find out who the celebrity is.
**Solution**
We can use two pointers to solve this problem. Let's start with two pointers, `i` and `j`, both initially pointing to the first person at the party (`i = 0` and `j = 0`).
We then ask the person at `i` if they know the person at `j`. If they do, then we move `i` to the next person (because we know that the person at `j` is not the celebrity).
If they don't, then we move `j` to the next person (because we know that the person at `i` is not the celebrity).
We continue this process until `i` and `j` meet. If they meet at the last person in the party, then that person is the celebrity. Otherwise, there is no celebrity at the party.
Here is the Python code for this solution:
```python
def find_the_celebrity(n):
i = 0
j = 1
while i < n and j < n:
if knows(i, j):
i += 1
else:
j += 1
if i == n:
return j
if j == n:
return i
return -1
Example
Let's say there are 5 people at the party and the following graph represents who knows who:
0 --> 1
1 --> 2
2 --> 3
3 --> 4
Using our two pointers approach, we would:
Start with
i = 0
andj = 1
.Ask person 0 if they know person 1. Yes, they do.
Move
i
to the next person (i = 1
).Ask person 1 if they know person 2. Yes, they do.
Move
i
to the next person (i = 2
).Ask person 2 if they know person 3. Yes, they do.
Move
i
to the next person (i = 3
).Ask person 3 if they know person 4. Yes, they do.
Move
i
to the next person (i = 4
).i
andj
have now met at the last person in the party, so person 4 is the celebrity.
Real-World Applications
This problem can be applied in a variety of real-world scenarios, such as:
Finding the most popular person in a social network.
Finding the most knowledgeable person in a group of experts.
Finding the most influential person in a community.
Problem Statement:
Given an array of integers containing only 0, 1, and 2, sort the array in-place such that all 0s appear first, then all 1s, and finally all 2s.
Implementation in Python:
def sort_colors(nums):
"""
Sorts an array of 0s, 1s, and 2s in-place.
Parameters:
nums: The array to sort.
Returns:
None. The array is sorted in-place.
"""
# Initialize three pointers:
# - p0: points to the first unsorted element
# - p1: points to the first 1 in the unsorted region
# - p2: points to the first 2 in the unsorted region
p0 = 0
p1 = 0
p2 = len(nums) - 1
# Iterate until p0 and p1 reach the end of the array
while p0 < p2:
# If the element at p0 is 0, swap it with the element at p0 and increment p0 and p1
if nums[p0] == 0:
nums[p0], nums[p1] = nums[p1], nums[p0]
p0 += 1
p1 += 1
# If the element at p0 is 1, increment p0 and p1
elif nums[p0] == 1:
p0 += 1
p1 += 1
# If the element at p0 is 2, swap it with the element at p2 and decrement p2
else:
nums[p0], nums[p2] = nums[p2], nums[p0]
p2 -= 1
**Example:**
```python
nums = [2, 0, 2, 1, 1, 0]
sort_colors(nums)
print(nums) # [0, 0, 1, 1, 2, 2]
Explanation:
The sort_colors
function uses three pointers to sort the array in-place. The pointer p0
points to the first unsorted element. The pointer p1
points to the first 1 in the unsorted region. The pointer p2
points to the first 2 in the unsorted region.
The function iterates over the array until p0
and p1
reach the end of the array. In each iteration, the function checks the value of the element at p0
. If the element is 0, the function swaps it with the element at p0
and increments p0
and p1
. If the element is 1, the function increments p0
and p1
. If the element is 2, the function swaps it with the element at p2
and decrements p2
.
The function continues iterating over the array until all the elements are sorted.
Real-World Applications:
The sort_colors
function can be used in various real-world applications, such as:
Sorting data in a database
Filtering data in a spreadsheet
Organizing files in a folder
Problem: Odd Even Linked List
Problem Statement:
Given a singly linked list, group all the odd nodes together followed by the even nodes. One way to solve this problem is to create two lists, one for the odd nodes and one for the even nodes. Then, merge the two lists together.
Code:
def oddEvenList(head):
"""
Reorders the given linked list such that all odd nodes are followed by all even nodes.
Args:
head: The head node of the linked list to be reordered.
Returns:
The head node of the reordered linked list.
"""
# Initialize two pointers, one for the odd nodes and one for the even nodes.
odd_head = ListNode(0)
even_head = ListNode(0)
odd_ptr = odd_head
even_ptr = even_head
# Iterate over the linked list, alternating between adding nodes to the odd and even lists.
while head:
odd_ptr.next = head
odd_ptr = odd_ptr.next
head = head.next
if head:
even_ptr.next = head
even_ptr = even_ptr.next
head = head.next
# Merge the two lists by connecting the tail of the odd list to the head of the even list.
odd_ptr.next = even_head.next
# Return the head of the merged list.
return odd_head.next
# Example usage.
head = ListNode(1)
head.next = ListNode(2)
head.next.next = ListNode(3)
head.next.next.next = ListNode(4)
head.next.next.next.next = ListNode(5)
reordered_head = oddEvenList(head)
Breakdown:
Initialize two pointers: One pointer for the odd nodes and one for the even nodes.
Iterate over the linked list: Alternate between adding nodes to the odd and even lists.
Merge the two lists: Connect the tail of the odd list to the head of the even list.
Return the head of the merged list: This is the head of the reordered linked list.
Applications in Real World:
This algorithm can be used in a variety of real-world applications, such as:
Sorting data: The algorithm can be used to sort data into two groups, such as odd and even numbers or positive and negative numbers.
Filtering data: The algorithm can be used to filter data based on a certain criterion, such as selecting only the odd or even elements from a list.
Data analysis: The algorithm can be used to analyze data by grouping it into different categories, such as odd and even numbers or positive and negative numbers.
Excel Sheet Column Number
Problem Statement: Given a string that represents a column in an Excel spreadsheet, return its corresponding column number. This column number is the same as the integer value of the column in that spreadsheet.
Examples:
For "A", return 1
For "Z", return 26
For "AA", return 27
For "AZ", return 52
Approach:
Convert each character to its numeric value: Subtract the ASCII value of 'A' (65) from the character. This gives us a number ranging from 0 to 25.
Multiply and add: Each position in the column number has a different weight. The first character represents the most significant digit, the second character represents the second most significant digit, and so on. Multiply the numeric value of each character by its weight and add these products together to get the column number.
Detailed Breakdown:
Step 1: Convert Character to Numeric Value
def char_to_value(char):
return ord(char) - ord('A') + 1 # Subtract 'A' and add 1 to start from 1
Step 2: Multiply and Add
def column_number(column_str):
result = 0
weight = 1 # Start with weight 1 for the first character
for char in reversed(column_str):
result += char_to_value(char) * weight
weight *= 26 # Increase weight by 26 for each character
return result
Real-World Applications:
Spreadsheet Software: Determining the column number of a given cell reference in spreadsheet applications like Excel or Google Sheets.
Complete Code Implementation and Example:
def column_number(column_str):
result = 0
weight = 1
for char in reversed(column_str):
result += char_to_value(char) * weight
weight *= 26
return result
column_str = "AZ"
print(column_number(column_str)) # Output: 52
In this example, the column number for "AZ" is calculated by:
Converting "A" to numeric value: 1
Converting "Z" to numeric value: 26
Multiplying 1 by weight 26 and adding 26, resulting in 26
Multiplying 26 by weight 26, resulting in 52
Problem Statement
Evaluate the value of an arithmetic expression in Reverse Polish Notation (RPN).
Solution
Define a stack: We will use a stack to store the operands and intermediate results.
Iterate over the expression:
If the current token is an operand, push it into the stack.
If the current token is an operator, pop two operands from the stack, apply the operator, and push the result back into the stack.
Return the top of the stack: The top of the stack contains the final result of the expression.
Code
def evaluate_rpn(tokens):
stack = []
operators = {"+": lambda x, y: x + y,
"-": lambda x, y: x - y,
"*": lambda x, y: x * y,
"/": lambda x, y: int(x / y)}
for token in tokens:
if token in operators:
op2 = stack.pop()
op1 = stack.pop()
operation = operators[token]
result = operation(op1, op2)
stack.append(result)
else:
stack.append(int(token))
return stack[0]
Example
tokens = ["2", "1", "+", "3", "*"]
result = evaluate_rpn(tokens)
print(result) # Output: 9
Real-World Applications
Financial calculations: RPN is commonly used in financial calculators for evaluating expressions involving stocks, bonds, and other financial instruments.
Scientific calculations: RPN is also popular in scientific calculators for evaluating mathematical expressions involving trigonometric functions, calculus, and other mathematical operations.
Problem Statement: You are given a 2D matrix, and you need to be able to perform the following operations:
Update a value in the matrix.
Query the sum of a rectangular sub-matrix.
Solution: One of the best solutions is to use a binary indexed tree (BIT) for each row of the matrix. A BIT is a data structure that can be used to efficiently update and query the sum of a range of elements.
Implementation:
class BIT:
def __init__(self, n):
self.tree = [0] * (n + 1)
def update(self, idx, val):
while idx <= len(self.tree) - 1:
self.tree[idx] += val
idx += idx & -idx
def query(self, idx):
sum = 0
while idx > 0:
sum += self.tree[idx]
idx -= idx & -idx
return sum
class RangeSumQuery2DMutable:
def __init__(self, matrix):
self.rows = len(matrix)
self.cols = len(matrix[0])
self.bits = [[BIT(self.cols) for _ in range(self.rows)] for _ in range(self.rows)]
for i in range(self.rows):
for j in range(self.cols):
self.update(i, j, matrix[i][j])
def update(self, row, col, val):
diff = val - self.bits[row][col].query(col + 1)
self.bits[row][col].update(col + 1, diff)
def query(self, row1, col1, row2, col2):
sum = 0
for i in range(row1, row2 + 1):
sum += self.bits[i][col2].query(col2 + 1) - self.bits[i][col1].query(col1)
return sum
Example:
matrix = [[3, 0, 1], [5, 2, 4], [6, 7, 8]]
rsq = RangeSumQuery2DMutable(matrix)
rsq.update(1, 1, 10) # Update the value at row 1, column 1 to 10
sum = rsq.query(0, 0, 2, 2) # Query the sum of the sub-matrix from (0, 0) to (2, 2)
print(sum) # Output: 41
Explanation: The above code creates a RangeSumQuery2DMutable object from the given matrix. Then, it updates the value at row 1, column 1 to 10 using the update
method. Finally, it queries the sum of the sub-matrix from (0, 0) to (2, 2) using the query
method.
Applications: Range sum queries are used in a variety of applications, including:
Image processing
Data analytics
Machine learning
Video games
For example, in image processing, range sum queries can be used to compute the average color of a region of an image. In data analytics, range sum queries can be used to compute the total sales for a given product over a period of time. In machine learning, range sum queries can be used to compute the dot product of two vectors. And in video games, range sum queries can be used to compute the total damage dealt by a player over a period of time.
Problem Definition:
Given an array of integers, find the longest increasing subsequence. A subsequence is a sequence that can be obtained by removing some elements from the original array while preserving the order of the remaining elements.
Longest Increasing Subsequence (LIS):
The LIS is the subsequence of the original array with the maximum number of elements in strictly increasing order.
Solution using Dynamic Programming:
This solution uses dynamic programming to find the LIS in O(n^2) time and O(n) space.
Steps:
Create a table
lengths
of sizen
, wheren
is the length of the input array.lengths[i]
will store the length of the LIS ending at indexi
.Initialize
lengths[i]
to 1 for alli
. This means that the LIS ending at any index initially contains only that element.Iterate over the array from index 1 to
n
.For each index
i
, iterate over all previous indicesj
from 0 toi-1
.Check if
array[j] < array[i]
. If this is true, then addingarray[i]
to the end of the LIS ending at indexj
will create a longer LIS.Update
lengths[i]
to the maximum of its current value andlengths[j] + 1
.Find the maximum value in
lengths
to get the length of the LIS.Reconstruct the LIS by backtracking through the
lengths
table.
Code:
def longest_increasing_subsequence(array):
# Create a table to store the lengths of LIS ending at each index
<