【数据结构与算法(十三)】

注意!
边界情况的考虑
函数命名、变量命名要有逻辑。最好用完整的英文单词组合命名变量和函数
选择合适的数据结构:数组、链表、树

1、从3个方面确保代码的完整性:功能测试+边界测试+负面测试
2、功能测试:突破常规思维,考虑隐形的限制条件,如整型int的最大值的限制
3、边界测试:递归、循环的退出条件
4、负面测试:一些可能的错误输入,输入之后要怎么判断,怎么作出反应。有三种错误处理方式:返回值、全局变量(专门用一个值来记录是否出错)、异常抛出try-catch

题目

数值的整数次方

实现函数double Power(double base,int exponent),求base的exponent次方,不得使用库函数,同时不需要考虑大数的问题

思路

全面但不高效的代码

1、只考虑指数为正数的情况,那么这道题就很简单了
2、考虑指数为负数的情况,如果指数是负数,那么结果就应该是base的exponent 的绝对值次方的倒数。
3、考虑了上面之后又要考虑特殊的情况了,想到求倒数,那0呢?对0求倒数?这不允许。其实0的0次方是没有意义的,以前学的时候是说任何数的0次方都等于1,但是如果base是0,但指数不是呢?

bool g_InvalidInput = false;//全局变量,用于判断是否有错误输入,即0的负数次幂
double Power(double base, int exponent)
{
    g_InvalidInput = false;
    //如果底数是0,指数还是负数,这是不允许的
    //所以要用全局变量提示出错了,那在调用的函数中就要对全局变量进行判断
    if (base < 0.00000001 && exponent < 0) {//double类型的比较只能用近似值
        g_InvalidInput = true;
        return 0.0;
    }

    unsigned int absExponent = (unsigned int)(exponent);    //求绝对值
    if (exponent < 0)
        absExponent = (unsigned int)(-exponent);
    double result = PowerWithUnsignedExponent(base, absExponent);
    if (exponent < 0)
        return 1.0 / result;
    return result;
}

double  PowerWithUnsignedExponent(double base, unsigned int absExponent)
{
    double result = 1.0;
    for (int i = 0; i < absExponent; i++)
        result *= base;
    return result;
}

既全面又高效的解法

如果输入的指数是32,则在函数要进行32次循环乘法。如果我们已经直到底数的16次方,那么我们只要在这个基础上再计算一次平方就可以了。而16次方又是8次方的平方,8次方又是4次方的平方,4次方又是 2次方的平方。如此下来计算32次方,只要做5次乘方就可以了。
当n为偶数时, an=an/2.an/2 a n = a n / 2 . a n / 2 ;当n为奇数时, an=an1/2.an1/2.a a n = a n − 1 / 2 . a n − 1 / 2 . a
以下用位运算代替除法和判断是否为奇数,提高了效率

double PowerWithUnsignedExponent(double base, unsigned int absExponent)
{
    if (absExponent == 0)
        return 1.0;
    if (absExponent == 1)
        return base;
    double result = PowerWithUnsignedExponent(base, absExponent >> 1);//!!!!!>>除以2
    result *= result;
    if (absExponent & 0x1 == 1) //与1相与,判断是不是奇数!!!!!
        result *= base;
    return result;
}

打印从1最大的n位数(看似简单其实一点也不简单的题)

输入数字n,按顺序打印出从1到最大的n位十进制数,比如输入3,则打印出1,2,3,……,999

思路

注意大数据,这是一个大数问题====》大数神器——字符串

在字符串上模拟数字加法

1、在字符串表达的数字上模拟加法
2、把字符串表达的数字打印出来

#include<algorithm>
#include<iostream>
using std::cout;
//打印从1到最大的n位数
void Print1ToMaxOfNDigits(int n)
{   //负面情况
    if (n <= 0)
        return;
    char* number = new char[n + 1];//因为n位长的数字的字符串其实长度应该为n+1,最后一位是'\0'
    memset(number, '0', n);//为number数组的元素清0
    number[n] = '\0';
    while (Increment(number))
        PrintNumber(number);
    delete[] number;
}
//功能1:判断是否到了最大的n位数:n个9
//功能2:得到下一个数,+1
bool Increment(char* number)
{
    bool isOverflow = false;
    int nTakeOver = 0;//进位标志
    int nLength = strlen(number);
    for (int i = nLength-1; i >=0; i--)
    {   //number[i] - '0':找到当前位置对应的int值
        //比如说百位数上是'5',那下一位i-1就没有进位了
        int nSum = number[i] - '0' + nTakeOver;
        //个位数上加1,其他位就是看有没有进位了
        if (i == nLength - 1)
            nSum++;
        if (nSum >= 10) {
            if (i == 0)
                //只有达到最大的n位数才会在第0位上有进位
                isOverflow = true;//已经到达最大的n位数了
            else {
                nSum -= 10;
//在当前的i对下一个位有进位,但是下下一个位也有进位吗?就是用完一次进位之后它并没有还原为0啊
//上面这个逻辑是错的,它是一直进位直到碰到不需要进位的就break,退出此次的循环了。
//比如199,在第一个9+1=10>=10之后,就有一个进位,之后进入下一个循环;
//下一位依然是9-0+1=10>=10,还是有进位,下一个循环;1-0+1=2<10所以没有进位了,直接break,后面的高位的数就不用改变了
                nTakeOver = 1;
                number[i] = '0' + nSum;
            }
        }
        else {
            number[i] = '0' + nSum;
            break;//到了这一步,高位的数就和上一个数一样,可以退出了
        }
    }
    return isOverflow;
}
//每生成一个数就打印一个数,为什么不能直接打印?
//因为在生成数的时候,我们在数的前面补了'0'字符。
//打印的时候我们希望能按照我们的阅读习惯打印出来
void PrintNumber(char* number)
{
    bool isBegining0 = true;
    int nLength = strlen(number);
    for(int i=0;i<nLength;i++){
        if (isBegining0&&number[i] != '0')
            isBegining0 = false;
        if (!isBegining0)
            cout << number[i];
    }
    cout << "\t";
}

把问题转换成数字排列的解法,递归

//每生成一个数就打印一个数,为什么不能直接打印?
//因为在生成数的时候,我们在数的前面补了'0'字符。
//打印的时候我们希望能按照我们的阅读习惯打印出来
void PrintNumber(char* number)
{
    bool isBegining0 = true;
    int nLength = strlen(number);
    for(int i=0;i<nLength;i++){
        if (isBegining0&&number[i] != '0')
            isBegining0 = false;
        if (!isBegining0)
            cout << number[i];
    }
    cout << "\t";
}
//使用递归:0到9的n个全排列
void Print1ToMaxOfDigitsWithRec(int n)
{
    if (n <= 0)
        return;
    char* number = new char[n + 1];
    number[n] = '\0';
    //最高位第0位开始触发
    for (int i = 0; i < 10; i++) {
        numb'aer[0] = i + '0';//使用ASCII
        PrintToMaxOfDigitsRecursively(number, n, 0);
    }
}

void PrintToMaxOfDigitsRecursively(char* number, int length, int index)
{
    if (index == length - 1) {
        PrintNumber(number);//循环到最后一个数了,生成数完成
        return;
    }
    for (int i = 0; i < 10; i++) {
        number[index + 1] = i + '0';
        PrintToMaxOfDigitsRecursively(number, length, index + 1);
    }
}

如果面试题是关于n位的整数,并且没有限定n的取值范围,或者输入任意大小的整数,那么这道题很可能需要考虑大数的问题。字符串是大数最好的朋友

大数乘法:https://blog.csdn.net/u010983881/article/details/77503519

点赞