线性查找(顺序查找)
顺序查找就是最直观的从头开始遍历序列,逐一比对,找到目标元素则返回索引
代码实现
public class LinearSearch {
public static void main(String[] args) {
int arr[] = {0,1,2,3,4,5,6,7,8,9};
int num=10;
int index = linearSearch(arr,num);
if(index==-1) {
System.out.printf("没有找到%d\n",num);
}else {
System.out.printf("%d在数组中的索引为%d\n",num,index);
}
}
private static int linearSearch(int []arr,int num) {
for(int i=0;i<arr.length;i++) {
if(arr[i]==num) {
return i;
}
}
return -1;
}
}
二分查找(折半查找)
在一个有序序列中,依次将目标数和有序序列中间的数进行对比,
- 如果目标数更大,表示目标数在右边,向右递归查找
- 如果目标数更小,表示目标数在左边,向左递归查找
- 如果两数相等,结束递归,返回索引
- 一直递归到最后,查找区间没有数据了,表示没有找到,退出递归
代码实现
public class BinarySearch {
public static void main(String[] args) {
int arr[]= {0,1,2,3,4,5,7,8,9,78};
int num=78;
int index = binarySearch(arr,num,0,arr.length-1);
if(index==-1) {
System.out.printf("没有找到%d\n",num);
}else {
System.out.printf("%d在数组中的索引为%d\n",num,index);
}
}
private static int binarySearch(int []arr,int num,int left,int right) {
if(left>right) {
return -1;
}
int mid = (left+right)/2;
if(arr[mid]>num) {
return binarySearch(arr,num,left,mid-1);
}
if(arr[mid]<num) {
return binarySearch(arr,num,mid+1,right);
}
return mid;
}
}
二分查找完善(找出所有的目标数)
public class BinarySearch {
public static void main(String[] args) {
int arr[]= {0,1,2,3,4,5,7,8,9,78,78,78,78,78,78,78,89};
int num=78;
List<Integer> indexList = binarySearch(arr,num,0,arr.length-1);
if(indexList.size()==0) {
System.out.printf("没有找到%d\n",num);
}else {
System.out.println(num+"在数组中的索引为"+indexList);
}
}
private static List<Integer> binarySearch(int []arr,int num,int left,int right) {
if(left>right) {
return new ArrayList<>(0);
}
int mid = (left+right)/2;
if(arr[mid]>num) {
return binarySearch(arr,num,left,mid-1);
}
if(arr[mid]<num) {
return binarySearch(arr,num,mid+1,right);
}
List list = new ArrayList<>();
list.add(mid);
int l=mid-1;
int r = mid+1;
while(l>=left&&arr[l]==num) {
list.add(l);
l--;
}
while(r<=right&&arr[r]==num) {
list.add(r);
r++;
}
return list;
}
}
插值查找
- 插值查找算法类似于二分查找,不同的是插值查找每次从自适应mid开始查找,是对二分查找的优化
- 将二分查找中求mid索引的公式改为:mid=(left+right)/2=left+(right-left)/2改为mid=left+(key-arr[left])*(right-left)/(arr[right]-arr[left]),mid就是插值索引
- 二分查找是每次都在区间的中间,而插值查找每次回根据序列中值的比例,映射到位置的比例从而自适应的往更靠近目标值的位置划分区间
代码实现
public class InsertSearch {
public static void main(String[] args) {
int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 11, 11, 13, 14, 15, 16, 17, 18, 19, 20 };
int num = 20;
List<Integer> indexList = insertSearch(arr, num, 0, arr.length - 1);
if (indexList.size() == 0) {
System.out.printf("%d不在这个数组中", num);
} else {
System.out.println(num + "在数组中的位置为" + indexList);
}
}
private static List<Integer> insertSearch(int arr[], int num, int left, int right) {
System.out.println("插值查找");
if (left > right) {
return new ArrayList<>(0);
}
int mid = left + (num - arr[left]) * (right - left) / (arr[right] - arr[left]);
if (arr[mid] > num) {
return insertSearch(arr, num, left, mid - 1);
}
if (arr[mid] < num) {
return insertSearch(arr, num, mid + 1, right);
}
List<Integer> list = new ArrayList<>();
list.add(mid);
int l = mid - 1;
int r = mid + 1;
while (l >= left && arr[l] == num) {
list.add(l);
l--;
}
while (r <= right && arr[r] == num) {
list.add(r);
r++;
}
return list;
}
}
对比
- 当查找的数靠数组边缘时,二分查找就会查找多次,而插值查找根据自适应找到插值索引,便能更快查找到目标数
- 对于数据量较大,关键字分布比较均匀的查找表,采用插值查找速度较快
- 在关键子分布不均匀的情况下,该方法不一定比折半查找好
斐波那契查找(黄金分割查找法)
- 黄金分割点是指用一点将一条线段分割成两部分,其中短比长等于长比全等于0.618,这个点即黄金分割点,这个比例称为黄金分割比
- 斐波那契数列{1,1,2,3,5,8,13,21,34,55…},前两个数字为1,往后每一个数都是前两个数的和,形成的数列即斐波那契数列,数列越往后,相邻两个数的比例无限趋近于黄金分割比
查找原理
斐波那契查找和二分查找、插值查找相似,区别在于改变了中间结点mid的位置,这里mid不在时通过中间或按比例插值得到,而是位于黄金分割点附近 ,即mid=left+F(k-1)-1
- 由斐波那契数列F(k)=F(k-1)+F(k-2)的性质,可以得到F[k]-1=(F[k-1]-1)+(F[k-2]-1)+1,即只要顺序表长度为F[k]-1则可以将该表分成长度为F[k-1]和F[k-2]的两部分,从而结点位置为mid=left+F[k-1]-1
- 但顺序表长度n不一定刚好等于F[k]-1,那么需要将原来的顺序表的长度增加到F[k]-1,步骤为先增加k值使得F[k]-1大于n,然后使n+1
代码实现(递归和非递归两种方式)
public class FibonacciSearch {
public static void main(String[] args) {
printArray(getFib(10));
int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 11, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 25, 25,
25, 255 };
int num = 25;
List<Integer> indexList = fibonacciSearch2(arr, num);
if (indexList.size() == 0) {
System.out.printf("%d不在这个数组中", num);
} else {
System.out.println(num + "在数组中的位置为" + indexList);
}
}
private static List<Integer> fibonacciSearch(int arr[], int key) {
int left = 0;
int right = arr.length - 1;
int k = 0;
int mid = 0;
int f[] = getFib(arr.length);
while (right > f[k] - 1) {
k++;
}
int temp[] = Arrays.copyOf(arr, f[k]);
for (int i = arr.length; i < temp.length; i++) {
temp[i] = arr[right];
}
List<Integer> indexList = new ArrayList();
while (left <= right) {
mid = left + f[k - 1] - 1;
if (key < temp[mid]) {
right = mid - 1;
k--;
} else if (key > temp[mid]) {
left = mid + 1;
k -= 2;
} else {
mid = mid < right ? mid : right;
int l = mid - 1;
int r = mid + 1;
indexList.add(mid);
while (l >= left && arr[l] == key) {
indexList.add(l);
l--;
}
while (r <= right && arr[r] == key) {
indexList.add(r);
r++;
}
break;
}
}
return indexList;
}
private static List<Integer> fibonacciSearch2(int arr[], int num) {
int f[] = getFib(arr.length);
int k = 0;
while (arr.length > f[k]) {
k++;
}
int temp[] = Arrays.copyOf(arr, f[k]);
for (int i = arr.length; i < temp.length; i++) {
temp[i] = arr[arr.length - 1];
}
return fibSearch(0, arr.length - 1, temp, f, k, num);
}
private static List<Integer> fibSearch(int left, int right, int temp[], int f[], int k, int num) {
if (left > right) {
return new ArrayList<>(0);
}
int mid = left + f[k - 1] - 1;
if (temp[mid] < num) {
return fibSearch(mid + 1, right, temp, f, k - 2, num);
}
if (temp[mid] > num) {
return fibSearch(left, mid - 1, temp, f, k - 1, num);
}
List<Integer> indexList = new ArrayList();
mid = mid > right ? right : mid;
int l = mid - 1;
int r = mid + 1;
indexList.add(mid);
while (l >= left && temp[l] == num) {
indexList.add(l);
l--;
}
while (r <= right && temp[r] == num) {
indexList.add(r);
r++;
}
return indexList;
}
private static int[] getFib(int length) {
if (length < 0) {
System.out.println("长度不能小于0");
}
int res[] = new int[length];
res[0] = 1;
if (length == 1) {
return res;
}
res[1] = 1;
if (length == 2) {
return res;
}
return fibonacciArray(res, 2);
}
private static int[] fibonacciArray(int res[], int t) {
if (t == res.length) {
return res;
}
res[t] = res[t - 2] + res[t - 1];
t++;
return fibonacciArray(res, t);
}
private static void printArray(int[] arr) {
for (int i : arr) {
System.out.print(i + " ");
}
System.out.println();
}
}
对比
斐波那契查找和折半查找的时间复杂度相同,而且有时执行次数可能会比折半查找更多,但是斐波那契每轮循环只有减法和赋值操作,而折半查找有除法操作,这可能是斐波那契查找效率略优于折半查找的原因
|