- /* 一个关于算法的面试题,来源于网络 */
- /*
- 请教大家一个算法题[问题点数: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
- **/
一个关于算法的面试题,来源于网络
最新推荐文章于 2024-06-04 14:18:54 发布