您的位置:首页 > 理论基础 > 计算机网络

一个关于算法的面试题,来源于网络

2011-10-15 21:10 501 查看
/* 一个关于算法的面试题,来源于网络 */
/*
请教大家一个算法题[问题点数:10分]
chu009
(chu009)

我面试的时候曾经遇到的一道算法题,写在这大家一起讨论下,争取找出个好的解决方案。
题目是这样的:有两个文件,A文件里有大量的电话号码,上亿条,里面有少量的重复号码,要求把A文件里面的重复号码去掉然后存入B文件。

我的解决方法:建立一个二叉排序树存储所有A文件中不重复的电话号码信息。我从A文件每次读取一条电话号码,然后插入到二叉树结构中,如果这条记录已经存在则不进行插入。最后二叉排序树中包含了所有A文件中不重复的电话号码信息。改进的方式是在插入过程中将二叉排序树调整为二叉平衡树。
将二叉树按某种方式进行遍历,将电话号码信息依次写入B文件。如果按中序遍历,则B文件中的电话号码是有序的。

这里我建立二叉树的时间复杂度是O(nlgn),写入B文件O(n),但是二叉树节点需要存储电话号码信息就要占用内存,上亿节点占用多大的内存啊,这是对方给我提出的challenge,我没给出更好的方法。

我的出发点是降低时间复杂度,但是没有解决内存占用问题。
但是不把A文件中的节点存入内存,假如这样做:将A文件一次取一条记录,然后在B文件从头至尾查找是否有重复的,如果没有重复的加入到B文件末尾,然后A文件再取下一条,重复此过程。
这样虽然节省了内存,但是时间复杂度为O(N*N)(上亿条记录,这个时间复杂度是很恐怖的),而且每插入一条就要把B文件读取一遍也是非常耗时的。

我没有想出更好的方法,大家帮忙看看吧。
*/

/*==============================================================================
文 件 名 : Win32CApp.c
功    能 : 从A文件中读入手机号码,去掉重复出现的手机号,内容输出到文件B中。
日    期 : 2011-10-16
作    者 : jernymy
版    权 : <Copyright(C) ... jernymy.>
版    本 : V1.0
-------------------------------------------------------------------------------
说    明 : 使用位图的模式,参考编程珠玑(2)中介绍的一种算法。

以下是中国的手机号码分段情况
新联通  (中国联通+中国网通)手机号码开头数字 130、131、132、145、
155、156、185、186
新移动  (中国移动+中国铁通)手机号码开头数字 134、135、136、137、
138、139、147、150、151、152、157、158、159、182、183、187、188
新电信  (中国电信+中国卫通)手机号码开头数字 133、153、189、180

号码段情况可以看到手机号码区间在130 0000 0000 - 189 9999 9999
范围 = 600 0000 0000, 无法用以一个u32的整形表示到。

而160,170均不可用,所以将18用16来替换,于是180->160, 189->169
则范围40 0000 0000,已经在u32最大范围之内
(0xFFFF FFFF) 42 9496 7295,所以我们只需要定义足够u32数组即可。

目前定一个区间为40 0000 0000,共需要(40 0000 0000/32)=
1 2500 0000个u32数组表示,
公占用内存空间(40 0000 0000/32)*4=5 0000 0000 Bytes
≈476.837158203125M Bytes,方法可行。
==============================================================================*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <malloc.h>

// 42 9496 7295
// 2147483648
typedef unsigned int   u32;

/* { jernymy - the status error no */
typedef u32                    STATUS;
#define ERR_OK                 0
#define ERR_PRMT_NULL          1
#define ERR_PHONE_ZERO         2
#define ERR_PHONE_NOTINRAGNE   3
#define ERR_PHONE_INVAILD      4
#define ERR_PHONE_LEN          5

#define ERR_MALLOC             10
#define ERR_CREATE_FILE        11
#define ERR_OPEN_FILE          12
/* } jernymy - the status error no */

// 1亿的数据
#define ONE_YI                  (u32)(10000*10000)
// for test
#define FILE_PHONE_NUM          (u32)(10000*100)

