连续数打乱,判断出少了哪些数字

2024-06-02 23:48

本文主要是介绍连续数打乱,判断出少了哪些数字,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1. 问题:100个连续 的数打乱 之后,随机取出1个数 ,问如何最快速 的判断出少了哪一个?

分析:对于所有100个连续的数,只要除余100。一定在0~99之间。一般来说,比较常规的做法就是先排序(利用Hash表定位),在循环查找。当然时间复杂度是O(2n)。现在介绍一种很牛的O(n)做法:求二进制异或运算。

异或运算: 0^0=1^1=0; 0^1=1^0=1。0~99个数全部异或的结果只能是0。如果缺少一个数,那么全部异或的结果正好就是那个数。为什么呢?我们做个小实验:假如有四个数: 0001 0010,0101, 0110 排列成一个matrix.
bits: 1 2 3 4
0 0 0 1
0 0 1 0
0 1 0 1
0 1 1 0
全部异或: 0 0 0 0
我们可以下结论了,要全部异或的结果为0,那么所有bit位上的1的个数必须为偶数。 反过来说:如果其中有一个数不存在了(比如0001),那么少0的的bit位上的值不变(因为1的个数还是偶数),而少1的bit位上的值就变成了1(1的个数为奇数了)。

这样0~99的道理也就一样了,所以异或的结果就是少的那个值。代码如下:
Java代码 收藏代码
int data=0;
for(int i=1;i<=99;i++){
if(i==78) //少78
continue;
data=data^i;
}
System.out.println(data);

2. 问题:100个连续 的数打乱 之后,随机取出2个数 ,问如何最快速 的判断出少了哪两个? (注意少2个数了)

分析:常用的做法可以先创建一个100个结构的Hash表,然后循环一次将所有数哈希100之后的位置上置1。然后顺序循环100次查找这个Hash表。前后需要O(2n)的时间。然而有没有更快速的做法呢?当然,直接操作bit.

假设我们有32个连续打乱的数字(0~31)缺少两个数2和11,希望把标记1标记在一个32位上。也就是一个整形变量,标记完之后就成为了:
bits position 31 30 29 28 ....... 11 10 .... 2 1 0
int a= 1 1 1 1 ....... 0 1 .... 0 1 1 (缺少数的bit位上为0)
至于如何标记成为a,我们可以看看下面的小段代码:
Java代码 收藏代码
long bits=0;
for(int i=0;i<32;i++){
long bitMove=1;
if(i==2||i==11)
continue;
bitMove=bitMove<<i;
bits=bits | bitMove;
System.out.println(bits+" : "+Long.toBinaryString(bits));
}

此时我们将数字a每8位作为一个数字b,如果b==255, 则说明全部8位都是1(没有缺少数字)。如果b!=255,则说明有某些位是0(有数字缺少),然后再在不等于255的8 bits上顺序查找等译0的位数即可。这样就相当于原来需要顺序查找32 bits(查找32次)。而现在只需要先查找4个8位的块,然后再需找某个8位块中的bit(也就是需要4+8=12次)即可。这就是分块查找的基本原理了。

通过一个32个连续的数,我们发现了敲门。这样对于100个连续的数呢?很简单,我们需要4个32位就够了。注意:由于最高位如果是1的话,整形数据将会变成负数,不方便我们的计算。因此我们用long数据来存储32个位数。

代码如下:
Java代码 收藏代码
public class Test{

public static void main(String[] args){
//需要4个32位数据,用long存储为了避开int存储可能带来的负数。
//intBits[0]的最低32位标识数据0~31
//intBits[1]的最低32位标识数据32~63,
//intBits[2]的最低32位标识数据64~95,
//intBits[3]的最低4位标识数据96~99
long[] intBits={0L,0L,0L,0L};

//用100bit位标识是否存在0~99的数据
//此时需要循环的次数为100次,时间复杂度O(n),n为连续的data数量。
int loop=0;
for(int data=0;data<=99;data++){
long bitMove=1;
if(data==2||data==11) //缺少数据2和11
continue;
bitMove=bitMove<<(data-32*(data/32));
intBits[data/32]=intBits[data/32] | bitMove;
loop++;
}
//中间打印标识结果
System.out.print("标识结果(循环"+loop+"次):");
for(int i=3;i>=0;i--)
System.out.print(Long.toBinaryString(intBits[i]));
System.out.println();

//分块查找,每8bit一块,一共需要100/8=13块
//其中如果8bit全部是1,则该数据等于2^8-1=255
//前3个intBits都全部位数都用来标识数据
loop=0;
int zeroSize=0;
for(int i=0;i<4;i++){
long bits=intBits[i];
long eightBits=0;
int eightSize=0;
while((eightBits=bits%256)!=0){
System.out.println("第"+((eightSize+1)+i*4)+"个8bits块:"+Long.toBinaryString(eightBits));
if(i<3&&eightBits!=255){
for(int j=0;j<8;j++){
loop++;
if(eightBits%2==0){
zeroSize=j+eightSize*8+i*32;
System.out.println(" zero size="+zeroSize);
}
eightBits=eightBits>>1;
}
}
bits=bits/256;
eightSize++;
loop++;
}
}

System.out.println("标识后查找需要循环"+loop+"次");

}
}
这段代码只需要循环98+29=117次,少于一般情况下的200次。


3. 问题:有1到10w这10w个数,去除2个并打乱次序,如何找出那两个数?(不准用位图)

