题目:
在非负数组(乱序)中找到最小可分配的id(从1开始编号),数据量1000000
思路:
第一种方法:暴力解法 时间复杂度O(n²) 从1开始探测每个自然数是否在数组中
第二种方法:先排序,再找第一个缺失数 时间复杂度O(NlgN) 第三种方法:开辟一个辅助空间 改进1 先建一个长度为n+1的数组helper,初始值全为0或false,扫描原数组中的数,小于n则将helper[a[i]]置为1或true 最后再扫描数组helper,返回值为0或false的小标 第四种方法:分区和递归
package 分治法;
import java.util.Arrays;
public class case05_最小可用ID {
public static void main(String[] args) {
int[] arr={1,2,3,4,5,7,8,9,11};
System.out.println(solve3(arr));
System.out.println(solve4(arr,0,8));
}
//第一种方法:暴力解法 时间复杂度O(n²)
//从1开始探测每个自然数是否在数组中
//static int solve1(int[] arr){
// int i=1;
// while(true){
// if(indexOf(arr,i)==-1){
// return i;
// }
// i++;
// }
//}
//第二种方法:先排序,再找第一个缺失数
//时间复杂度O(NlgN)
static int solve2(int[] arr){
Arrays.sort(arr);
int i=0;
while(i<arr.length){
if(i+1!=arr[i]){
return i+1;
}
i++;
}
return i+1;
}
//第三种方法:开辟一个辅助空间
/**改进1
* 先建一个长度为n+1的数组helper,初始值全为0或false,扫描原数组中的数,小于n则将helper[a[i]]置为1或true
*最后再扫描数组helper,返回值为0或false的小标
*/
public static int solve3(int[] arr){
int n=arr.length;
int[] helper=new int[n+1];
for(int i=0;i<n;i++){
if(arr[i]<n+1) {
helper[arr[i]] = 1;
}
}
for(int i=1;i<=n;i++){
if(helper[i]==0){
return i;
}
}
return n+1;
}
//第四种方法:分区、递归
public static int solve4(int[] arr,int l,int r){
if(l>r){
return l+1;
}
int midIndex=l+((r-l)>>1);//中间下标
int q=selectK(arr,l,r,midIndex-l+1);//返回中间下标实际对应的值
int t=midIndex+1;//期望值
if(q==t){//左侧紧密
return solve4(arr,midIndex+1,r);
}else{
return solve4(arr,l,midIndex-1);
}
}
public static int selectK(int[] A,int p,int r,int k){
int q=partion(A,p,r);//主元下标
int qk=q-p+1;//主元是第几个元素
if(qk==k) {
return A[q];
}else if(qk>k){
return selectK(A,p,q-1,k);
}else{
return selectK(A,q+1,r,k-qk);
}
}
private static int partion(int[] A, int p, int r) {
int pivot=A[p];
int sp=p+1;//扫描指针
int bigger=r;//右侧指针
while(sp<=bigger){
if(A[sp]<=pivot){
sp++;//扫描元素小于主元指针右移
}else{
swap(A,sp,bigger);
bigger--;//扫描元素大于主元,二指针所指元素交换,右侧指针左移
}
}
swap(A,p,bigger);
return bigger;
}
private static void swap(int[] A, int sp, int bigger) {
int temp=A[sp];
A[sp]=A[bigger];
A[bigger]=temp;
}
}