#define PHONE_EVERY             (u32)(10*ONE_YI)

#define VAR_LEN                 (u32)(32)

// phone like   130 xxxx xxxx
#define MAX_PHONE_NUM_LEN        11

// process like  30 xxxx xxxx, remove high bit
#define MAX_PHONE_USE_LEN        10

// current is 40 0000 0000
#define TOTAL_PHONE_NUM          (u32)(40*ONE_YI)

// current is 30 0000 0000
#define SPECIAL_PHONE_NUM        (u32)(30*ONE_YI)

// current is 40 0000 0000/32 = 1 2500 0000
#define MAX_PHONE_BIT            (u32)(TOTAL_PHONE_NUM/VAR_LEN)

// source file, will process
#define PHONE_FILE_NAME          "phone.txt"

// the dest file, not have repeat phone
#define PHONE_NO_RPT_FILE_NAME   "phone_norpt.txt"

// set array one bit is 1
#define SET_ARY_BIT(ary,id)      ( ary[id/VAR_LEN] |= (1 << (id%VAR_LEN)) )

// check array one bit is 1 ?
#define IS_SET_BIT(ary,id)       ( ary[id/VAR_LEN] &  (1 << (id%VAR_LEN)) )

// current phone rang in used
static short g_sPhoneAry[] =
{
130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
145,      147,
150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
180,      182, 183,      185, 186, 187, 188, 189,
};

#define PHONE_TITLE_NUM        ( sizeof(g_sPhoneAry) / sizeof(short) )

// u32 array pointer, need malloc
static u32 *g_padwPhoneAry = NULL;

#define PHONE_REPEAT_FILE_NAME     "phone_rpt.txt"
static FILE *g_fpRepeat = NULL;

/*--------------------------------- { function ---------------------------------*/
/*==============================================================================
函 数 名 : TstInputFile
功    能 : 建立一个手机号码文件,100万个,其中包括一部分重复的号码,仅测试使用
参    数 : 无
返 回 值 : 无
日    期 : 2011-10-16
作    者 : jernymy
版    本 : V1.0
==============================================================================*/
static void TstInputFile(void)
{
u32 dwIdx;
u32 dwVar;
short wAryId;
FILE *fpDst;
char achPhone[12] = {0};

fpDst = fopen(PHONE_FILE_NAME, "wt");
if (NULL == fpDst)
{
printf("open file:%s fail\n", PHONE_FILE_NAME);
return;
}

srand( (unsigned)time(NULL) );

printf("PHONE_TITLE_NUM:%d\n", PHONE_TITLE_NUM);
// create FILE_PHONE_NUM phone number
for (dwIdx = 0; dwIdx < FILE_PHONE_NUM; dwIdx++)
{
wAryId = rand()%PHONE_TITLE_NUM;
dwVar  = rand()%FILE_PHONE_NUM;
fprintf(fpDst, "%3d%08d\n", g_sPhoneAry[wAryId], dwVar);

// create 100 repeat phone number
// and total phone may have other repeat
if (0 == (dwIdx % (FILE_PHONE_NUM/100)) )
{
fprintf(fpDst, "%3d%08d\n", g_sPhoneAry[wAryId], dwVar);
}
}
fclose(fpDst);
}

/*==============================================================================
函 数 名 : TstCrtRepeatFile
功    能 : 建立一个文件句柄,该文件记录所有重复号码
参    数 : 无
返 回 值 : 无
日    期 : 2011-10-16
作    者 : jernymy
版    本 : V1.0
==============================================================================*/
static void TstCrtRepeatFile(void)
{
g_fpRepeat = fopen(PHONE_REPEAT_FILE_NAME, "wt");
if (NULL == g_fpRepeat)
{
printf("Create file:%s fail\n", PHONE_REPEAT_FILE_NAME);
}
}

