Java和c++中二分法查找数组元素的实现机制
在数据结构和算法中,我们会见到二分法查找数组元素这个经典的算法。事实上,二分法也称为折半查找法。该算法的主要机制是:
**(1)**先将数组元素从小到大排列(或者从大到小排列,下面算法是基于小到大排序)
**(2)**有一维数组arr1D[len],len为数组的长度,首先定义int low =0,int high = len;确定该数组区间的中间位置int mid= (low+high)/2,将查找的值key与arr1D[mid]比较。若相等,查找成功返回此位置的mid;否则确定新的查找区域,继续新二分查找。新的区域可用如下的算法进行:
在low<=high时开始二分寻找, 当 arr1D[mid]>key时,由数组的有序性可知arr1D[i,i+1,……,high]>key,所以key的值只能在新的区间arr1D[low,……,mid-1]中,此时 low 不变,high更新为 high=mid-1;
当arr1D[mid]<key时, 类似上面新的查找区间为arr1D[mid+1,……,high],此时high不变,low更新为low=mid+1。
每找到新的区间后,mid更新为mid= (low+high)/2 ,将查找的值key与arr1D[mid]比较,若相等,查找成功返回此位置的mid; 若不相等,则当前查找区间将缩小一半,递归继续查找,直到low>high结束查找,,这时若是未找到与key相等的arr1D[mid],则说明该数组没有key这个元素。
(3) 该算法的时间复杂度为:O(log2n)。
例如给出数组:arr1D={1,13,11,6,4,9};
首先将数组从小到大排序: arr1D ={ 1,4,6,9,11,13}
int low =0;
int high = 6;
int mid= (low+high)/2;即mid = 3, 可知arr1D[mid]=9;
假设查找的元素为 int key =11;
第1次查找:
由于key !=arr1D[mid],且key>arr1D[mid],则key应该在arr1D[mid+1,high-1 ]中
第2次查找:
此时 low = mid+1=4, 而high =6不变,则mid更新为 mid = (low +high)/2 =5;则arr1D[mid]=13, 由于key !=arr1D[mid],且key<arr1D[mid],则key应该在arr1D[low,mid-1 ]中
第3次查找:
此时 low = 4不变, 而high =mid-1=4,则mid更新为 mid = (low +high)/2 =4;则arr1D[mid]=11, 由于key =arr1D[mid]=11,则找到该元素key=8,返回索引mid =4,二分法查找结束。假设查找的元素为 int key =10;
第1次查找:
由于key !=arr1D[mid],且key>arr1D[mid],则key应该在arr1D[mid+1,high-1 ]中
第2次查找:
此时 low = mid+1=4, 而high =6不变,则mid更新为 mid = (low +high)/2 =5;则arr1D[mid]=13, 由于key !=arr1D[mid],且key<arr1D[mid],则key应该在arr1D[low,mid-1 ]中
第3次查找:
此时 low = 4不变, 而high =mid-1=4,则mid更新为 mid = (low +high)/2 =4;则arr1D[mid]=11, 由于key !=arr1D[mid],且key<arr1D[mid],则key应该在arr1D[low,mid-1 ]中,
第4次查找:
此时 low = 4不变, 而high =mid-1=3,则mid更新为 mid = (low +high)/2 =3;则由于此时low<=high不成立,从而结束查找,返回 -1,表示未在arr1D中找到key这个元素。
二分法可以使用递归来实现:
下面是基于c++的代码实现,使用一个动态的一维数组来输入一维数组的值再进行查找测试:
``#include<iostream>
#include<time.h>
using namespace std;
//定义指向一位数组的指针
int *arr1D = NULL;
//定义一维数组的长度
int len = 0;
//要查询的值
int key = -1;
//返回的索引
int index = -1;
void Sort()
{
//冒泡排序法将数组从小到大排序
for (int i = 0; i < len - 1; i++)
{
for (int j = 0; j < len - i - 1; j++)
{
int temp;
if (arr1D[j] > arr1D[j + 1])
{
temp = arr1D[j + 1];
arr1D[j + 1] = arr1D[j];
arr1D[j] = temp;
}
}
}
//打印排序好的数组
for (int i = 0; i < len; i++)
{
cout << arr1D[i] << " ";
}
cout << endl;
}
//折半查找(二分法查找),找到元素key返回对应索引,否则返回-1
int search(int *arr, int key, int low, int high)
{
//定义一维数组的中间索引
int mid = (low + high) / 2;
//使用while循环,找到要查询的值直接返回索引
while (low <= high)
{
if (arr[mid] == key)
{
//找到key的值返回对应的索引
return mid;
}
//key的值在arr[mid]左侧查找
else if (arr[mid] > key)
{
//更新high的值
high = mid - 1;
//递归继续查找
return search(arr, key, low, high);
}
//key的值在arr[mid]右侧查找
else if (arr[mid] < key)
{
//更新low的值
low = mid + 1;
//递归继续查找
return search(arr, key,low, high);
}
}
//未找到返回-1
return -1;
}
void input()
{
cout << "请输入一维数组的长度:" << endl;
cin >> len;
//給一维数组开辟空间
arr1D = new int[len];
cout << "请输入一维数组的元素:" << endl;
//给一维数组赋值
for (int i = 0; i < len; i++)
{
cin >> arr1D[i];
}
//排序
Sort();
//多次调试
while (true)
{
cout << "请输入要查询的值(按下-1跳出查询):" << endl;
if (key == -1)
{
break;
}
cin >> key;
//查找并返回对应索引
index = search(arr1D, key, 0, len);
cout << "该元素的索引为:" << index << endl;
}
}
int main()
{
input();
system("pause");
return 0;
}
下面是基于VS2017 的c++代码的实现截图:
二分法也可以使用非递归来实现:
#include<iostream>
using namespace std;
//一维数组指针
int *arr1D = NULL;
//一维数组的长度
int len = 0;
//要查询的值
int key = -1;
int FindIndex(int *arr,int key,int low,int high)
{
//数组长度的中间值
int mid = (low + high) / 2;
//使用while循环进行判断
while (low<=high)
{
//判断key是否与arr[mid]相等
if (key==arr[mid])
{
//找到key的对应索引并返回
return mid;
}else if(key>arr[mid])
{
//更新low的值,往mid右侧寻找
low = mid + 1;
//更新mid的值
mid = (low + high) / 2;
}else
{
//更新high的值,往mid左侧寻找
high= mid -1;
//更新mid的值
mid = (low + high) / 2;
}
}
//未找到返回-1
return -1;
}
//冒泡排序将数组元素从小到大排序
void sort()
{
for (int i = 0; i < len-1; i++)
{
for (int j = 0; j < len - i - 1; j++)
{
int temp;
if (arr1D[j+1]<arr1D[j])
{
temp = arr1D[j + 1];
arr1D[j + 1]= arr1D[j];
arr1D[j] = temp;
}
}
}
cout << "排序后的数组为:";
for (int k = 0; k < len; k++)
{
cout << arr1D[k] << " " ;
}
cout << endl;
}
//初始化
void invi()
{
cout << "请输入数组的长度" << endl;
cin >> len;
arr1D = new int[len];
cout << "请输入一维数组的元素:";
for (int i = 0; i < len; i++)
{
cin >> arr1D[i];
}
//将数组排序
sort();
//多次调试
while (true)
{
cout << "请输入要查询的值(按下-2跳出查询):" << endl;
cin >> key;
if (key == -2)
{
break;
}
//查找并返回对应索引
int index = FindIndex(arr1D, key, 0, len);
cout << "该元素的索引为:" << index << endl;
}
}
int main()
{
invi();
system("pause");
return 0;
}
运行截图为:
对于Java来说,思想也是一样的,不使用递归的代码为:
package Homework;
import java.util.Scanner;
/**
*
* 内容: 二分法查找不用递归
*
* @author 陌意随影
* @date:2019年7月10日下午9:48:51
*/
public class HomeWork17 {
public static void main(String[] args) {
// 定义并初始化一个一维数组
int[] arr = {1,13,11,6,4,9};
//排序
sort(arr);
//多次调试
while(true) {
Scanner s = new Scanner(System.in);
System.out.println("请输入要查找的值(输入-2跳出测试):");
int key = s.nextInt();
if(key == -2) {
break;
}
int index = search(arr, key);
if (index == -1) {
System.out.println(key + "未找到!");
} else {
System.out.println(key + "的索引是:" + index);
}
}
}
public static int search(int[] arr, int key) {
//定义low
int low = 0;
//定义high
int high = arr.length;
//定义中间位置mid
int mid = (low + high) / 2;
//使用while循环进行判断
while (low <= high) {
//判断key是否与arr[mid]相等
if (key == arr[mid]) {
//找到key的对应索引并返回
return mid;
} else if (key > arr[mid]) {
//更新low的值,往mid右侧寻找
low = mid + 1;
//更新mid的值
mid = (low + high) / 2;
} else {
//更新high的值,往mid左侧寻找
high = mid - 1;
//更新mid的值
mid = (low + high) / 2;
}
}
//未找到返回-1
return -1;
}
//使用冒泡排序法对数组进行从小到大的排序
public static void sort(int[] arr) {
for (int i = 0; i < arr.length; i++) {
for (int j = i; j < arr.length; j++) {
int temp;
if (arr[i] > arr[j]) {
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
//打印排序后的数组
System.out.println("排序后的数组为:");
for (int k : arr) {
System.out.print(k + " ");
}
System.out.println();
}
}
运行截图为:
使用递归的代码为:
package Homework;
import java.util.Scanner;
/**
*• 内容: 二分法查找用递归
•
• @author 陌意随影
• @date:2019年7月10日下午9:48:51
*/public class HomeWork18 {
public static void main(String[] args) {
// 定义并初始化一个一维数组
int[] arr = { 1, 13, 11, 6, 4, 9 };
// 排序
sort(arr);
// 多次调试
while (true) {
Scanner s = new Scanner(System.in);
System.out.println("请输入要查找的值(输入-2跳出测试):");
int key = s.nextInt();
if (key == -2) {
break;
}
int index = search(arr, key, 0, arr.length);
if (index == -1) {
System.out.println(key + "未找到!");
} else {
System.out.println(key + "的索引是:" + index);
}
}
}
public static int search(int[] arr, int key, int low, int high) {
// 定义中间位置mid
int mid = (low + high) / 2;
// 使用while循环进行判断
while (low <= high) {
// 判断key是否与arr[mid]相等
if (arr[mid] == key) {
// 找到key的对应索引并返回
return mid;
}
// 往左侧继续递归寻找
else if (arr[mid] > key) {
// 更新high的值,往mid左侧寻找
high = mid - 1;
// 递归寻找
return search(arr, key, low, mid - 1);
}
// 往右侧继续递归寻找
else {
// 更新low的值,往mid右侧寻找
low = mid + 1;
// 递归寻找
return search(arr, key, low, high);
}
}
// 未找到返回-1
return -1;
}
// 使用冒泡排序法对数组进行从小到大的排序
public static void sort(int[] arr) {
int temp;
for (int i = 0; i < arr.length; i++) {
for (int j = i; j < arr.length; j++) {
if (arr[i] > arr[j]) {
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
// 打印排序后的数组
System.out.println("排序后的数组为:");
for (int k : arr) {
System.out.print(k + " ");
}
System.out.println();
}}
运行截图为: