您的位置:首页 > 其它

bit-map牛刀小试:数组test[X]的值全部在区间[1, 8000]中, 现要输出test中重复的数。要求:1. 不能改变原数组; 2.时间复杂度为O(X);3.除test外空间不超过1KB

2015-06-22 18:45 525 查看
       先来看看这个题目:数组test[X]的值全部在区间[1, 8000]中, 现要输出test中重复的数。要求:1. 不能改变原数组; 2.时间复杂度为O(X);3.除test外空间不超过1KB.

       好, 我们先给出一个不限空间的解法(为了程序方便, 假设X为10, 实际上可能很大):

#include <iostream>
using namespace std;

#define X 10
#define N 8000

// 输出重复的数字
void printDup(const int test[], int n)
{
int a
= {0};
int i = 0;
for(i = 0; i < n; i++)
{
a[test[i] - 1]++;
}

for(i = 0; i < N; i++) // 注意, 此处是N而不是n
{
if(a[i] > 1)
{
cout << i + 1 << endl;
}
}
}

int main(void)
{
int test[X] = {1, 2, 3, 4, 2, 5, 6, 7, 5, N};
printDup(test, X);

return 0;
}


        结果为:

2

5

       显然, 上述程序在空间上超标(且当X<N=8000时, 时间超标), 究其原因是: 让一个int去存一个二值状态, 太浪费空间了, 和不用一个bit来存呢? 所以, 我们自然想到了用bit-map来操作, 如下:

#include <iostream>
using namespace std;

#define X 10

#define BIT_INT 32   // 1个int可以标志32个坑
#define SHIFT 5
#define MASK 0x1f
#define N 8000
int a[1 + N / BIT_INT]; // 需要1 + N / BIT_INT 个整数来标志N个事物

// 将所有位都初始化为0状态
void setAllZero()
{
memset(a, 0, (1 + N / BIT_INT) * sizeof(int));
}

// 设置第i位为1
void setOne(int i)
{
a[i >> SHIFT] |= (1 << (i & MASK));
}

// 设置第i位为1
void setZero(int i)
{
a[i >> SHIFT] &= ~(1 << (i & MASK));
}

// 检查第i位的值
int getState(int i)
{
return (a[i >> SHIFT] & (1 << (i & MASK))) && 1;
}

// 输出重复的数字
void printDup(const int test[], int n)
{
int i = 0;
for(i = 0; i < n; i++)
{
int state = getState(test[i] - 1);
if(0 == state)
{
setOne(test[i] - 1);
}
else
{
cout << test[i] << endl;
}

}
}

int main(void)
{
setAllZero();
int test[X] = {1, 2, 3, 4, 2, 5, 6, 7, 5, N};
printDup(test, X);

return 0;
}
       结果为:

2

5

      且满足题目要求。 但是, 我随后发现这个程序还有个问题: 当test数组中某元素出现次数大于2时, 会重复输出, 比如:

#include <iostream>
using namespace std;

#define X 10

#define BIT_INT 32   // 1个int可以标志32个坑
#define SHIFT 5
#define MASK 0x1f
#define N 8000
int a[1 + N / BIT_INT]; // 需要1 + N / BIT_INT 个整数来标志N个事物

// 将所有位都初始化为0状态
void setAllZero()
{
memset(a, 0, (1 + N / BIT_INT) * sizeof(int));
}

// 设置第i位为1
void setOne(int i)
{
a[i >> SHIFT] |= (1 << (i & MASK));
}

// 设置第i位为1
void setZero(int i)
{
a[i >> SHIFT] &= ~(1 << (i & MASK));
}

// 检查第i位的值
int getState(int i)
{
return (a[i >> SHIFT] & (1 << (i & MASK))) && 1;
}

// 输出重复的数字
void printDup(const int test[], int n)
{
int i = 0;
for(i = 0; i < n; i++)
{
int state = getState(test[i] - 1);
if(0 == state)
{
setOne(test[i] - 1);
}
else
{
cout << test[i] << endl;
}

}
}

int main(void)
{
setAllZero();
int test[X] = {1, 2, 3, 4, 2, 5, 6, 2, 5, N}; // 2出现3次
printDup(test, X);

return 0;
}
      结果为:

2

2

5

     我想了一下, 暂时没有想到只打印2, 5且符合题意的方法。 如果大家有好的思路, 欢迎赐教


 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: