华为OJ之正数相减

题目描述:

请编写程序实现:
两个任意长度的正数相减,这两个正数可以带小数点,也可以是整数,请输出结果.
系统假设:
输入的字符串中,不会出现除了数字与小数点以外的其它字符,不会出现多个小数点以及小数点在第一个字符的位置
等非法情况,所以考生的程序中无须考虑输入的数值字符串非法的情况。
详细要求以及系统约束
1.输入均为正数,但输出可能为负数
2.输入输出均为字符串形式,输入的字符串以“\0”结束,输出的结果字符串也必须以“\0”结束
3.如果输出是正数则不需要带符号,如果为负数,则输出的结果字符串需要带负号
例如:2.2-1.1 直接输出为“1.1”,1.1-2.2 则需要输出为“-1.1”
4.输出的结果字符串需要过滤掉整数位前以及小数位后无效的0,小数位为全0的,直接输出整数位
例如相减结果为11.345,此数值前后均不可以带0,“011.345”或者“0011.34500”等等前后带无效0的均视为错误
输出。例如1.1-1.1结果为0.0,则直接输出0。
5.输出的字符串内存由考生的程序负责分配,测试程序负责释放

本题出自高级篇,该题看起来意思简单,实则要注意的地方有很多,特别是标红的地方。简要说说该题的几点注意事项:

  1. 由于长度任意,因此想把字符串转换为数字后再计算的想法就被枪毙了;
  2. 涉及到小数运算,对齐借位的问题更加麻烦;
  3. 得到结果字符串中“0”的处理。
先来几个测试用例,如果顺利通过了,那提交正确的概率就很大了:

// 用例1:小数点+前0+负数结果
void CExampleTest::TestCase01()
{
    const char * pMinuend = "01.";
    const char * pSubtrahend = "2.";
    char * pResult = NULL;
    Decrease(pMinuend, pSubtrahend, &pResult);
    CPPUNIT_ASSERT(pResult != NULL);
    CPPUNIT_ASSERT(strcmp(pResult, "-1") == 0);
    free(pResult);
}


// 用例2:位数测试
void CExampleTest::TestCase02()
{
    const char * pMinuend = "8.1";
    const char * pSubtrahend = "0.00000000000000000000001";
    char * pResult = NULL;
    Decrease(pMinuend, pSubtrahend, &pResult);
    CPPUNIT_ASSERT(pResult != NULL);
    CPPUNIT_ASSERT(strcmp(pResult, "8.09999999999999999999999") == 0);
    free(pResult);
}

// 用例3:后0
void CExampleTest::TestCase03()
{
    const char * pMinuend = "8.5";
    const char * pSubtrahend = "7.5";
    char * pResult = NULL;
    Decrease(pMinuend, pSubtrahend, &pResult);
    CPPUNIT_ASSERT(pResult != NULL);
    CPPUNIT_ASSERT(strcmp(pResult, "1") == 0);
    free(pResult);
}

// 用例4:负数+小数点
void CExampleTest::TestCase04()
{
    const char * pMinuend = "12.34";
    const char * pSubtrahend = "17.24";
    char * pResult = NULL;
    Decrease(pMinuend, pSubtrahend, &pResult);
    CPPUNIT_ASSERT(pResult != NULL);
    CPPUNIT_ASSERT(strcmp(pResult, "-4.9") == 0);
    free(pResult);
}

// 用例5:整数+后0
void CExampleTest::TestCase05()
{
    const char * pMinuend = "12.";
    const char * pSubtrahend = "2";
    char * pResult = NULL;
    Decrease(pMinuend, pSubtrahend, &pResult);
    CPPUNIT_ASSERT(pResult != NULL);
    CPPUNIT_ASSERT(strcmp(pResult, "10") == 0);
    free(pResult);
}

根据上述的分析可知,本题的思路为:

  • 先判断结果是否为负,若为负则将标志位置位,转换为大数减小数,最后要将结果加上负号;
  • 小数位对齐;
  • 从最低位开始相减,不够则向前借位;
  • 进行结果中“0”的处理,去掉整数部分头部的“0”和小数部分尾部的“0”;
最后的代码如下:

/******************************************************************************

  Copyright (C), 2001-2011, Huawei Tech. Co., Ltd.

 ******************************************************************************
  File Name     : 
  Version       : 
  Author        : 
  Created       : 2009/10/10
  Last Modified :
  Description   : 
  Function List :
              
  History       :
  1.Date        : 2009/10/10
    Author      : 
    Modification: Created file

******************************************************************************/
#include <stdlib.h>
#include <string.h>

typedef struct MYNUM
{
    char *pZhengshu;
    char *pXiaoshu;
}MyNum;

/*获取整数部分和小数部分*/
int GetTwoParts(const char *pcStr,char **pcZhengshu,char **pcXiaoshu)
{
    if (NULL == pcStr)
        return 0;
    char *pZhengshu = NULL;
    char *pXiaoshu = NULL;
    size_t nLen = strlen(pcStr);
    int i = 0;
    while ('\0' != pcStr[i])
    {
        if ('.' == pcStr[i])
            break;
        i++;
    }
    i++;
    pZhengshu = (char *)malloc(i * sizeof (char));
    if (NULL == pZhengshu)
        return 0;
    strncpy(pZhengshu,pcStr,i - 1);
    pZhengshu[i - 1] = '\0';
    *pcZhengshu = pZhengshu;
    int nXiaoshuLen = 0;
    if (nLen <= i)
        nXiaoshuLen = 1;
    else
        nXiaoshuLen = nLen - i + 1;
    pXiaoshu = (char *)malloc(nXiaoshuLen * sizeof (char));
    if (NULL == pXiaoshu)
        return 0;
    if (nLen > i)
    {
        strncpy(pXiaoshu,&pcStr[i],(nLen - i));
        
    }
    pXiaoshu[nXiaoshuLen - 1] = '\0';
    *pcXiaoshu = pXiaoshu;
    return (i - 1);
}

/*获取整数部分位数*/
size_t GetIntNum(const char *pcStr)
{
    size_t i = 0;
    size_t nNum = 0;
    bool bHeadZero = true;
    while ('\0' != pcStr[i])
    {
        if ('0' != pcStr[i])
            bHeadZero = false;
        if ('.' == pcStr[i])
            break;
        if (!bHeadZero)
            nNum++;
        i++;
    }
    return nNum;
}

/*正数减法*/
void PosDecrease(const char *pMinuend,const char* pSubtrahend,char **pResult,bool bIsNeg)
{
    bool bPointFlag = false;
    int nLen1 = strlen(pMinuend);
    int nLen2 = strlen(pSubtrahend);
    if ('.' == pMinuend[nLen1 - 1])
        nLen1--;
    if ('.' == pSubtrahend[nLen2 - 1])
        nLen2--;
    char *pTem = NULL;
    char *pMinTem = NULL;
    pTem = (char *)malloc((nLen1 + 1) * sizeof (char));
    pMinTem = (char*)malloc((nLen1 + 1) * sizeof (char));
    if ((NULL == pTem) || (NULL == pMinTem))
        return;
    strncpy(pMinTem,pMinuend,nLen1);
    pMinTem[nLen1] = '\0'; 
    for (int i = 0;i < nLen1;i++)
    {
        if ('.' == pMinTem[nLen1 - i - 1])
        {
            pTem[nLen1 - 1 - i] = '.';
            bPointFlag = true;
            continue;
        }
        char cSub;
        if (i >= nLen2)
            cSub = '0';
        else
            cSub = pSubtrahend[nLen2 - i - 1];
        char cTem = pMinTem[nLen1 - i - 1] - cSub;
        if (0 > cTem)
        {
            for (int j = 0;j < nLen1 -i;j++)
            {
                if ('.' == pMinTem[nLen1 - i - 1 - j -1])
                    continue;
                if ('0' > (pMinTem[nLen1- i - 1 - j - 1]--))
                {
                    pMinTem[nLen1 - i - 1 - j - 1] += 10;
                }
                else
                    break;
            }
            cTem += 10;
        }
        cTem += '0';
        pTem[nLen1 - 1 - i] = cTem;
    }
    pTem[nLen1] = '\0';
    int nStart,nEnd;
    for (nStart= 0;nStart< nLen1;nStart++)
    {
        if ('0' != pTem[nStart])
            break;
    }
    for (nEnd = nLen1 - 1;nEnd >= 0;nEnd--)
    {
        if ('0' != pTem[nEnd])
            break;
    }
    if ('.' == pTem[nStart])
        nStart--;
    if ('.' == pTem[nEnd])
        nEnd--;
    if (!bPointFlag)
        nEnd = nLen1 - 1;
    char *pLast = NULL;
    if (nLen1 == nStart)
    {
        pLast = (char *)malloc(2 * sizeof (char));
        if (NULL == pLast)
            return;
        strcpy(pLast,"0");
    }
    else
    {
        int nLen = nEnd - nStart + 1;
        if (bIsNeg)
        {
            pLast = (char *)malloc((nLen + 2) * sizeof(char));
            if (NULL == pLast)
                return;
            pLast[0] = '-';
            strncpy(&pLast[1],&pTem[nStart],nLen);
            pLast[nLen + 1] = '\0';
        }
        else
        {
            pLast = (char *)malloc((nLen+ 1) * sizeof(char));
            strncpy(pLast,&pTem[nStart],nLen);
            pLast[nLen] = '\0';
        }
    }
    *pResult = pLast;
    free(pTem);
    free(pMinTem);
}


