顺序(线性查找)(可查有序也可查无序)
顺序查找,又称为线性查找,主要用于在线性表中进行查找。
顺序查找 就是:遍历序列 逐一比对 找到就返回即可
代码实现:
package com.algorithm.search;
import java.util.ArrayList;
import java.util.List;
public class SeqSearch {
public static void main(String[] args) {
int[] arr = {1, 3, 2, 4, 6, 9, 8, 7, 1};
seqSearch2(arr, 1);
int index = seqSearch(arr, 1);
if (index == -1) {
System.out.println("没找到您要找的数据");
} else {
System.out.println("您要找的数据在数组中的下标为:" + index);
}
}
//顺序(线性)查找:逐一比对 发现有相同值就返回下标
//只有一个查找项
public static int seqSearch(int[] arr, int value) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] == value) {
return i;
}
}
return -1;
}
//有多个相同的查找项
public static List<Integer> seqSearch2(int[] arr, int value) {
ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < arr.length; i++) {
if (arr[i] == value) {
list.add(i);
}
}
for (Integer index : list) {
System.out.println("您要找的数据在数组中的下标为:" + index);
}
return null;
}
}
二分(折半)查找(只可查有序)
二分查找前提是表是按递增或递减顺序的规范表。
二分查找的思路分析
- 首先确定该数组的中间的下标
mid = (left + right) / 2 - 然后让需要查找的数 findVal 和 arr[mid] 比较
- 1 findVal > arr[mid] , 说明你要查找的数在mid 的右边, 因此需要递归的向右查找
2.2 findVal < arr[mid], 说明你要查找的数在mid 的左边, 因此需要递归的向左查找
2.3 findVal == arr[mid] 说明找到,就返回
什么时候我们需要结束递归?
- 找到就结束递归
- 递归完整个数组,仍然没有找到findVal ,也需要结束递归 当 left > right 就需要退出
代码实现:
package com.algorithm.search;
import java.util.ArrayList;
public class BinarySearch {
// 二分查找使用前提:数组必须是有序的
public static void main(String[] args) {
/* int[] arr = {1, 8, 10, 89, 1000, 1234};
int index = binarySearch(arr, 0, arr.length-1, 88);
if (index == -1) {
System.out.println("没找到您要找的数据");
} else {
System.out.println("您要找的数据在数组中的位置是:" + index);
}*/
int[] arr = {1, 8, 10, 89, 1000,1000,1000, 1234};
ArrayList<Integer> res = binarySearch2(arr, 0, arr.length-1, 1000);
System.out.println("res="+res);
}
// 只有一个查找项
public static int binarySearch(int[] arr, int left, int right, int findVal) {
if (left > right) {//递归结束的条件
return -1;
}
int mid = (left + right) / 2;
int midVal = arr[mid];
if (findVal > midVal) {
return binarySearch(arr, mid + 1, right, findVal);//向右递归
} else if (findVal < midVal) {
return binarySearch(arr, left, mid - 1, findVal);//向左递归
} else {
return mid;
}
}
// 有多个相同的查找项
public static ArrayList<Integer> binarySearch2(int[] arr, int left, int right, int findVal) {
if (left > right) {//递归结束的条件
return new ArrayList<Integer>();
}
int mid = (left + right) / 2;
int midVal = arr[mid];
if (findVal > midVal) {
return binarySearch2(arr, mid + 1, right, findVal);//向右递归
} else if (findVal < midVal) {
return binarySearch2(arr, left, mid - 1, findVal);//向左递归
} else {
/*思路分析:
1、当查找到mid索引值后 向左右两边扫描看是否有多个相同查找值
2、向mid索引值的左边扫描,若存在满足findVal的值则将其加入集合中
3、向mid索引值的右边扫描,若存在满足findVal的值则将其加入集合中
4、不要忘了把mid索引处的数据也加入集合中 最后返回集合即可
*/
ArrayList<Integer> list = new ArrayList<>();
int temp = mid - 1;
while (true) {
if (temp < 0 || arr[temp] != findVal) {
break;
} else {
list.add(temp);
temp--;//temp左移继续扫描
}
}
list.add(mid);
temp = mid + 1;
while (true) {
if (temp > arr.length - 1 || arr[temp] != findVal) {
break;
} else {
list.add(temp);
temp++;//temp右移继续扫描
}
}
return list;
}
}
}
插值查找(只可查有序)
插值查找原理介绍:
插值查找算法类似于二分查找,不同的是插值查找每次从自适应mid处开始查找。
将折半查找中的求mid 索引的公式 , low 表示左边索引left, high表示右边索引right.key 就是前面我们讲的 findVal
对应的代码公式:int mid = left + (right – left) * (findVal – arr[left]) / (arr[right] – arr[left])
代码实现:
package com.algorithm.search;
public class InsertValueSearch {
public static void main(String[] args) {
/* int[] arr = new int[100];
for (int i = 0; i < 100; i++) {
arr[i] = i + 1;
}*/
int[] arr = {1, 8, 10, 89, 1000,1000,1000, 1234};
int index = insertValSearch(arr, 0, arr.length-1, 1234);
System.out.println("index="+index);
}
//插值查找 要求数组有序
public static int insertValSearch(int[] arr, int left, int right, int findVal) {
//findVal < arr[0] || findVal > arr[arr.length - 1] 必须有 防止mid越界
if (left > right || findVal < arr[0] || findVal > arr[arr.length - 1]) {
return -1;
}
int mid = left + (right - left) * (findVal - arr[left]) / (arr[right] - arr[left]) ;
int midVal = arr[mid];
if (findVal > midVal) {
return insertValSearch(arr, mid + 1, right, findVal);//向右递归
} else if (findVal < midVal) {
return insertValSearch(arr, left, mid - 1, findVal);//向左递归
} else {
return mid;
}
}
}
插值查找注意事项:
1、对于数据量较大,关键字分布比较均匀的查找表来说,采用插值查找, 速度较快.
2、关键字分布不均匀的情况下,该方法不一定比折半查找要好。
斐波那契(黄金分割法)查找(只可查有序)
利用斐波那契数列配合找到黄金分割点 然后再查找
代码实现:
package com.algorithm.search;
import java.util.Arrays;
public class FibonacciSearch {
public static int maxSzie = 20;
public static void main(String[] args) {
int[] arr = {1, 8, 10, 89, 1000, 1234};
int index= fibSearch(arr,1234);
System.out.println("您要找的数据的下标是:"+index);
}
//获得斐波那契数列
//非递归方式
public static int[] fib() {
int[] fib = new int[maxSzie];
fib[0] = 1;
fib[1] = 1;
for (int i = 2; i < maxSzie; i++) {
fib[i] = fib[i - 1] + fib[i-2];
}
return fib;
}
public static int fibSearch(int[] arr, int value) {
int low = 0;
int high = arr.length - 1;
int k = 0;//表示斐波那契分割数值的下标
int mid = 0;
int fib[] = fib();//获取到斐波那契数列
//获取到斐波那契额分割数值的下标
while (arr.length > fib[k] - 1) {
k++;
}//退出循环时说明已经找到斐波那契分割的数值即fib[k] fib[k]的值表示 要使用此方法所需的 元素的个数
//因为fib[k]可能大于arr的长度 所以 这里构建一个新数组 补齐所需的元素
// copyOf(oringinal, int newlength) oringinal:原数组 newlength:复制数组的长度
int[] temp = Arrays.copyOf(arr, fib[k]);//不足的地方自动填充0
//此方法实际应该在不足的地方填充arr的最后一个数据的值
for (int i = high + 1; i < fib[k]; i++) {
temp[i] = arr[high];
}
//开始查找(非递归)
while (low <= high) {
mid = low + fib[k - 1] - 1;
if (value < temp[mid]) {//向数组左边查找
high = mid - 1;
//fib[k]=fib[k-1]+fib[k-2] 因为要找的值小于temp[mid] 所以去fib[k-1]这部分继续找
//所以k-- 即 下次循环mid=low+fib[k-1-1]-1
k--;
} else if (value > temp[mid]) {//向数组右边查找
low = mid + 1;
//fib[k]=fib[k-1]+fib[k-2] 因为要找的值大于于temp[mid] 所以去fib[k-2]这部分继续找
//所以k-=2 即 下次循环mid=low+fib[k-2-1]-1
k -= 2;
} else {
//找到的话需要确定返回的下标
if (mid <= high) return mid;
else return high;
}
}
return -1;
}
}