题目
* 给你一个按升序排序的整数数组 num(可能包含重复数字),请你将它们分割成一个或多个长度至少为 3 的子序列,其中每个子序列都由连续整数组成。
*
* 如果可以完成上述分割,则返回 true ;否则,返回 false 。
* 示例 1:
*
* 输入: [1,2,3,3,4,5]
* 输出: True
* 解释:
* 你可以分割出这样两个连续子序列 :
* 1, 2, 3
* 3, 4, 5
* 示例 2:
*
* 输入: [1,2,3,3,4,4,5,5]
* 输出: True
* 解释:
* 你可以分割出这样两个连续子序列 :
* 1, 2, 3, 4, 5
* 3, 4, 5
*
* 示例 3:
*
* 输入: [1,2,3,4,4,5]
* 输出: False
*
解法
本地要求能让队列满足长度至少为3即可,所以这里考虑应该使用
贪心算法
.让每个数先优先满足让更多的队列大于等于3即可.否则就满足等于2,否则就添加到大于等于3的队列上,最后再考虑新创建队列(每次新创建的队列都有可能让结果为false所以最后考虑)
解法二的优化: 不需要存储对应的队列结果,只需要记录对应所需队尾元素的队列有几个即可.
解法三isPossible2
是1ms用时的大佬,但是方法有点难以理解.有通俗易懂解释的可以留言指教.个人感觉这个大佬看出了这个序列的规律 : 将每个数和第一个数的差值映射到数组上(因为反正是递增的),当数组出现断裂的数据孤岛且孤岛长度小于3则必定失败.
实现
public class LK659 {
public static void main(String[] args) {
int[] nums = new int[]{1,2,3,4,6,7,8};
System.out.println(isPossible2(nums));
}
public static boolean isPossible(int[] nums) {
if(nums == null || nums.length < 3){
return false;
}
// 使用贪心+标记存储 --> 优先满足让更多的队列大于等于3即可.否则就满足等于2,否则就添加到大于等于3的队列上,最后考虑新创建队列(每次新创建的队列都有可能让结果为false所以最后考虑)
// 声明一个存放已经满足队列长度的Map. key为队列最后一个数字.
Map<Integer, List<List<Integer>>> readyMap = new HashMap<>();
// 声明一个存放还在增长的Map. key为队列最后一个数字,大于三则移至readyMap中
Map<Integer, List<List<Integer>>> growingMap = new HashMap<>();
// 声明一个初创建只有一个元素的队列存储Map
Map<Integer, List<List<Integer>>> createdMap = new HashMap<>();
for(int i = 0; i< nums.length; i++){
int currentNum = nums[i];
int pre = currentNum - 1;
if(growingMap.containsKey(currentNum-1)){
List<List<Integer>> growingLists = growingMap.get(pre);
List<Integer> growingList = growingLists.get(0);
growingLists.remove(0);
if(growingLists.isEmpty()){
growingMap.remove(pre);
}
// 添加完毕已经成长为三元素队列,移除成长Map至已准备好队列. 并判断当前成长队列是否为空,为空则删除当前map中该key
growingList.add(currentNum);
List<List<Integer>> readLists = readyMap.getOrDefault(currentNum, new ArrayList<>());
readLists.add(growingList);
readyMap.put(currentNum, readLists);
}else if(createdMap.containsKey(pre)){
List<List<Integer>> createdLists = createdMap.get(pre);
List<Integer> createdList = createdLists.get(0);
createdLists.remove(0);
if(createdLists.isEmpty()){
createdMap.remove(pre);
}
// 添加完毕已经成长为二元素队列,移除创建Map至成长队列. 并判断当前队列是否为空,为空则删除当前map中该key
createdList.add(currentNum);
List<List<Integer>> growingLists = growingMap.getOrDefault(currentNum, new ArrayList<>());
growingLists.add(createdList);
growingMap.put(currentNum, growingLists);
}else if(readyMap.containsKey(pre)){
List<List<Integer>> readyLists = readyMap.get(pre);
List<Integer> readyList = readyLists.get(0);
readyLists.remove(0);
if(readyLists.isEmpty()){
readyMap.remove(pre);
}
readyList.add(currentNum);
List<List<Integer>> nextReadyLists = readyMap.getOrDefault(currentNum, new ArrayList<>());
nextReadyLists.add(readyList);
readyMap.put(currentNum, nextReadyLists);
}else{
List<Integer> createdList = new ArrayList<>();
createdList.add(currentNum);
List<List<Integer>> createdLists = createdMap.getOrDefault(currentNum, new ArrayList<>());
createdLists.add(createdList);
createdMap.put(currentNum, createdLists);
}
}
// 遍历完还有growing的Map则表示为false
return growingMap.isEmpty() && createdMap.isEmpty();
}
public static boolean isPossible1(int[] nums) {
if(nums == null || nums.length < 3){
return false;
}
// 使用贪心+标记存储 --> 优先满足让更多的队列大于等于3即可.否则就满足等于2,否则就添加到大于等于3的队列上,最后考虑新创建队列(每次新创建的队列都有可能让结果为false所以最后考虑)
// 声明一个存放已经满足队列长度的Map. key为队列最后一个数字.
Map<Integer, Integer> readyMap = new HashMap<>();
// 声明一个存放还在增长的Map. key为队列最后一个数字,大于三则移至readyMap中
Map<Integer, Integer> growingMap = new HashMap<>();
// 声明一个初创建只有一个元素的队列存储Map
Map<Integer, Integer> createdMap = new HashMap<>();
for(int i = 0; i< nums.length; i++){
int currentNum = nums[i];
int pre = currentNum - 1;
if(growingMap.containsKey(currentNum-1)){
Integer growingLists = growingMap.get(pre);
growingLists -=1;
if(growingLists == 0){
growingMap.remove(pre);
}else{
growingMap.put(pre, growingLists);
}
// 添加完毕已经成长为三元素队列,移除成长Map至已准备好队列. 并判断当前成长队列是否为空,为空则删除当前map中该key
Integer readLists = readyMap.getOrDefault(currentNum, 0);
readLists +=1;
readyMap.put(currentNum, readLists);
}else if(createdMap.containsKey(pre)){
Integer createdLists = createdMap.get(pre);
createdLists -=1;
if(createdLists == 0){
createdMap.remove(pre);
}else{
createdMap.put(pre, createdLists);
}
// 添加完毕已经成长为二元素队列,移除创建Map至成长队列. 并判断当前队列是否为空,为空则删除当前map中该key
Integer growingLists = growingMap.getOrDefault(currentNum, 0);
growingLists +=1;
growingMap.put(currentNum, growingLists);
}else if(readyMap.containsKey(pre)){
Integer readyLists = readyMap.get(pre);
readyLists -=1;
if(readyLists == 0){
readyMap.remove(pre);
}else{
readyMap.put(pre, readyLists);
}
Integer nextReadyLists = readyMap.getOrDefault(currentNum, 0);
nextReadyLists +=1;
readyMap.put(currentNum, nextReadyLists);
}else{
Integer createdLists = createdMap.getOrDefault(currentNum, 0);
createdLists +=1;
createdMap.put(currentNum, createdLists);
}
}
// 遍历完还有growing的Map则表示为false
return growingMap.isEmpty() && createdMap.isEmpty();
}
// 最后大佬这个根据递增规律. 根据每个位置和第一个数的差值映射对应位置(如果是连续的则对应差值位置也连续)并将该位置+1; 如果有连续不为0的数据孤岛小于3个的则会返回false
public static boolean isPossible2(int[] nums) {
int min = nums[0];
int max = nums[nums.length-1];
int[] numNumber = new int[max-min+1];
for (int i = 0; i<nums.length; i++){
numNumber[nums[i]-min]++;
}
for (int i = 0; i<numNumber.length; i++){
while (numNumber[i]!=0){
int num=1;
for (int j = i; j+1!=numNumber.length && numNumber[j+1]>=numNumber[j];j++){
num++;
}
if (num<3){
return false;
}
for (int j = i; j-i<num; j++){
numNumber[j]--;
}
}
}
return true;
}
}