假设一个机器仅存储一个标号为ID的记录(假设ID是小于10忆的整数) 假设每份数据保存两个备份 这样就有两个机器存储了同样的数据
1.在某个时间 如果得到一个数据文件ID的列表 是否能够快速找出这个表中仅出现一次的ID
2.如果已经知道只有一台机器死机(也就是说只有一个备份丢失)?如果两台机器死机(假设同一个数据的两个备份不会同时丢失)?
问题1:
直接遍历表 利用一个数组记录下每个ID出现的次数 遍历完毕 出现次数小于2 的ID即仅出现一次的ID
假设有n个ID 空间复杂度和时间复杂度均为O(n)
对于该种方法改进 可以将出现2次的ID不必存储 因此可以用一个边长数组存储ID出现次数 但这种方法最好情况下空间复杂度为O(1) 最坏情况下仍为O(N)
ID成对出现的 表明数据未丢失 两个相同的数相异或的结果为0 因此将所有数据异或 得到的非0 结果 即为只出现一次的ID
这种方法只需要遍历ID数组一次 空间复杂度为O(1)
问题2:
可以利用异或的方法 但是这种方法只适合只有一台机器死机 而不适合同一个数据的两个备份同时丢失
将所有ID相与 得到一个不为0的结果 那么这个二进制的某一位为1 显然丢失的两个ID中有且仅有一个数该位上为1
这样 将所有ID分为两类:一类该位上为1 另一类该位上为0 每一类都含有一个丢失的ID 然后对这两类ID进行异或 得到的值即为丢失的ID
#include <iostream>
using namespace std;
int Is1(int n, int a)
{
n = n>> a;
return(n & 1);
}
int last1(int n)
{
int t = 0;
while ( n )
{
t++;
n = n >> 1;
}
return t;
}
void find2num(int A[], int n,int *a,int *b)
{
if (A == NULL || n < 2)
return;
int t = 0;
for (int i = 0;i < n;i++)
t ^= A[i];
int index = last1(t);
*a = *b = 0;
for (int i = 0;i <= n;i++)
{
if (Is1(A[i], index))
*a ^= A[i];
else
*b ^= A[i];
}
}
int main()
{
int A[] = {1, 2,3,4,4,5,5,7,7,3 };
int n = 10;
int a = 0;
int b = 0;
find2num(A, n - 1, &a, &b);
cout << a << ' ' << b << endl;
return 0;
}
异或的方法不适同一个数据的两个备份同时丢失的情况 因此要寻求别的方法
我们可以通过构造方程 来求解丢失的ID
对于问题1 预先求出所有ID的和s1 然后再求出当前所有ID的和s2 这样s1-s2即为丢失的ID 该方法时间复杂度为O(N) 空间复杂度为O(1)
对于问题2 我们可以构造一个新的方程 来求解两个未知数 可以预先求出所有ID的平方和 然后求出当前所有ID的平方和
也可以预先求出所有ID的乘机 然后再求出当前所有ID的乘积 。。。。。
该方法解决问题2 时间复杂度为O(N) 空间复杂度为O(1)
扩展问题:如果所有机器有3个备份 也就是说同一台ID的机器有3台 而且同时又有3台机器死机 ?
可以用构造方程的方法解决 不过解方程不是件容易的事情啊~