1 顺序查找
就是简单的从左往右或者从右往左遍历,待查找的数组并不需要有序,查到返回true
,否则返回false
。
时间复杂度:
- 最优时间复杂度是
O(1)
,需要查找的元素就是第一个或者是最后一个 - 最差时间复杂度是
O(n)
,待查找的在查找顺序的最优一个位置 - 平均时间复杂度是
O(n)
。
public class SequenseSearch {
public boolean search(int[]nums,int target){
if (nums.length==0)
return false;
for (int i = 0; i < nums.length; i++) {
if (target==nums[i])
return true;
}
return false;
}
public static void main(String[] args) {
int[] nums = new int[20];
for (int i = 0; i < 20; i++) {
nums[i] = i + 1;
}
SequenseSearch binarySearch = new SequenseSearch();
for (int i = 1; i < 25; i++) {
System.out.println(i+" "+binarySearch.search(nums, i));
}
}
}
2 二分查找
二分查找可以将查找的时间复杂度降低到O(logn)
,但是前提是待查找的数组必须是有序的数组,当数组有序时,可以使用三个指针,一个左指针和一个右指针以及一个中间指针如果待查找的元素小于中间元素,则将查找范围缩小到数组的前半部分,如果待查找元素大于中间元素,则将查找范围缩小数组后半部分。
left=0
,right=n-1
,mid=(left+right)/2
。
为了防止mid
越界,使用right-(right-left)/2
或者(right-left)/2+left
进行缓冲。
时间复杂度:O(logn)
。
public class BinarySearch {
public boolean search(int[] nums, int target) {
if (nums.length == 0)
return false;
int l=0,r=nums.length-1;
while (l<=r){
int mid = r-(r-l)/2;
if (nums[mid]==target)
return true;
else if(nums[mid] > target){
r=mid-1;
}else{
l=mid+1;
}
}
return false;
}
public static void main(String[] args) {
int[] nums = new int[20];
for (int i = 0; i < 20; i++) {
nums[i] = i + 1;
}
BinarySearch binarySearch = new BinarySearch();
for (int i = 1; i < 25; i++) {
System.out.println(i+" "+binarySearch.search(nums, i));
}
}
}
3 插值查找
插值查找是二分查找的一种优化,将mid
指针的值进行了优化,当待查询数组(有序)的值分布较均匀时,使用插值查找效率较高,但是如果分布不均匀,效率较低。中间指针的公式如下:
mid=left+(target-a[left])/(a[right]-a[left])*(right-left)
。
其中,target
表示待查找的值,a
表示待查询数组。
public class InsertSearch {
public boolean search(int[]nums,int target){
int n=nums.length;
if (n==0)
return false;
return insertSearch(nums,target,0,n-1);
}
public boolean insertSearch(int[]nums,int target,int l,int h){
//终止递归的条件
if (l>h||target>nums[h]||target<nums[l])
return false;
//当前逻辑
int mid = l + (target-nums[l])/((nums[h]-nums[l])*(h-l));
if (nums[mid]==target)
return true;
else if (nums[mid]>target){ //向下递归
return insertSearch(nums,target,l,mid-1);
}
else //if (nums[mid]<target) //向下递归
return insertSearch(nums,target,mid+1,h);
}
public static void main(String[] args) {
int[] nums = new int[20];
for (int i = 0; i < 20; i++) {
nums[i] = i + 1;
}
InsertSearch insertSearch = new InsertSearch();
for (int i = 1; i < 25; i++) {
System.out.println(i+" "+insertSearch.search(nums, i));
}
}
}
4 斐波那契查找
斐波那契查找也是对二分查找的一种改进,改进思路是基于黄金分割点(0.618:1或者1.618:1),我们发现斐波那契数列从第3个数开始,前一个数与后一个数的商随着n
的增大,越来越接近于0.618
。
[借助一张网络图片]
我们需要先维护一个斐波那契数列的数组,每次指针移动都根据黄金分割比例进行分割,不再是简单的对半分。
target>a[mid]
:k-=1,left=mid+1
target<a[mid]
:k-=2,right=mid-1
时间复杂度分析:
- 最坏情况下的时间复杂度是
O(logn)
, - 且平均时间复杂度也是
O(logn)
斐波那契数列一般计算到第20
个就差不多,因为第20
个数已经很大了。
public class FabonacciSearch {
public boolean search(int []nums,int target){
int n=nums.length;
if (n==0)
return false;
int []f=new int[20];
Fabonacci(f,20);
int k=0;
while (n>f[k]-1)
++k;
int []nums2=new int[f[k]];
System.arraycopy(nums,0,nums2,0,n);
for (int i = n; i < f[k]; i++) {
nums2[i]=nums[n-1];
}
int l=0,r=f[k]-1;
while (l<=r){
int mid = l+f[k]-1;
if(nums2[mid]>target){
r=mid-1;
k-=2;
}else if(nums2[mid]<target){
l=mid+1;
k-=1;
}else{// if (nums2[mid]==target)
if (mid>=n){//说明在加长的数组里面,即最后一个元素
return true;
}else{//说明mid小于数组长度,直接返回mid的值
return true;
}
}
}
return false;
}
public void Fabonacci(int []f,int size){
f[0]=1;
f[1]=1;
for (int i = 2; i < size; i++) {
f[i]=f[i-1]+f[i-2];
}
}
public static void main(String[] args) {
int[] nums = new int[20];
for (int i = 0; i < 20; i++) {
nums[i] = i + 1;
}
FabonacciSearch fabonacciSearch = new FabonacciSearch();
for (int i = 1; i < 25; i++) {
System.out.println(i+" "+fabonacciSearch.search(nums, i));
}
}
}