对于有序表查询,我们一般会有三种方式可选:二分查找、插值查找、斐波那契查找。
下边先看第一种查询方式:二分查找,也叫折半查找。其他两种方式是在这种方式上延伸出来的性能相对有所提升的查询方式。
引言:要从杂乱无章随意堆放的物品中找到目标可能不是一件容易的事,但是有规律可循的实物中,我们总是能通过一定的手段快速“悠闲地”找到我们想要的东西。比如从一本字典中可以快速查找到我们要查询的汉字或者单词。
这里就引申出来一种有序性。这种有序性可以是按照数字方式由大到小、由小到大,或者可以按照字母、拼音的排列方式。
在数据结构中的二叉树中,不难见识到所谓的折半查找(也叫作二分查找法)。比如如何每次都能够做到快速准确地猜到一个1到100(包括1和100)之中的数呢?相信下面这张图能够给你启发(下边这种图来源渡摆):
下面有java来实现这种算法:
public class FabnacciSearch {
private static int count = 0;// 用来统计查询次数
public static void main(String[] args) {
int[] intArr = new int[]{35, 62, 59, 47, 1, 0, 16, 24, 73, 99, 88};
System.err.println("数组排序前:");
printn(intArr);
rank(intArr);
System.out.println();
System.err.println("数组排序后:");
printn(intArr);
System.out.println();
int key = 24;
int searchResult = binarySearch(key, intArr);
System.err.print("对 " + key + " 二分查找结果,其在数组中的下标为:\n" + searchResult + "\n");
System.out.println("本次查询次数为:" + count);
}
private static void printn(int[] a) {
for (int b : a) {
System.out.print(b + " ");
}
}
// 对数组进行排序
private static void rank(int[] a) {
for (int i = 1; i < a.length; i++) {
for (int j = i; j > 0; j--) {
if (a[j] < a[j - 1]) {
int tem = a[j - 1];
a[j - 1] = a[j];
a[j] = tem;
}
}
}
}
// 进行二分查找的数组需要是有序的
private static int binarySearch(int key, int[] ints) {
int low = 0;
int hig = ints.length - 1;
while (low <= hig) {
count++;
int middle = (int) Math.ceil((double) (hig + low) / 2);
if (key < ints[middle]) hig = middle - 1;
else if (key > ints[middle]) low = middle + 1;
else return middle;
}
return -1;
}
}
打印结果如下:
数组排序前:
35 62 59 47 1 0 16 24 73 99 88
数组排序后:
0 1 16 24 35 47 59 62 73 88 99
对 24 二分查找结果,其在数组中的下标为:
3
本次查询次数为:4
另外一个知识点,对于二分查找,其算法复杂度为多少呢?答案是:用大O表示法表示,算法复杂度为O(logn)。
莫急,下边是其推导过程:
首先我们可以把一个数组进行排序(就按从小到大好了),然后按照上边图示一样,有序数组其实就是树的一种特殊情况。我们不管是用肉眼观察(),还是运行一段代码都不难发现其长度,当做树来考虑就是有多少个节点。而对于完全二叉树有这样一条性质:具有 n 个结点的完全二叉树的深度为 log2n + 1 。
所以呢,答案就很明显了,其算法就是O(logn)。
没有接触过树和大O表示法的元凶(猿兄)们可以参考相关资料文档,说不定会有意外收获。
不过由于折半查拢的前提条件是需要有序表顺序存储,对于静态查找表,一次排序后不再变化,这样的算法已经比较好了。但对于需要频繁执行插入或删除操作的数据集来说,维护有序的排序会带来不小的工作量,那就不建议使用。
该有序表查询系列内容均参考《大话数据结构》一书中章节“查找”“有序表查找”节次进行总结,个人按照java方式进行实现。如有纰漏之处,还请各位元凶们第一时间指正,虽然觉得是很基础的内容,却在开始进行实现的时候也还是错误百出,还望各位不吝赐教。