/*==============================================================================
函 数 名 : TstAtoll
功    能 : 建立一个文件句柄,该文件记录所有重复号码
参    数 : [in]  const char *pchStr     - 手机号码字符串
[out] u32         *pdwNumber - 要输出的手机号码整形,将最高位的1去
掉,同时范围在0-3之间。前3为变动
130-159-->100-129-->00-29
180-189-->130-139-->30-39
返 回 值 : STATUS (ERR_OK-success, other-error)
日    期 : 2011-10-16
作    者 : jernymy
版    本 : V1.0
==============================================================================*/
static STATUS TstAtoll(const char *pchStr, u32 *pdwNumber)
{
u32  dwLen = 0;
char achPhone[MAX_PHONE_NUM_LEN+1] = {0};

if (NULL == pchStr)
{
printf("parameter is NULL!\n");
return ERR_PRMT_NULL;
}

if (NULL == pchStr[0])
{
printf("parameter[0] is 0, phone is 0!\n");
return ERR_PHONE_ZERO;
}

// check phone is valid
if (   (pchStr[1] > '8') || (pchStr[1] < '3') )
{
printf("parameter:%s not a phone!\n", pchStr);
printf("must in range (130 0000 0000 - 189 9999 9999)!\n");
return ERR_PHONE_NOTINRAGNE;
}

// remove the high bit char, 130 xxxx xxxx - 30 xxxx xxxx
strncpy(achPhone, pchStr+1, MAX_PHONE_USE_LEN);

/* change 18x xxxx xxxx -> 16x xxxx xxxx */
if (achPhone[0] == '8')
{
achPhone[0] = '6';
}

// range in u32 data, so change (13x-16x) (10x-13x)
achPhone[0] -= 3;
while (achPhone[dwLen] != '\n' && (dwLen < 10))
{
if ( (achPhone[dwLen] < '0') || (achPhone[dwLen] > '9') )
{
printf("parameter:%s is not a string number!\n", dwLen);
return ERR_PHONE_INVAILD;
}
*pdwNumber = (*pdwNumber * 10) + (achPhone[dwLen]-'0');
dwLen++;
}

// check phone length is valid
if (MAX_PHONE_USE_LEN != dwLen)
{
printf("parameter:%s length error, must is:%d\n",
pchStr, MAX_PHONE_NUM_LEN);
return ERR_PHONE_LEN;
}
return ERR_OK;
}

/*==============================================================================
函 数 名 : GetPhone
功    能 : 从A文件中读取手机号码,并使用位图模式保存到分配的内存中。
参    数 : 无
返 回 值 : STATUS (ERR_OK-success, other-error)
日    期 : 2011-10-16
作    者 : jernymy
版    本 : V1.0
==============================================================================*/
static STATUS GetPhone(void)
{
u32  dwNum;
char achPhone[16] = {0};
FILE *fpSrc;

// 5 0000 0000 Bytes
// 476.837158203125 M Bytes
// if here can not new this succ, please use 4 array, every new 1 2500 0000 Bytes
printf("TOTAL_PHONE_NUM:%u, VAR_LEN:%u\n", TOTAL_PHONE_NUM, VAR_LEN);
printf("malloc %u u32 size memory start\n", MAX_PHONE_BIT);
g_padwPhoneAry = (u32 *)malloc(MAX_PHONE_BIT*sizeof(u32)); //    g_padwPhoneAry = new u32[MAX_PHONE_BIT];
if (NULL == g_padwPhoneAry)
{
printf("malloc %u u32 size memory fail\n", MAX_PHONE_BIT);
return ERR_MALLOC;
}
memset(g_padwPhoneAry, 0, MAX_PHONE_BIT*sizeof(u32));

fpSrc = fopen(PHONE_FILE_NAME, "rt");
if (NULL == fpSrc)
{
printf("open file:%s fail\n", PHONE_FILE_NAME);
return ERR_OPEN_FILE;
}

while (!feof(fpSrc))
{
if (!fgets(achPhone, sizeof(achPhone), fpSrc))
{
break;
}

// switch string phone to u32 data
dwNum = 0;
if (ERR_OK != TstAtoll(achPhone, &dwNum))
{
continue;
}

// check the bit is set, no set the set
if (!IS_SET_BIT(g_padwPhoneAry, dwNum))
{
SET_ARY_BIT(g_padwPhoneAry, dwNum);
}
else
{
// save the data to repeat file
if (NULL != g_fpRepeat)
{
// the string have \n
fprintf(g_fpRepeat, "%s", achPhone);
}
//            printf("get a repeat num:%s\n", achPhone);
}
}

fclose(fpSrc);
if (NULL != g_fpRepeat)
{
fclose(g_fpRepeat);
g_fpRepeat = NULL;
}

return ERR_OK;
}

