顺序查找
线性查找指按一定的顺序检查数组中每一个元素,直到找到所要寻找的特定值为止。
/**
* @ClassName SeqSearch
* @author: shouanzh
* @Description 线性查找
* @date 2022/5/11 20:02
*/
public class SeqSearch {
public static void main(String[] args) {
int[] array = new int[]{10, 3, -1, 9, 20, -3};
int i = seqSearch(array, 9);
if (i == -1) {
System.out.println("没有该值");
} else {
System.out.println(array[i]);
}
}
/**
* 线性查找
* 线性查找是逐一比对,发现有相同值,就返回下标
*
* 这里我们实现的线性查找是找到一个满足条件的值,就返回
* 如果存在多个可以遍历全部,可以返回集合
* @param array
* @param value
* @return 返回下标
*/
public static int seqSearch(int[] array, int value) {
for (int i = 0; i < array.length; i++) {
if (array[i] == value) {
return i;
}
}
return -1;
}
}
二分查找(递归和非递归实现)
二分查找是一种在有序数组中查找某一特定元素的搜索算法。搜索过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束;如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一步骤数组为空,则代表找不到。这种搜索算法每一次比较都使搜索范围缩小一半。
代码实现
/**
* @ClassName BinarySearch
* @author: shouanzh
* @Description 二分查找(数组必须有序)
*
* 二分查找法只适用于从有序的数列中进行查找(比如数字和字母等),将数列排序后再进行查找
*
* 二分查找法的运行时间为对数时间O(㏒₂n) ,即查找到需要的目标位置最多只需要㏒₂n步,
* 假设从[0,99]的队列(100个数,即n=100)中寻到目标数30,则需要查找步数为㏒₂100 ,
* 即最多需要查找7次( 2^6 < 100 < 2^7)
*
* @date 2022/5/11 17:34
*/
public class BinarySearch {
public static void main(String[] args) {
int[] array = new int[]{1, 3, 5, 9, 20, 100, 100, 256, 567};
// 递归方式
List<Integer> resultIndexList = binarySearchAll(array, 100);
System.out.println(resultIndexList); // [5, 6]
// 非递归方式
int index = binarySearch(array, 256);
System.out.println(index); // 7
}
/**
* 二分查找(递归方式)
* @param array 查找数据
* @param left 左下标
* @param right 右下标
* @param findValue 要查找的值
* @return 找到就返回下标,没有找到返回-1
*/
public static int binarySearch(int[] array, int left, int right, int findValue) {
// 当 left > right 时,说明递归整个数组,但是没有找到要查找的值
if (left > right) {
return -1;
}
// 中间索引
int mid = (left + right) / 2;
// 中间值
int midValue = array[mid];
// 要查找的值 > 中间值 则向右递归
if (findValue > midValue) {
return binarySearch(array, mid + 1, right, findValue);
} else if (findValue < midValue) {
// 要查找的值 < 中间值 则向左递归
return binarySearch(array, left, mid - 1, findValue);
} else {
return mid;
}
}
/**
* 二分查找(非递归方式)
* @param array 待查找的数组(升序)
* @param findValue 要查找的值
* @return 找到就返回下标,没有找到返回-1
*/
public static int binarySearch(int[] array, int findValue) {
if (array == null) {
return -1;
}
int left = 0;
int right = array.length - 1;
while (left <= right) { // 可以继续查找
// 中间索引
int mid = (left + right) / 2;
// 找到要查找的值 返回索引
if (array[mid] == findValue) {
return mid;
} else if (array[mid] > findValue) {
right = mid - 1; // 向左边查询
} else {
left = mid +1; // 向右边查询
}
}
// 没有找到
return -1;
}
/**
* 当一个有序数组中有多个相同的值时,将所有的值都查到
* 思路:{1, 3, 5, 9, 20, 100, 100, 100, 567} 比如100
* 1.在找到mid 索引值,不要马上返回
* 2.向mid索引值的左边扫描,将所有满足要查找的值(100)的元素的下标,加到集合ArrayList
* 3.向mid索引值的右边扫描,将所有满足要查找的值(100)的元素的下标,加到集合ArrayList
* 4.将ArrayList返回
* @param array
* @param findValue
* @return
*/
public static List<Integer> binarySearchAll(int[] array, int findValue) {
// findValue的索引值
int index = binarySearch(array, 0, array.length - 1, findValue);
List<Integer> result = new ArrayList<>();
if (index == -1) {
return result;
}
int left = index - 1;
int right = index + 1;
while (left >= 0 && array[left] == findValue) {
left--;
}
while (right < array.length && array[right] == findValue) {
right++;
}
for (int i = left + 1; i < right; i++) {
result.add(i);
}
return result;
}
}
插值查找
插值查找算法类似于二分查找,不同的是插值查找每次从自适应mid处开始查找。
m i d = l e f t + r i g h t 2 = l e f t + 1 2 ( r i g h t − l e f t ) mid = \frac{left + right}{2} = left + \frac{1}{2}(right-left) mid=2left+right=left+21(right−left)
改为
m i d = l e f t + k e y − a r r [ l e f t ] a r r [ r i g h t ] − a r r y [ l e f t ] ( r i g h t − l e f t ) mid = left + \frac{key-arr[left]}{arr[right]-arry[left]}(right-left) mid=left+arr[right]−arry[left]key−arr[left](right−left)
代码实现
/**
* @ClassName InsertValueSearch
* @author: shouanzh
* @Description 插值查找
* @date 2022/5/11 22:16
*/
public class InsertValueSearch {
public static void main(String[] args) {
int[] array = new int[]{1, 3, 5, 7, 9, 11, 13, 15, 17};
int resultIndex = insertValueSearch(array, 0, array.length - 1, 15);
System.out.println(resultIndex); // 7
}
/**
* 插值查找
* @param arr 已排序的数组
* @param left 开始位置,如:0
* @param right 结束位置,如:array.length-1
* @param findValue 要查找的值
* @return 找到就返回下标,没有找到返回-1
*/
public static int insertValueSearch(int[] arr, int left, int right, int findValue) {
// 因为传入的数组是有序的,所以,可以限制搜索值
// 注意:findVal < arr[left] 和 findVal > arr[right] 必须需要, 否则我们得到的 mid 可能越界
if (left > right || findValue < arr[left] || findValue > arr[right]) {
return -1;
}
// 保证arr[right]-arr[left]不为0,否则会发生除0异常
if(arr[left] == arr[right]) {
if(findValue == arr[left]) {
return left;
}
return -1;
}
// 求出mid, 自适应
int mid = left + (right - left) * (findValue - arr[left]) / (arr[right] - arr[left]);
int midValue = arr[mid];
if (findValue > midValue) {
// 向右递归
return insertValueSearch(arr, mid + 1, right, findValue);
} else if (findValue < midValue) {
// 向左递归
return insertValueSearch(arr, left, mid - 1, findValue);
} else {
return mid;
}
}
}
注意事项
- 对于数据量较大,关键字分布比较均匀的查找表来说,采用插值查找,速度较快。
- 关键字分布不均匀的情况下,该方法不一定比折半查找要好。
- 插值公式注意除0异常。
🤯🧐