📅  最后修改于: 2023-12-03 15:39:49.681000             🧑  作者: Mango
贪婪算法是一种常用的算法,常用于优化问题和近似算法。其核心思想是每一步都选择最优解,最终得到的结果则是全局最优解的一部分。
在面试时,对于贪婪算法掌握程度的考察能够反映出面试者的算法实现能力和思维灵活度。本文将介绍排名前20位的贪婪算法面试问题,希望对读者有所帮助。
解决背包问题,是贪心算法的一个经典问题。可以将物品按单位重量的价值排序,然后从最有价值的物品开始放入背包中,直到放满为止。
def knapsack(w, v, c):
N = len(w)
unit_value_list = [(v[i] / w[i], i) for i in range(N)]
unit_value_list.sort(reverse=True)
res = 0
for unit_value, index in unit_value_list:
if w[index] <= c:
res += v[index]
c -= w[index]
else:
res += c * unit_value
break
return res
任务调度问题可以被认为是一种贪心选择策略问题,在每个时间点选择最优的任务。
def task_schedule(n, tasks):
tasks.sort(key=lambda x: x[1])
res = [0] * n
for i in range(n):
j = i
while j >= 0 and tasks[j][1] <= tasks[i][0]:
j -= 1
res[i] = res[j - 1] + tasks[i][2] if j >= 0 else tasks[i][2]
return res
区间覆盖问题需要找到最少数量的区间来覆盖目标区间。可以将所有区间按照右端点从小到大排序,然后从前往后找到第一个右端点大于当前标记点的区间,将该区间记为当前覆盖的区间,同时将该区间的右端点作为下一个标记点,重复该过程。
def interval_cover(n, intervals):
intervals.sort(key=lambda x: x[1])
res = []
start, end = intervals[0]
for i in range(1, n):
if end < intervals[i][0]:
res.append([start, end])
start, end = intervals[i]
else:
end = max(end, intervals[i][1])
res.append([start, end])
return res
硬币找零问题是一个简单的贪心问题,每次选择面值最大的硬币,直到最终目标得到满足。
def coin_change(amount, coins):
coins.sort(reverse=True)
res = 0
for coin in coins:
res += amount // coin
amount = amount % coin
return res
最大子序和问题可以被认为是一种贪心算法。遍历序列中的每一个元素,同时维护当前子序列的最大和。
def max_subarray(nums):
res = cur_sum = nums[0]
for i in range(1, len(nums)):
cur_sum = max(nums[i], cur_sum + nums[i])
res = max(res, cur_sum)
return res
区间调度问题是一种经典的贪心算法问题,在一组区间中选择最多的相互不重叠的区间。
def interval_schedule(intervals):
intervals = sorted(intervals, key=lambda x: x[1])
res = []
end = float('-inf')
for interval in intervals:
if interval[0] >= end:
res.append(interval)
end = interval[1]
return res
跳跃游戏问题需要计算在数组中最少需要跳多少次可以到达最后一个位置。从后往前遍历数组,每次跳到当前位置最少需要的次数。
def jump(nums):
end = len(nums) - 1
res, start = 0, end
while start > 0:
for i in range(start - 1, -1, -1):
if nums[i] >= start - i:
start = i
res += 1
break
return res
根据身高和序列重建队列问题是一种贪心算法问题,在一个数组中给定每个人的身高和身高相等的人的个数,重组队列。
def reconstruct_queue(people):
people = sorted(people, key=lambda x: (-x[0], x[1]))
res = []
for p in people:
res.insert(p[1], p)
return res
煎饼排序是一种经典的贪心算法问题,在一个数组中,依次翻转前n个元素,将最大的数翻转到正确的位置,再翻转前n-1个元素,重复该过程。
def pancake_sort(arr):
res = []
n = len(arr)
for i in range(n, 1, -1):
max_index = arr.index(max(arr[:i]))
if max_index != i - 1:
arr[:max_index + 1] = arr[:max_index + 1][::-1]
arr[:i] = arr[:i][::-1]
res.extend([max_index + 1, i])
return res
区域和检索问题需要实现区间的和查询和区间内的数值更新。可以通过维护累加和数组和差分数组来实现。
class NumArray:
def __init__(self, nums: List[int]):
n = len(nums)
self.sums = [0] * (n + 1)
for i in range(n):
self.sums[i + 1] = self.sums[i] + nums[i]
def sumRange(self, i: int, j: int) -> int:
return self.sums[j + 1] - self.sums[i]
def update(self, i: int, val: int) -> None:
diff = val - (self.sums[i + 1] - self.sums[i])
for j in range(i + 1, len(self.sums)):
self.sums[j] += diff
最小总距离问题需要计算一组数的中位数,最小总距离可以通过所有数到中位数的距离和来计算。
def min_total_distance(nums):
nums.sort()
i, j = 0, len(nums) - 1
res = 0
while i < j:
res += nums[j] - nums[i]
i += 1
j -= 1
return res
最多可达成的换楼任务数问题需要找到一组人有多个任务,每个任务有开始时间和结束时间,如果完成所有任务,最多能完成多少个任务。
def max_tasks(tasks):
tasks.sort(key=lambda x: x[1])
res, end = 0, float('-inf')
for task in tasks:
if task[0] >= end:
res += 1
end = task[1]
return res
最小花费路径问题需要确定从左上角到右下角的最小路径和,在每个格子只能选择向右或向下走。
def min_cost_path(grid):
m, n = len(grid), len(grid[0])
for i in range(1, m):
grid[i][0] += grid[i - 1][0]
for j in range(1, n):
grid[0][j] += grid[0][j - 1]
for i in range(1, m):
for j in range(1, n):
grid[i][j] += min(grid[i - 1][j], grid[i][j - 1])
return grid[m - 1][n - 1]
剪绳子问题需要将长度为n的绳子剪成m段,使得每段绳子长度的乘积最大。
def cut_rope(n):
if n <= 3:
return n - 1
quotient, remainder = divmod(n, 3)
if remainder == 0:
return 3 ** quotient
elif remainder == 1:
return 3 ** (quotient - 1) * 4
else:
return 3 ** quotient * 2
最大平均值和问题需要从一个长度为n的数组中选择一个长度为k的子数组,并且该子数组的平均值最大。
def max_average(nums, k):
def check(x):
sums, min_sum = 0, 0
for i in range(k):
sums += nums[i] - x
if sums >= 0:
return True
for i in range(k, len(nums)):
sums += nums[i] - x
min_sum = min(min_sum, sums)
if sums >= min_sum:
return True
return False
l, r = min(nums), max(nums)
while r - l > 1e-5:
mid = (l + r) / 2
if check(mid):
l = mid
else:
r = mid
return l
跳水板问题需要从一根长度为shorter,另一根长度为longer的木板中,得到所有可能的长度为k的木板,shorter和longer只能使用一次。
def diving_board(shorter, longer, k):
if shorter == longer:
return [shorter * k]
res = [0] * (k + 1)
for i in range(k + 1):
res[i] = (k - i) * shorter + i * longer
return res
股票问题需要找到最大利润,可以用贪心算法,从前往后遍历数组,计算差值的最大和。
def max_profit(prices):
res = 0
for i in range(1, len(prices)):
res += max(prices[i] - prices[i - 1], 0)
return res
最长湍流子数组问题是一种贪心算法问题,在一个数组中找到最长的子数组,满足奇数位置元素大于相邻元素且偶数位置元素小于相邻元素。
def max_turbulence_size(arr):
res, start = 1, 0
for i in range(1, len(arr)):
if arr[i - 1] == arr[i]:
start = i
elif i == len(arr) - 1 or arr[i - 1] > arr[i] < arr[i + 1] or arr[i - 1] < arr[i] > arr[i + 1]:
res = max(res, i - start + 1)
start = i
return res
摆动序列问题需要找到最长的子序列,满足元素交替上升和下降。可以用贪心算法,从前往后遍历数组,同时维护上升和下降的标记。
def wiggle_max_length(nums):
if len(nums) < 2:
return len(nums)
up, down = 1, 1
for i in range(1, len(nums)):
if nums[i] > nums[i - 1]:
up = down + 1
elif nums[i] < nums[i - 1]:
down = up + 1
return max(up, down)
最大桶的汇总问题需要汇总n个桶的数量,每次可以将k个桶汇总成一个桶,并且每次汇总的成本为被汇总的桶数量。
def min_skips(dist, speed, hours_before):
n = len(dist)
dp = [0] * n
for i in range(n):
for j in range(i, -1, -1):
dp[j] = (dp[j] + dist[i] + speed - 1) // speed * speed
if j > 0:
dp[j] = min(dp[j], dp[j - 1] + dist[i])
for i in range(n):
if dp[i] <= hours_before * speed:
return i
return -1
本文介绍了排名前20位的贪婪算法面试问题,涵盖了贪心算法的多个经典问题,在面试中对贪心算法的理解和实现有所帮助。同时,对于贪心算法的掌握程度也反映了一个程序员的算法实现能力和思维灵活度。