“爸爸,我那本《孤独的小螃蟹》在哪里?我找不到了!昨天还看了的……”
哼!你当然找不到!什么东西都到处乱扔,最后还不是要老爸擦屁股!
“在书架第三层,右边的位置,你自己找一下!!”
“在哪呀!!这么多书怎么找!”
“怎么找?”噔,老爸眼睛一亮!线性查找啊!“一本一本找呗!”
来来来,老爸给你讲算法!
(网络素材)
查找(搜索)算法
查找算法就是从一组数据中找到目标数据的算法,这组数据可以是无序的,也可以是有序的。书架第三层所有的书就是这个无序的数组,我们要找到《孤独的小螃蟹》这本书,需要从左到右(当然,我已经说过了,书收到了第三层右边,我们可以从右边开始找)一本一本看书脊,是否是《孤独的小螃蟹》,如果是当然就找到了,如果没有……嗯嗯,老爸上年纪了,这个记性有点……
(算法动画图解app,推荐)
线性查找可以用在有序或无序的数组上,但由于这个算法需要从开始不断地按照顺序检查数据,因此在数据量大且目标数据靠后,或者目标数据不存在时,比较的次数就会更多,消耗的时间当然也更多。
算法的流程图很简单∶
(继续手绘,点赞啊)
Scratch实现线性查找算法
“初始化列表”积木
我们仍延用“插入排序算法”中使用的“初始化列表”积木,随机生成10个不重复的数。
算法实现
算法比较简单,可以用“侦测”中的“询问……并等待”积木获取需要查找的数,并将“回答”赋给“待查找”变量。找到(或没找到)后,控制角色“外观”中“说……”积木来输出结果。
运行结果如下∶
数组越界问题
在上面的实现中,我们使用了“答案”变量,并初始化赋值为0,如果在列表中找到了目标数字,则将“答案”设置为查找位置。最后判断答案是否为0来确定是否找到目标数字。
可以看运行结果,我们总能得到正确的回答,但似乎少了点什么。对了,从“查找位置”变量看,我们得到正确答案后,程序仍然继续在执行!通常,我们在书架上找一本书,找到后就不会再继续找下去了。我们可以优化代码,在找到后立即停止全部脚本,直接输出结果,从而得到一个更快的程序。
另外,我们在循环中判断“查找位置”是否大于列表的项目数作为退出条件。这里,涉及到一个数组越界的问题。比如,我们的列表初始化了10个数,如果我们取第11个数会发生什么?对于计算机程序而言,当你试图访问越过数组末尾的元素时,往往会导致数据损坏,甚至程序崩溃(Scratch不会有这个问题,其内部对越界数据返回空,但由于数组的越界问题仍然可能会导致程序结果异常)。那有没有办法不使用数组项目数作为退出条件呢?当然是有办法的。我们可以先将列表中最后一项临时存起来,再将其设置为目标数字,循环遍历时,只需要判断列表[查找位置]是否等于待查找的目标数字就可以了。如果查找位置小于列表的项目数,则答案为查找位置;如果查找位置是最后一位,则判断我们存起来的临时数据是否等于目标数据就可以得出最后的答案了。
运行结果∶
可以看到,程序找到正确的答案后就停止运行了,执行效率显然比上一种要高。而且,这个算法在循环时没有使用列表的长度,不会产生数组越界问题。
递归初探
“小骞,从第二种实现中你能发现什么规律吗?”
“看不出来……”
“如果,只看某一个位置的数据与目标数据比较呢”
“嗯……如果这个位置的数据跟目标数据相同就找到了啊!”
“对了!那如果不同呢?”
“不同就找下一个位置呗!”
“好的!还有吗?”
“如果在整个列表中都没找到,那就没有。”
“真是个爱动脑子的小宝贝!那么,对于某一个位置的数而言,我们可以判断这三种情况”
- 如果当前位置大于列表项目数,则“没找到”
- 如果列表当前位置的数等于待查找的数,则找到了
- 如果列表当前位置的数不等于待查找的数,则找下一个位置
“你看,在第三种情况中,下一个位置的判断,是否也需要遵循这三种情况啊?”
“是的!”
“这样的话,我们依次检查每一个位置的过程都是相同的。这个方法叫做递归技术。我们同样可以用自定义积木实现它!”
然后我们使用这个积木来实现线性查找。
程序是不是简单多了?试试效果吧。
使用递归技术的算法叫递归算法,这个技术在算法中很常见,而且递归算法很容易理解。
小结
我们用三种不同的实现方法实现了线性查找算法。锅一样,菜谱一样,炒出的菜也一样,只是用了不同的刀工或技术。算法的世界真奇妙!
“小骞,下次书看了就放回到书架上。不然又会找不到了!”
“我不!老爸帮我放!找不到就怪你!”
“我……”凑不死你,臭丫头!