本文将对数据结构中各种查找算法进行总结,对于每一种查找算法,本文都会给出详细的解释以及实现。
1、查找:根据给定的某个值,在查找表中确定一个其关键值等于给定值的数据元素(或记录)。
2、查找表:由同一类型的数据元素构成的集合。
3、关键字:数据元素中某个数据项的值。
查找算法按照操作方式可以分为两种:静态查找算法和动态查找算法。其中,静态查找算法只作查找操作,而动态查找算法将在查找的过程中同时插入查找表中不存在的数据元素,或者从查找表中删除已经存在的某个数据元素。
顺序查找(Sequential Search),又叫线性查找,是一种最基本的查找算法。它的查找过程是:从表中第一个(或最后一个)记录开始,逐个进行记录的关键字和给定值比较,若某个记录的关键字和给定值相等,则查找成功,如果查找到表中最后一个元素,还没有找到,则查找不成功。
顺序查找算法的Java实现如下:
/**
* 顺序查找算法
* @param array 数组
* @param key 要查找的关键字
* @return
*/
public int Sequential_Search(int[] array,int key){
int i;
for(i = 0;i < array.length;i++){
if(array[i] == key){//查找成功
return i;
}
}
return -1;
}
时间复杂度:顺序查找的时间复杂度为 O(n) ;
优点:简单,是对表中数据元素的存储没有要求;
缺点:当n很大时,查找效率极为低下。
折半查找(Binary Search),又称二分查找。它的前提是线性表中的记录必须是关键码有序,线性表必须采用顺序存储。折半查找的基本思想是:在有序表中,取中间记录作为比较对象,若给定值与中间记录的关键字相等,则查找成功;若给定值小于中间记录的关键字,则在中间记录的左半区继续查找;若给定值大于中间记录的关键字,则在中间记录的右半区继续查找。不断重复上述过程,直到查找成功,或所有查找区域无记录,查找失败为止。
折半查找算法的Java实现如下:
/**
* 折半查找算法
* @param array 数组
* @param key 要查找的关键字
* @return
*/
public int Binary_Search(int[] array, int key){
int low,high,mid;
low = 0; //定义最低下标为数组首位
high = array.length-1; //定义最高下标为数组末位
while (low <= high){
mid = (low+high)/2; //折半
if(key < array[mid])
high = mid-1;
else if(key > array[mid])
low = mid+1;
else
return mid;
}
return -1;
}
时间复杂度:顺序查找的时间复杂度为 O(logn) ;
优点:效率快;
缺点:需要查找表有序。
插值查找(Interpolation Search)是基于二分查找算法,将查找点的选择改进为自适应选择,可以提高查找效率。即根据要查找的关键字key与查找表中最大最小记录的关键字比较后的查找方法,其核心就在于插值的计算公式mid=low+(key-a[low])/(a[high]-a[low])(high-low),替换了二分查找的计算公式mid=low+1/2(high-low)。
插值查找算法的Java实现如下:
/**
* 插值查找算法
* @param array 数组
* @param key 要查找的关键字
* @return
*/
public int Interpolation_Search(int[] array, int key){
int low,high,mid;
low = 0; //定义最低下标为数组首位
high = array.length-1; //定义最高下标为数组末位
while (low <= high){
mid = low + (high-low)*(key-array[low])/(array[high]-array[low]); //自适应选择查找点
if(key < array[mid])
high = mid-1;
else if(key > array[mid])
low = mid+1;
else
return mid;
}
return -1;
}
时间复杂度:顺序查找的时间复杂度为 O(logn) ;
优点:效率快,且优于二分查找;
缺点:需要查找表有序且不适合极端不均匀的数据。
斐波那契查找(Fibonacci Search),也是一种改进的二分查找,通过运用黄金比例的概念在数列中选择查找点进行查找,提高查找效率。同样地,斐波那契查找也属于一种有序查找算法。
首先介绍斐波那契数列。斐波那契数列如下所示:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89…….(从第三个数开始,后边每一个数都是前两个数的和)。然后我们会发现,随着斐波那契数列的递增,前后两个数的比值会越来越接近0.618,利用这个特性,我们就可以将黄金比例运用到查找技术中。
斐波那契查找与折半查找很相似,他是根据斐波那契序列的特点对有序表进行分割的。他要求开始表中记录的个数为某个斐波那契数小1,及n=F(k)-1。所以如果表中记录的个数不足够的话,需要手动添加一部分数据。
斐波那契查找算法的Java实现如下:
/**
* 斐波那契查找算法
* @param array 数组
* @param key 要查找的关键字
* @return
*/
public int Fibonacci_Search(int[] array, int key){
//首先构造一个斐波那契数组
int[] F = new int[20];
F[0] = 0;
F[1] = 1;
for(int i = 2;i < 20;i++){
F[i] = F[i-1] + F[i-2];
}
int low,high,mid,i,k;
low = 0; //定义最低下标为数组首位
high = array.length-1; //定义最高下标为数组末位
k = 0;
int n = array.length;
while(n > F[k]-1) //计算n位于斐波那契数列的位置
++k;
int[] temp = new int[F[k]-1];//将数组扩展到F[k]-1的长度
for(i = 0;i < n;i++)
temp[i] = array[i];
for(i = n;i < F[k]-1;++i){
temp[i] = array[n-1];
}
while (low <= high){
mid = low + F[k-1] - 1; //计算当前分隔的下标
if(key < temp[mid]){
high = mid - 1;
k = k - 1; //斐波那契数列的下标减一位(说明范围[low,mid-1]内的元素个数为F(k-1)-1个)
}else if(key > temp[mid]){
low = mid + 1;
k = k - 2; //斐波那契数列的下标减两位(说明范围[mid+1,high]内的元素个数为n-(F(k-1))= Fk-1-F(k-1)=Fk-F(k-1)-1=F(k-2)-1个)
}else{
if(mid < n)
return mid; //若相等则说明mid即为查找到的位置
else
return n-1; //若mid>=n则说明是扩展的数值,返回n-1
}
}
return -1;
}
时间复杂度:顺序查找的时间复杂度为 O(logn) ;
优点:效率快,且平均性能优于折半查找,斐波那契数列只是最简单的加减法运算,所以在海量数据的查找过程中,这种细微的差别可能会影响最终的查找效率。
缺点:需要查找表有序。
二叉排序树(Binary Sort Tree),又称为二叉查找树。它或者是一棵空树,或者是具有下列性质的二叉树:
1)若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
2)若任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
3)任意节点的左、右子树也分别为二叉查找树。
二叉查找树是先对待查找的数据进行生成树,确保树的左分支的值小于右分支的值,然后在就行和每个节点的父节点比较大小,查找最适合的范围。 这个算法的查找效率很高,但是如果使用这种查找方法要首先创建树。
二叉查找树性质:对二叉查找树进行中序遍历,即可得到有序的数列。
不同形态的二叉查找树如下图所示: