1. 分配饼干
Input: grid[1,3], size[1,2,4]
Output: 2
题目描述:每个孩子都有一个满足度 grid,每个饼干都有一个大小 size,只有饼干的大小大于等于一个孩子的满足度,该孩子才会获得满足。求解最多可以获得满足的孩子数量。
- 给一个孩子的饼干应当尽量小并且又能满足该孩子,这样大饼干才能拿来给满足度比较大的孩子。
- 因为满足度最小的孩子最容易得到满足,所以先满足满足度最小的孩子。
解题思想:在以上的解法中,我们只在每次分配时饼干时选择一种看起来是当前最优的分配方法,但无法保证这种局部最优的分配方法最后能得到全局最优解。我们假设能得到全局最优解,并使用反证法进行证明,即假设存在一种比我们使用的贪心策略更优的最优策略。如果不存在这种最优策略,表示贪心策略就是最优策略,得到的解也就是全局最优解。
JAVA代码:
class Solution {
//贪心问题
public int findContentChildren(int[] g, int[] s) {
Arrays.sort(g);//先对孩子的胃口值进行排序
Arrays.sort(s);//再对饼干值进行排序
int gi=0;
int si=0;
while(gi<=g.length-1&&si<=s.length-1){
if(g[gi]<=s[si]){
gi++;
}
si++;
}
return gi;
}
}
2.不重叠的区间个数
435.无重叠区间 (452类似)
给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。
注意:
可以认为区间的终点总是大于它的起点。区间 [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
解释: 你不需要移除任何区间,因为它们已经是无重叠的了。
解题思想:贪心策略:统计不重合的最大区间集合总数,所有任务的数量-不重合的最大区间集合总数=删除的重叠区间数。 按照每个区间的end排序(结束时间最早,比如你一天要参加几个活动,这个活动开始的多早其实不重要,重要的是你结束的多早,早晨7点就开始了然后一搞搞一天,那你今天也就只能参加这一个活动;但如果这个活动开始的不早,比如9点才开始,但是随便搞搞10点就结束了,那你接下来就还有大半天的时间可以参加其他活动),将第一个区间的end设置为默认end,依次遍历排序后的其他区间,进行重叠性判断。
JAVA代码:
import java.util.Arrays;
import java.util.Comparator;
/**
* Created by 高先森 on 2020/6/20.
*/
public class leetcode_435_Non_overlappingIntervals {
public static void main(String[] args){
int[][] ints={{1,2},{2,3},{3,4},{1,3}};
//解题思想:贪心策略
// 统计不重合的最大区间集合总数,所有任务的数量-不重合的最大区间集合总数=删除的重叠区间数。
// 按照每个区间的end排序,将第一个区间的end设置为默认end,依次遍历排序后的其他区间,进行重叠性判断
System.out.println(eraseOverlapIntervals(ints));
}
public static int eraseOverlapIntervals(int[][] intervals) {
if(intervals.length==0)
return 0;
//1.二维数组N*2存放的每个任务的起止时间star和end,先按照end对二位数组进行从小到大排序
Arrays.sort(intervals, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[1]-o2[1];//从小到大排序
}
});
//2.选取第一个区间的end为默认end,依次遍历intervals其它的区间,进行重叠性判断
int temp_end=intervals[0][1]; 第一个任务end最小,所以第一个选出第一个任务的end
int start,end,count=1;
for(int[] inte:intervals){
start=inte[0];
end=inte[1];
//如果当前区间的start在已经选个的区间中最后一个end之前,则当前区间重叠,忽略处理
if(start<temp_end)//相等不属于重合
continue;
//否则:当前区间不重叠,作为可选任务区间,总数加1,所有区间的end发生改变
count++;
temp_end=end;
}
//3.返回要删除的区间数目
return intervals.length-count;
}
}
3. 射箭刺破气球
在二维空间中有许多球形的气球。对于每个气球,提供的输入是水平方向上,气球直径的开始和结束坐标。由于它是水平的,所以y坐标并不重要,因此只要知道开始和结束的x坐标就足够了。开始坐标总是小于结束坐标。平面内最多存在10的4次个气球。一支弓箭可以沿着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(射爆另外两个气球)。
解题思想:使用贪心策略,因为一支箭可以射穿重叠区间内的气球,每个不重叠区间内的气球都额外需要一直箭射穿,将此问题转换为不重合的最大区间集合总数(不重叠区间的数目也就是所需要的最少弓箭数目),算法如下:
算法:
根据 x_end 将气球进行排序。
初始化 first_end 为第一个气球结束的坐标 points[0][1]。
初始化箭的数量 arrows = 1。
遍历所有的气球:
如果气球的开始坐标小于等于first_end:(边界可以算重合,能射穿)
跳过当前气球区间(重叠:同一支箭可以射穿)
否则
增加箭的数量(需要额外一支弓箭射穿当前气球)。
将 first_end 设置为当前气球的 x_end。
返回 arrows。
JAVA代码如下;
public static int findMinArrowShots(int[][] points) {
//贪心策略:
//1.先按照气球的end进行排序
Arrays.sort(points, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[1]-o2[1];//从小到大排序
}
});
//2.遍历每个气球区间,进行重叠性判断(用最少数量的箭引爆气球)
// 算法:
// 根据 x_end 将气球进行排序。
// 初始化 first_end 为第一个气球结束的坐标 points[0][1]。
// 初始化箭的数量 arrows = 1。
// 遍历所有的气球:
// 如果气球的开始坐标大于 first_end:
// 则增加箭的数量。
// 将 first_end 设置为当前气球的 x_end。
// 否则跳过当前气球区间(重叠:同一支箭可以射穿)
// 返回 arrows。
int temp_end=points[0][1],num=1;//对temp_end进行初始话赋值,一开始弓箭的数目为1
int start,end;
for(int[] p:points) {
start = p[0];
end = p[1];
if (start <= temp_end) {//如果当前气球的start区间小于temp_end,说明可以用同一支弓箭射穿,此处边界处相等也算射穿
continue;
}
num++;//否则还得额外增加一支弓箭才能射穿当前气球
temp_end = end;
}
return num;
}
4. 根据身高和序号重组队列
假设有打乱顺序的一群人站成一个队列。 每个人由一个整数对(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]]
解题思路:
解题思路:先排序再插入
1.排序规则:按照身高从大到小排序,身高相同的按照K值从小到大排序(因为身高相同时候,k的值依次递增才符合题意)
2.遍历排序后的数组,根据K插入到K的位置上
核心思想:高个子先站好位,矮个子插入到K位置上,对于当前矮个子而言:前面肯定有K个高个子已经站好位,对于当前高个子而言:矮个子的插入对高个子本身的k不受影响
JAVA代码如下:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
/**
* Created by 高先森 on 2020/6/21.
*/
public class leetcode_406_QueueReconstructionbyHeight {
public static void main(String[] args){
int[][] ints={{7,0},{4,4}, {7,1}, {5,0}, {6,1}, {5,2}};
//[9,0],[7,0],[1,9],[3,0],[2,7],[5,3],[6,0],[3,4],[6,2],[5,2]
int[][] ints1={{9,0},{7,0},{1,9},{3,0},{2,7},{5,3},{6,0},{3,4},{6,2},{5,2}};
reconstructQueue(ints1);
}
// 解题思路:先排序再插入
// 1.排序规则:按照身高从大到小排序,身高相同的按照K值从小到大排序(因为身高相同时候,k的值一次递增才符合题意)
// 2.遍历排序后的数组,根据K插入到K的位置上
// 核心思想:高个子先站好位,矮个子插入到K位置上,对于当前矮个子而言:前面肯定有K个高个子,
// 对于高个子而言:矮个子的插入对高个子的k不受影响
//示例一:
//[9,0],[7,0],[1,9],[3,0],[2,7],[5,3],[6,0],[3,4],[6,2],[5,2]
//排序:[9,0],[7,0],[6,0],[6,2],[5,2],[5,3],[3,0],[3,4],[2,7],[1,9]
//一个一个插入到自己的k位置上
//[9,0]
//[7,0],[9,0]
//[6,0],[7,0],[9,0]
//[6,0],[7,0],[6,2],[9,0]
//[6,0],[7,0],[5,2],[6,2],[9,0]
//[6,0],[7,0],[5,2],[5,3],[6,2],[9,0]
//[3,0],[6,0],[7,0],[5,2],[5,3],[6,2],[9,0]
//[3,0],[6,0],[7,0],[5,2],[3,4],[5,3],[6,2],[9,0]
//[3,0],[6,0],[7,0],[5,2],[3,4],[5,3],[6,2],[2,7],[9,0]
//[3,0],[6,0],[7,0],[5,2],[3,4],[5,3],[6,2],[2,7],[9,0],[1,9]
//示例二:
// [7,0], [7,1], [6,1], [5,0], [5,2], [4,4]
// 再一个一个插入。
// [7,0]
// [7,0], [7,1]
// [7,0], [6,1], [7,1]
// [5,0], [7,0], [6,1], [7,1]
// [5,0], [7,0], [5,2], [6,1], [7,1]
// [5,0], [7,0], [5,2], [6,1], [4,4], [7,1]
public static int[][] reconstructQueue(int[][] people) {
//1.按照身高从大到小排序,身高相同的按照K值从小到大排序(因为身高相同时候,k的值一次递增才符合题意)
Arrays.sort(people, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
//身高不等,按照身高从高到低排序;身高相等按照k值从小到大排序
return o1[0]==o2[0]?o1[1]-o2[1]:o2[0]-o1[0]; //个人理解:o1是右侧数,o2是左侧数,当o1[1]-o2[1]<0时交换,eg: 2、1排列,o1==1,o2==2,o1-o2<0交换,实现从小到大排序
}
});
//2.在K位置上插入当前(h,k)
List<int[]> list=new ArrayList<>();
for(int[] p:people)
list.add(p[1],p);//参数1为index(当前人(h,k)的k),参数2为添加到list中的人(h,k)
return list.toArray(new int[list.size()][2]);
}
}
5. 买卖股票最大的收益
给定一个数组,它的第 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。
解题思想:
用minPrice记录最低的股票买入价格,初始话为第一支股票的价格,依次遍历剩余的股票,用maxValue记录当前股票价卖出获得的最大价值。
JAVA代码如下:
class Solution {
public int maxProfit(int[] prices) {
if(prices.length<=0)
return 0;
int minPrice=prices[0];
int maxValue=0;
for(int price:prices){
if(price<minPrice)
minPrice =price;
else
maxValue=maxValue>(price-minPrice)?maxValue:(price-minPrice);
}
return maxValue;
}
}
6. 买卖股票的最大收益 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。
解题思想:
循环遍历每支股票,买入时机:如果当前手里不持有股票并且之后的股票价会长则买入(flag==0&&prices[i]<prices[i+1]),卖出时机:手里持有股票并且当前股票价格大于持有股票的买入价格,并且后续发生降价(如果继续增值prices[i]<prices[i+1],不会选择卖出)。
JAVA代码如下:
/**
* Created by 高先森 on 2020/6/21.
*/
public class leetcode_121_121_bestTimeToBuyAndSellStock {
public static void main(String[] args){
int[] ints={7,1,5,3,6,4};
int[] ints1={1,2,3,4,5};
System.out.println(maxProfit122(ints1));
}
public static int maxProfit122(int[] prices) {
if(prices.length<=0)
return 0;
int buyPrice=0;
int value=0;
int flag=0;//用于标记手里是否持有股票(要求手里同时只能持有一支股票)
int i;
for(i=0;i<prices.length-1;i++){
//1.如果当前手里不持有股票并且之后的股票价会长则买入
if(flag==0&&prices[i]<prices[i+1]){
buyPrice=prices[i];
flag=1;//当前手里持有股票
}else if(flag==1&&prices[i]>buyPrice&&prices[i]>prices[i+1]){
//2.卖出:手里持有股票并且当前股票价格大于持有股票的买入价格,并且发生降价(如果继续增值prices[i]<prices[i+1],不会选择卖出)
value=value+prices[i]-buyPrice;//盈利
flag=0;
}
}
if(flag==1) //因为i遍历到最后一直股票会退出,如果手里仍持有股票,则最后一直股票价格一定大于持有股票价格,卖出
value=value+(prices[i]-buyPrice);
return value;
}
}
7.种花问题
假设你有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花卉不能种植在相邻的地块上,它们会争夺水源,两者都会死去。给定一个花坛(表示为一个数组包含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 是非负整数,且不会超过输入数组的大小
解题思想:使用left,right分别标记当前位置前面和后面的种花情况,如果都是0,则当前位置可以种。
JAVA代码:
/**
* Created by 高先森 on 2020/6/24.
*/
public class leetcode_605_canPlaceFlowers {
public static void main(String[] args){
int[] ints={1,0,0,0,1};
System.out.println(canPlaceFlowers(ints,2));
}
public static boolean canPlaceFlowers(int[] flowerbed, int n) {
int maxnum=0,left,right;
int len=flowerbed.length;
for(int i=0;i<len;i++){
//1.已经种植的直接略过
if(flowerbed[i]==1)
continue;
//2.确定left值
left=(i==0?flowerbed[i]:flowerbed[i-1]);
//3.确定right值
right=(i==len-1?flowerbed[i]:flowerbed[i+1]);
//4.当前位置i可以种植
if(left==0&&right==0){
flowerbed[i]=1;
maxnum++;
}
}
return maxnum>=n?true:false;
}
}
8.判断是否为子序列
给定字符串 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.
解题思想:1.使用双指针分别遍历s和t进行字符判断 2.遍历短串s,利用t.indexof(参数1,参数2)进行字符匹配判断
JAVA代码:
/**
* Created by 高先森 on 2020/6/24.
*/
public class leetcode_392_isSubsequence {
public static void main(String[] args){
isSubsequence1("abE","ahbgdc");
}
public static boolean isSubsequence(String s, String t) {
int sLen=s.length();
int tLen=t.length();
int i=0,j=0;
while (i<sLen&&j<tLen){
if(s.charAt(i)==t.charAt(j)){
i++;
j++;
}else
j++;
}
return i==sLen?true:false;
}
public static boolean isSubsequence1(String s, String t) {
int index=-1;
for(char ch:s.toCharArray()){
//从index+1的位置开始匹配ch,返回匹配到的t中ch的下标
index=t.indexOf(ch,index+1);
if(index==-1)//如果没有匹配到则返回-1
return false;
}
return true;
}
}
9.修改数组中的一个数使其变为非递减
给你一个长度为 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
解题思路:对于给定的数组,如果出现num[i]>num[i+1],需要变更数字的位置为i或者i+1。(1)当num[i-1]<num[i+1] eg:1(i-1) 3(i) 2(i+1),最优的变更为num[i]=num[i+1](1 2 2) ,因为如果变更为num[i+1]=num[i](1 3 3)则更违反非递减的规则。(2)当num[i-1]>num[i+1] eg:2 3 1,则只能使 num[i+1]=num[i](2 3 3)。还需要设置个变量count统计变更次数,如果次数超过1则直接返回false。
JAVA代码:
/**
* Created by 高先森 on 2020/6/24.
*/
public class leetcode_665_NoneDecreasingArray {
public static void main(String[] args){
int[] ints={4,2,1};
System.out.println(checkPossibility(ints));
}
public static boolean checkPossibility(int[] nums) {
int len=nums.length;
int count=0;//用于标记交换的次数,超过一次为不符合
//1.如果数组的长度小于三,只要通过一次变换一定能满足非递减
if(len<3)
return true;
//2.大于三时对 i-1,i,i+1位置的数字就行判断
//1 3 2
//2 3 1
for(int i=0;i<len-1;i++){
if(nums[i]<=nums[i+1])
continue;
//存在nums[i]>nums[i+1],需要增加一次交换
count++;
if(count>1)//交换次数大于一直接返回
return false;
if(i==0)
nums[i]=nums[i+1];
//如果i>0且num[i-1]<nums[i+1],eg:1 3 2 尽量将i位置的数字置小更易满足非递减1 2 2 (1 3 3 更容易引起num[i+1]>num[i+2])
else if(i>0&&nums[i-1]<nums[i+1])
nums[i]=nums[i+1];
else{ //eg:2 3 1 只能将num[i+1]置为num[i] 2 3 3才符合
nums[i+1]=nums[i];
}
}
return true;
}
}
10.最大子数组和
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
进阶:
如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。
解题思想:动态规划:dp[i]为到i位置最大子序列的和 dp[i]=Math.max(dp[i-1]+num[i],num[i])
JAVA代码:
/**
* Created by 高先森 on 2020/6/24.
*/
public class leetcode_53_MaxinumSubarray {
public static void main(String[] args){
int[] ints={-2,1,-3,4,-1,2,1,-5,4};
//System.out.println(maxSubArray(ints));
System.out.println(maxSubArray1(ints));
}
//1.动态规划:dp[i]为到i位置最大子序列的和
//nums:{-2,1,-3,4,-1,2,1,-5,4}
//dp[i]内容:-2,1,-2,4,3,5,6,1,5
public static int maxSubArray(int[] nums) {
if(nums.length==0||nums==null)
return 0;
int[] dp=new int[nums.length];
int maxRes=nums[0];//随机初始化
dp[0]=nums[0];
for(int i=1;i<nums.length;i++){
//dp[i]为到i位置的最大子序列和,dp[i]的值为到i-1位置最大子序列的和加上当前数和当前数比较的最大值
dp[i]=Math.max(dp[i-1]+nums[i],nums[i]);
if(maxRes<dp[i])
maxRes=dp[i];
}
return maxRes;
}
//2.降低空间复杂度的动态规划,每个状态只与前一个状态有关,所以为了降低空间复杂度只用一个变量来保存
public static int maxSubArray1(int[] nums) {
if(nums.length==0||nums==null)
return 0;
//sum初始化nums[0],sum用于记录每个子序列的和,maxRes用于记录所有子序列中的最大值
int sum=nums[0],maxRes=sum;
for(int i=1;i<nums.length;i++){
//dp[i]为到i位置的最大子序列和,dp[i]的值为到i-1位置最大子序列的和加上当前数和当前数比较的最大值
sum=Math.max(sum+nums[i],nums[i]);
if(maxRes<sum)//maxRes为所有子序列中的最大子序列和
maxRes=sum;
}
return maxRes;
}
}
11.划分字母区间使相同字母出现在同一区间
字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一个字母只会出现在其中的一个片段。返回一个表示每个字符串片段的长度的列表。
示例 1:
输入:S = "ababcbacadefegdehijhklij"
输出:[9,7,8]
解释:
划分结果为 "ababcbaca", "defegde", "hijhklij"。
每个字母最多出现在一个片段中。
像 "ababcbacadefegde", "hijhklij" 的划分是错误的,因为划分的片段数较少。
解决思路:
定义数组 charAt[26] 来表示字符 字符串中每个char最后一次出现的下标。定义 left和right来表示当前区间的左边界和右边界。如果遇到的字符最后一次出现的位置下标大于 right, 就让 right=charAt[i] 来拓展当前的区间。当遍历到了当前区间的末尾时(即 i==right ),把当前区间加入答案,同时将 left 设为 i+1 去找下一个区间。
JAVA代码:
import java.util.ArrayList;
import java.util.List;
/**
* Created by 高先森 on 2020/6/24.
*/
public class leetcode_763_PartitionLabels {
public static void main(String[] args){
System.out.println(partitionLabels1("ababcbacadefegdehijhklij"));
}
//方法一:常规解法
public static List<Integer> partitionLabels(String S) {
int len=S.length();
//1.定义一个list存放结果
List<Integer> list=new ArrayList<>();
//2.将String转换为字符数组
char[] strChars=S.toCharArray();
//3.left标记当前区间的最左端,right标记当前区间的最右端,j>=left&&j<=right,通过遍历j来扩大(区间)right的范围
int left,right,j;
//4.寻找区间
for(int i=0;i<len;){
left=i;
right=S.lastIndexOf(strChars[left]);
j=left;
while (j<right){
j++;
right=right>S.lastIndexOf(strChars[j])?right:S.lastIndexOf(strChars[j]);
}
//将划分的区间结果进行存储
list.add(right-left+1);
i=right+1;//下一个子区间左边界
}
return list;
}
//方法二:贪心思想
public static List<Integer> partitionLabels1(String S) {
//1.使用charAt数组标记每个字符在串中出现的最后位置
int[] charAt=new int[26];
List<Integer> list=new ArrayList<>();
int len=S.length();
for(int i=0;i<len;i++){
charAt[S.charAt(i)-'a']=i;
}
//2.进行最大子区间的划分
int left=0,right=0;//left标记当前可划分的最大子区间的左端,right为右端
for(int i=0;i<len;i++){
//在left和right区间内的字母可能会扩大区间的右边界 eg:defegde 开始:left=0,right=5,下一次遍历到e,right扩充为6
right=Math.max(right,charAt[S.charAt(i)-'a']);
if(i==right){//当前最大子区间确定完毕
list.add(right-left+1);
left=i+1;//下一个子区间的左边界
}
}
return list;
}
}