我想用分块查找的思想,首先开辟一个a[100][1001]存储结构,遍历10W个数存放这个结构
Java代码 收藏代码
int[][] a=new[100][1001];
for(int i=0;i<99998){
a[i/1000-1][i%1000+1]=1;
a[i/1000-1][0]++; //记1000个数是否已经满了。
}

for(int j=0;j<100;j++){
if(a[j][0]<1000){ //这1000个数里面有数少了
for(int k=1;k<1000;i++){
if(a[j][k]!=1){
System.out.println("少了的呢个数就是:"+(j*1000+k));
}
}
}
}

性能分析: 首先循环99998次为数组设置标志位。然后双重循环(其实真正需要双循环的是有2次,因为就少了2个数),因此双重循环的次数因该是: 100(块查找次数)+2*1000=2100次。
一共循环99998+2100 因此效率是O(n+m) 其中n为待查找数字的个数(10W),m为分割的块数与快内查找的次数。如果n远远大于m,则查询效率接近于O(n)。因此这种查找数据越多效率越好。

这篇关于连续数打乱,判断出少了哪些数字的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1025441

相关文章

从去中心化到智能化:Web3如何与AI共同塑造数字生态

在数字时代的演进中,Web3和人工智能(AI)正成为塑造未来互联网的两大核心力量。Web3的去中心化理念与AI的智能化技术,正相互交织,共同推动数字生态的变革。本文将探讨Web3与AI的融合如何改变数字世界,并展望这一新兴组合如何重塑我们的在线体验。 Web3的去中心化愿景 Web3代表了互联网的第三代发展,它基于去中心化的区块链技术,旨在创建一个开放、透明且用户主导的数字生态。不同于传统

poj2406(连续重复子串)

题意:判断串s是不是str^n,求str的最大长度。 解题思路:kmp可解,后缀数组的倍增算法超时。next[i]表示在第i位匹配失败后,自动跳转到next[i],所以1到next[n]这个串 等于 n-next[n]+1到n这个串。 代码如下; #include<iostream>#include<algorithm>#include<stdio.h>#include<math.

usaco 1.2 Name That Number(数字字母转化)

巧妙的利用code[b[0]-'A'] 将字符ABC...Z转换为数字 需要注意的是重新开一个数组 c [ ] 存储字符串 应人为的在末尾附上 ‘ \ 0 ’ 详见代码: /*ID: who jayLANG: C++TASK: namenum*/#include<stdio.h>#include<string.h>int main(){FILE *fin = fopen (

poj 3259 uva 558 Wormholes(bellman最短路负权回路判断)

poj 3259: 题意:John的农场里n块地,m条路连接两块地,w个虫洞,虫洞是一条单向路,不但会把你传送到目的地,而且时间会倒退Ts。 任务是求你会不会在从某块地出发后又回来,看到了离开之前的自己。 判断树中是否存在负权回路就ok了。 bellman代码: #include<stdio.h>const int MaxN = 501;//农场数const int

XTU 1233 n个硬币连续m个正面个数(dp)

题面: Coins Problem Description: Duoxida buys a bottle of MaiDong from a vending machine and the machine give her n coins back. She places them in a line randomly showing head face or tail face o

zoj 1721 判断2条线段(完全)相交

给出起点,终点,与一些障碍线段。 求起点到终点的最短路。 枚举2点的距离,然后最短路。 2点可达条件:没有线段与这2点所构成的线段(完全)相交。 const double eps = 1e-8 ;double add(double x , double y){if(fabs(x+y) < eps*(fabs(x) + fabs(y))) return 0 ;return x + y ;

POJ1269 判断2条直线的位置关系

题目大意:给两个点能够确定一条直线,题目给出两条直线(由4个点确定),要求判断出这两条直线的关系:平行,同线,相交。如果相交还要求出交点坐标。 解题思路: 先判断两条直线p1p2, q1q2是否共线, 如果不是,再判断 直线 是否平行, 如果还不是, 则两直线相交。  判断共线:  p1p2q1 共线 且 p1p2q2 共线 ,共线用叉乘为 0  来判断,  判断 平行:  p1p

Codeforces Round #113 (Div. 2) B 判断多边形是否在凸包内

题目点击打开链接 凸多边形A, 多边形B, 判断B是否严格在A内。  注意AB有重点 。  将A,B上的点合在一起求凸包,如果凸包上的点是B的某个点,则B肯定不在A内。 或者说B上的某点在凸包的边上则也说明B不严格在A里面。 这个处理有个巧妙的方法,只需在求凸包的时候, <=  改成< 也就是说凸包一条边上的所有点都重复点都记录在凸包里面了。 另外不能去重点。 int

【408DS算法题】039进阶-判断图中路径是否存在

Index 题目分析实现总结 题目 对于给定的图G,设计函数实现判断G中是否含有从start结点到stop结点的路径。 分析实现 对于图的路径的存在性判断,有两种做法:(本文的实现均基于邻接矩阵存储方式的图) 1.图的BFS BFS的思路相对比较直观——从起始结点出发进行层次遍历,遍历过程中遇到结点i就表示存在路径start->i,故只需判断每个结点i是否就是stop

linux 判断某个命令是否安装

linux 判断某个命令是否安装 if ! [ -x "$(command -v git)" ]; thenecho 'Error: git is not installed.' >&2exit 1fi