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

    文章

    4458

    浏览量

    90766
  • 软件设计
    +关注

    关注

    3

    文章

    55

    浏览量

    17676
  • 周立功
    +关注

    关注

    38

    文章

    130

    浏览量

    37085

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

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

收藏 人收藏

    评论

    相关推荐

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

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

    立功大师EASY FPGA原理图

    本帖最后由 eehome 于 2013-1-5 09:47 编辑 立功EASYFPGA原理图立功大师经典力作,FPGA原理图。欢迎大家下载学习
    发表于 03-16 11:02

    立功的NIOS视频

    立功的NIOS视频
    发表于 07-19 09:55

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

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

    立功的人生经历

    ,“特例是并非能够广而推之的典型”,我仅仅是闲来无事之时“瞎”掺合而以。因为我总不能将自己在很多年前当工人的时候就有钱购买8万元的PC机,而且用汇编在PC机上写“窗口软件经历来告诉年轻人或者教训大学生
    发表于 03-25 10:53

    立功labview can

    立功 u*** can2 读取动态数据有点慢,一秒钟只有150组数据,测试要求1秒钟1500组数据,太慢了。labview是不是就是慢呀!
    发表于 09-12 10:18

    跳槽,对和你的boss意味着什么

    无法左右的客观因素,比如像最近微软、索尼等的大规模集体裁员。建议在工作履历中省略这段经历,帮助面试官避免因为经历过的无关议题而浪费时间。18个月是得到公认的下限18个月是社会公认的
    发表于 11-20 15:16

    立功CANTest软件

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

    立功CANTest软件

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

    请问STM32还能参考立功的书吗?

    想玩ARM,准备入手块STM32 的板,可是找书籍方面的资料遇到点问题.网上很多人推荐立功的书,但是他们是基于LPC写的,STM32还能参考那个吗?恳请各位前辈帮我解答,谢谢
    发表于 08-25 22:25

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

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

    大佬都在推荐的嵌入式书单

    《时间触发嵌入式系统设计模式》《嵌入式系统软件设计中的数据结构》航慈《嵌入式系统软件设计中的常用算法航慈《基于嵌入式实时操作系统的程序
    发表于 10-28 08:09

    NodeMCU开发板踩经历分享

    写在前面今天入手了一个NodeMCU的板子,准备学习一下物联网相关的知识。不过由于博主学艺不精,在第一步烧写固件上就踩了,所以就想着把自己的踩经历写出来分享给大家,希望能有一些帮助~ 材料准备硬件:NodeMCU开发板*1(
    发表于 11-01 07:55

    在STM32L431上使用TSC的简单3按钮触摸键会出错有没有人经历过这种情况

    当 VDD 电源从 3.3V 降至大约 3.0V(刚好高于 BOR 电平)然后恢复到 3.3V 时,在 STM32L431 上使用 TSC 的简单 3 按钮触摸键应用会出错。从此时起,所有 3 个按钮都被读回为活动状态。有没有人经历过这种行为?如果检测到这种情况,解决方案是进行
    发表于 12-06 08:20

    《电子产品设计宝典可靠性原则2000条》+ 避免踩别人踩——一本非常由价值的经验总结分享书

    遍安全事故不容忽视可能觉得和自己无关,反正自己没出过事,还是不注意,但是当你经历过一次,或者看到事故惨烈的现场才会真正的感受严重性,才
    发表于 05-13 20:50