查找是指在数据集合中寻找满足某种条件的数据元素的过程。
用于查找的数据集合则称为查找表(search table)。查找表中的数据元素类型是一致的,并且有能够唯一标识出元素的关键字。
如果从查找表中找出了关键字等于某个给定值的数据元素,则称为查找成功,否则称为查找不成功。
通常对查找表有4种操作:
(1)查找:在查找表中查看某个特定的记录是否存在;
(2)检索:查找某个特定记录的各种属性;
(3)插入:将某个不存在的数据元素插入到查找表中;
(4)删除:从查找表中删除某个特定元素。
如果对查找表只执行前两种操作,则称这类查找表为静态查找表。静态查找表建立以后,就不能再执行插入或删除操作,查找表也不再发生变化,其操作主要包括顺序查找、折半查找、分块查找等。如果对查找表还要执行后两种操作,则称这类查找表为动态查找表。其操作往往使用二叉平衡树、B-树或哈希表来处理。
通常,我们使用平均查找长度(ASL)来衡量查找算法的性能。对于含有n个元素的查找表,定义查找成功的平均查找长度为:
ASL = sum(i=0:n)[Pi*Ci],其中Pi是搜索查找表中第i个记录的概率,并且sum(i=0:n)[Pi] = 1,Ci是指搜索查找表中第i个元素时直到查找成功为止,表中元素的比较次数。考虑到查找不成功的情况,查找算法的平均查找长度应该是查找成功的平均查找长度和查找不成功的平均查找长度之和。通常我们在说平均查找长度时,不考虑查找不成功的情况。
静态查找算法的效率从高到低为:折半查找、分块查找、顺序查找。
折半查找的基本流程为:
(1)首先确定待查关键字在有序(假设是升序)的查找表中的范围。通常用两个下标来表示范围:left=0,right=length-1;
(2)然后用给定的关键字和查找表的正中间位置(下标为mid=(left+right)/2)元素的关键字进行比较,若相等,则查找成功。若待查找关键字比正中间位置元素的关键字大,则继续对右子表(left=mid+1)进行折半查找,否则对左子表(right=mid-1)进行折半查找;
(3)如此重复进行,直到查找成功或范围缩小为空(left>right)即查找不成功为止。
C++ STL的中包含有查找算法的实现:
(1)std::find(),使用顺序查找算法,通过operator==()函数比较,在容器中查找指定元素。如果找到,返回第一个找到元素的iter,如果找不到返回container.end()。不要求容器内元素事先排序。
(2)std::find_if(),使用顺序查找算法,返回第一个满足给定条件函数的元素。
(3)std::find_if_not(),使用顺序查找算法,返回第一个不满足给定条件函数的元素。
(4)std::find_last(),使用顺序查找算法,返回最后一个满足给定条件函数的子序列的第一个元素,其所在的位置。
(5)std::find_first_of(),使用顺序查找算法,返回第一个存在于指定查找目标子序列中元素,其所在的位置。
(6)std::adjacent_find(),使用顺序查找算法,返回第一次出现连续两个元素相同时,其第一个元素所在的位置。
(7)std::search(),使用顺序查找算法,返回要查找子序列在待查找序列中第一次出现时,其第一个元素所在的位置。
(8)std::search_n(),使用顺序查找算法,找到区间内连续n个满足某一条件或者等于某一值的情况,并返回满足条件的第一个元素所在的位置。
(9)std::lower_bound(),使用折半查找算法,返回第一个大于等于给定值的元素的位置。该函数要求容器内元素已经过排序,或至少对于给定值已对容器内元素进行分组划分。
(10)std::upper_bound(),使用折半查找算法,返回第一个大于给定值的元素的位置。该函数要求容器内元素已经过排序,或至少对于给定值已对容器内元素进行分组划分。
(11)std::equal_range(),相当于std::make_pair(std::lower_bound(), std::upper_bound() )。
(12)std::binary_search(),使用折半查找算法,如果找到给定值元素则返回true,否则返回false。该函数要求容器内元素已经过排序,或至少对于给定值已对容器内元素进行分组划分。
在STL中,并没有直接关于动态查找算法的函数实现,而是包含在具体的数据结构类型的成员函数中,如Hash表或树。
关于动态查找算法的相关内容,在树和Hash表的数据结构部分再进行讨论。