数组 A 是 [0, 1, …, N - 1] 的一种排列,N 是数组 A 的长度。全局倒置指的是 i,j 满足 0 <= i < j < N 并且 A[i] > A[j] ,局部倒置指的是 i 满足 0 <= i < N 并且 A[i] > A[i+1] 。
当数组 A 中全局倒置的数量等于局部倒置的数量时,返回 true 。
示例 1:
输入: A = [1,0,2]
输出: true
解释: 有 1 个全局倒置,和 1 个局部倒置。
示例 2:
输入: A = [1,2,0]
输出: false
解释: 有 2 个全局倒置,和 1 个局部倒置。
注意:
A 是 [0, 1, …, A.length - 1] 的一种排列
A 的长度在 [1, 5000]之间
这个问题的时间限制已经减少了。
初始想法是找出所有的全局倒置和局部倒置:
局部倒置很好找,遍历一遍O(N)
全局变量晚上这么想的,维护一个列表,遍历A中每一个数字a,将其插入到列表之中,记录插入的位置index,那么a能组成的全局倒置就是和小于a但是又跑到a后面的数,也就是小于a但是在列表中还没有出现的数字,所以a能组成的倒置数量是a-index。这样一遍下来是O(nlog(n)).
超时,5000数据量对于O(nlog(n))超时我也是醉了
class Solution {
public boolean isIdealPermutation(int[] A) {
int len = A.length;
int global = 0;
int local = 0;
List<Integer> list = new LinkedList<Integer>();
for (int i = 0;i < len - 1;i++){
if (A[i] > A[i + 1]){
local++;
}
}
for (int a:A){
//System.out.println(a+","+insert(a,list));
global += a - insert(a,list);
}
//System.out.println(global+","+local);
return local == global;
}
public int insert(int x,List<Integer> list){
int min = 0;
int max = list.size();
if (max == 0 || x < list.get(0)){
list.add(0,x);
return 0;
} else if (x > list.get(max - 1)){
list.add(x);
return max;
} else {
while(min <= max){
int med = (min + max) / 2;
int temp1 = list.get(med);
if (x > temp1){
if (med == max - 1 || x < list.get(med + 1)){
list.add(med + 1,x);
return med + 1;
} else {
min = med + 1;
}
} else {
max = med - 1;
}
}
}
return -1;
}
}
O(n)的解法:
因为每个局部倒置一定是全局倒置,也就是说全局倒置>=局部倒置,而如果某个数的位置和它在排好序的数组的位置差别>1,那么它就会产生>1个全局倒置,全局和局部一定不相等
public boolean isIdealPermutation(int[] A) {
int len = A.length;
for (int i= 0;i < len;i++){
if (Math.abs(A[i] - i) > 1){
return false;
}
}
return true;
}