找出0~100的数组中缺失的数据

来源:https://blog.csdn.net/dQCFKyQDXYm3F8rB0/article/details/82864223

思路:先使用bitmap算法进行标记,再遍历一遍标记即可找出缺失的

扩展:如果用一个较小的内存对大量数据进行排序,如使用10M内存1G数据排序,也可以采用这种思路。bitmap算法使用二进制标识一个数据是否出现,一个long类型64位,可以标记64个不同的数据,但只占8Byte。例如一个long可以标记0~63,那么2个long可以标记0~127,即x个long数据可以标记2exp(x+5)个数据。1G的数据如果范围确定,比如int范围2exp(32),则只需要2个long即可标记,27*8=216Byte,这远小于10M。对于排序,如果要求相同的数据仍然出现在排序后的结果中,可以用一个二维数组记录个数,如果相同的可以虑掉,则更简单,使用一维数组就可以解决了。

package algorithm;

/*
 * @author weijie
 * 使用bitmap算法实现了查找0~100的数据中缺失的数据
 * 实现过程可优化为更通用的,如查找整数x~y的数据中缺失的数据
 * 同样可以优化为对大量数据进行排序
 * 关键的是标记过程
 */
public class BitMap {
    public static void main(String[] args)
    {
        int[] array = {0,1,64,31,32,63,64,65,100,101,127,128,135};
        bitMap(array);
    }
    
    public static void bitMap(int[] array)
    {
        
        long[] check = new long[2];
        check = mark(array);
        int[] result = new int[101];
        System.out.println(Long.toBinaryString(check[0])+" "+Long.toBinaryString(check[1]));
        result= searchMissingNumber(check);
        for(int i=0;i<result.length;i++)
        {
            if(result[i]!=200) System.out.println(result[i]);
        }
            
    }
    
    public static long[] mark(int[] data)
    {
        if (data.length <= 0) {
            long[] mark_result = { 0l, 0l };
            return mark_result;
        }
        long[] check = { 0l, 0l };
        int min = 0;
        int max = 100;
        long num1 = 1l;
        int[] result;
        for(int i=0;i<data.length;i++)
        {
            long temp=0; 
            if(data[i]<64&&data[i]>=min)
            {
                temp = num1<<data[i];
                if((check[0]&temp)!=temp) check[0]+=temp;
            }
            else if(data[i]>=64&&data[i]<=max)
            {
                temp = num1<<(data[i]-64);
                if((check[1]&temp)!=temp) check[1]+=temp;
            }
        }
        return check;
    }
    
    /*如果定义一个数组,数组元素初始化为0,那么怎么知道一个0是来自需要识别的0还是初始化的0呢?
     *思路,初始化为范围之外的数*
     */
    public static int[] searchMissingNumber(long[] array)
    {
        if(array.length==0)
        {
            int[] result = {};
            return result;
        }
        final int max=100;
        int[] result = new int[max+1];
        //初始化数组为一个范围之外的数
        for(int i=0;i<max+1;i++) result[i]=200;
        long num1=1;
        //数组的索引
        int index=0;
        for(int i=0;i<array.length;i++)
        {
            long temp =1;
            
            for(int j=0;j<64;j++)
            {
                temp = num1<<j;
                if((array[i]&temp)!=temp)
                {
                    //如果超出范围,则这一轮循环不需要再算下去了
                    if((j+64*i)>max) break;
                    result[index] = j+64*i;
                    index++;
                }
            }
        }
        return result;
    }

}
测试结果:

经过优化,变得更加通用,待识别的数据不需要从0开始,最大值在int范围即可,标记数组的长度根据实际数据范围确定。标记位仍然从0开始,只是对应的不是数据0,而是第一个数据。这个标记方法mark()可用于对大量数据的排序,只不过要分批调用这个方法,将标记数组markArray作为参数传入

package algorithm;

/*
 * @author weijie
 * 使用bitmap算法实现了查找0~100的数据中缺失的数据
 * 实现过程可优化为更通用的,如查找整数x~y的数据中缺失的数据
 * 同样可以优化为对大量数据进行排序
 * 关键的是标记过程
 */
public class BitMap {
    public static void main(String[] args)
    {
        int[] array = {0,1,64,31,32,63,64,65,67,83,95,98,100,101,127,128,135};
        bitMap(array,1,100);
    }
    
    public static void bitMap(int[] array,int min, int max)
    {
        if(max<min) return;
        
        long[] check;
        //标记给定数组中出现的数据
        check = mark(array,min,max);
        
        int[] result ;
        for(int i =0;i<check.length;i++)
        System.out.print(Long.toBinaryString(check[i])+" ");
        System.out.println();
        result= searchMissingNumber(check,min,max);
        for(int i=0;i<array.length;i++) System.out.print(array[i]+" ");
        System.out.println();
        for(int i=0;i<result.length;i++)
        {
            if(result[i]<=max) System.out.print(result[i]+" ");
        }
            
    }
    
    /*给定的数据如果不从0开始,标记仍然从0开始,但是比较时需要处理原始数据
     * 即如果从5开始,则标记位的0位表示5
    */
    public static long[] mark(int[] data, int min, int max)
    {
        if (data.length <= 0) {
            long[] mark_result = { 0l };
            return mark_result;
        }
        //计算标记min到max范围的数据需要的long类型的个数
        int lengthOfMarkArray = 0;
        if((max-min+1)%64==0) lengthOfMarkArray = (max-min+1)/64;
        else lengthOfMarkArray = (max-min+1)/64+1;
                
        long[] markArray = new long[lengthOfMarkArray];
        for(int i=0;i<markArray.length;i++) markArray[i] = 0l;        
        
        long num1 = 1l;
        
        for(int i=0;i<data.length;i++)
        {
            long temp=0;
            //超出范围的数据不需要标记,本身也没有定义那么长的标记数组
            //待标记的数据未必从0开始,但是标记位仍然从0开始
            if(min<=data[i]&&data[i]<=max)
            {
                temp = num1<<(data[i]-min);
                if((markArray[(data[i]-min)/64]&temp)!=temp) markArray[(data[i]-min)/64]+=temp;
            }
        }
        return markArray;
    }
    
    /*如果定义一个数组,数组元素初始化为0,那么怎么知道一个0是来自需要识别的0还是初始化的0呢?
     *思路,初始化为范围之外的数*
     *返回到是从0开始搜索的,如果待判断的数据从n开始,则取数据可以在返回的数据上加n
     */
    public static int[] searchMissingNumber(long[] array, int min, int max)
    {
        if(array.length==0)
        {
            int[] result = {};
            return result;
        }
        //final int max=100;
        int[] result = new int[max-min+1];
        //初始化数组为一个范围之外的数,这里如果max为int的最大值,则有问题
        for(int i=0;i<max-min+1;i++) result[i]=max+1;
        long num1=1;
        //数组的索引
        int index=0;
        for(int i=0;i<array.length;i++)
        {
            long temp =1;
            //if(i*64>max) break;可以不需要,因为array数组的长度是按需(min~max)定义的
            for(int j=0;j<64;j++)
            {
                //如果超出范围,则这一轮循环不需要再算下去了
                if((j+64*i)>max) break;
                
                temp = num1<<j;
                if((array[i]&temp)!=temp)
                {
                    
                    result[index] = j+64*i+min;
                    index++;
                }
            }
        }
        return result;
    }

}
测试如下:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值