复杂问题简单化的表驱动法

    之前在看一些算法和面试题目时经常会碰到类似于:请写一段程序,输出某个月份有多少天;某个班级成绩根据分数段划分为A,B,C,D,E之类的。之前都是不假思索,这类问题这么简单,最直接if-else解决就可以了,为什么会考。知道阅读《代码大全》中的表驱动法才发现,这类简单的问题中原来蕴藏着表驱动法这么高端的编程方法。下面具体介绍一下:

   表驱动法是一种编程模式,从表里面查询信息而不使用逻辑语句

   举个简单例子,需要输出某个月份的天数,那么就会有如下最基础的写法:

int getTotalDayInMonth(int month)
{ 
    int totalDay = 0;
    if(month == 2)
    {
        totalDay = 28;
    }
    else if(month == 4 || month == 6 || month == 9 || month == 11)
    {
        totalDay = 30;
    }
    else
    {
        totalDay = 31;
    }
    return totalDay;
}

这是一年只有12个月,所以分支不是很多,相对还简单,但是如果使用表驱动法,会更加简单易懂。如下面代码,只需要短短几行就可以,而且不用复杂的if-else分支。这就是表驱动法的精妙之处。

const int totalDayTable[12] =
{
    31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};

int getTotalDayInMonthFromTable(int month)
{
    return totalDayTable[month - 1];
}

表驱动法的查表方式分为三类:

(1)     直接访问

(2)     索引访问

(3)     阶梯访问

(1)其中第一类很简单,如同上面讲的月份的例子。那么现在有个问题,万一“键”是不能直接用的呢?比如我想设计一个幼儿学习动物的软件,若小孩想查找牛的信息,这时屏幕上会打印出牛的特性,而若小孩想查找狗的信息,则屏幕上会打印出狗的信息。显然,这里的键是动物名,而值是相应的描述。下标必须是整数,但动物名是string,怎么办呢?数据结构中的hash表当然可以了,它就是计算string的hash值,通过hash值来索引表格的,但在这里我们不打算用hash值,而是由程序员自行设计string到int的映射,怎么做呢?很简单啊,自己做个菜单呗,让用户只能选择相应的数字,这样“键”就成int了哈。

class Animal
{
public:
    virtual void print() = 0;
};

class Dog: public Animal
{
public:
    void print()
    {
        cout << "This is Dog..." << endl;
    }
};

class Cat: public Animal
{
public:
    void print()
    {
        cout << "This is Cat..." << endl;
    }
};

class Cow: public Animal
{
public:
    void print()
    {
        cout << "This is Cow..." << endl;
    }
};

Animal* animalTable[] = {
    new Dog, new Cat, new Cow
};

int main()
{
    cout << "想知道哪种动物的描述?" << endl;
    cout << "1. 狗" << endl << "2. 猫" << endl << "3. 奶牛" << endl << endl;
    int choiceIndex; 
    cout << "我选择:";
    cin >> choiceIndex;
    assert(choiceIndex >= 1 && choiceIndex <= 3);
    animalTable[choiceIndex - 1]->print();
}

(2)索引访问

假设你经营一家商店,有100种商品,每种商品都有一个ID号,但很多商品的描述都差不多,所以只有30条不同的描述,现在的问题是建立商品与商品描述的表,如何建立?还是同上面做法来一一对应吗?那样描述会扩充到100的,会有70个描述是重复的!如何解决这个问题呢?方法是建立一个100长的索引,然后这些索引指向相应的描述,注意不同的索引可以指向相同的描述,这样就解决了表数据冗余的问题啦。

(3)阶梯访问

它适用于数据不是一个固定的值,而是一个范围的问题,比如将百分制成绩转成五级分制(我们用的优、良、中、合格、不合格,西方用的A、B、C、D和F),假定转换关系是当成绩在90-100区间,判为A,成绩在80-90区间,判为B,成绩在70-80区间,判为C,成绩在60-70区间,判为D,成绩在60以下,判为F(failure)。现在的问题是,怎么用表格对付这个范围问题?一种笨笨的方法是申请一个100长的表,然后在这个表中填充相应的等级就行了,但这样太浪费空间了,有没有更好的方法?

const char gradeTable[] = {
    'A', 'B', 'C', 'D', 'F'
};

const int downLimit[] = {
    90, 80, 70, 60
};

int main()
{
    int score = 87;
    int gradeLevel = 0;
    while(gradeTable[gradeLevel] != 'F')
    {
        if(score < downLimit[gradeLevel])
        {
            ++ gradeLevel;
        }
        else
        {
            break;
        }
    }
    cout << "等级为 " << gradeTable[gradeLevel] << endl;
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值