Java实现二分查找(遍历查找,递归查找)
1.简介
二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。
2.思路分析
- 设置一个指向中间元素下标的变量mid,mid=(left + right)/2
- 如果left 、right 是一个非常大的数那么就可能溢出
- 所以(left+right)/2 = left - left/2 + right/2 = left+(right-left)/2
- 让要查找的元素和数组mid下标的元素进行比较
- 如果查找的元素大于arr[mid],则left变为mid后面一个元素的下标
- 如果查找的元素小于arr[mid],则right变为mid前一个元素的下标
- 如果查找的元素等于arr[mid],则mid就是要查找元素所在的位置
- 当left > rigth时,说明元素不在该数组中
3.图解
3.1 算出mid的位置,和查找的数进行比较
3.2 比较后,移动mid
因为num>arr[mid],那么就要从mid后面开始检索
就直接找到了
3.3 如果我们寻找的数是一个不存在的数?
num<arr[mid],mid向左边检索
继续向左边检索
这个时候right = mid - 1,因为mid是0 所以right= -1,left> right ,那么循环就结束了
4.代码分析
4.1 使用遍历的方式解决二分查找
package com.sky.find;
/**
* 二分查找非递归版
* @author 尹稳健~
* @version 1.0
* @time 2022/9/23
*/
public class BinarySearch {
public static void main(String[] args) {
//进行二分查找的数组必须是有序
int[] arr = {-1, 0, 2, 11, 30};
int result = binarySearch(arr, 11);
if(result == -1) {
System.out.println("未找到该元素");
}else {
System.out.println("该元素的下标是:" + result);
}
}
/**
* 设置一个指向中间元素下标的变量mid,mid=(left + right)/2
* 让要查找的元素和数组mid下标的元素进行比较
* 如果查找的元素大于arr[mid],则left变为mid后面一个元素的下标
* 如果查找的元素小于arr[mid],则right变为mid前一个元素的下标
* 如果查找的元素等于arr[mid],则mid就是要查找元素所在的位置
* 当left > rigth时,说明元素不在该数组中
* @param arr
* @param num
* @return
*/
public static int binarySearch(int[] arr,int num){
int left = 0;
int right = arr.length - 1;
while (left<=right){
/**
* 这里为什么需要 int mid = left+(right-left)/2;?
* 原因是如果left 、right 是一个非常大的数那么就可能溢出,所以
* (left+right)/2 = left - left/2 + right/2 = left+(right-left)/2
*/
int mid = left+(right-left)/2;
// 如果寻找的数大于mid那么就从mid的后面开始寻找
if (arr[mid] < num){
left = mid+1;
// 如果寻找的数小于mid那么就从mid的前面开始寻找
}else if (arr[mid] > num){
right = mid -1;
}else{
return mid;
}
}
return -1;
}
}
4.2 使用递归方式解决二分查找
package com.sky.find;
/**
* 使用递归实现二分查找
* @author 尹稳健~
* @version 1.0
* @time 2022/9/23
*/
public class BinarySearch2 {
public static void main(String[] args) {
//进行二分查找的数组必须是有序
int[] arr = {-1, 0, 2, 11, 30};
int left = 0;
int right = arr.length - 1;
int result = binarySearch(arr, left,right,11);
if(result == -1) {
System.out.println("未找到该元素");
}else {
System.out.println("该元素的下标是:" + result);
}
}
public static int binarySearch(int[] arr , int left ,int right,int num){
int mid = left+(right-left)/2;
while (left<=right){
if (arr[mid] < num){
return binarySearch(arr,mid + 1,right,num);
}else if (arr[mid] > num){
return binarySearch(arr,left,mid - 1,num);
}else{
return mid;
}
}
return -1;
}
}
5.思考
突然想到如果一个数中又多个相同的值那么,这种方式就只能找到一个位置,所以我们需要在这个代码的基础之上进行优化。
5.1 思路分析
有可能要查找的元素有多个。这时就需要在找到一个元素后,不要立即返回,而是扫描其左边和右边的元素,将所有相同元素的下标保存到一个数组中,然后一起返回
5.2代码实现
import java.util.ArrayList;
import java.util.List;
/**
* 二分查找非递归版
* @author 尹稳健~
* @version 1.0
* @time 2022/9/23
*/
public class BinarySearch3 {
public static void main(String[] args) {
//进行二分查找的数组必须是有序
int[] arr = {-1, 0, 11, 11, 11, 11, 30};
List<Integer> result = binarySearch(arr, 11);
if(result.size() ==0) {
System.out.println("未找到该元素");
}else {
System.out.println("该元素的下标是:" + result);
}
}
/**
* 设置一个指向中间元素下标的变量mid,mid=(left + right)/2
* 让要查找的元素和数组mid下标的元素进行比较
* 如果查找的元素大于arr[mid],则left变为mid后面一个元素的下标
* 如果查找的元素小于arr[mid],则right变为mid前一个元素的下标
* 如果查找的元素等于arr[mid],则mid就是要查找元素所在的位置
* 当left > rigth时,说明元素不在该数组中
* @param arr
* @param num
* @return
*/
public static List<Integer> binarySearch(int[] arr, int num){
List<Integer> result = new ArrayList<>();
int left = 0;
int right = arr.length - 1;
while (left<=right){
/**
* 这里为什么需要 int mid = left+(right-left)/2;?
* 原因是如果left 、right 是一个非常大的数那么就可能溢出,所以
* (left+right)/2 = left - left/2 + right/2 = left+(right-left)/2
*/
int mid = left+(right-left)/2;
// 如果寻找的数大于mid那么就从mid的后面开始寻找
if (arr[mid] < num){
left = mid+1;
// 如果寻找的数小于mid那么就从mid的前面开始寻找
}else if (arr[mid] > num){
right = mid -1;
// 如果找到了,不要立刻返回查找左右两边是否还有相同的元素
}else{
result.add(mid);
int leftIndex = mid - 1;
while (leftIndex > 0 && arr[leftIndex] == num){
result.add(leftIndex);
leftIndex--;
}
int rightIndex = mid +1;
while (rightIndex < right && arr[rightIndex] == num){
result.add(rightIndex);
rightIndex++;
}
return result;
}
}
return result;
}
}
6.插值查找
6.1 简介
插值查找,有序表的一种查找方式。插值查找是根据查找关键字与查找表中最大最小记录关键字比较后的查找方法。插值查找基于二分查找,将查找点的选择改进为自适应选择,提高查找效率。
6.2 算法公式
// 插值查找算法:
int mid = left + (right – left) * (findVal – arr[left]) / (arr[right] – arr[left])
6.3代码实现
package com.sky.find;
import java.util.ArrayList;
import java.util.List;
/**
* 插值查找
* @author 尹稳健~
* @version 1.0
* @time 2022/9/23
*/
public class BinarySearch4 {
public static void main(String[] args) {
//进行二分查找的数组必须是有序
int[] arr = {-1, 0, 11, 11, 11, 11, 30};
List<Integer> result = binarySearch(arr, 11);
if(result.size() ==0) {
System.out.println("未找到该元素");
}else {
System.out.println("该元素的下标是:" + result);
}
}
/**
* 设置一个指向中间元素下标的变量mid,
* int mid = left + (right – left) * (findVal – arr[left]) / (arr[right] – arr[left])
* 让要查找的元素和数组mid下标的元素进行比较
* 如果查找的元素大于arr[mid],则left变为mid后面一个元素的下标
* 如果查找的元素小于arr[mid],则right变为mid前一个元素的下标
* 如果查找的元素等于arr[mid],则mid就是要查找元素所在的位置
* 当left > rigth时,说明元素不在该数组中
* @param arr
* @param num
* @return
*/
public static List<Integer> binarySearch(int[] arr, int num){
List<Integer> result = new ArrayList<>();
int left = 0;
int right = arr.length - 1;
while (left<=right){
int mid = left + (right - left) * (num - arr[left]) / (arr[right]- arr[left]);
// 如果寻找的数大于mid那么就从mid的后面开始寻找
if (arr[mid] < num){
left = mid+1;
// 如果寻找的数小于mid那么就从mid的前面开始寻找
}else if (arr[mid] > num){
right = mid -1;
// 如果找到了,不要立刻返回查找左右两边是否还有相同的元素
}else{
result.add(mid);
int leftIndex = mid - 1;
while (leftIndex > 0 && arr[leftIndex] == num){
result.add(leftIndex);
leftIndex--;
}
int rightIndex = mid +1;
while (rightIndex < right && arr[rightIndex] == num){
result.add(rightIndex);
rightIndex++;
}
return result;
}
}
return result;
}
}
7.总结
插值查找在二分查找进行优化,记住公式就行