顺序查找
顺序查找,是一种最直观的查找方式。原理闲荡简单就是我们正常思维的查找,从给定的序列出发,依次检查序列中的每一个项目是否为我们给定的关键字。是则查找成功,否则查找失败。
bool searchByOrder(vecter<int> vec){
for(int i = 0; i < vec.size(); i++){
if(vec[i] = k ) return true;
return false;
}
}
这段代码是现场写的,应该都非常熟悉,如果有N个元素,那么最坏的情况下我们需要O(n)的时间。因此也称为线性查找。
而对于有序表,我们则可以进行改进。有序表就是表中的数据已经排好序,我们只需要对其遍历即可。考虑这样一种情况:
我们要对一串数字进行查找 10 20 30 40 50,给定的要查找的key为 25。那么我们至少要进行多少次比较呢?显然是3次,依次是 10 20 30因为后面的数不用看都知道一定比25大(这是有序表),因此这样我们遍历的次数就可以减少一些。因为 20 < 25 <30,因此25只能存在20 和 30之间。而他们之间又没有其他数,所以必定查询失败。如果查找的是40,那么我们就能在不遍历50的情况下找到40
折半查找法
但是对于有序表来讲,有比上面更好的做法。我们称为折半查找法,也称二分查找(binary search)。这是一种典型的递归算法。具体的思想和代码我就不说了。为什么?在递归的博客中就谈到过:C++抽象编程——递归简介(5)——检查回文数,折半查找
说说简单的思想:我们在一个线性表中找一个数,最坏的情况是O(n),如果我们每次将寻找的区间缩小一半,也就相当于将搜索的范围缩小一半。那么就大大提高了查找的效率。贴个C++代码看看:
int findInSortedVector(string key, Vector<string> & vec) {
return binarySearch(key, vec, 0, vec.size() - 1);//返回折半查找的结果
}
int binarySearch(string key, Vector<string> & vec, int p1, int p2) {
if (p1 > p2) return -1; //设置折半查找的边界从p1 到 p2
int mid = (p1 + p2) / 2; //取中点(向上取整)划分区间
if (key == vec[mid]) return mid;//如果中间的值恰好等于要找的值,直接返回
if (key < vec[mid]) { //否则,判断给定的值在哪个区间内
return binarySearch(key, vec, p1, mid - 1); //在此区间 ,递归调用折半查找
} else {
return binarySearch(key, vec, mid + 1, p2);//在另一个区间调用折半查找
}
}
折半查找与二叉搜索树
有没有发现这个过程跟什么有点像?没错,其实这就是二叉搜索树的构成原理。我们曾经举过例子将一个无序的数字序列构建出一颗二叉搜索树。可以看看这里:数据结构——树(7)——二叉搜索树及其操作原理
因此,由折半查找法找到给定的值,最坏的结果是把整个树的高度走了一遍,即树高h = log2^(n + 1)。因此最坏情况是O(log2^n);
但是尤其注意,由于折半查找法要方便的定位查找固定区域,因此其存储结构必须符合随机存取,即顺序存储结构且要求序列已经排好序。