import java.util.*; /** * NC384 132序列 * @author d3y1 */ public class Solution { /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * * @param nums int整型一维数组 * @return bool布尔型 */ public boolean find132Subseq (ArrayList<Integer> nums) { // return solution1(nums); // return solution2(nums); // return solution3(nums); // return solution4(nums); return solution5(nums); } /** * 枚举j: 二分 -> 超时! * * i<j<k * ... i ... j ... k ... * ... 1 ... 3 ... 2 ... * * nums[i]<nums[k] && nums[k]<nums[j] * => nums[i]<nums[k]<nums[j] * * @param nums * @return */ private boolean solution1(ArrayList<Integer> nums){ int n = nums.size(); if(n < 3){ return false; } // 左边比nums[j]小的所有数的最小值 默认满足条件nums[i]<nums[j] int[] leftMin = new int[n]; // 右边比nums[j]小的所有数的最大值 默认满足条件nums[k]<nums[j] int[] rightMax = new int[n]; // init leftMin[0] = Integer.MAX_VALUE; rightMax[n-1] = Integer.MAX_VALUE; // nums[i]<nums[j] int min = nums.get(0); int num; for(int j=1; j<n; j++){ num = nums.get(j); if(min < num){ leftMin[j] = min; }else{ leftMin[j] = Integer.MAX_VALUE; min = num; } } int max = nums.get(n-1); LinkedList<Integer> list = new LinkedList<>(); list.add(nums.get(n-1)); // 枚举j for(int j=n-2; j>=0; j--){ num = nums.get(j); if(num > max){ list.add(num); rightMax[j] = max; max = num; } // 二分 else{ int left = 0; int right = list.size()-1; int mid,pos; while(left <= right){ mid = left+(right-left)/2; // left最终指向list中第一个大于等于num的位置 if(list.get(mid) < num){ left = mid+1; }else{ right = mid-1; } } pos = left; list.add(pos, num); // nums[k]<nums[j] if(pos == 0){ rightMax[j] = Integer.MAX_VALUE; }else{ rightMax[j] = list.get(pos-1); } } if(leftMin[j]==Integer.MAX_VALUE || rightMax[j]==Integer.MAX_VALUE){ continue; } // nums[i]<nums[k] => 根据定义可推: nums[i]<nums[k]<nums[j] if(leftMin[j] < rightMax[j]){ return true; } } return false; } /** * 枚举j: TreeMap(优化)[红黑树] * * i<j<k * ... i ... j ... k ... * ... 1 ... 3 ... 2 ... * * nums[i]<nums[k] && nums[k]<nums[j] * => nums[i]<nums[k]<nums[j] * * @param nums * @return */ private boolean solution2(ArrayList<Integer> nums){ int n = nums.size(); if(n < 3){ return false; } // 左边比nums.get(i)小的所有数的最小值 默认满足条件nums[i]<nums[j] int[] leftMin = new int[n]; // 右边比nums.get(i)小的所有数的最大值 默认满足条件nums[k]<nums[j] int[] rightMax = new int[n]; // init leftMin[0] = Integer.MAX_VALUE; rightMax[n-1] = Integer.MAX_VALUE; // nums[i]<nums[j] int min = nums.get(0); int num; for(int j=1; j<n; j++){ num = nums.get(j); if(min < num){ leftMin[j] = min; }else{ leftMin[j] = Integer.MAX_VALUE; min = num; } } // 右侧元素 降序 TreeMap<Integer, Integer> rightAll = new TreeMap<>((o1, o2) -> (o2 - o1)); rightAll.put(nums.get(n-1), rightAll.getOrDefault(nums.get(n-1), 0)+1); Integer next; // 枚举j for(int j=n-2; j>=0; j--){ num = nums.get(j); // nums[k]<nums[j] // 下一个数(小于等于num-1的所有数的最大值) -> 右边比num小的所有数的最大值 next = rightAll.ceilingKey(num-1); if(next == null){ rightMax[j] = Integer.MAX_VALUE; }else{ rightMax[j] = next; } rightAll.put(num, rightAll.getOrDefault(num, 0)+1); if(leftMin[j]==Integer.MAX_VALUE || rightMax[j]==Integer.MAX_VALUE){ continue; } // nums[i]<nums[k] => 根据定义可推: nums[i]<nums[k]<nums[j] if(leftMin[j] < rightMax[j]){ return true; } } return false; } /** * 枚举j: TreeMap(优化)[红黑树]{从左往右遍历} * * i<j<k * ... i ... j ... k ... * ... 1 ... 3 ... 2 ... * * nums[i]<nums[k] && nums[k]<nums[j] * => nums[i]<nums[k]<nums[j] * * @param nums * @return */ private boolean solution3(ArrayList<Integer> nums){ int n = nums.size(); if(n < 3){ return false; } // 左侧最小值 nums[i] int leftMin = nums.get(0); // 右侧所有元素 升序 nums[k] TreeMap<Integer, Integer> rightAll = new TreeMap<>(); for(int k=2; k<n; ++k) { rightAll.put(nums.get(k), rightAll.getOrDefault(nums.get(k), 0)+1); } // 枚举j for(int j=1; j<n-1; j++) { // nums[i]<nums[j] if(leftMin < nums.get(j)){ // 下一个数(当前元素右边大于等于leftMin+1的所有数的最小值) nums[i]<nums[k] Integer next = rightAll.ceilingKey(leftMin+1); // nums[k]<nums[j] => 可推: nums[i]<nums[k]<nums[j] if(next!=null && next<nums.get(j)){ return true; } } leftMin = Math.min(leftMin, nums.get(j)); // 右移 -> 移除 rightAll.put(nums.get(j+1), rightAll.get(nums.get(j+1))-1); if(rightAll.get(nums.get(j+1)) == 0){ rightAll.remove(nums.get(j+1)); } } return false; } /** * 枚举i: 单调栈(单调减) * * i<j<k * ... i ... j ... k ... * ... 1 ... 3 ... 2 ... * * nums[i]<nums[k] && nums[k]<nums[j] * => nums[i]<nums[k]<nums[j] * * @param nums * @return */ private boolean solution4(ArrayList<Integer> nums){ int n = nums.size(); if (n < 3) { return false; } Stack<Integer> stack = new Stack<>(); stack.push(nums.get(n-1)); int maxK = Integer.MIN_VALUE; int num; for(int i=n-2; i>=0; i--){ num = nums.get(i); // nums[i]<nums[k] if(num < maxK){ return true; } // nums[k]<nums[j] while(!stack.isEmpty() && num>stack.peek()){ maxK = stack.pop(); } // stack.push(num); // 优化 -> 不会将maxK更新为更大的值(不用入栈) if(num > maxK){ stack.push(num); } } return false; } /** * 枚举k: 单调栈(单调减)+二分(单调栈上二分) * * i<j<k * ... i ... j ... k ... * ... 1 ... 3 ... 2 ... * * nums[i]<nums[k] && nums[k]<nums[j] * => nums[i]<nums[k]<nums[j] * * @param nums * @return */ private boolean solution5(ArrayList<Integer> nums){ int n = nums.size(); if (n < 3) { return false; } // 模拟单调栈(单调减) List<Integer> candidateI = new ArrayList<Integer>(); candidateI.add(nums.get(0)); // 模拟单调栈(单调减) List<Integer> candidateJ = new ArrayList<Integer>(); candidateJ.add(nums.get(0)); int num,cISize,cJSize; for(int k=1; k<n; ++k) { num = nums.get(k); // i位置 int idxI = binarySearchFirst(candidateI, num); // j位置 int idxJ = binarySearchLast(candidateJ, num); if(idxI>=0 && idxJ>=0){ // 保证 nums[i]在前 nums[j]在后 if(idxI <= idxJ){ return true; } } if(num < candidateI.get(candidateI.size()-1)){ candidateI.add(num); candidateJ.add(num); }else if(num > candidateJ.get(candidateJ.size()-1)){ int lastI = candidateI.get(candidateI.size()-1); while(!candidateJ.isEmpty() && num>candidateJ.get(candidateJ.size()-1)){ candidateI.remove(candidateI.size()-1); candidateJ.remove(candidateJ.size()-1); } // 尽量小 candidateI.add(lastI); candidateJ.add(num); } } return false; } /** * 二分查找最前一个小于target(nums[k])的索引(i位置) * @param candidate * @param target * @return */ public int binarySearchFirst(List<Integer> candidate, int target) { int left=0; int right = candidate.size()-1; if (candidate.get(right) >= target) { return -1; } int result = -1; int mid; while(left <= right){ mid = left+(right-left)/2; if(candidate.get(mid) >= target){ left = mid+1; }else{ result = mid; right = mid-1; } } return result; } /** * 二分查找最后一个大于target(nums[k])的索引(j位置) * @param candidate * @param target * @return */ public int binarySearchLast(List<Integer> candidate, int target) { int left = 0; int right = candidate.size()-1; if (candidate.get(left) <= target) { return -1; } int result = -1; int mid; while(left <= right){ mid = left+(right-left)/2; if(candidate.get(mid) > target){ result = mid; left = mid+1; }else{ right = mid-1; } } return result; } }