0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

算法的泛化问题,这些坑你可能都经历过!|周立功教你学软件设计

AGk5_ZLG_zhiyua 来源:未知 作者:电子大兵 2017-09-01 09:18 次阅读

第一章为程序设计基础,本文为1.6.3泛型编程

>>>>1.泛型编程

下面将进一步以一个简单的循环查找为例,全面考察算法的泛化问题。假设要编写一个findValue()函数,在array数组中寻找一个特定的int值,详见程序清单 1.29。

程序清单 1.29 findValue()查找函数(1)

1 int *findValue(int *arrayHead, int arraySize, int value)

2 {

3 for(int i =0; i < arraySize; ++i) 

4 if(arrayHead[i] == value)

5 break;

6 return &(arrayHead[i]);

7 }

该函数在某个范围内查找value,返回的是一个指针,指向它所找到的第一个符合条件的元素。如果没有找到,则返回最后一个元素的下一个位置(地址)。“最后元素的下一个位置”称为end,其作用是返回end表示“查找无结果”,为何不返回null呢?因为end指针可以对其它种类的数据结构带来泛化的效果,这是null做不到的。

在学习数组时,我们就被告诫,千万不要超越其下标范围,但事实上一个指向array元素的指针,不但可以合法地指向array的任何位置,也可以指向array尾端以外的任何位置。只不过,当指针指向array尾端以外的位置时,它只能用于与其它array指针相比较,不能间接引用其值。findValue()函数的使用方式如下:

const int arraySize = 7;

int array[arraySize] = {0, 1, 2, 3, 4, 5, 6};

int *end = array + arraySize;

int *ip = findValue(array, sizeof(array) / sizeof(int), 4);

if(ip == end)

return false;

else

return true;

显然,findValue()函数暴露了数组的实现细节,比如,arraySize,太过于依赖特定的数据结构。那么,如何设计一个算法,使它适用于大多数数据结构呢?或者说,如何在即将处理的未知的数据结构上,正确地实现所有的操作呢?事实上,一个序列有开始和结尾,既可以使用++得到下一个元素,也可以使用“*”得到当前元素的值。

显然,让阅读代码的人理解你的本意,至关重要是取一个不会让人产生误解的名字。对于包含范围,常用first和last。对于包含/排序范围,常用begin和end。比如,对于大多数需要分片的数组,使用begin和end表示包含/排除范围是最好的选择。遗憾的是,类似limit、filter和length这样具有多义性的英文单词会带来一定的困惑,而定义一个值的上限或下限时,max_和min_就是很好的前缀。

为了让findValue()函数适用于所有类型的数据结构,其操作应该更抽象化,让findValue()函数接受两个指针作为参数,表示一个操作范围,详见程序清单 1.30。

程序清单1.30findValue()查找函数(2)

1 int *findValue(int *begin, int *end, int value)

2 {

3 while(begin != end && *begin != value)

4 ++begin;

5 return begin;

6 }

由于findValue函数的返回值begin是一个指针,因此该函数是一个返回指针的函数,即指针函数。这个函数在“前闭后开”范围[begin, end)内(包含了begin迭代器的当前元素,而到end迭代器的前一个元素为止)查找value,并返回一个指针,指向它所找到的第一个符合条件的元素,如果没有找到就返回end。这里之所以用“!=”,而不是用“<”判断是否到达数组的尾部,因为这样处理更精确。findValue()函数的使用方式如下:

const int arraySize = 7;

int array[arraySize] = {0, 1, 2, 3, 4, 5, 6};

int *end = array + arraySize;

int *ip = findValue(array, end, 4);

if(ip == end)

return false;

else

return true;

当然,findValue()函数也可以方便地用于查找array的子范围:

int *ip = findValue(array + 2, array + 5, 3);

if(ip == end)

return false;

else

return ture;

由此可见,findValue()函数中并无任何操作是针对特定的整数array的,即只要将操作对象的类型加以抽象化,且将操作对象的表示法和范围目标的移动行为抽象化,则整个算法就可以工作在同一个抽象层面上了。通常将整个算法的过程称为算法的泛型化,简称泛化。泛化的目的旨在使用同一个findValue()函数处理各种数据结构,通过抽象创建可重用代码。

如果一个序列是有序的,则不需要用finValue()从开始位置查找,可以使用标准C提供的bsearch()二分查找算法。对于一个更长的序列,二分查找也比findValue()线性查找法的速度更快。即使序列中只有10个元素,也足以体现二分查找的比较优势。对于一个有1000个元素的序列,最多需要进行10次比较,其查找的速度要快200倍。

显然,求数组中元素的最大值,其最好的方法是通过传递2个指针指定元素范围。一个指针标识数组的开头,另一个指针标识数组的尾部。比如:

int iMax(const int *begin, const int *end);

显然,如果只是传递指针,数据就有被修改的可能。如果不希望数据被修改,就要传递指向整数常量的指针。使用for循环的示例如下:

for(ptr = begin; ptr != end; ptr++)

total = total +*ptr;

将ptr设置为待处理的第一个元素(begin指向的元素)的指针,并将*ptr(元素的值)加入到total中。然后循环通过递增操作来更新ptr,使之指向下一个元素。只要ptr不等于end,这一过程将继续下去。当ptr等于end时,它将指向范围中的最后一个元素后面的位置,此时循环结束。其次,请注意不同的函数调用是如何指定数组中不同的范围的。比如:

