经典算法-----顺序查找,折半查找,索引查找
🟡一、什么是算法
算法是如何解决一类问题的明确规范,可以执行计算、数据处理、自动推理和其他任务。
️1.算法概念:
算法可以在有限的空间和时间内用定义明确的形式语言来表示,以计算函数。算法的一个典型例子是欧几里德算法,用于确定两个整数的最大公约数。在逻辑上,一个算法完成所需的时间是无法测量的,因为它与我们习惯的物理维度无关,这种不确定性导致无法找到既适合在某种意义上又适合抽象术语使用的算法定义。
算法(Algorithm)是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。也就是说,能够对一定规范的输入,在有限时间内获得所要求的输出。如果一个算法有缺陷,或不适合于某个问题,执行这个算法将不会解决这个问题。不同的算法可能用不同的时间,空间或效率来完成同样的任务。一个算法的优劣可以用空间复杂度与时间复杂度来衡量。
算法中的指令描述的是一个计算,当其运行时能从一个初始状态和(可能为空的)初始输入开始,经过一系列有限而清晰定义的状态,最终产生输出并停止于一个终态。一个状态到另一个状态的转移不一定是确定的。随机化算法在内的一些算法,包含了一些随机输入。
算法的五大特征
有穷性(Finiteness)
算法的有穷性是指算法必须能在执行有限个步骤之后终止;
确切性(Definiteness)
算法的每一步骤必须有确切的定义;
输入项(Input)
一个算法有0个或多个输入,以刻画运算对象的初始情况,所谓0个输入是指算法本身定出了初始条件;
输出项(Output)
一个算法有一个或多个输出,以反映对输入数据加工后的结果。没有输出的算法是毫无意义的;
可行性(Effectiveness)
算法中执行的任何计算步骤都是可以被分解为基本的可执行的操作步骤,即每个计算步骤都可以在有限时间内完成(也称之为有效性)。
2.算法表达:
算法可用多种符号表示,包括自然语言、伪代码、流程图、drakon图表、编程语言或控制表。伪代码、流程图、drakon图表和控制表是表达算法的结构化方式,避免了自然语言语句中常见的许多歧义。编程语言主要用于以计算机可以执行的形式表达算法,但通常被用作定义或记录算法的一种方式。
3.计算机算法:
在计算机系统中,算法是软件开发人员用软件编写的逻辑实例, 使预定的“目标”计算机能够有效地从给定输入生成输出。一个最优算法,在旧硬件中运行,会比在更高效的硬件中运行的时间复杂度更高的算法产生更快的结果。
相信你也看过很多书上的定义,比如“算法是一组完成任务的指令”,“算法是操作数据的一组方法”。但是,你能举例说明吗?能让一个外行听明白吗?
它是什么
- 计算机算法,是指前人提炼出高效的、不断被验证过的标准流程。
举例说明
你去书店,要买一本《人生故事》,你用什么方式找到这本书呢?
方式1:一本本的去找,估计会累瘫在书店。
方式2:使用电脑查询一下书所在的编号,比如202015,202表示2楼第2个分区,015表示第15个书架。你到2楼找到02分区,第15个书架,很快就找到了那本书。
以上两种方式都是可以称为算法:
- 使用方式1的流程:能找到但是非常的慢,而且费力。
- 使用方式2的流程:能快速的找到且省力。因为使用了 麦尔威·杜威发明的很多国家都在使用的杜威十进制图书分类法。
那么我们就可以得出 好的算法又快又省事;
使用的时间少,就是要快。
消耗的资源少,就是要省。
总结:概括的说,算法就是解决问题的工具。在描述一个算法时,我们关注的是输入与输出。也就是说只要把原始数据和结果数据描述清楚了,那么算法所做的事情也就清楚了。我们在设计一个算法时也是需要先明确我们有什么和我们要什么,这一点相信大家在后面的文章中会慢慢体会到。
扩展:
- 算法本质是一个流程,与生活中的做事的流程类似。
- 生活中算法与计算机算法中的区别:比较计算机算法数时,需要考虑数据量特别特别大,大到近乎无穷大的情况。
因为,计算机的发明就是用于处理大量数据的。 - 我们需要学习前人的算法,避免重复造轮子,站在巨人的肩膀上前进,才能走的更远。
4.数据结构
数据结构(Data Structure)是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率。数据结构往往同高效的检索算法和索引技术有关。
数据结构定义
数据结构是指相互之间存在着一种或多种关系的数据元素的集合和该集合中数据元素之间的关系组成。记为:
Data_Structure=(D,R)
其中 D 是数据元素的集合,R 是该集合中所有元素之间的关系的有限集合。
数据结构具体指同一类数据元素中各元素之间的相互关系,包括三个组成成分,数据的逻辑结构、存储结构和数据运算结构。
数据的逻辑结构
数据的逻辑结构是指反映数据元素之间的逻辑关系的数据结构,其中的逻辑关系是指数据元素之间的前后件关系,而与他们在计算机中的存储位置无关。逻辑结构分为以下几种:
集合结构:数据元素同属一个集合,单个数据元素之间没有任何关系。
线性结构:数据结构中的元素存在一对一的相互关系。
树形结构:数据结构中的元素存在一对多的相互关系。
图形结构:数据结构中的元素存在多对多的相互关系。 作者:
**那么为什么算法经常会和数据结构一起出现呢?**这是因为对于同一个问题(如:排序),使用不同的数据结构来存储数据,对应的算法可能千差万别。所以在整个学习过程中,也会涉及到各种数据结构的使用。
常见的数据结构包括:数组、堆、栈、队列、链表、树等等。
5.算法的效率
算法效率是指算法执行的时间,算法执行时间需通过依据该算法编制的程序在计算机上运行时所消耗的时间来度量。在现在的计算机硬件环境中,比较少需要考虑这个问题了,特别是pc机的编程,内存空间越来越大,所以被考虑得也越来越少,不过一个好的程序员,都应该对自己的程序有要求,每一个for比别人少一次判断1000个for就能够少掉很多的运行时间。所以能够理解,能够大概的去运用"效率度量"还是有很大意义的。
在我们日常开发中,一个算法设计完成后,还需要对算法的执行情况做一个评估。**一个好的算法,可以大幅度的节省运行的资源消耗和时间。**在进行评估时不需要太具体,毕竟数据量是不确定的,通常是以数据量为基准来确定一个量级,通常会使用到
时间复杂度和空间复杂度这两个概念。 |
二.时间和空间复杂度
1.时间复杂度
在计算机科学中,时间复杂性,又称时间复杂度,算法的时间复杂度是一个函数,它定性描述该算法的运行时间。这是一个代表算法输入值的字符串的长度的函数。时间复杂度常用大O符号表述,不包括这个函数的低阶项和首项系数。使用这种方式时,时间复杂度可被称为是渐近的,亦即考察输入值大小趋近无穷时的情况。
通常把算法中的基本操作重复执行的频度称为算法的时间复杂度。算法中的基本操作一般是指算法中最深层循环内的语句(赋值、判断、四则运算等基础操作)。我们可以把时间频度记为T(n),它与算法中语句的执行次数成正比。其中的n被称为问题的规模,大多数情况下为输入的数据量。
对于每一段代码,都可以转化为常数或与n相关的函数表达式,记做f(n) 。如果我们把每一段代码的花费的时间加起来就能够得到一个刻画时间复杂度的表达式,在合并后保留量级最大的部分即可确定时间复杂度,记做O(f(n)) ,其中的O就是代表数量级。
常见的时间复杂度有(由低到高):O(1)、O( log 2 n \log _{2} n log2n)、O(n)、O( n log
2 n n\log _{2} n nlog2n)、O( n 2 n^{2} n2)、O( n 3 n^{3} n3)、O( 2 n
2^{n} 2n)、O(n!)。
四个时间复杂度
同一段代码在不同输入的情况下,可能存在时间复杂度量级不一样的情况,所以有以下四种不同的时间复杂度。
-
最好情况时间复杂度(best case time complexity);
-
最坏情况时间复杂度(worst case time complexity);
-
平均情况时间复杂度(average case time complexity);
-
均摊时间复杂度(amortized time complexity)。
1、最好、最坏、平均情况时间复杂度
// n表示数组array的长度
int find(int *array, int n, int x) {
int i = 0;
int pos = -1;
for ( ; i < n; ++i) {
if (array[i] == x) {
pos = i;
break;
}
}
return pos;
}
这是一个find()函数,这段代码的作用是查找参数x在数组array中的位置,如果没有就返回-1。
最好情况时间复杂度:
在最理想的情况下,代码的时间复杂度。本例中,如果数组中的第一个元素就是要查找的变量,则时间复杂度为O(1)。
最坏情况时间复杂度:
在最糟糕的情况下,代码的时间复杂度。本例中,如果数组中没有变量x,则需要遍历数组中的每一个元素,则时间复杂度为O(n)。
平均情况时间复杂度:
最好、最坏情况时间复杂度表示的都是代码在极端情况下的时间复杂度,发生的概率并不大,所以平均情况时间复杂度用于表示平均情况下的时间复杂度。
本例中,首先,变量x分为在数组中和不在数组中两种情况,假设两种情况的概率相同为;其次,要查找的变量出现在数组0 ~ n-1共n个位置的概率是一样的,都为
;最后,根据概率论的知识,变量x出现在0 ~ n-1这n个位置的概率都为
,变量不在数组中的概率为
。
根据概率论中的加权平均值,也叫期望值的计算放法(每一种情况下的时间复杂度乘以其发生的概率)得出平均时间复杂度的值:
用大O表示法表示,则平均时间复杂度为O(n),所以平均时间复杂度又叫加权平均时间复杂度,或者期望时间复杂度。
一般情况下,我们并不会区分这三种时间复杂度,只使用其中一种即可。当同一代码块在不同情况下的时间复杂度有量级上的差距,才会区分这三种复杂度。
2、均摊时间复杂度
由上述可知,一般情况下,并不区分最好、最坏、平均情况时间复杂度,平均情况时间复杂度也只有在某些特殊的情况下才会使用,而均摊时间复杂度的应用场景比平均复杂度更特殊,更有限。
// array表示一个长度为n的数组
// 代码中的array.length就等于n
int *array = new int[n];
int count = 0;
void insert(int val) {
if (count == array.length) {
int sum = 0;
for (int i = 0; i < array.length; ++i) {
sum = sum + array[i];
}
array[0] = sum;
count = 1;
}
array[count] = val;
++count;
}
这是一个insert()函数,实现往数组中插入一个数据的功能,如果数组满了的话,即当count == array.length
时,遍历数组求和,并把数据放到数组第一个位置,然后再把新的数据插入。
本段代码的时间复杂度分析:
最好情况时间复杂度:数组未满,有空闲的位置时为O(1);
最坏情况时间复杂度:数组已满,需要遍历求和时为O(n);
平均情况时间复杂度:分为数组未满和已满两种情况,未满时的插入有n种情况,每种情况的时间复杂度为O(1),已满时的时间复杂度度为O(n),所以共n+1种可能,这n+1种可能的概率相同都是,所以平均情况时间复杂度为:
2)、均摊时间复杂度
-
本例中的insert()函数区别于之前的find()函数:find()函数在极端情况下,时间复杂度为O(1),而insert()函数在大多数情况下时间复杂度都为O(1);find()函数时间复杂度的多种情况并没有任何规律。而insert()函数O(n)之后,必有n-1个O(1),循环往复。
-
针对这种特殊的场景,可以采用一种特殊的时间复杂度分析方法:摊还分析法,得出的是均摊时间复杂度。
-
分析方法:
因为时间复杂度有规律的在O(n) -> n-1个O(1)之间循环,所以把耗时最多的那次操作(O(n)),均摊到耗时最少的n-1次操作(O(1)),这样,每一组操作的时间复杂度都是O(1),即均摊时间复杂度为O(1)。 -
应用场景:
均摊时间复杂度就是一种特殊的平均情况时间复杂度,没有必要过度区分。当大部分情况下的时间复杂度较低,而只有极少数情况下的时间复杂度较高,且这些情况的出现有固定的时序性规律时,使用均摊时间复杂度。这时,尝试将较高复杂度操作的耗时均摊到较低复杂度的操作上,这就叫摊还分析法。
一般能应用摊还分析法的场景,均摊时间复杂度就等于最好情况时间复杂度
2.空间复杂度
程序从开始执行到结束所需要的内存容量,也就是整个过程中最大需要占用多少的空间。为了评估算法本身,输入数据所占用的空间不会考虑,通常更关注算法运行时需要额外定义多少临时变量或多少存储结构。如:如果需要借助一个临时变量来进行两个元素的交换,则空间复杂度为O(1)。
时间复杂度
最坏的情况
最坏的情况就是完整的遍历了整个集合,也并未找到目标的key,此时循环被完整的执行,循环执行次数与n相关,所以时间复杂度为O(n) 。
最好的情况
最好的情况就是第一次就找到了元素,此时的时间复杂度为常数级O(1) 。
平均情况
综合两种情况,顺序查找的时间复杂度为O(n) ,属于查找较慢的算法。
3. 空间复杂度
由于算法不会改变原有的元素集合,只需要一个额外的变量控制索引变化,所以空间复杂度为常数级:O(1) 。
空间复杂度涉及的空间类型有:
输入空间: 存储输入数据所需的空间大小;
暂存空间: 算法运行过程中,存储所有中间变量和对象等数据所需的空间大小;
输出空间: 算法运行返回时,存储输出数据所需的空间大小;
通常情况下,空间复杂度指在输入数据大小为 N 时,算法运行所使用的「暂存空间」+「输出空间」的总体大小。
而根据不同来源,算法使用的内存空间分为三类:
指令空间:
编译后,程序指令所使用的内存空间。
数据空间:
算法中的各项变量使用的空间,包括:声明的常量、变量、动态数组、动态对象等使用的内存空间。
struct Node {
int val;
Node *next;
Node(int x) : val(x), next(NULL) {}
};
void algorithm(int N) {
int num = N; // 变量
int nums[N]; // 动态数组
Node* node = new Node(N); // 动态对象
}
栈帧空间:
程序调用函数是基于栈实现的,函数在调用期间,占用常量大小的栈帧空间,直至返回后释放。如以下代码所示,在循环中调用函数,每轮调用 test() 返回后,栈帧空间已被释放,因此空间复杂度仍为 O(1)。
int test() {
return 0;
}
void algorithm(int N) {
for (int i = 0; i < N; i++) {
test();
}
}
算法中,栈帧空间的累计常出现于递归调用。如以下代码所示,通过递归调用,会同时存在 N 个未返回的函数 algorithm() ,此时累计使用 O(N) 大小的栈帧空间。
int algorithm(int N) {
if (N <= 1) return 1;
return algorithm(N - 1) + 1;
}
根据从小到大排列,常见的算法空间复杂度有:
O(1) < O(logN) < O(N) < O(N^2) < O(2^N)
只做简单概念简读;具体可查看 博客
三.顺序查找
顺序查找是按照序列原有顺序对数组进行遍历比较查询的基本查找算法。
1. 元素查找介绍
查找也被称为检索,算法的主要目的是在某种数据结构中找出满足给定条件的元素(以等值匹配为例)。如果找到满足条件的元素则代表查找成功,否则查找失败。
在进行查找时,对于不同的数据结构以及元素集合状态,会有相对匹配的算法,在使用时也需要注意算法的前置条件。在元素查找相关文章中只讨论数据元素只有一个数据项的情况,即关键字(key)就是对应数据元素的值,对应到具体的数据结构,可以理解为一维数组。
顺序查找
也称线性查找,是最简单的查找方法。思路也很简单,从数组的一边开始,逐个进行元素的比较,如果与给定的待查找元素相同,则查找成功;如果整个扫描结束后,仍未找到相匹配的元素,则查找失败。
顺序查找的实现
作为一种最直观的查找方法, 其基本思想是从线性表的一端开始,逐个检查关键字是否满足给定的条件。若查找到某个元素的关键字满足给定条件,则查找成功,返回该元素在线性表中的位置;若已经查找到表的另一端,但还没有查找到符合给定条件的元素,则返回查找失败的信息。
typedef struct //查找表的数据结构
{
ElemType *elem; //动态数组基址,建表时按实际长度分配,0号单元留空
Int TableLen; //表的长度
}SSTable;
//顺序查找
int Search_Seq(SSTable ST, ElemType key)
{
//在顺序表ST中顺序查找关键字为key的元素。若找到则返回该元素在表中的位置
for(i=ST.TableLen; ST.elem[i]!=key; --i); //从后往前找
return i; //若表中不存在关键字为key的元素,将查找到i为0时退出for循环
}
顺序查找的实现(哨兵)
下面给出其算法,主要是为了说明其中引入的"哨兵"的作用。
typedef struct //查找表的数据结构(顺序表)
{
ElemType *elem; //动态数组基址,建表时按实际长度分配,0号单元留空
Int TableLen; //表的长度
}SSTable;
//顺序查找
int Search_Seq(SSTable ST, ElemType key)
{
//在顺序表ST中顺序查找关键字为key的元素。若找到则返回该元素在表中的位置
ST.elem[0]=key; //哨兵
int i;
for(i=ST.TableLen; ST.elem[i]!=key; --i); //从后往前找
return i; //若表中不存在关键字为key的元素,将查找到i为0时退出for循环
}
上述算法中,将ST.elem[0]称为"哨兵"。引入它的目的是使得Search_Seq内的循环不必判断数组是否会越界,因为满足i==0时,循环一定会跳出。需要说明的是,在程序中引入"哨兵"并不是这个算法独有的。引入"哨兵"可以避免很多不必要的判断语句,从而提高程序效果。
查找效率分析
对于有n个元素的表,给定值key与表中第i个元素的关键字相等,即定位第i个元素时,需进行n-i+1次关键字的比较,即Ci=n-i+1。查找成功时,顺序查找的平均长度为
ASL成功=∑Pi(n-i+1)
当每个元素的查找概率相等, 即Pi= 1/n时,有
ASL成功=∑Pi(n-i+1)=(n+1)/2
查找不成功时,与表中各关键字的比较次数显然是n + 1次,从而顺序查找不成功的平均查找长度为ASL不成功=n+ 1。
通常,查找表中记录的查找概率并不相等。若能预先得知每个记录的查找概率,则应先对记录的查找概率进行排序,使表中记录按查找概率由小至大重新排列。
综上所述,顺序查找的缺点是当n较大时,平均查找长度较大,效率低;优点是对数据元素的存储没有要求,顺序存储或链式存储皆可。对表中记录的有序性也没有要求,无论记录是否按关键码有序,均可应用。同时还需注意,对线性的链表只能进行顺序查找。
只做简单概念简读理解;具体可查看 博客
折半查找
折半查找(Binary Searh) 也称二分查找,它是一种效率较高的查找方法。使用该算法的前提要求是元素已经有序,因为算法的核心思想是尽快的缩小搜索区间,这就需要保证在缩小范围的同时,不能有元素的遗漏, 而且表中元素也按关键字有序排列。在下面及后续的讨论中,均假设有序表是递增有序的。
折半查找的查找过程为:从表的中间记录开始, 如果给定值和 中间记录的关键字相等, 则查找成功;如果给定值大于或者小千中间记录的关键字,则在表中大于或小千中间记录的那一半中查找,这样重复操作, 直到查找成功或者在某一步中查找区间为空, 则代表查找失败。
折半查找每一次查找比较都使查找范围缩小一半,与顺序查找相 比,很显然会提高查找效率。
为了标记查找过程中每一次的查找区间,下面分别用low和high来表示当前查找区间的下界和上界,mid为区间的中间位置。
【算法步骤】
1 :置查找区间初值,low 为 1,high为表长。
2 :当 low 小于等于 high 时, 循环执行以下操作:
• mid取值为 low 和 high 的中间值;
• 将给定值key与中间位置记录的关键字进行比较,若相等则查找成功,返回中间位置 mid ;
• 若不相等则利用中间位置记录将表对分成前、后两个子表 。如果 key 比中间位置记录
的关键字小,则 high 取为 mid - 1 , 否则 low 取为 mid + l 。
3 :循环结束,说明查找区间为空,则查找失败,返回 0。
【算法描述】
int Search_Bin(SSTable ST, KeyType key)
{
//在有序表ST中折半查找其关键字等于key的数据元素。若找到, 则函数值为该元素在表中的位置, 否则为0
low = l;
high = ST.length; //置查找区间初值
while (low <= high)
{
mid = (low + high) / 2;
if (key == ST.R[mid].key)
return mid; //找到待查元素
else if (key < ST.R[mid].key)
high = mid - 1; //继续在前一子表进行查找
else
low = mid + l; //继续在后一子表进行查找
return 0;
}
唯一需要注意的是,循环执行的条件是 low <= high , 而不是low < high,因为 low = high 时,查找区间还有最后一个结点,还要进一步比较 。
索引查找
索引查找主要分为基本索引查找和分块查找,核心思想是对于无序的数据集合,先建立索引表,使得索引表有序或分块有序,结合顺序查找与索引查找的方法完成查找。
数据太多,杂乱无章,查找困难!但是,在数据库中利用索引能过很容易的就查找到我们所需要的数据。
索引查找又称为分块查找,是一种介于顺序查找和二分查找(折半查找)之间的一种查找方法,索引查找的基本思想是:首先查找索引表,可用二分查找或顺序查找,然后在确定的块中进行顺序查找。
在实现索引查找算法前需要弄清楚以下三个术语。
(1)主表:即要查找的序列。
(2)查找表:一般我们会将主表分成几个块,每个块中的元素被称为是查找表。
(3)索引表:即索引项的集合。
在利用索引查找时,需要先对数据进行分块。
在索引表中记录了两个数据 :
最大关键字,起始地址(指针项)。
索引表的构建:
- 分块:
第Rk 块中所有关键字< Rk+1块中所有关键字(k=1, 2, …, L-1)
- 建立索引项:
关键字项:记载该块中最大关键字值;
指针项: 记载该块第一个记录在表中位置。
- 所有索引项组成索引表。
如下数据索引查找:
上数据转化成索引表如下:
当我要查找数据:
k = 38 时
是大于第一个块中的最大关键字,但是小于第二个块中的最大关键字,易得 和数据进行匹配的数据在第二个块中,在第二个块中进行顺序查找。查找出结果,返回索引。
k = 50 时
是大于第二个块中的最大关键字,但是小于第三个块中的最大关键字,易得 和数据进行匹配的数据在第三个块中,在第三个块中进行顺序查找。查找出结果,返回索引。
但是问题就来了 ······························
块内的查找如何判断结束 ?
首先根据待查找关键字在索引表当中定位块。定位的方法是:只要 key>索引块i的最大关键值,则i++,定位下一个索引项;直到定位到 索引块,或者把索引项都定位完也没有比key关键字大的索引项。 如果定位到块,则在块内部进行顺序查找。
int IndexSequelSearch(IndexType ls[], DataType s[], int m, KeyType key){
/*索引表为ls[0]-ls[m-1],顺序表为s */
i=0;
while(i<m && key>ls [i ].key) i++;
/*块间查找*/
if(i==m)return -1; /*查找失败*/
else{ /*在块内顺序查找*/
j=ls[ i ].Link;
while(Key!=s[j].key && j<ls[ i+1 ].Link) j++;
if(key = = s[j].key)
return j;
/*查找成功*/
else return -1; /*查找失败*/
}
}
索引顺序查找的ASL?
• ASL=ASL(索引表)+ASL(块内)。
总结:
在索引查找方法中 ,利用的是首先将所得的数据进行排序分块,
将要查找的数据 k 和分块中的最大值进行比较,判断k在哪个分块,
在分块中判断是否数据中有和K 匹配的数据。
返回结果:
注意:
查找表中的数据可以利用顺序存储结构或者是链式存储结构。(建议采用链式存储结构)。
输入
n个数的序列,通常直接存放在数组中,可以是任何顺序。
待查找元素key。
输出
查找成功:返回元素所在位置的编号。
查找失败:返回-1或自定义失败标识。
算法说明
算法执行的过程简单粗暴,就是从数组的一端开始逐个扫描,挨个元素进行比较,直到找到元素位置,或将所有的元素扫描一遍。
四.算法实践
1. 算法实现
输入数据:A = {11,34,20,10,12,35,41,32,43,14},key = 41
Java源代码
public class SequentialSearch {
public static void main(String[] args) {
// input data
int[] a = {11,34,20,10,12,35,41,32,43,14};
int key = 41;
// 调用算法,并输出结果
int result = search(a, key);
System.out.println(result);
}
private static int search(int[] a,int key) {
// 初始化变量
int i = 0;
// 使用循环遍历整个数组
while (i < a.length){
// 将集合中的元素与key进行比较
if (a[i] == key){
// 找到目标元素,提前返回
return i + 1;
}
// 每次索引下标后移
i++;
}
// 循环结束还未触发内部的return则代表未找到,此时返回-1
return -1;
}
}
输出数据(output):7