顺序查找
顺序查找也可以叫做线性查找。它对顺序表和链表都适用。对于顺序表可以通过数组下标递增扫描每个元素;链表通过指针 next 依次扫描每个元素。顺序表通常分为:对一般的无序线性表的顺序查找和按关键字有序的线性表的顺序查找。
一般线性表的顺序查找
基本思想:从线性表的一段开始,逐个检查关键字是否满足给定的条件。若查找到某个元素的关键字满足给定的条件,那么就查找成功,返回该元素在线性表中的位置;若已经查到了表的另一端,但是没有查找到符合条件的元素,那么久返回查找失败的信息。
算法思想(正常版):
typedef struct { //查找表的数据结构(顺序表) ElemType* elem; //动态数组基址 int TableLen; //表的长度 }SSTable; //顺序查找 int Search_Seq(SSTable ST, ElemType key) { int i ; ST.elem[0] = key; //哨兵 for (i = 0; i < ST.TableLen && ST.elem[i] != key; ++i); { //从后往前找 //查找成功,则返回元素下标;查找失败则返回 -1 return i == ST.TableLen ? -1 : i; } }
算法图解:
算法思想(哨兵版):
typedef struct { //查找表的数据结构 ElemType* elem; //元素存储空间基址,建表时按实际长度分配。0号单位留空 int TableLen; //表的长度 }SSTable; int Search_Seq(SSTable ST, ElemType key) { int i = 0; ST.elem[0] = key; //哨兵 for ( i = ST.TableLen; ST.elem[i] != key; --i); //从后往前找 return i; //若表中不存在关键字为 key 的元素,将查找到 i为0时退出 for 循环 }
算法图解:
在上述算法中,将 ST.elem[0]称为“哨兵”。引入它的目的是使得Search_Seq内的循环不必判断数组是否会越界,因为满足i==О时,循环一定会跳出。需要说明的是,在程序中引入“哨兵”并不是这个算法独有的。引入“哨兵”可以避免很多不必要的判断语句,从而提高程序效率。
查找效率分析:
查找不成功时,与表中各关键字的比较次数显然是n+1次,从而顺序查找不成功的平均查找长度为ASL不成功= n+1。
通常,查找表中记录的查找概率并不相等。若能预先得知每个记录的查找概率,则应先对记录的查找概率进行排序,使表中记录按查找概率由小至大重新排列。
综上所述,顺序查找的缺点:是当n较大时,平均查找长度较大,效率低;优点:是对数据元素的存储没有要求,顺序存储或链式存储皆可。对表中记录的有序性也没有要求,无论记录是否按关键字有序,均可应用。同时还需注意,对线性的链表只能进行顺序查找。
有序表的顺序查找
若在查找之前就已经知道表是关键字有序的,则查找失败时可以不用再比较到表的另一端就能返回查找失败的信息,从而降低顺序查找失败的平均查找长度。
假设表工是按关键字从小到大排列的,查找的顺序是从前往后,待查找元素的关键字为key,当查找到第i个元素时,发现第i个元素对应的关键字小于key,但第i+1个元素对应的关键字大于 key,这时就可返回查找失败的信息,因为第i个元素之后的元素的关键字均大于key,所以表中不存在关键字为key的元素。
可以用如图7.1所示的判定树来描述有序顺序表的查找过程。树中的圆形结点表示有序顺序表中存在的元素;树中的矩形结点称为失败结点(若有n个结点,则相应地有n +1个查找失败结点),它描述的是那些不在表中的数据值的集合。若查找到失败结点,则说明查找不成功。
在有序表的顺序查找中,查找成功的平均查找长度和一般线性表的顺序查找一样。查找失败时,查找指针一定走到了某个失败结点。这些失败结点是我们虚构的空结点,实际上是不存在的,所以到达失败结点时所查找的长度等于它上面的一个圆形结点的所在层数。查找不成功的平均查找长度在相等查找概率的情形下为
式中,q,是到达第j个失败结点的概率,在相等查找概率的情形下,它为 1/(n + 1); lj是第j个失败结点所在的层数。当n=6时,ASL 不成功 =6/2+6/7=3.86,比一般的顺序查找算法好一些。
注意,有序表的顺序查找和后面的折半查找的思想是不一样的,且有序表的顺序查找中的线性表可以是链式存储结构。