Problem Solving Strategies Flashcards
Strategies to use for solving problems for Leetcode, Codewars, etc
Frequency Counter Technique
function createFrequencyCounter(array) {
let frequencies = {};
for (let val of array) {
let valCount = frequencies[val] || 0;
frequencies[val] = valCount + 1;
}
return frequencies;
}
function squaresWithFreqCounter(nums1, nums2) {
if (nums1.length !== nums2.length) return false;
let nums1Freqs = createFrequencyCounter(nums1);
let nums2Freqs = createFrequencyCounter(nums2);
for (let key of nums1Freqs) {
if (!nums2Freqs[key ** 2]) return false;
if (nums1Freqs[key] !== nums2Freqs[key ** 2]) return false;
}
return true;
}
The Greedy Approach
Imagine you are on a number line, and you want to reach the last point (the highest number) starting from the first point (the lowest number). At each step, you can jump a certain distance indicated by the numbers on the number line.
Greedy Approach:
Start at the first point: You begin at the first point on the number line.
Greedy Choice: At each point, make the greedy choice by jumping as far as you can. Choose the jump that takes you the maximum distance.
Continue making greedy choices: Keep making greedy choices at each point, always selecting the jump that gets you the farthest.
Reach the last point or determine it’s not possible: Continue making jumps until you either reach the last point or realize that you cannot reach the last point.
Return Result: If you successfully reach the last point, you’ve solved the problem. If not, you’ve determined it’s not possible to reach the end.
Example:
Let’s consider the number line [2, 3, 1, 1, 4]. The numbers represent the maximum distance you can jump from each point.
Start at 2: You can jump to 3 or 1. Choose 3 because it’s the farthest.
Now you’re at 3: Jump to 4.
You’ve reached the last point (4).
Conclusion:
In this example, by consistently making the greedy choice at each step (choosing the jump that takes you the farthest), you successfully reach the last point. This is the essence of the greedy approach—making locally optimal choices with the hope that they lead to a globally optimal solution.
Certainly! Let’s break down the greedy approach with a simpler example.
Problem:
Imagine you are on a number line, and you want to reach the last point (the highest number) starting from the first point (the lowest number). At each step, you can jump a certain distance indicated by the numbers on the number line.
Greedy Approach:
Start at the first point: You begin at the first point on the number line.
Greedy Choice: At each point, make the greedy choice by jumping as far as you can. Choose the jump that takes you the maximum distance.
Continue making greedy choices: Keep making greedy choices at each point, always selecting the jump that gets you the farthest.
Reach the last point or determine it’s not possible: Continue making jumps until you either reach the last point or realize that you cannot reach the last point.
Return Result: If you successfully reach the last point, you’ve solved the problem. If not, you’ve determined it’s not possible to reach the end.
Example:
Let’s consider the number line [2, 3, 1, 1, 4]. The numbers represent the maximum distance you can jump from each point.
Start at 2: You can jump to 3 or 1. Choose 3 because it’s the farthest.
Now you’re at 3: Jump to 4.
You’ve reached the last point (4).
Conclusion:
In this example, by consistently making the greedy choice at each step (choosing the jump that takes you the farthest), you successfully reach the last point. This is the essence of the greedy approach—making locally optimal choices with the hope that they lead to a globally optimal solution.
Key Idea:
Always choose the jump that maximizes your progress toward the goal, and by doing this at each step, you increase the chances of reaching the end.
Multiple Pointers Strategy
function sumZeroMultiplePointers(nums) {
let left = 0;
let right = nums.length - 1;
while(left < right) {
const sum = nums[left] + nums[right];
if(sum === 0) {
return [nums[left], nums[right]]
}else if(sum > 0){
right–;
}else{
left++;
}
}
}
The multiple pointers pattern is a strategy used in problem-solving where instead of using a single pointer or index to track in an array, we use more than one. This pattern is particularly useful when dealing with sorted arrays or linked lists. It can significantly reduce the time complexity of an algorithm from O(N^2) to O(N) by eliminating the need for nested loops
The Sliding Window
- Define the Problem:
Identify a problem that involves finding a subarray, substring, or subsequence that meets certain criteria.
Understand the Constraints:
- Determine the constraints of the problem, such as the size of the desired subarray or substring.
Initialize Variables: - Set up variables to represent the window boundaries, pointers, or any other relevant information.
Iterate Through the Array or Sequence: - Use a loop to iterate through the array or sequence, updating the window at each step.
Adjust the Window: - Depending on the problem, adjust the window size by expanding, contracting, or maintaining it.
Update Results or Conditions: - Update any results, conditions, or computations as the window slides.
- Repeat Until Completion:
- Continue the process until you have covered the entire array or sequence.
def max_sum_subarray(nums, k):
n = len(nums)
# Check for invalid input if k > n or k <= 0: return "Invalid input" # Initialize variables window_sum = sum(nums[:k]) max_sum = window_sum # Slide the window through the array for i in range(k, n): # Expand the window by adding the next element and removing the first element window_sum = window_sum + nums[i] - nums[i - k] # Update the maximum sum if needed max_sum = max(max_sum, window_sum) return max_sum
Example usage:
nums = [1, 4, 2, 10, 2, 3, 1, 0, 20]
k = 3
result = max_sum_subarray(nums, k)
print(result) # Output: 29 (subarray: [2, 10, 2, 3, 1, 0, 20])