基礎部分
455. 分發餅干
簡單
假設你是一位很棒的家長,想要給你的孩子們一些小餅干。但是,每個孩子最多只能給一塊餅干。對每個孩子 i ,都有一個胃口值 gi ,這是能讓孩子們滿足胃口的餅干的最小尺寸;並且每塊餅干 j ,都有一個尺寸 sj 。如果 sj >= gi ,我們可以將這個餅干 j 分配給孩子 i ,這個孩子會得到滿足。你的目標是盡可能滿足越多數量的孩子,並輸出這個最大數值。
注意:
你可以假設胃口值為正。
一個小朋友最多只能擁有一塊餅干。
示例 1:
輸入: [1,2,3], [1,1]
輸出: 1
解釋:
你有三個孩子和兩塊小餅干,3個孩子的胃口值分別是:1,2,3。
雖然你有兩塊小餅干,由於他們的尺寸都是1,你只能讓胃口值是1的孩子滿足。
所以你應該輸出1。
示例 2:
輸入: [1,2], [1,2,3]
輸出: 2
解釋:
你有兩個孩子和三塊小餅干,2個孩子的胃口值分別是1,2。
你擁有的餅干數量和尺寸都足以讓所有孩子滿足。
所以你應該輸出2.
class Solution {
public int findContentChildren(int[] g, int[] s) {
Arrays.sort(g);
Arrays.sort(s);
int res = 0;
int i = g.length-1;
int j = s.length-1;
while (i >= 0 && j >=0){
if (s[j] >= g[i]){ //滿足了拿去吃
res++;
j--;
}
i--;
}
return res;
}
}
435. 無重疊區間
中等
給定一個區間的集合,找到需要移除區間的最小數量,使剩余區間互不重疊。
注意:
- 可以認為區間的終點總是大於它的起點。
- 區間 [1,2] 和 [2,3] 的邊界相互“接觸”,但沒有相互重疊。
示例 1:
輸入: [ [1,2], [2,3], [3,4], [1,3] ]
輸出: 1
解釋: 移除 [1,3] 后,剩下的區間沒有重疊。
示例 2:
輸入: [ [1,2], [1,2], [1,2] ]
輸出: 2
解釋: 你需要移除兩個 [1,2] 來使剩下的區間沒有重疊。
示例 3:
輸入: [ [1,2], [2,3] ]
輸出: 0
解釋: 你不需要移除任何區間,因為它們已經是無重疊的了。
class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
int len = intervals.length;
if (len < 2) return 0;
Arrays.sort(intervals,(a,b)->(a[0]-b[0])); //以第一位升序排列
List<int[]> list = new LinkedList<>();
list.add(intervals[0]);
for (int i = 0; i < intervals.length; i++) {
int tail = list.get(list.size()-1)[1];
if (tail > intervals[i][0]){
if (tail > intervals[i][1]) //留尾巴短的,后邊不易重疊
list.get(list.size()-1)[1] = intervals[i][1];
}else list.add(intervals[i]);
}
return len - list.size();
}
}
452. 用最少數量的箭引爆氣球
中等
在二維空間中有許多球形的氣球。對於每個氣球,提供的輸入是水平方向上,氣球直徑的開始和結束坐標。由於它是水平的,所以y坐標並不重要,因此只要知道開始和結束的x坐標就足夠了。開始坐標總是小於結束坐標。平面內最多存在104個氣球。
一支弓箭可以沿着x軸從不同點完全垂直地射出。在坐標x處射出一支箭,若有一個氣球的直徑的開始和結束坐標為 xstart,xend, 且滿足 xstart ≤ x ≤ xend,則該氣球會被引爆。可以射出的弓箭的數量沒有限制。 弓箭一旦被射出之后,可以無限地前進。我們想找到使得所有氣球全部被引爆,所需的弓箭的最小數量。
Example:
輸入:
[[10,16], [2,8], [1,6], [7,12]]
輸出:
2
解釋:
對於該樣例,我們可以在x = 6(射爆[2,8],[1,6]兩個氣球)和 x = 11(射爆另外兩個氣球)。
class Solution {
public int findMinArrowShots(int[][] points) {
if (points.length < 2) return points.length;
Arrays.sort(points,(a,b)->(a[0]-b[0]));
int res = 0;
int i = 0;
while (i < points.length){
res++;
int last = points[i][1];
do {
i++;
if (i < points.length && points[i][1] < last)
last = points[i][1]; //整體的尾巴往前才能都中
}while (i < points.length && points[i][0] <= last);
}
return res;
}
}
406. 根據身高重建隊列
中等
假設有打亂順序的一群人站成一個隊列。 每個人由一個整數對(h, k)
表示,其中h
是這個人的身高,k
是排在這個人前面且身高大於或等於h
的人數。 編寫一個算法來重建這個隊列。
注意:
總人數少於1100人。
示例
輸入:
[[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]
輸出:
[[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]]
class Solution {
public int[][] reconstructQueue(int[][] people) {
Arrays.sort(people,(a,b)->a[1]-b[1]); //第二位正序
Arrays.sort(people,(a,b)->b[0]-a[0]); //第一位倒序
for (int i = 0; i < people.length; i++)
if (people[i][1] != i) { //people[i][1]:該去的位置
int j = i;
int[] tmp = people[j];
while (j != tmp[1]) { //tmp很關鍵,因為i和j都變化了,必須tmp
people[j] = people[j - 1];
j--;
}
people[j] = tmp;
}
return people;
}
}
121. 買賣股票的最佳時機
簡單
給定一個數組,它的第 i 個元素是一支給定股票第 i 天的價格。
如果你最多只允許完成一筆交易(即買入和賣出一支股票一次),設計一個算法來計算你所能獲取的最大利潤。
注意:你不能在買入股票前賣出股票。
示例 1:
輸入: [7,1,5,3,6,4]
輸出: 5
解釋: 在第 2 天(股票價格 = 1)的時候買入,在第 5 天(股票價格 = 6)的時候賣出,最大利潤 = 6-1 = 5 。
注意利潤不能是 7-1 = 6, 因為賣出價格需要大於買入價格;同時,你不能在買入前賣出股票。
示例 2:
輸入: [7,6,4,3,1]
輸出: 0
解釋: 在這種情況下, 沒有交易完成, 所以最大利潤為 0。
class Solution {
public int maxProfit(int[] prices) {
int res = 0;
int prof = 0;
for (int i = 1; i < prices.length; i++) {
int minus = prices[i] - prices[i-1];
prof += minus;
if (prof < 0) prof = 0; //負收益還不如不買
else if (prof > res) res = prof;
}
return res;
}
}
122. 買賣股票的最佳時機 II
簡單
給定一個數組,它的第 i 個元素是一支給定股票第 i 天的價格。
設計一個算法來計算你所能獲取的最大利潤。你可以盡可能地完成更多的交易(多次買賣一支股票)。
注意:你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。
示例 1
輸入: [7,1,5,3,6,4]
輸出: 7
解釋: 在第 2 天(股票價格 = 1)的時候買入,在第 3 天(股票價格 = 5)的時候賣出, 這筆交易所能獲得利潤 = 5-1 = 4 。
隨后,在第 4 天(股票價格 = 3)的時候買入,在第 5 天(股票價格 = 6)的時候賣出, 這筆交易所能獲得利潤 = 6-3 = 3 。
示例 2:
輸入: [1,2,3,4,5]
輸出: 4
解釋: 在第 1 天(股票價格 = 1)的時候買入,在第 5 天 (股票價格 = 5)的時候賣出, 這筆交易所能獲得利潤 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接連購買股票,之后再將它們賣出。
因為這樣屬於同時參與了多筆交易,你必須在再次購買前出售掉之前的股票。
示例 3:
輸入: [7,6,4,3,1]
輸出: 0
解釋: 在這種情況下, 沒有交易完成, 所以最大利潤為 0。
提示:
1 <= prices.length <= 3 * 10 ^ 4
0 <= prices[i] <= 10 ^ 4
class Solution {
public int maxProfit(int[] prices) {
int prof = 0;
for (int i = 1; i < prices.length; i++) {
int minus = prices[i] - prices[i-1];
prof += minus > 0 ? minus : 0;
}
return prof;
}
}
605. 種花問題
簡單
假設你有一個很長的花壇,一部分地塊種植了花,另一部分卻沒有。可是,花卉不能種植在相鄰的地塊上,它們會爭奪水源,兩者都會死去。
給定一個花壇(表示為一個數組包含0和1,其中0表示沒種植花,1表示種植了花),和一個數 n 。能否在不打破種植規則的情況下種入 n 朵花?能則返回True,不能則返回False。
示例 1:
輸入: flowerbed = [1,0,0,0,1], n = 1
輸出: True
示例 2:
輸入: flowerbed = [1,0,0,0,1], n = 2
輸出: False
注意:
- 數組內已種好的花不會違反種植規則。
- 輸入的數組長度范圍為 [1, 20000]。
- n 是非負整數,且不會超過輸入數組的大小。
class Solution {
public boolean canPlaceFlowers(int[] flowerbed, int n) {
int k = 0;
for (int i = 0; i < flowerbed.length && k<n; i++) {
if (flowerbed[i] == 0){
if (i+1 == flowerbed.length || flowerbed[i+1] == 0){
flowerbed[i] = 1;
k++;
i++; //下一個地方肯定不能種,跳過
}
}else {
i++; //下一個地方肯定不能種,跳過
}
}
return k >= n;
}
}
392. 判斷子序列
簡單
給定字符串 s 和 t ,判斷 s 是否為 t 的子序列。
你可以認為 s 和 t 中僅包含英文小寫字母。字符串 t 可能會很長(長度 ~= 500,000),而 s 是個短字符串(長度 <=100)。
字符串的一個子序列是原始字符串刪除一些(也可以不刪除)字符而不改變剩余字符相對位置形成的新字符串。(例如,"ace"
是"abcde"
的一個子序列,而"aec"
不是)。
示例 1:
s = "abc"
, t = "ahbgdc"
返回 true
.
示例 2:
s = "axc"
, t = "ahbgdc"
返回 false
.
后續挑戰 :
如果有大量輸入的 S,稱作S1, S2, ... , Sk 其中 k >= 10億,你需要依次檢查它們是否為 T 的子序列。在這種情況下,你會怎樣改變代碼?
class Solution {
public boolean isSubsequence(String s, String t) {
char[] schars = s.toCharArray();
char[] tchars = t.toCharArray();
int i, j;
for (i = 0,j = 0; i < schars.length; i++) {
while (j < tchars.length && tchars[j] != schars[i]) j++;
if (j >= tchars.length) break;
j++;
}
return i == schars.length;
}
}
665. 非遞減數列
簡單
給你一個長度為 n
的整數數組,請你判斷在 最多 改變 1
個元素的情況下,該數組能否變成一個非遞減數列。
我們是這樣定義一個非遞減數列的: 對於數組中所有的 i
(0 <= i <= n-2)
,總滿足 nums[i] <= nums[i + 1]
。
示例 1:
輸入: nums = [4,2,3]
輸出: true
解釋: 你可以通過把第一個4變成1來使得它成為一個非遞減數列。
示例 2:
輸入: nums = [4,2,1]
輸出: false
解釋: 你不能在只改變一個元素的情況下將其變為非遞減數列。
說明:
1 <= n <= 10 ^ 4
- 10 ^ 5 <= nums[i] <= 10 ^ 5
class Solution {
public boolean checkPossibility(int[] nums) {
int fix = 0;
for (int i = 1; i < nums.length && fix <= 1; i++){
if (nums[i] < nums[i-1]){
fix++;
if (i-2 >= 0 && nums[i]<nums[i-2]){ //突然有個數字小了
nums[i]=nums[i-1];
}else { //前邊的數字小了
nums[i-1]=nums[i];
}
}
}
return fix <= 1;
}
}
53. 最大子序和
簡單
給定一個整數數組 nums
,找到一個具有最大和的連續子數組(子數組最少包含一個元素),返回其最大和。
示例:
輸入: [-2,1,-3,4,-1,2,1,-5,4],
輸出: 6
解釋: 連續子數組 [4,-1,2,1] 的和最大,為 6。
進階:
如果你已經實現復雜度為 O(n) 的解法,嘗試使用更為精妙的分治法求解。
class Solution {
public int maxSubArray(int[] nums) {
int res = Integer.MIN_VALUE;
int sum = 0;
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
res = res > sum ? res : sum;
sum = sum < 0 ? 0 : sum;
}
return res;
}
}
763. 划分字母區間
中等
字符串 S
由小寫字母組成。我們要把這個字符串划分為盡可能多的片段,同一個字母只會出現在其中的一個片段。返回一個表示每個字符串片段的長度的列表。
示例 1:
輸入:S = "ababcbacadefegdehijhklij"
輸出:[9,7,8]
解釋:
划分結果為 "ababcbaca", "defegde", "hijhklij"。
每個字母最多出現在一個片段中。
像 "ababcbacadefegde", "hijhklij" 的划分是錯誤的,因為划分的片段數較少。
提示:
S
的長度在[1, 500]
之間。S
只包含小寫字母'a'
到'z'
。
class Solution {
public List<Integer> partitionLabels(String S) {
char[] chars = S.toCharArray();
Map<Character,Integer> map = new HashMap<>();
for (int i = 0; i < chars.length; i++) {
map.put(chars[i],i);
}
List<Integer> list = new LinkedList<>();
int max = 0;
int i = 0;
int j = 0;
while (j < chars.length){
max = Math.max(max,map.get(chars[j]));
if (j++ == max) {
list.add(j-i);
i = j;
}
}
return list;
}
}
頻率排序
135. 分發糖果
困難
老師想給孩子們分發糖果,有 N 個孩子站成了一條直線,老師會根據每個孩子的表現,預先給他們評分。
你需要按照以下要求,幫助老師給這些孩子分發糖果:
- 每個孩子至少分配到 1 個糖果。
- 相鄰的孩子中,評分高的孩子必須獲得更多的糖果。
那么這樣下來,老師至少需要准備多少顆糖果呢?
示例 1:
輸入: [1,0,2]
輸出: 5
解釋: 你可以分別給這三個孩子分發 2、1、2 顆糖果。
示例 2:
輸入: [1,2,2]
輸出: 4
解釋: 你可以分別給這三個孩子分發 1、2、1 顆糖果。
第三個孩子只得到 1 顆糖果,這已滿足上述兩個條件。
class Solution {
public int candy(int[] ratings) {
int len = ratings.length;
int[] res = new int[len];
res[0] = 1;
for (int i = 1; i < len; i++) {
if (ratings[i] == ratings[i-1]) res[i] = 1;
else if (ratings[i] > ratings[i-1]) res[i] = res[i-1]+1;
else { //發現前面的孩子給少了
res[i] = 1;
int j = i;
do {
j--;
if (res[j] <= res[j+1]){
res[j] = res[j+1] + 1;
}else {
break;
}
}while (j > 0 && ratings[j] < ratings[j-1]);
}
}
int candys = 0;
for (int candy : res) candys += candy;
return candys;
}
}
316. 去除重復字母 ※
困難
給你一個僅包含小寫字母的字符串,請你去除字符串中重復的字母,使得每個字母只出現一次。需保證返回結果的字典序最小(要求不能打亂其他字符的相對位置)。
示例 1:
輸入: "bcabc"
輸出: "abc"
示例 2:
輸入: "cbacdcbc"
輸出: "acdb"
public class Solution {
public String removeDuplicateLetters(String s) {
int[] cnt = new int[26];
int pos = 0;
for (int i = 0; i < s.length(); i++) //字母出現次數的字典
cnt[s.charAt(i) - 'a']++;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) < s.charAt(pos)) pos = i; //后邊的比pos的字典順序小
if (--cnt[s.charAt(i) - 'a'] == 0) break; //字典里減沒了,pos可以放第一位
}
return s.length() == 0 ? "" : s.charAt(pos) +
removeDuplicateLetters(s.substring(pos + 1) //遞歸
.replaceAll("" + s.charAt(pos), "") //這個字符統計完了,全部去掉
);
}
}
45. 跳躍游戲 II
困難
給定一個非負整數數組,你最初位於數組的第一個位置。
數組中的每個元素代表你在該位置可以跳躍的最大長度。
你的目標是使用最少的跳躍次數到達數組的最后一個位置。
示例:
輸入: [2,3,1,1,4]
輸出: 2
解釋: 跳到最后一個位置的最小跳躍數是 2。
從下標為 0 跳到下標為 1 的位置,跳 1 步,然后跳 3 步到達數組的最后一個位置。
說明:
假設你總是可以到達數組的最后一個位置。
class Solution {
public int jump(int[] nums) {
int res = 0;
for (int i = nums.length - 1; i > 0; ) {
int pos = i - 1;
for (int j = 0; j < i; j++)
if (nums[j] >= i-j) pos = Math.min(pos,j);
res++;
i = pos;
}
return res;
}
}
714. 買賣股票的最佳時機含手續費
中等
給定一個整數數組 prices
,其中第 i
個元素代表了第 i
天的股票價格 ;非負整數 fee
代表了交易股票的手續費用。
你可以無限次地完成交易,但是你每筆交易都需要付手續費。如果你已經購買了一個股票,在賣出它之前你就不能再繼續購買股票了。
返回獲得利潤的最大值。
注意:這里的一筆交易指買入持有並賣出股票的整個過程,每筆交易你只需要為支付一次手續費。
示例 1:
輸入: prices = [1, 3, 2, 8, 4, 9], fee = 2
輸出: 8
解釋: 能夠達到的最大利潤:
在此處買入 prices[0] = 1
在此處賣出 prices[3] = 8
在此處買入 prices[4] = 4
在此處賣出 prices[5] = 9
總利潤: ((8 - 1) - 2) + ((9 - 4) - 2) = 8.
注意:
0 < prices.length <= 50000
.0 < prices[i] < 50000
.0 <= fee < 50000
.
//未通過,不知道為什么
class Solution {
public int maxProfit(int[] prices, int fee) {
int sum = 0;
int[] profs = new int[prices.length-1];
for (int i = 0; i < profs.length; i++)
profs[i] = prices[i+1] - prices[i];
int prof = 0;
int drop = 0;
for (int i = 0; i < profs.length; i++) {
if (prof == 0 && profs[i] < 0) continue;
if (profs[i] < 0){
drop += profs[i];
if (drop <= -fee) { //賠fee塊錢,還不如交管理費賣了
if (prof > fee) sum += prof-fee;
drop = 0;
prof = 0;
}
}else {
prof += drop + profs[i];
drop = 0;
}
}
sum += prof > fee ? prof - fee : 0; //最后一波沒加上
return sum;
}
}
402. 移掉K位數字
中等
給定一個以字符串表示的非負整數 num,移除這個數中的 k 位數字,使得剩下的數字最小。
注意:
- num 的長度小於 10002 且 ≥ k。
- num 不會包含任何前導零。
示例 1 :
輸入: num = "1432219", k = 3
輸出: "1219"
解釋: 移除掉三個數字 4, 3, 和 2 形成一個新的最小的數字 1219。
示例 2 :
輸入: num = "10200", k = 1
輸出: "200"
解釋: 移掉首位的 1 剩下的數字為 200. 注意輸出不能有任何前導零。
示例 3 :
輸入: num = "10", k = 2
輸出: "0"
解釋: 從原數字移除所有的數字,剩余為空就是0。
class Solution {
public String removeKdigits(String num, int k) {
LinkedList<Character> stack = new LinkedList<>();
for(char digit : num.toCharArray()) {
while(!stack.isEmpty() && k > 0 && stack.peekLast() > digit) {
//stack不為空,k大於0,前面的數大於后面的數
stack.removeLast(); //去掉前面的數
k -= 1; //去掉一次少一次機會
}
stack.addLast(digit);
}
for(int i=0; i<k; ++i) stack.removeLast(); //還有機會沒用完
StringBuilder res = new StringBuilder();
while (stack.peekFirst() == '0') stack.pollFirst();
for (Character c : stack) res.append(c);
return res.length() > 0 ? res.toString() : "0";
}
}
1029. 兩地調度
簡單
公司計划面試 2N
人。第 i
人飛往 A
市的費用為 costs[i][0]
,飛往 B
市的費用為 costs[i][1]
。
返回將每個人都飛到某座城市的最低費用,要求每個城市都有 N
人抵達。
示例:
輸入:[[10,20],[30,200],[400,50],[30,20]]
輸出:110
解釋:
第一個人去 A 市,費用為 10。
第二個人去 A 市,費用為 30。
第三個人去 B 市,費用為 50。
第四個人去 B 市,費用為 20。
最低總費用為 10 + 30 + 50 + 20 = 110,每個城市都有一半的人在面試。
提示:
1 <= costs.length <= 100
costs.length
為偶數1 <= costs[i][0], costs[i][1] <= 1000
class Solution {
public int twoCitySchedCost(int[][] costs) {
int potA = costs.length/2;
int potB = costs.length/2;
Arrays.sort(costs,(x, y)->Math.abs(y[0]-y[1])-Math.abs(x[0]-x[1])); //相差越多權重越高,,權重高的人先挑
int res = 0;
for (int i = 0; i < costs.length; i++) {
int a = costs[i][0];
int b = costs[i][1];
if (a < b) {
if (potA-- > 0) res += a;
else res += b;
} else {
if (potB-- > 0) res += b;
else res += a;
}
}
return res;
}
}
//官方的,(A地-B地)的升序,這么簡單嗎,憑什么
class Solution {
public int twoCitySchedCost(int[][] costs) {
Arrays.sort(costs,(a,b)->a[0]-a[1]-b[0]+b[1]);
int total = 0;
int n = costs.length / 2;
for (int i = 0; i < n; ++i) total += costs[i][0] + costs[i + n][1];
return total;
}
}