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

/* 一个关于算法的面试题,来源于网络 */
/*
请教大家一个算法题[问题点数: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
**/


 

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值