/**********************************************************
描述:  小数位对齐
输入:
const char *pStr 需要对齐的字符串
int nAlignLen  对齐后的位数
输出:
char **pAlign 对齐后的字符串,以'\0'结束
返回值:
0 成功
-1 失败

***********************************************************/
int XiaoshuAlign(const char *pStr,int nAlignLen,char **pAlign)
{
    if (NULL == pStr)
        return -1;
    int nLen = strlen(pStr);
    if (nLen > nAlignLen)
        return -1;
    bool bPointFlag = false;
    int i = 0;
    while ('\0' != pStr[i])
    {
        if ('.' == pStr[i])
        {
            bPointFlag = true;
            break;
        }
        i++;
    }
    if (!bPointFlag)
        nAlignLen++;
    char *pAlignTem = NULL;
    pAlignTem = (char*)malloc((nAlignLen + 1) * sizeof(char));
    if (NULL == pAlignTem)
        return -1;
    strcpy(pAlignTem,pStr);
    for (int i = nLen;i < nAlignLen;i++)
    {
        if (!bPointFlag && (i == nLen))
            pAlignTem[i] = '.';
        else
            pAlignTem[i] = '0';
    }
    pAlignTem[nAlignLen] = '\0';
    *pAlign = pAlignTem;
    return 0;
}

/*****************************************************************************
Description  : 两个任意长度的正数相减
Prototype    : int Decrease(const char *pMinuend, const char *pSubtrahend, char **ppResult)
Input Param  : const char *pMinuend     被减数,以\0表示字符串结束
               const char *pSubtrahend  减数,以\0表示字符串结束
Output       : char **ppResult          减法结果,必须以\0表示字符串结束
Return Value : 成功返回0   失败返回-1
*****************************************************************************/
int Decrease(const char *pMinuend, const char *pSubtrahend, char **ppResult)
{

    /* 在这里实现功能 */
    if ((NULL == pMinuend) || (NULL == pSubtrahend))
        return -1;
    

    char *pResult = NULL;

    size_t n1,n2;
    n1 = n2 = 0;
    n1 = GetIntNum(pMinuend);
    n2 = GetIntNum(pSubtrahend);

    const char *cVar1 = NULL;
    const char *cVar2 = NULL;
    bool bIsNeg = false;
    if (n1 > n2)
    {
        cVar1 = pMinuend;
        cVar2 = pSubtrahend;
    }
    else if (n1 == n2)
    {
        if (0 <= strcmp(pMinuend,pSubtrahend))
        {
            cVar1 = pMinuend;
            cVar2 = pSubtrahend;
        }
        else
        {
            cVar1 = pSubtrahend;
            cVar2 = pMinuend;
            bIsNeg = true;
        }
    }
    else
    {
        cVar1 = pSubtrahend;
        cVar2 = pMinuend;
        bIsNeg = true;
    }

    
    size_t nLen1 = strlen(cVar1);
    size_t nLen2 = strlen(cVar2);
    MyNum m_Num1,m_Num2;
    m_Num1.pZhengshu = m_Num1.pXiaoshu = m_Num2.pZhengshu = m_Num2.pXiaoshu = NULL;
    GetTwoParts(cVar1,&(m_Num1.pZhengshu), &(m_Num1.pXiaoshu));
    GetTwoParts(cVar2,&(m_Num2.pZhengshu),&(m_Num2.pXiaoshu));
    
    int nXiaoshuLen1 = strlen(m_Num1.pXiaoshu);
    int nXiaoshuLen2 = strlen(m_Num2.pXiaoshu);
    if (nXiaoshuLen1 < nXiaoshuLen2)
    {
        char *pAlignStr = NULL;
        if (0 != XiaoshuAlign(cVar1,nLen1 + nXiaoshuLen2 - nXiaoshuLen1,&pAlignStr))
            return -1;
        PosDecrease(pAlignStr,cVar2,&pResult,bIsNeg);
    }
    else
    {
        char *pAlignStr = NULL;
        if (0 != XiaoshuAlign(cVar2,nLen2 + nXiaoshuLen1 - nXiaoshuLen2,&pAlignStr))
            return -1;
        PosDecrease(cVar1,pAlignStr,&pResult,bIsNeg);
    }
    if (NULL == pResult)
        return -1;
    *ppResult = pResult;
    if (0 != strcmp("",m_Num1.pZhengshu))
    {
        free(m_Num1.pZhengshu);
        m_Num1.pZhengshu = NULL;
    }
    if (0 != strcmp("",m_Num1.pXiaoshu))
    {
        free(m_Num1.pXiaoshu);
        m_Num1.pZhengshu = NULL;
    }
    if (0 != strcmp("",m_Num2.pZhengshu))
    {
        free(m_Num2.pZhengshu);
        m_Num2.pZhengshu = NULL;
    }
    if (0 != strcmp("",m_Num2.pXiaoshu))
    {
        free(m_Num2.pXiaoshu);
        m_Num2.pXiaoshu = NULL;
    }
    return 0;

}


整体思路和上面是对应的,具体实现可能小的地方没有完善好。在提交、修改七八次后,终于全部通过了所有用例。

尾记:第一遍提交的时候,结果为“运行时间超限”,查找之后发现是,strcpy函数用法错误的问题。后来自己测用例的时候出现了CRT detected that the application wrote to memory after end of heap buffer的问题,调试后定位到错误的原因是调用free函数时出错,找找后发现还是分配内存后,使用strcpy的没注意到字符串长度问题,解决的办法是用strncpy,比较安全。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值