/*==============================================================================
函 数 名 : PutPhone
功    能 : 将内存中的数据存入到文件B中,此时已经没有重复的数据。
参    数 : 无
返 回 值 : STATUS (ERR_OK-success, other-error)
日    期 : 2011-10-16
作    者 : jernymy
版    本 : V1.0
==============================================================================*/
static STATUS PutPhone(void)
{
u32 dwIdx;
u32 dwHigh;
FILE *fpDst;
char achPhone[12] = {0};

fpDst = fopen(PHONE_NO_RPT_FILE_NAME, "wt");
if (NULL == fpDst)
{
printf("Create file:%s fail\n", PHONE_NO_RPT_FILE_NAME);
return ERR_CREATE_FILE;
}

// the max range (40 0000 0000)
for (dwIdx = 0; dwIdx < TOTAL_PHONE_NUM; dwIdx++)
{
if (IS_SET_BIT(g_padwPhoneAry, dwIdx))
{
dwHigh = dwIdx / SPECIAL_PHONE_NUM;
dwHigh += 30;
if (dwHigh >= 60)
{
dwHigh += 20;
}
// 1xx xxxx xxxx to file B
fprintf(fpDst, "1%2d%08d\n", dwHigh, dwIdx);
}
}
fclose(fpDst);
return ERR_OK;
}

/*==============================================================================
函 数 名 : PrintTime
功    能 : 打印输入的字符串和当前时间
参    数 : [in] const char *pchStr - 要打印的字符串
返 回 值 : 无
日    期 : 2011-10-16
作    者 : jernymy
版    本 : V1.0
==============================================================================*/
static void PrintTime(const char *pchStr)
{
time_t dwCurTime = time(NULL);
printf("%s:%s", pchStr, ctime(&dwCurTime));
}

/*==============================================================================
函 数 名 : main
功    能 : 文件的主函数
参    数 : 无
返 回 值 : 无
日    期 : 2011-10-16
作    者 : jernymy
版    本 : V1.0
==============================================================================*/
int main(void)
{
// jernymy used create a phone file for test
//    TstInputFile();

// jernymy used create a phone repeat file
// for save repeat phone, just test
TstCrtRepeatFile();

// get phone data, from file string to memory u32
// and remove repeat data
printf("get file string phone start...\n");
PrintTime("Start Time");
if (ERR_OK != GetPhone())
{
printf("get file string phone fail\n");
return -1;
}
PrintTime("End   Time");
printf("get file string phone finish\n");

printf("---------------------------------------------\n");

// put phone data, from memory u32 to file string
printf("put memory phone to file string start...\n");
PrintTime("Start Time");
if (ERR_OK != PutPhone())
{
printf("put memory phone to file string fail\n");
return -2;
}
PrintTime("End   Time");
printf("put memory phone to file string finish\n");

return 0;
}
/*--------------------------------- } function ---------------------------------*/

/*
jernymy
测试结果VC6.0上,使用100万的测试数据中有筛选了重复之后剩下的62万多的数据
耗时不到2分钟
硬件信息
CPU--intel Pentium Dual E2160 1.8G
内存-3G

get file string phone start...
Start Time:Sun Oct 16 19:06:50 2011
TOTAL_PHONE_NUM:4000000000, VAR_LEN:32
malloc 125000000 u32 size memory start
End   Time:Sun Oct 16 19:06:51 2011
get file string phone finish
---------------------------------------------
put memory phone to file string start...
Start Time:Sun Oct 16 19:06:51 2011
End   Time:Sun Oct 16 19:08:13 2011
put memory phone to file string finish
Press any key to continue
**/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