文章目錄
860.檸檬水找零
在20元找零處用到了貪心,貪10元的個數,如果有就先找10元+5元,沒有再找三個5元
public class Solution {
public boolean lemonadeChange(int[] bills) {
int five = 0;
int ten = 0;
for (int bill : bills) {
if (bill == 5) five++;//支付5元
else if (bill == 10) {//支付10元
if (five > 0) {//如果有5元的,找5元
five--;
ten++;
} else {//如果沒有5元的就無法找零退出
return false;
}
} else {//支付20元
if (ten > 0 && five > 0) {//如果有10元和5元的,找10元+5元
ten--;
five--;
} else if (ten == 0 && five >= 3) {//如果沒有10元的有3張及以上5元的
five -= 3;
} else//如果零錢不夠,退出
return false;
}
}
return true;
}
}
455.分發餅乾
將兩個數組進行排序
從最小胃口的孩子和最小的餅乾開始比較,如果不滿足餅乾後移一位,如果滿足計數加一,餅乾和小孩都後移一位
public class Solution {
public int findContentChildren(int[] g, int[] s) {
Arrays.sort(g);
Arrays.sort(s);
int i = 0;
int j = 0;
int count = 0;
while (i < g.length && j < s.length) {
if (g[i] <= s[j]) {
count++;
i++;
j++;
} else {
j++;
}
}
return count;
}
}
121.買賣股票的最佳時機
遍歷數組,如果值小於當前最小值更換當前最小值,如果值大於當前最小值和最小值做差更新最大差
public class Solution {
public int maxProfit(int[] prices) {
if (prices.length == 0) return 0;
int max = 0;
int minPrice = prices[0];
for (int i = 1; i < prices.length; i++) {
if (prices[i] < minPrice) minPrice = prices[i];
else max = Math.max(prices[i] - minPrice, max);
}
return max;
}
}
122.買賣股票的最佳時機II
貪心:如果第二天大於第一天就第一天買第二天賣
public class Solution {
public int maxProfit(int[] prices) {
int res = 0;
for (int i = 0; i < prices.length - 1; i++) {
if (prices[i] < prices[i + 1]) res += prices[i + 1] - prices[i];
}
return res;
}
}
55.跳躍遊戲
採用從後往前的貪心算法
設置一個變量爲endReachable
初始化爲最後位置
從後往前遍歷數組,如果nums[i] + i >= endReachable
說明從i位置可以到達endReachable
,於是將endReachable
設置爲i
遍歷完數組,如果endReachable == 0
,說明從第一個位置可以到達endReachable
,而通過各個endReachable
能到達最後位置,所以就能到達
public class Solution {
public boolean canJump(int[] nums) {
if (nums == null) return false;
int endReachable = nums.length - 1;
for (int i = nums.length - 1; i >= 0; i--) {
if (nums[i] + i >= endReachable) {
endReachable = i;
}
}
return endReachable == 0;
}
}
從前往後的貪心算法
如下圖,開始的位置是 2,可跳的範圍是橙色的。然後因爲 3 可以跳的更遠,所以跳到 3 的位置。
如下圖,然後現在的位置就是 3 了,能跳的範圍是橙色的,然後因爲 4 可以跳的更遠,所以下次跳到 4 的位置。
如果發生endReachable < i
,即出現了0,且0無法跳過,則不能到達最後位置,返回
我們令
- endReachable 爲當前能跳的邊界值
- maxPosition能跳的這幾個點能跳到的最遠位置
public class Solution {
public boolean canJump(int[] nums) {
int endReachable = 0;//一步到達的邊界值
int maxPosition = 0;//在範圍內可以到達的最遠你的值
for (int i = 0; i < nums.length - 1; i++) {
//出現0了,且還跳不過的那種,不能到達最後
if (endReachable < i) return false;
maxPosition = Math.max(maxPosition, nums[i] + i);
if (i == endReachable) {
endReachable = maxPosition;
}
}
return maxPosition >= nums.length - 1;
}
}
45.跳躍遊戲II
正向的進行貪心
如下圖,開始的位置是 2,可跳的範圍是橙色的。然後因爲 3 可以跳的更遠,所以跳到 3 的位置。
如下圖,然後現在的位置就是 3 了,能跳的範圍是橙色的,然後因爲 4 可以跳的更遠,所以下次跳到 4 的位置。
循環爲什麼到nums.length - 1
就停止了,因爲
if (i == endReachable) {
endReachable = maxPosition;
steps++;
}
這一步的判斷,如果最遠步數剛好跳到了最後一個點,setps會多加一次,如案例[2,3,1,1,4]的第二步可以到最後位置,如果遍歷到nums.length - 1
,則上述代碼會執行三次,結果爲3
我們令
- endReachable 爲當前能跳的邊界值
- maxPosition能跳的這幾個點能跳到的最遠位置
- steps跳了的步數
public class Solution {
public int jump(int[] nums) {
int endReachable = 0;//一步到達的邊界值
int maxPosition = 0;//在範圍內可以到達的最遠你的值
int steps = 0;//步數
for (int i = 0; i < nums.length - 1; i++) {
maxPosition = Math.max(maxPosition, nums[i] + i);
if (i == endReachable) {
endReachable = maxPosition;
steps++;
}
}
return steps;
}
}
435.無重疊區間
貪心算法
- 將數組按結尾升序排列
- 找出共有多少個沒有重疊的區域,記爲count
- 用
總數-count
得到需要移除的數目
public class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
if (intervals == null || intervals.length == 0) return 0;
//將數組按區間結尾升序排列
Arrays.sort(intervals, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[1] - o2[1];
}
});
//第一段的結尾
int x_end = intervals[0][1];
//至少有1段
int count = 1;
for (int[] cur : intervals) {
if (cur[0] >= x_end) {
x_end = cur[1];
count++;
}
}
return intervals.length - count;
}
}
452.用最少數量的箭引爆氣球
問題讀起來聽繞口的,仔細分析後其實就是求:不重疊區間的個數
與無重疊區間問題中的求不重疊區間 個數有一點點不同,邊界值可以取等號,如[1,2]和[2,3]是存在重疊的
所以cur[0] >= x_end
的條件需要改成cur[0] > x_end
public class Solution {
//問題轉化爲:求獨立區間的個數
public int findMinArrowShots(int[][] points) {
if (points.length == 0) return 0;
Arrays.sort(points, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[1] - o2[1];
}
});
//獨立區間個數,至少有一個
int count = 1;
//第一個氣球的x_end
int x_end = points[0][1];
for (int[] cur : points) {
if (cur[0] > x_end) {
count++;
x_end = cur[1];
}
}
return count;
}
}
406.根據身高重建隊列
解法太美麗,可能解釋不清楚,官方題解看圖就懂了
首先,將數組按身高降序排列,如果身高相同的按k大小升序排列
然後,按照k的大小進行插入
下插入的最大的數字是符合k的定義的,再插入次大的數字,最大的數字後移,因爲小的數在大的數面前是看不見的,所以不影響大的數的k的定義,因爲是由大到小排列的,所以可以直接插入
public class Solution {
public int[][] reconstructQueue(int[][] people) {
if (people == null || people.length == 0 || people[0].length == 0) return people;
///身高按降序,身高相同時,次數按升序
Arrays.sort(people, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] == o2[0] ? o1[1] - o2[1] : o2[0] - o1[0];
}
});
//插入
List<int[]> queue = new ArrayList<>();
for (int[] p : people) {
queue.add(p[1], p);
}
return queue.toArray(new int[queue.size()][]);
}
}
605.種花問題
貪心+常數優化
依次遍歷數組,如果不爲1,判斷求一個數和後一個數是否都爲0,如果是將值改爲1,並計數+1,當計數等於n時就退出不再繼續
public class Solution {
public boolean canPlaceFlowers(int[] flowerbed, int n) {
int len = flowerbed.length;
int count = 0;
for (int i = 0; i < len && count < n; i++) {
if (flowerbed[i] == 1) continue;
int pre = i == 0 ? 0 : flowerbed[i - 1];
int next = i == len - 1 ? 0 : flowerbed[i + 1];
if (pre == 0 && next == 0) {
flowerbed[i] = 1;
count++;
}
}
return count == n;
}
}
392.判斷子序列
遍歷s,將s的字符在t中進行匹配,每次匹配從上一個匹配到的數的後一位開始,如果出現沒有匹配到,則不是子串
public class Solution {
public boolean isSubsequence(String s, String t) {
int index = -1;
for (char c : s.toCharArray()) {
index = t.indexOf(c, index + 1);
if (index == -1) return false;
}
return true;
}
}
665.非遞減數列
依次遍歷數組,如果nums[i] < nums[i - 1],則需要進行修改,修改的情況有兩種
- 修改nums[i]的值,判斷一下nums[i-2]是否大於nums[i],如果大於,說明nums[i]纔是錯誤的,將值改爲nums[i-1]的值
- 修改nums[i-1]的值,將nums[i]賦值給nums[i-1]
public class Solution {
public boolean checkPossibility(int[] nums) {
if (nums.length < 2) return true;
int count = 0;
for (int i = 1; i < nums.length && count < 2; i++) {
if (nums[i] >= nums[i - 1]) continue;
count++;
if (i - 2 >= 0 && nums[i] < nums[i - 2]) {
nums[i] = nums[i - 1];
} else {
nums[i - 1] = nums[i];
}
}
return count <= 1;
}
}
53.最大子序和
每一步都選擇最佳方案,到最後就是全局最優的方案。
public class Solution {
public int maxSubArray(int[] nums) {
int curMax = nums[0];
int trueMax = nums[0];
for (int i = 1; i < nums.length; i++) {
curMax = Math.max(nums[i], curMax + nums[i]);
trueMax = Math.max(curMax, trueMax);
}
return trueMax;
}
}
763.劃分字母區間
將每個字母的最後一次出現下標存放在last數組中
依次遍歷字符串,並更新訪問過的字符的最大last值
當遍歷到i==last
時,說明一個區域劃分完了,記錄位置並存儲
public class Solution {
public List<Integer> partitionLabels(String S) {
List<Integer> res = new ArrayList<>();
int[] last = new int[26];
//記錄每個字母最後出現的位置
for (int i = 0; i < S.length(); i++) {
last[S.charAt(i) - 'a'] = i;
}
int curend = 0;
int newfirst = 0;
for (int i = 0; i < S.length(); i++) {
curend = Math.max(curend, last[S.charAt(i) - 'a']);
if (i == curend) {
res.add(i - newfirst + 1);
newfirst = i + 1;
}
}
return res;
}
}