17人参与 • 2026-08-02 • Java
贪心(greedy)是五大基础算法思想之一,逻辑简单、时间复杂度极低(大多 o (n) / o (nlogn)),笔试、面试高频考点。 很多初学者只会套模板,分不清什么时候能用贪心、贪心策略怎么推导,本文从零拆解:核心定义、适用条件、解题通用步骤、区分贪心 / 动态规划,搭配简单入门→中等进阶→困难高频共 10 道经典 leetcode 原题,每道题含题意、贪心思路、完整 java 代码、复杂度分析、易错点,看完可直接刷题、面试复盘。
每一步只做当前局部最优选择,不回头、不回溯、不预判未来,依靠不断累积局部最优,最终得到全局最优解。 特点:短视、无回溯、只看当下最优,执行效率远高于动态规划。
只有同时满足下面两点,贪心才能求出全局最优,否则只能局部最优,答案错误。
表格
| 维度 | 贪心算法 | 动态规划 dp |
|---|---|---|
| 决策方式 | 每一步固定选局部最优,不回头 | 记录所有子问题解,遍历所有选择 |
| 是否回溯 | 无,决策不可逆 | 可回溯,缓存子问题结果 |
| 时间复杂度 | o (nlogn)、o (n),极快 | o (n²)、o (nk),开销更大 |
| 适用场景 | 区间、分配、跳跃、股票、找零 | 01 背包、最长子序列、路径规划 |
| 核心矛盾 | 局部最优能否推全局最优 | 存在多种选择,需要对比所有方案 |
举例:01 背包不能用贪心(物品不可拆分),部分背包(物品可分割)可以贪心;这是判断贪心能否使用的经典例子。
孩子数组g:每个孩子胃口;饼干数组s:每块饼干大小。 饼干尺寸≥孩子胃口才能满足,求最多满足多少孩子。
import java.util.arrays;
public class solution455 {
public int findcontentchildren(int[] g, int[] s) {
arrays.sort(g);
arrays.sort(s);
int child = 0; // 孩子指针
int cookie = 0;// 饼干指针
while (child < g.length && cookie < s.length) {
// 当前饼干能满足孩子
if (s[cookie] >= g[child]) {
child++;
}
cookie++;
}
return child;
}
}排序 o (nlogn + mlogm),遍历 o (n+m);空间 o (1)。
柠檬水 5 元一杯,顾客只会付 5/10/20 元,按顺序排队,判断能否正确找零。
public class solution860 {
public boolean lemonadechange(int[] bills) {
int five = 0, ten = 0;
for (int money : bills) {
if (money == 5) {
five++;
} else if (money == 10) {
if (five == 0) return false;
five--;
ten++;
} else {
// 20元,优先10+5
if (ten > 0 && five > 0) {
ten--;
five--;
} else if (five >= 3) {
five -= 3;
} else {
return false;
}
}
}
return true;
}
}20 元优先消耗 10 元,不能直接三张 5,否则后续 10 元顾客无法找零。
股价数组,可无限次买卖,不能同时持有多只股票,求最大利润。
只要后一天价格 > 前一天,当天买入次日卖出,累加所有正向差值。 逻辑等价:拆分所有上涨区间,每一段都赚差价。
public class solution122 {
public int maxprofit(int[] prices) {
int profit = 0;
for (int i = 1; i < prices.length; i++) {
if (prices[i] > prices[i - 1]) {
profit += prices[i] - prices[i - 1];
}
}
return profit;
}
}二维数组存放区间起止,求最少移除多少区间,让剩余区间互不重叠。
java
import java.util.arrays;
public class solution435 {
public int eraseoverlapintervals(int[][] intervals) {
if (intervals.length == 0) return 0;
// 按右端点升序
arrays.sort(intervals, (a, b) -> a[1] - b[1]);
int count = 1;
int lastend = intervals[0][1];
for (int i = 1; i < intervals.length; i++) {
int start = intervals[i][0];
if (start >= lastend) {
count++;
lastend = intervals[i][1];
}
}
return intervals.length - count;
}
}求最多不重叠区间 → 按右端点排序; 区间覆盖 → 按左端点排序。
数组每个元素代表当前位置最大跳跃距离,判断能否跳到数组最后一位。
遍历数组,持续维护当前能到达的最远距离 maxreach; 如果当前下标 i > maxreach,说明无法到达该位置,直接 false; 遍历结束 maxreach 覆盖末尾则 true。
public class solution55 {
public boolean canjump(int[] nums) {
int maxreach = 0;
int len = nums.length;
for (int i = 0; i < len; i++) {
if (i > maxreach) return false;
maxreach = math.max(maxreach, i + nums[i]);
if (maxreach >= len - 1) return true;
}
return true;
}
}数组代表最大跳跃距离,保证一定能到达末尾,求最少跳跃次数。
java
public class solution45 {
public int jump(int[] nums) {
int len = nums.length;
if (len == 1) return 0;
int step = 0;
int curmax = 0;
int nextmax = 0;
for (int i = 0; i < len - 1; i++) {
nextmax = math.max(nextmax, i + nums[i]);
// 到达当前边界,必须跳一次
if (i == curmax) {
step++;
curmax = nextmax;
if (curmax >= len - 1) break;
}
}
return step;
}
}数组可翻转数字符号,最多翻转 k 次,同一数字可多次翻转,求数组最大和。
import java.util.arrays;
public class solution1005 {
public int largestsumafterknegations(int[] nums, int k) {
arrays.sort(nums);
// 翻转负数
for (int i = 0; i < nums.length && k > 0; i++) {
if (nums[i] < 0) {
nums[i] = -nums[i];
k--;
} else break;
}
int sum = 0;
int min = integer.max_value;
for (int num : nums) {
sum += num;
min = math.min(min, num);
}
// 剩余奇数次翻转,减去两倍最小值
if (k % 2 == 1) sum -= 2 * min;
return sum;
}
}环形路线,gas [i] 是加油站油量,cost [i] 开到下一站消耗;判断是否存在起点绕环一周,返回起点下标。
public class solution134 {
public int cancompletecircuit(int[] gas, int[] cost) {
int totalgas = 0, totalcost = 0;
int curoil = 0;
int start = 0;
int n = gas.length;
for (int i = 0; i < n; i++) {
totalgas += gas[i];
totalcost += cost[i];
curoil += gas[i] - cost[i];
// 当前起点无法走到i,起点改为i+1
if (curoil < 0) {
curoil = 0;
start = i + 1;
}
}
return totalgas >= totalcost ? start : -1;
}
}一排孩子,每个有评分,规则:
public class solution135 {
public int candy(int[] ratings) {
int n = ratings.length;
int[] candy = new int[n];
// 初始每人1颗
for (int i = 0; i < n; i++) candy[i] = 1;
// 左到右
for (int i = 1; i < n; i++) {
if (ratings[i] > ratings[i - 1]) {
candy[i] = candy[i - 1] + 1;
}
}
// 右到左
for (int i = n - 2; i >= 0; i--) {
if (ratings[i] > ratings[i + 1] && candy[i] <= candy[i + 1]) {
candy[i] = candy[i + 1] + 1;
}
}
int sum = 0;
for (int num : candy) sum += num;
return sum;
}
}给定若干区间,选择最少的点,让每个区间至少包含一个点。
按右端点升序,每次选当前区间右端点,跳过所有包含该点的区间。
import java.util.arrays;
public class rangepoint {
public static int minpoint(int[][] ranges) {
arrays.sort(ranges, (a, b) -> a[1] - b[1]);
int res = 0;
int lastpoint = integer.min_value;
for (int[] range : ranges) {
int l = range[0], r = range[1];
if (l > lastpoint) {
res++;
lastpoint = r;
}
}
return res;
}
}贪心算法是性价比最高的算法思想,代码简短、性能优秀,绝大多数场景只需要排序 + 单次线性遍历。核心难点不在于编码,而是推导正确的局部最优策略,刷题时不要直接抄代码,先手动模拟贪心选择过程,验证策略正确性。 后端、算法笔试、蓝桥杯、leetcode 热题中贪心占比极高,熟练掌握本文 10 道例题,可覆盖 90% 贪心面试场景。
到此这篇关于java贪心算法完整代码及常见面试题的文章就介绍到这了,更多相关java贪心算法内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论