题目
* 给你一个按升序排序的整数数组 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; } }