JAVA程序设计:全局倒置与局部倒置(LeetCode:775)

数组 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]之间
这个问题的时间限制已经减少了。

方法一:两层循环暴力(按理说n只有5000,暴力是肯定能过的,但是出题人故意卡时间了)

class Solution {
    public boolean isIdealPermutation(int[] A) {
    	
    	int sum1=0,sum2=0;
    	
    	for(int i=0;i<A.length;i++) {
    		if(i+1<A.length && A[i]>A[i+1])
    			sum2++;
    		for(int j=i+1;j<A.length;j++)
    			if(A[i]>A[j]) sum1++;
    	}
    	
    	return sum1==sum2;
    }
}

方法二:树状数组(归并排序也可以)

对于局部倒置我们直接遍历一遍就行,而全局导致仔细看其实就是求的数组的逆序数呀,这还不简单吗,树状数组或者归并排序完美解决,ok了。

class Solution {
	
	class node implements Comparable<node>{
		int val;
		int index;
		public node(int val,int index) {
			this.val=val;
			this.index=index;
		}
		@Override
		public int compareTo(node o) {
			// TODO 自动生成的方法存根
			if(val==o.val)
				return index-o.index;
			return val-o.val;
		}
	}
	
	int[] c;
	int sum1,sum2;
	
    public boolean isIdealPermutation(int[] A) {
    	
    	sum1=0;sum2=0;
    	int n=A.length;
    	c=new int[n+1];
    	node[] arr=new node[n];
    	
    	for(int i=0;i<n;i++) {
    		arr[i]=new node(A[i],i+1);
    		if(i+1<n && A[i]>A[i+1])
    			sum2++;
    	}
    	
    	Arrays.parallelSort(arr);
    	
    	for(int i=0;i<n;i++) {
    		update(arr[i].index,n);
    		sum1+=query(arr[i].index-1);
    	}
    	
    	sum1=n*(n-1)/2-sum1;
    	
    	return sum1==sum2;
    }
    
    private void update(int x,int n) {
    	while(x<=n) {
    		c[x]++;
    		x+=x&-x;
    	}
    }
    
    private int query(int x) {
    	int res=0;
    	while(x>0) {
    		res+=c[x];
    		x-=x&-x;
    	}
    	return res;
    }
}

方法三:最小值。想想什么时候sum1才不等于sum2,不就是存在j >= i+2 的 A[i] > A[j]的情况嘛,我们可以从后往前遍历记录最小值,如果存在以上情况直接return false即可。

class Solution {
    public boolean isIdealPermutation(int[] A) {
    	
    	int mn=A.length;
    	int sum1=0,sum2=0;
    	
    	for(int i=A.length-1;i>=2;i--) {
    		mn=Math.min(mn, A[i]);
    		if(A[i-2]>mn) return false;
    	}
    	
    	return true;
    }
}

方法四:线性搜索(官方题解的一个方法)

假设有一个长度为 n,其中元素为 0 到 n-1 的数组。对于这种数组,定义 理想 排列为该数组的一个不存在非局部倒置的排列。

对于 理想 排列,0 应该在哪里呢? 如果 0 的下标大于等于 2,一定会有 A[0] > A[2] = 0,这是一个非局部倒置。所以 0 只能出现在下标 0 或者下标 1。当 A[1] = 0,显然 A[0] = 1,否则就会有 A[0] > A[j] = 1,这也是一个非局部倒置。当 A[0] = 0,这时候问题就转化成了一个子问题。

根据上述讨论,可以归纳出 理想 数组的充分必要条件为 Math.abs(A[i] - i) <= 1。

class Solution {
    public boolean isIdealPermutation(int[] A) {
    	
    	for(int i=0;i<A.length;i++)
    		if(Math.abs(A[i]-i)>1)
    			return false;
    	
    	return true;
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值