Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions k_diff_pairs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
class Solution:
"""
------------------------------------------------------------
K-diff Pairs in an Array
------------------------------------------------------------
Given:
- nums[] : array of integers
- k : integer (non-negative difference value)

Goal:
Count the number of UNIQUE k-diff pairs (nums[i], nums[j]) where:
- i ≠ j (different indices)
- |nums[i] - nums[j]| == k (absolute difference equals k)
- Pairs are unique by VALUES, not indices
- (1, 3) and (3, 1) are considered the SAME pair

Key Insights:
1. k = 0 Special Case:
- We need pairs where nums[i] == nums[j] with i ≠ j
- This means the value must appear AT LEAST TWICE
- Each duplicate value forms exactly ONE unique pair
- Example: [1,1,1] with k=0 → only 1 pair: (1,1)

2. k > 0 Case:
- For each unique value x, check if (x + k) exists
- No need to check (x - k) separately due to iteration
- Example: nums=[1,3], k=2 → when we check 1, we find 3
- Duplicates don't matter: [1,1,3,3] still just 1 pair

3. Why Counter Works:
- Reduces problem to unique values + frequencies
- Eliminates index management complexity
- Natural separation of k=0 vs k>0 logic

Examples Walkthrough:

Example 1: nums = [3,1,4,1,5], k = 2
- Counter: {3:1, 1:2, 4:1, 5:1}
- k > 0, so check each unique value:
- 3+2=5 ✓ exists → count=1
- 1+2=3 ✓ exists → count=2
- 4+2=6 ✗
- 5+2=7 ✗
- Result: 2 pairs (1,3) and (3,5)

Example 2: nums = [1,3,1,5,4], k = 0
- Counter: {1:2, 3:1, 5:1, 4:1}
- k = 0, so count values with freq ≥ 2:
- 1 appears 2 times ✓ → count=1
- Result: 1 pair (1,1)

Approaches:
1. Brute Force (Check all pairs) – O(n²)
2. Sort + Two Pointers – O(n log n)
3. Hash Map + Set for uniqueness – O(n)
4. Counter (Frequency Map) – O(n) ✔ Optimal & Cleanest

------------------------------------------------------------
Algorithm (Counter Approach):

Step 1: Count frequency of each unique value
Step 2: If k = 0:
Count how many values appear ≥ 2 times
If k > 0:
For each unique value, check if (value + k) exists
Step 3: Return count

Why This is Better:
- No index management needed
- Clear separation of edge cases
- Only iterates unique values (not all elements)
- More intuitive and readable

Time Complexity: O(n)
- Building Counter: O(n)
- Iterating unique values: O(u) where u = unique count ≤ n
- Counter membership check: O(1) average
- Total: O(n)

Space Complexity: O(n)
- Counter storage: O(u) where u ≤ n unique values
- Worst case all elements unique: O(n)
------------------------------------------------------------
"""

def findPairs(self, nums: List[int], k: int) -> int:
# Edge case: negative k is mathematically invalid
# (absolute value cannot be negative)
if k < 0:
return 0

# Count frequency of each number in the array
# This reduces the problem to unique values + their counts
counter = Counter(nums)
count = 0

if k == 0:
# Special case: k=0 means we need identical pairs
# A number can form a pair with itself only if it appears ≥ 2 times
# Each such number contributes exactly ONE unique pair
for num, freq in counter.items():
if freq >= 2:
count += 1
else:
# General case: k>0 means we need distinct values
# For each unique number x, check if (x + k) exists
#
# Why only check forward (x + k) and not backward (x - k)?
# - We iterate through ALL unique values
# - For pair (a, b) where b = a + k:
# → We find it when iterating at x=a (checking a+k)
# → No need to find it again at x=b (checking b-k)
# → Checking both directions would double count!
for num in counter:
if num + k in counter:
count += 1

return count

# Time Complexity: O(n) - counting + iterating unique values
# Space Complexity: O(n) - counter storage
61 changes: 61 additions & 0 deletions pascals_traingle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
class Solution:
"""
------------------------------------------------------------
Pascal's Triangle
------------------------------------------------------------
Given:
- numRows : integer representing number of rows to generate

Goal:
Return the first numRows of Pascal's triangle as a 2D list.

Pascal's Triangle Properties:
- First and last element of each row is 1
- Each interior element is the sum of two elements above it
- Row i has (i+1) elements

Example:
Row 0: 1
Row 1: 1 1
Row 2: 1 2 1
Row 3: 1 3 3 1
Row 4: 1 4 6 4 1

Approaches:
1. Brute Force (Recalculate each element) – O(n^3)
2. Dynamic Programming (Build row by row) – O(n^2) ✔ Optimal
- Space: O(n^2) for output, O(1) auxiliary space
3. Combinatorial Formula (nCr) – O(n^2) with O(n) per element

------------------------------------------------------------
Algorithm (Dynamic Programming):
1. Initialize triangle with all 1s (correct for first/last elements)
2. For each row i starting from row 2:
For each position j from 1 to i-1:
dp[i][j] = dp[i-1][j-1] + dp[i-1][j]
3. Return the complete triangle

Time Complexity: O(n^2)
- We have n rows
- Row i has i elements to potentially compute
- Total: 1 + 2 + 3 + ... + n = n(n+1)/2 = O(n^2)

Space Complexity: O(n^2)
- Output requires O(n^2) space
- Auxiliary space: O(1) (only loop variables)
------------------------------------------------------------
"""

def generate(self, numRows: int) -> List[List[int]]:
# Initialize triangle with all 1s
# Row i has (i+1) elements
dp = [[1 for _ in range(i + 1)] for i in range(numRows)]

# Fill interior elements starting from row 2 (index 2)
for i in range(2, numRows):
# Only update interior elements (skip first and last)
for j in range(1, i):
# Each element is sum of two elements from row above
dp[i][j] = dp[i-1][j-1] + dp[i-1][j]

return dp