最小可用id(个人分析)

题目:在非负数组(乱序)中找到最小的可分配的id(从1开始编号),数据量10000000。

题目解读:在一个不重复的乱序的自然数组中找到最小的缺失的那个数,比如1,2,3,6,4,5,8,11。那么最小可用id就为7。

个人总结有三个方法来进行解答,但是算法性能不一样:

第一种,暴力解决,设置一个变量,依次遍历数组中每个元素,如果哪个元素没有直接return

public static int g(int a[]){
		int i=1;
		for (int j = 0; j < a.length; j++) {
			if(judge(a,i)==true){//判断数组a中是否有i这个数字,没有return
				return i;
			}
			i++;
		}
		return -1;
	}
	private static boolean judge(int[] a, int i) {//判断数组a中是否有i
		// TODO 自动生成的方法存根
		for (int j = 0; j < a.length; j++) {
			if(a[j]==i){
				return false;
			}
		}
		return true;
	}

该算法复杂度O(n^2);

 

第二种类似于计数排序算法的思想,就是把数组中的值转下标,把值+1,有数字的里面的值+1,只需判断数组中哪些地方没有1就行,典型的空间换区时间复杂度算法

	public static int f(int a[]){
		int max=0;
		for (int i = 0; i < a.length; i++) {//找出数组中的最大值
			if(max<a[i]){
				max=a[i];
			}
		}
		int b[]=new int[max];//建立新数组
		for (int i = 0; i < a.length; i++) {//数字换成下标减1
			b[a[i]-1]++;
		}
		for (int i = 0; i < b.length; i++) {//扫面辅助数组
			if(b[i]==0){//为0就是没有对应的数字
				return i+1;
			}
		}
		return -1;
	}

时间复杂度O(n),但是空间资源浪费的很大。

 

第三种:使用快排里面的一种思想,就是在乱序中,可以知道下标的思想;

在将这种方法之前,可以先做一下如何在乱序中找到指定下标顺序中所对应的数字?

这个就是快排里面的分区思想:

具体代码如下:

public static int select(int a[],int p,int r,int index){//index就是想要知道的下标
		int q=partition(a,p,r);//partition方法就是快排分区算法
		if(index==q){//相同直接返回
			return a[q];
		}else if(index<q){//index在左边
			return select(a, p, q-1, index);//向左找
		}else{
			return select(a, q+1, r, index);//向右找
		}
	}

接下来就是partition代码:

private static int partition(int[] a, int p, int r) {
		// TODO 自动生成的方法存根
		int povit=a[p];//定主元
		int left=p+1;//左指针
		int right=r;//右指针
		while(left<=right){
			while(left<=right&&a[left]<=povit){//左边小于主元
				left++;
			}
			while(left<=right&&a[right]>povit){//右边大于主元
				right--;
			}
			if(left<=right){//左边大,右边小,交换
				swap(a,left,right);
			}
		}
		swap(a,p,right);//把主元放法主元应该放到的位置上面
		return right;//返回主元下标
	}

上面代码就是如何在乱序中找到指定下标顺序中所对应的数字,但是和最小id有什么关系呢?

关系就是,每次返的主元下标+1是否等于主元,如果等于主元说明主元的左边包括主元是稠密的,那么位置上面数字不同的情况就在主元的右边,如果主元和下标不对应,说明主元的左侧是稀疏的,接下来的算法类似于二分思想:

public static int minid(int a[],int p,int r){
		if(p<r){
			int mid=(p+r)/2;//每次找区间的中间下标
			int middle=select(a, 0, a.length-1, mid);//找到下标对应的数字
			if((mid+1)==middle){//开始比较==就在右边
				return minid(a, mid+1, r);
			}else{//不等于就在左边
				return minid(a, p, mid);
			}
		}else{//当p>=r时候
			return p+1;
		}
	}

该算法复杂度O(NlogN);

具体完整测试代码:

import java.util.Arrays;

public class Main {
	public static void main(String[] args) {
		int a[]={1,2,3,6,4,5,8,11};
		int test1=f(a);
		System.out.println(test1);
		int test2=g(a);
		System.out.println(test2);
		int test3=minid(a, 0, a.length-1);
		System.out.println(test3);
	}
	public static int f(int a[]){//方法二
		int max=0;
		for (int i = 0; i < a.length; i++) {
			if(max<a[i]){
				max=a[i];
			}
		}
		int b[]=new int[max];
		for (int i = 0; i < a.length; i++) {
			b[a[i]-1]++;
		}
		for (int i = 0; i < b.length; i++) {
			if(b[i]==0){
				return i+1;
			}
		}
		return -1;
	}
	public static int g(int a[]){//方法一
		int i=1;
		for (int j = 0; j < a.length; j++) {
			if(judge(a,i)==true){
				return i;
			}
			i++;
		}
		return -1;
	}
	private static boolean judge(int[] a, int i) {
		// TODO 自动生成的方法存根
		for (int j = 0; j < a.length; j++) {
			if(a[j]==i){
				return false;
			}
		}
		return true;
	}
	public static int minid(int a[],int p,int r){//方法三
		if(p<r){
			int mid=(p+r)/2;
			int middle=select(a, 0, a.length-1, mid);
			if((mid+1)==middle){
				return minid(a, mid+1, r);
			}else{
				return minid(a, p, mid);
			}
		}else{
			return p+1;
		}
	}
	public static int select(int a[],int p,int r,int index){
		int q=partition(a,p,r);
		if(index==q){
			return a[q];
		}else if(index<q){
			return select(a, p, q-1, index);
		}else{
			return select(a, q+1, r, index);
		}
	}
	private static int partition(int[] a, int p, int r) {
		// TODO 自动生成的方法存根
		int povit=a[p];
		int left=p+1;
		int right=r;
		while(left<=right){
			while(left<=right&&a[left]<=povit){
				left++;
			}
			while(left<=right&&a[right]>povit){
				right--;
			}
			if(left<=right){
				swap(a,left,right);
			}
		}
		swap(a,p,right);
		return right;
	}
	private static void swap(int[] a, int left, int right) {
		// TODO 自动生成的方法存根
		int temp=a[left];
		a[left]=a[right];
		a[right]=temp;
	}
}

如果喜欢记得点个关注哟!

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值