DSA Notes
HubAlgorithm vs Data Structure — Key Differences, Definitions, and How They Work TogetherApplications of DSA — How Data Structures and Algorithms Power Real-World SoftwareTime and Space Complexity — A Complete BeginnerWhat is DSA? — A Complete BeginnerWhy Learn DSA? — Career Benefits, Real-World Impact, and Long-Term ValueAsymptotic Analysis — Complete Framework with Recurrence Relations and Master TheoremBest, Average, and Worst Case Analysis — A Practical Guide for Every AlgorithmBig O Notation — Complete Guide with Formal Definition, Rules, and ExamplesBig Omega Notation — Lower Bounds, Best Case, and Formal DefinitionBig Theta (Θ) Notation — Tight Bounds and Exact Growth RatesSpace Complexity — Memory Analysis, Auxiliary Space, and Time-Space TradeoffsTime Complexity Examples — 25 Worked Problems from Basic to AdvancedArray Interview Problems — 15 Classic Problems with Complete SolutionsArray Operations — Every Operation Explained with Code, Complexity, and Edge CasesArray Rotation — Algorithms, Techniques, and Interview ProblemsArray Searching — Linear Search, Binary Search, and Advanced TechniquesArrays — The Complete Introduction to the Most Fundamental Data StructureDynamic Arrays — How Python Lists and ArrayLists Work InternallyMultidimensional Arrays — 2D Arrays, Matrices, and Memory LayoutSliding Window Technique — Complete Guide with 10 Classic ProblemsAnagram Problems — Detection, Grouping, and Sliding Window VariationsPalindrome Problems — Check, Longest, Count, and Expand Around CenterPattern Searching — KMP, Rabin-Karp, and Boyer-Moore AlgorithmsString Interview Problems — 10 Classic Problems with Complete SolutionsString Matching — Longest Common Subsequence, Edit Distance, and Z-AlgorithmString Operations — Complete Reference with Complexity and ImplementationStrings — Complete Introduction to String Data Structure and OperationsCircular Linked List — Structure, Operations, and ApplicationsDoubly Linked List — Complete Implementation with Bidirectional OperationsLinked List Interview Problems — 12 Classic Problems with Complete SolutionsLinked Lists — A Complete Introduction with Memory Model and TypesLinked List Operations — Every Technique with Code and ComplexityReverse Linked List — All Approaches from Basic to AdvancedSingly Linked List — Complete Implementation with All OperationsBalanced Parentheses — Stack-Based Validation with All VariantsInfix, Prefix, and Postfix Expressions — Stack-Based Conversion and EvaluationStack Implementation — Three Complete Implementations with Edge CasesStack Interview Problems — 10 Classic Problems with Complete SolutionsStack — Complete Introduction to LIFO Data StructureStack Using Arrays — Memory Layout, Capacity Management, and PerformanceStack Using Linked List — Implementation, Analysis, and When to Use ItCircular Queue — The Ring Buffer Explained with ImplementationDeque — Double-Ended Queue with Complete Implementation and ApplicationsPriority Queue — Complete Guide from Concept to ImplementationQueue Applications — BFS, Level-Order Traversal, 01-BFS, and MoreQueue Implementation — Array, Linked List, and Two-Stack ApproachesQueue Interview Problems — 10 Classic Problems with Complete SolutionsQueue — Complete Introduction to the FIFO Data StructureAVL Tree - Self-Balancing Binary Search TreeBinary Search Tree (BST)Binary Tree - Structure and OperationsFenwick Tree (Binary Indexed Tree)Red-Black Tree - Another Balanced BSTSegment Tree - Range Query Data StructureTree Interview Problems CollectionTree Data Structure FundamentalsTree Traversals - Inorder, Preorder, Postorder, Level OrderHeap — Complete Introduction to the Priority-Based Tree Data StructureHeap Operations — Complete Reference with Complexity AnalysisHeap Problems — 8 Classic Interview Problems with Complete SolutionsMax Heap — Complete Implementation and ApplicationsMin Heap — Complete Implementation with All OperationsPriority Queue Using Heap — Implementation, Applications, and PatternsBellman-Ford Algorithm — Shortest Path with Negative Weights and Cycle DetectionBFS Algorithm — Breadth-First Search with All Applications and VariantsDFS Algorithm — Depth-First Search with Cycle Detection and All ApplicationsDijkstraFloyd-Warshall Algorithm — All-Pairs Shortest Paths with Complete AnalysisGraph Interview Problems — 12 Classic Problems with Complete SolutionsGraph — Complete Introduction to the Most Powerful Data StructureGraph Representation — Adjacency List, Matrix, Edge List with Complete AnalysisKruskalMinimum Spanning Tree — Definition, Properties, and ApplicationsPrimShortest Path Algorithms — Complete Comparison and Selection GuideTopological Sorting — DFS and BFS (KahnUnion-Find (DSU) in Graphs — Connected Components and Dynamic ConnectivitySeparate Chaining — Complete Implementation and AnalysisCollision Handling — Chaining vs Open Addressing Complete ComparisonHash Functions — Design, Properties, and Common ImplementationsHashing — Complete Introduction to Hash Tables and Hash FunctionsHashing Problems — 10 Classic Interview Problems with Complete SolutionsOpen Addressing — Linear Probing, Quadratic Probing, and Double HashingBubble Sort — Complete Analysis with Optimizations and When to Use ItCounting Sort — O(n+k) Sorting That Breaks the Comparison Lower BoundHeap Sort — Guaranteed O(n log n) with O(1) SpaceInsertion Sort — The O(n) Best Case Sort That Powers PythonMerge Sort — Complete Guide to the Guaranteed O(n log n) Stable SortQuick Sort — The Fastest Practical Sorting Algorithm with Complete AnalysisRadix Sort — O(nk) Digit-by-Digit Sorting for Large Integer RangesSelection Sort — Complete Analysis with Swaps Minimization PropertySorting Algorithm Comparison — The Complete Decision GuideSorting — Complete Introduction to Sorting AlgorithmsBinary Search — Complete Guide with All Variants and Proof of CorrectnessInterpolation Search — O(log log n) Search for Uniformly Distributed DataLinear Search — Complete Guide with All Variants and ApplicationsSearch Algorithm Comparison — Choosing the Right Search for Every SituationSearching Interview Problems — 10 Classic Problems with Complete SolutionsTernary Search — Finding Maximum/Minimum of Unimodal FunctionsBacktracking Introduction — The Recursive Exploration with UndoRecursion Basics — The Complete BeginnerRecursion Problems — 12 Classic Problems from Basic to AdvancedRecursion Tree — Visualizing Recursion for Complexity AnalysisRecursion vs Iteration — When to Use Each and How to ConvertTail Recursion — Definition, Optimization, and Converting to IterationDP Interview Problems — 12 Classic Problems with Pattern Recognition GuideDynamic Programming — Complete Introduction with Properties and Problem IdentificationFibonacci DP — From Exponential to Linear with Memoization and Matrix ExponentiationKnapsack Problem — 0/1, Unbounded, and Fractional with Complete AnalysisLongest Common Subsequence — Complete Guide with DP Table and String VariantsLongest Increasing Subsequence — O(n log n) with Patience SortingMatrix Chain Multiplication — Interval DP with Complete AnalysisMemoization — Top-Down Dynamic Programming with CachingTabulation — Bottom-Up Dynamic Programming with Complete Table BuildingActivity Selection Problem — Greedy Scheduling with Proof of CorrectnessFractional Knapsack — Greedy by Value Density with ProofGreedy Interview Problems — 10 Classic Problems with Complete SolutionsGreedy Algorithms — Complete Introduction with Proof TechniquesHuffman Coding — Optimal Prefix-Free Data CompressionJob Sequencing Problem — Greedy Scheduling with DeadlinesBacktracking — The Complete Guide to Recursive Exploration with UndoBacktracking Problems — 12 Classic Interview Problems with Complete SolutionsN-Queens Problem — Complete Solution with Backtracking and OptimizationRat in a Maze — Backtracking Path Finding with All SolutionsSubset Generation — All Subsets, Combinations, and Partitions with BacktrackingSudoku Solver — Backtracking with Constraint PropagationCodeforces Guide — Complete Strategy to Reach 1800+ RatingCommon CP Techniques — Essential Algorithms Every Competitive Programmer Must KnowContest Strategies — How to Perform Your Best in Competitive Programming ContestsCompetitive Programming — Complete Introduction for BeginnersFast Input/Output — Why I/O Speed Matters and Complete Templates for C++ and PythonLeetCode Guide — Complete Strategy to Crack FAANG InterviewsBit Manipulation — Complete Guide with All Tricks and Interview ProblemsBloom Filter — Space-Efficient Probabilistic Membership TestingComputational Geometry — Points, Lines, Convex Hull, and Geometric AlgorithmsDisjoint Set Union (DSU) — Union-Find with Path Compression and Union by RankRandomized Algorithms — Las Vegas, Monte Carlo, and Probabilistic TechniquesSkip List — The Probabilistic Data Structure that Competes with Balanced BSTsSuffix Array — Construction, LCP Array, and String Search ApplicationsTrie Data Structure — Complete Guide with Implementation and ApplicationsAutocomplete System Using TrieLibrary Management System Using DSARoute Finder Using GraphsMini Search Engine ProjectSocial Network Analysis Using GraphsEssential Coding Patterns for InterviewsDSA Cheatsheet: Quick ReferenceEssential DSA Interview QuestionsFAANG DSA Sheet: Company-Specific Patterns and ProblemsProblem Solving Strategies for Coding InterviewsComplexity CheatsheetImportant Algorithms SummaryBest Practice Platforms for DSARecommended Books for DSA
Solve 10 classic greedy interview problems — gas station, candy distribution, jump game, task scheduler, assign cookies, lemonade change, boats to save people, partition labels, minimum arrows, and queue reconstruction by height — with proofs and pattern recognition.
Problem 1 — Gas Station
Can you complete a full circle? If yes, find the starting station.
Example
def can_complete_circuit(gas, cost):
"""
Greedy insight: If total gas >= total cost, a solution exists.
If we run out at station i, the start must be after i.
Time: O(n), Space: O(1)
"""
total_gas = sum(gas) - sum(cost)
if total_gas < 0:
return -1 # Impossible
start = 0
tank = 0
for i in range(len(gas)):
tank += gas[i] - cost[i]
if tank < 0:
start = i + 1 # Current start doesn't work, try next
tank = 0
return start
print(can_complete_circuit([1,2,3,4,5],[3,4,5,1,2])) # 3
print(can_complete_circuit([2,3,4],[3,4,3])) # -1
Problem 3 — Jump Game (Can You Reach the End?)
Example
def can_jump(nums):
"""
Track max reachable index as we scan.
Greedy: if i > max_reach, we're stuck.
Time: O(n), Space: O(1)
"""
max_reach = 0
for i, jump in enumerate(nums):
if i > max_reach:
return False # Cannot reach index i
max_reach = max(max_reach, i + jump)
return True
print(can_jump([2,3,1,1,4])) # True
print(can_jump([3,2,1,0,4])) # False
Problem 4 — Task Scheduler
Minimum intervals to execute tasks with n cooldown between same tasks.
Example
from collections import Counter
import heapq
def least_interval(tasks, n):
"""
Greedy: always execute the most frequent remaining task.
Use a cooldown queue to respect the constraint.
Time: O(T log T) where T = number of unique tasks.
"""
freq = Counter(tasks)
max_heap = [-f for f in freq.values()]
heapq.heapify(max_heap)
time = 0
cooldown = [] # (available_time, neg_freq)
while max_heap or cooldown:
time += 1
if max_heap:
freq_left = heapq.heappop(max_heap) + 1 # Execute task
if freq_left < 0: # More instances remain
heapq.heappush(cooldown, (time + n, freq_left))
# Release tasks that have cooled down
if cooldown and cooldown[0][0] <= time:
_, f = heapq.heappop(cooldown)
heapq.heappush(max_heap, f)
return time
# Mathematical approach (faster):
def least_interval_math(tasks, n):
freq = sorted(Counter(tasks).values(), reverse=True)
f_max = freq[0]
idle_slots = (f_max - 1) * n
for f in freq[1:]:
idle_slots -= min(f_max - 1, f)
return len(tasks) + max(0, idle_slots)
print(least_interval(["A","A","A","B","B","B"], 2)) # 8
print(least_interval(["A","A","A","B","B","B"], 0)) # 6
Problem 5 — Assign Cookies
Example
def find_content_children(g, s):
"""
Greedily satisfy smallest greed factor children first.
Give each child the smallest cookie that satisfies them.
Time: O(n log n + m log m)
"""
g.sort(); s.sort()
child = cookie = 0
while child < len(g) and cookie < len(s):
if s[cookie] >= g[child]:
child += 1 # Child satisfied
cookie += 1 # Move to next cookie regardless
return child # Number of content children
print(find_content_children([1,2,3],[1,1])) # 1
print(find_content_children([1,2],[1,2,3])) # 2
Problem 6 — Lemonade Change
Example
def lemonade_change(bills):
"""
Can you make change for every customer?
Greedy: when making $15 change, prefer to give $10+$5 (preserves $5s).
Time: O(n), Space: O(1)
"""
fives = tens = 0
for bill in bills:
if bill == 5:
fives += 1
elif bill == 10:
if fives == 0: return False
fives -= 1; tens += 1
else: # bill == 20
if tens > 0 and fives > 0:
tens -= 1; fives -= 1 # Prefer $10+$5 (saves $5 bills)
elif fives >= 3:
fives -= 3
else:
return False
return True
print(lemonade_change([5,5,5,10,20])) # True
print(lemonade_change([5,5,10,10,20])) # False
Problem 7 — Boats to Save People
Example
def num_rescue_boats(people, limit):
"""
Pair heaviest with lightest person if possible.
Two-pointer greedy.
Time: O(n log n)
"""
people.sort()
left, right = 0, len(people) - 1
boats = 0
while left <= right:
if people[left] + people[right] <= limit:
left += 1 # Lightest can ride with heaviest
right -= 1 # Heaviest always takes one boat
boats += 1
return boats
print(num_rescue_boats([1,2],[3])) # 1
print(num_rescue_boats([3,2,2,1],[3])) # 3
print(num_rescue_boats([3,5,3,4],[5])) # 4
Problem 8 — Partition Labels
Example
def partition_labels(s):
"""
Partition string so each character appears in at most one part.
Greedy: for each position, extend partition to last occurrence of all chars seen.
"""
last = {c: i for i, c in enumerate(s)} # Last index of each char
partitions = []
start = end = 0
for i, c in enumerate(s):
end = max(end, last[c]) # Extend to cover last occurrence of c
if i == end: # Reached end of current partition
partitions.append(end - start + 1)
start = i + 1
return partitions
print(partition_labels("ababcbacadefegdehijhklij")) # [9,7,8]
Problem 9 — Minimum Arrows to Burst Balloons
Example
def find_min_arrow_shots(points):
"""
Minimum arrows to burst all balloons (intervals).
Sort by end, shoot at end of each non-covered balloon.
Same pattern as activity selection!
Time: O(n log n)
"""
points.sort(key=lambda x: x[1])
arrows = 1
arrow_pos = points[0][1] # Shoot at end of first balloon
for start, end in points[1:]:
if start > arrow_pos: # This balloon not hit by current arrow
arrows += 1
arrow_pos = end
return arrows
print(find_min_arrow_shots([[10,16],[2,8],[1,6],[7,12]])) # 2
print(find_min_arrow_shots([[1,2],[3,4],[5,6],[7,8]])) # 4
Problem 10 — Queue Reconstruction by Height
Example
def reconstruct_queue(people):
"""
people[i] = [h, k] where h=height, k=number of taller/equal people in front.
Greedy: sort by height desc (ties by k asc), then insert at position k.
"""
people.sort(key=lambda x: (-x[0], x[1]))
result = []
for person in people:
result.insert(person[1], person) # Insert at position k
return result
print(reconstruct_queue([[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]))
# [[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]]
Pattern Recognition
| Trigger in Problem | Likely Greedy Strategy |
|---|---|
| "Minimum cost/steps" | Sort by some criterion, process greedily |
| "Maximum compatible items" | Earliest end time (interval scheduling) |
| "Fill remaining with best" | Value density ratio (knapsack) |
| "Always feasible?" | Check total balance (gas station) |
| "Pair items" | Sort, use two pointers from both ends |
| "Partition into sections" | Track maximum reachable endpoint |
| "Satisfy constraints" | Smallest-first or largest-first pairing |
*Greedy Algorithms section complete.*
Continue the lesson path
Study next in this module
01Greedy AlgorithmsFractional Knapsack — Greedy by Value Density with ProofMaster the Fractional Knapsack problem — why greedy by value/weight ratio works, complete implementation with fractional items, proof using exchange argument, comparison with 0/1 knapsack, and real-world applications in resource allocation.02Greedy AlgorithmsGreedy Algorithms — Complete Introduction with Proof TechniquesMaster greedy algorithms from the ground up — what makes an algorithm greedy, the greedy choice property, optimal substructure, exchange argument proof technique, when greedy works vs when it fails, comparison with DP, and examples of correct and incorrect greedy choices.03Greedy AlgorithmsActivity Selection Problem — Greedy Scheduling with Proof of CorrectnessMaster the Activity Selection Problem completely — problem definition, why sorting by end time is the correct greedy criterion, exchange argument proof, implementation variants (maximum activities, weighted interval scheduling, minimum rooms), and interval scheduling interview problems.04Greedy AlgorithmsHuffman Coding — Optimal Prefix-Free Data CompressionMaster Huffman coding completely — what prefix-free codes are, why Huffman coding is optimal, building the Huffman tree with a priority queue, encoding and decoding, calculating compression ratio, and applications in ZIP, JPEG, and MP3 compression.