int array[] = {39, 33, 18, 64, 73, 30, 49, 51, 81};

int n = sizeof(array) / sizeof(array[0]);

int *past = array + n;

int max = iMax(array, array + n);

int max = iMax(array, array + 3);

int max = iMax(array +3, array + 8);

指针array+n指向最后一个元素后面的一个位置(数组只有n个元素,最后一个元素的地址为array+n-1),因此范围[array,array+n]指定的是整个数组。同样array,array+3指定了前3个元素,依此类推。注意,根据指针减法规则,表达式end–begin是一个整数值,等于数组的元素个数。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 算法
    +关注

    关注

    23

    文章

    4629

    浏览量

    93292
  • 软件设计
    +关注

    关注

    3

    文章

    58

    浏览量

    17809
  • 周立功
    +关注

    关注

    38

    文章

    130

    浏览量

    37735

原文标题:周立功:算法的泛化问题,你应该知道

文章出处:【微信号:ZLG_zhiyuan,微信公众号:ZLG致远电子】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    立功教你C语言编程与程序设计:这样写函数指针数组最好用

    立功教授数年之心血之作《程序设计与数据结构》以及《面向AMetal框架与接口的编程(上)》,电子版已无偿性分享到电子工程师与高校群体,在公众号回复【编程】即可在线阅读。
    的头像 发表于 08-31 14:06 6990次阅读
    <b class='flag-5'>周</b><b class='flag-5'>立功</b><b class='flag-5'>教你</b><b class='flag-5'>学</b>C语言编程与程序设计:这样写函数指针数组最好用

    基于labview立功公司的USBCAN-II型CAN卡的各种通讯子VI

    点击学习>>《龙哥手把手教你LabVIEW视觉设计》视频教程基于labview立功公司的USBCAN-II型CAN卡的各种通讯子VI:[hide] [/hide]
    发表于 02-23 11:16

    立功写给单片机的年轻人 经典励志

    立功写给单片机的年轻人经典励志
    发表于 08-11 18:39

    立功CANTest软件

    立功CANTest软件
    发表于 01-15 16:52

    立功CANTest软件

    立功CANTest软件
    发表于 02-27 09:26

    【微信精选】转行or坚守,是否每个硬件工程师经历过这样的迷思?

    这些腿脚不好使的追不上风口,飞不起来就脚踏实地吧,行业有起伏,说不定哪天吃饱正睡呢,就被卷上天了,这个时代,太多不可能成为可能了(诺基亚,呵呵),那天之前,起码自我修炼,把体重减轻,方便被卷。2.
    发表于 10-11 07:30

    立功写给单片机的年轻人

    立功写给单片机的年轻人 作为过来人思前想后,我感到完全有责任将发自心底的感受传递给年轻一代,一个企业家心灵深处渴望优秀人才的卓越追求和深层次的叹息、痛苦和感受。
    发表于 05-14 16:40 0次下载

    立功电子从业经历回顾一:初生牛犊不怕虎

    国内电子行业的人对立功先生相信都会有很深刻的了解,不知道你们是怎么看,反正在小便读书的时候,我们老师总是很推崇这个技术牛人,这个开拓了一个新时代的印记。近日,立功先生在其博客上写下
    发表于 04-17 10:53 9656次阅读

    立功电子从业经历回顾二:柳暗花明又一村

    在昨天的立功从业经历回顾里面,我们给大家介绍了立功先生早前的求学经历,受到大家的欢迎,接下来
    发表于 04-18 09:21 2687次阅读

    嵌入式系统软件设计中的常用算法航慈 清晰完整版)

    电子发烧友网站提供《嵌入式系统软件设计中的常用算法航慈 清晰完整版).txt》资料免费下载
    发表于 05-27 11:04 0次下载

    驾驶一辆被黑客黑掉 油门和刹车失灵的车是什么体验?

    是否经历过在开车时刹车和油门失灵的危险状况?也许没有经历过,但是这种事情一定有可能发生,而
    发表于 07-10 09:21 976次阅读

    TinyM0_tools.pdf立功官方文件 教你怎么下载 很不错

    TinyM0_tools.pdf立功官方文件 教你怎么下载 很不错
    发表于 10-13 15:14 15次下载
    TinyM0_tools.pdf<b class='flag-5'>周</b><b class='flag-5'>立功</b>官方文件 <b class='flag-5'>教你</b>怎么下载 很不错

    手把手教你LabVIEW视觉设计

    手把手教你LabVIEW视觉设计手把手教你LabVIEW视觉设计手把手教你LabVIEW视
    发表于 03-06 01:41 3176次阅读

    推挽电路的没?

    推挽电路的没?
    的头像 发表于 11-24 16:25 1171次阅读
    推挽电路的<b class='flag-5'>坑</b>,<b class='flag-5'>你</b>踩<b class='flag-5'>过</b>没?

    反相输入放大器的没有?

    反相输入放大器的没有?
    的头像 发表于 12-06 15:35 713次阅读
    反相输入放大器的<b class='flag-5'>坑</b>,<b class='flag-5'>你</b>踩<b class='flag-5'>过</b>没有?