CodeSage / data /docs /greedy_algorithms.txt
Aditya
Add LLM vs RAG vs Fine-Tuning comparison project
4a3f117
Raw
History Blame Contribute Delete
5.35 kB
GREEDY ALGORITHMS
=================
Greedy algorithms make the locally optimal choice at each step hoping to find a global optimum.
They do NOT reconsider past choices (unlike DP).
When to use greedy: when local optimum leads to global optimum (greedy choice property + optimal substructure).
--- Activity Selection Problem ---
Select maximum number of non-overlapping activities.
Sort by end time, always pick activity that ends earliest.
Time: O(n log n).
def activity_selection(activities):
# activities: [(start, end), ...]
activities.sort(key=lambda x: x[1]) # sort by end time
selected = [activities[0]]
last_end = activities[0][1]
for start, end in activities[1:]:
if start >= last_end:
selected.append((start, end))
last_end = end
return selected
acts = [(1,4),(3,5),(0,6),(5,7),(3,9),(5,9),(6,10),(8,11),(8,12),(2,14),(12,16)]
print(activity_selection(acts))
# [(1,4),(5,7),(8,11),(12,16)] — 4 activities
--- Fractional Knapsack ---
Take fractions of items to maximize value. Unlike 0/1 knapsack, items can be split.
Sort by value/weight ratio. Greedy works here (not for 0/1).
Time: O(n log n).
def fractional_knapsack(weights, values, capacity):
items = sorted(zip(values, weights), key=lambda x: x[0]/x[1], reverse=True)
total_value = 0
for value, weight in items:
if capacity >= weight:
total_value += value
capacity -= weight
else:
total_value += value * (capacity / weight)
break
return round(total_value, 2)
weights = [10, 20, 30]
values = [60, 100, 120]
print(fractional_knapsack(weights, values, 50)) # Output: 240.0
--- Coin Change (Greedy — only works for canonical coin systems) ---
For standard denominations (1,5,10,25 cents), greedy gives minimum coins.
Note: greedy FAILS for arbitrary denominations — use DP instead.
def coin_change_greedy(coins, amount):
coins.sort(reverse=True)
count = 0
result = []
for coin in coins:
while amount >= coin:
amount -= coin
count += 1
result.append(coin)
return count, result
print(coin_change_greedy([1, 5, 10, 25], 41))
# Output: (4, [25, 10, 5, 1])
--- Job Scheduling to Maximize Profit ---
Schedule jobs with deadlines to maximize total profit. Each job takes 1 unit of time.
Time: O(n^2).
def job_scheduling(jobs):
# jobs: [(id, deadline, profit), ...]
jobs.sort(key=lambda x: x[2], reverse=True) # sort by profit
max_deadline = max(j[1] for j in jobs)
slots = [None] * (max_deadline + 1)
total_profit = 0
for job_id, deadline, profit in jobs:
for t in range(min(deadline, max_deadline), 0, -1):
if slots[t] is None:
slots[t] = job_id
total_profit += profit
break
scheduled = [s for s in slots if s]
return scheduled, total_profit
jobs = [('a',2,100),('b',1,19),('c',2,27),('d',1,25),('e',3,15)]
print(job_scheduling(jobs)) # (['c', 'a', 'e'], 142)
--- Huffman Encoding ---
Build optimal prefix-free codes for data compression.
Characters with higher frequency get shorter codes.
Time: O(n log n).
import heapq
def huffman_encoding(freq):
heap = [[f, [ch, ""]] for ch, f in freq.items()]
heapq.heapify(heap)
while len(heap) > 1:
lo = heapq.heappop(heap)
hi = heapq.heappop(heap)
for pair in lo[1:]:
pair[1] = '0' + pair[1]
for pair in hi[1:]:
pair[1] = '1' + pair[1]
heapq.heappush(heap, [lo[0] + hi[0]] + lo[1:] + hi[1:])
return sorted(heap[0][1:], key=lambda x: len(x[1]))
freq = {'a': 5, 'b': 9, 'c': 12, 'd': 13, 'e': 16, 'f': 45}
codes = huffman_encoding(freq)
for char, code in codes:
print(f"{char}: {code}")
# f: 0
# c: 100
# d: 101
# a: 1100
# b: 1101
# e: 111
--- Minimum Platforms (Greedy) ---
Find minimum number of platforms needed at a railway station.
Time: O(n log n).
def min_platforms(arrivals, departures):
arrivals.sort()
departures.sort()
platforms = 1
max_platforms = 1
i = 1
j = 0
while i < len(arrivals) and j < len(departures):
if arrivals[i] <= departures[j]:
platforms += 1
i += 1
else:
platforms -= 1
j += 1
max_platforms = max(max_platforms, platforms)
return max_platforms
arr = [900, 940, 950, 1100, 1500, 1800]
dep = [910, 1200, 1120, 1130, 1900, 2000]
print(min_platforms(arr, dep)) # Output: 3
--- Gas Station (Circular Route) ---
Find starting gas station to complete a circular route. Greedy: if total gas >= total cost, solution exists.
Time: O(n). Space: O(1).
def can_complete_circuit(gas, cost):
total_tank = 0
curr_tank = 0
start = 0
for i in range(len(gas)):
diff = gas[i] - cost[i]
total_tank += diff
curr_tank += diff
if curr_tank < 0:
start = i + 1
curr_tank = 0
return start if total_tank >= 0 else -1
gas = [1, 2, 3, 4, 5]
cost = [3, 4, 5, 1, 2]
print(can_complete_circuit(gas, cost)) # Output: 3
--- Greedy vs Dynamic Programming ---
Greedy: fast O(n log n), works when greedy choice is provably optimal.
DP: slower O(n^2) or O(n*W), works for all optimization problems.
Example: Fractional Knapsack → Greedy. 0/1 Knapsack → DP.