顺序查找
使用for循环来依次遍历,实现查找
二分查找
二分查找,又称为折半查找,查找的效率很高
算法思想
给定一串有序的序列,以这串序列的中间元素为基准,
若要查找元素 > 中间位置元素,说明要查找元素在序列前半部分
若要查找元素 < 中间位置元素,说明要查找元素在序列后半部分
这样使得每次查找的范围都将减少一半
算法优缺点
优点:查找的次数较少,查找速度快,平均性能好
缺点:要求查找的序列必须为有序序列
适用序列
有序序列,且查找频繁,变动较少的序列
时间复杂度
最坏的情况时间复杂度为O(log2 N)
最好的情况时间复杂度为O(1)
空间复杂度
非递归实现的空间复杂度为O(1)
递归实现的空间复杂度为O(log2 N)
代码实现
- 递归实现
package checkHalf;
/**
* @author LXY
* @email 403824215@qq.com
* @date 2018/8/15 10:37
*
* 递归版本二分查找
*/
/**
* arr[]为查找的范围数组
* key为要查找元素
* start为查找的起点
* end为查找的终点
*/
public class recursionBinarySearch {
public static int recursionBinarySearch(int[] arr,int key,int start,int end)
{
if(key < arr[start] || key > arr[end - 1] || start > end)
{
return -1;
}
if(arr[start] == key)
{
return start;
}
int mid = start + (end - start) / 2;
if(arr[mid] > key)
{
//说明key在前半部分
return recursionBinarySearch(arr,key,start,mid - 1);
}
else if(arr[mid] < key)
{
//说明key在前半部分
return recursionBinarySearch(arr,key,mid + 1,end);
}
else
{
return mid;
}
}
public static void main(String[] args) {
int[] arr = {1,2,4,5,6,7,8,9};
System.out.println(recursionBinarySearch(arr,9,0,arr.length));
}
}
- 非递归实现
package checkHalf;
/**
* @author LXY
* @email 403824215@qq.com
* @date 2018/8/15 10:53
*
* 非递归版本二分查找
*/
public class commonBinarySearch {
//可以没有start和end
public static int commonBinarySearch(int[] arr,int key)
{
int start = 0;
int end = arr.length - 1;
int mid = 0;
if(arr[start] == key)
{
return start;
}
if(arr[end] == key)
{
return end;
}
while (start < end)
{
mid = start + (end - start) / 2;
if(key < arr[mid])
{
end = mid - 1;
}
else if(key > arr[mid])
{
start = mid + 1;
}
else
{
return mid;
}
}
return -1;
}
public static void main(String[] args) {
int[] arr = {1,2,4,5,6,7,8,9};
System.out.println(commonBinarySearch(arr,0));
}
}
插值查找
差值查找是二分查找的改进版本
算法思想
上面介绍了二分查找的算法思想,关键在于每次都将查找范围缩小一半,但是这种二分查找的算法有些过于死板,查找有些过于被动
比如:如果在1~1000中要查找5的位置,要是按照二分查找的方式,那么要要查找的次数会很多,那么大多数人的第一反映一定是从开头开始查找,这就是差值查找的思想,不是根据中间位置来分区,而是根据离所求值的距离来进行搜索
对于二分查找:mid=(start+end)/2, 即mid=start+1/2*(end-start);
对于插值查找:mid=start+(key-a[start])/(arr[end]-arr[start])*(end-start);
算法优缺点
- 优点:对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找的性能比折半查找要好很多
- 缺点:要求查找的序列必须为有序序列,且数组中元素要分布均匀
适用序列
有序序列,且查找频繁,变动较少的序列
时间复杂度
时间复杂度为O(log2 (log2 N))
代码实现
代码与二分查找类似,不同在于对于分界点的不同
- 递归实现
package chazhi;
/**
* @author LXY
* @email 403824215@qq.com
* @date 2018/8/15 11:37
*
* 递归版本插值查找
*/
public class InsertValueSearch {
public static int insertValueSearch(int[] arr,int key,int start,int end)
{
if(key < arr[start] || key > arr[end] || start > end)
{
return -1;
}
if(arr[start] == key)
{
return start;
}
if(arr[end] == key)
{
return end;
}
int mid = start+(end-start)*((key-arr[start])/(arr[end]-arr[start]));
if(arr[mid] > key)
{
//说明key在前半部分
return insertValueSearch(arr,key,start,mid - 1);
}
else if(arr[mid] < key)
{
//说明key在前半部分
return insertValueSearch(arr,key,mid + 1,end);
}
else
{
return mid;
}
}
public static void main(String[] args) {
int[] arr = {1,2,4,5,6,7,8,9};
System.out.println(insertValueSearch(arr,10,0,arr.length - 1));
}
}
- 非递归实现
package chazhi;
/**
* @author LXY
* @email 403824215@qq.com
* @date 2018/8/15 10:53
*
* 非递归版本插值查找
*/
public class commonInsertValueSearch {
//可以没有start和end
public static int commonInsertValueSearch(int[] arr,int key)
{
int start = 0;
int end = arr.length - 1;
int mid = 0;
if(arr[start] == key)
{
return start;
}
if(arr[end] == key)
{
return end;
}
while (start < end)
{
mid = start+(end-start)*((key-arr[start])/(arr[end]-arr[start]));
if(key < arr[mid])
{
end = mid - 1;
}
else if(key > arr[mid])
{
start = mid + 1;
}
else
{
return mid;
}
}
return -1;
}
public static void main(String[] args) {
int[] arr = {1,2,4,5,6,7,8,9};
System.out.println(commonInsertValueSearch(arr,3));
}
}
斐波那契查找
斐波那契查找是二分查找的改进版本
算法思想
斐波那契查找是根据斐波那契序列的特点对有序表进行分割的。他要求开始表中记录的个数为某个斐波那契数小1,及n=F(k)-1;开始将k值与第F(k-1)位置的记录进行比较(即mid=low+F(k-1)-1),比较结果也分为三种
- k = mid,mid位置的元素即为所求
- k > mid,low=mid+1,k-=2;
说明:low=mid+1说明待查找的元素在[mid+1,high]范围内,k-=2 说明范围[mid+1,high]内的元素个数为n-(F(k-1))= Fk-1-F(k-1)=Fk-F(k-1)-1=F(k-2)-1个,所以可以递归的应用斐波那契查找。 - k < mid,high=mid-1,k-=1。
说明:low=mid+1说明待查找的元素在[low,mid-1]范围内,k-=1 说明范围[low,mid-1]内的元素个数为F(k-1)-1个,所以可以递归 的应用斐波那契查找。
时间复杂度
最坏情况的时间复杂度为O(log2 N)
期望的时间复杂度也为O(log2 N)
代码实现
package fibonacci;
/**
* @author LXY
* @email 403824215@qq.com
* @date 2018/8/15 19:11
*/
public class FibonacciSearch {
static int FibonacciSearch(int[] a, int n, int key) {
int[] F = {0, 1, 1, 2, 3, 5, 8, 13, 21, 34};
int low = 1;
int high = n;
int k = 0;
int mid = 0;
while (n > F[k] - 1) /* 计算n位于斐波那契数列的位置 */
k++;
while (low <= high) {
mid = low + F[k - 1] - 1;
if (key < a[mid]) {
high = mid - 1;
k = k - 1;
} else if (key > a[mid]) {
low = mid + 1;
k = k - 2;
} else {
if (mid <= n)
{
return mid;
}
else
{
return n;
}
}
}
return -1;
}
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9};
System.out.println(FibonacciSearch(arr,arr.length,5));
System.out.println(FibonacciSearch(arr,arr.length,1));
System.out.println(FibonacciSearch(arr,arr.length,9));
}
}