如何在大量排序好的数据中找到想要的数据?
二分查找:先选取中间的数据,如果要查找的数据就是中间的数据则直接返回,如果比中间的数据大就在右边的数据区间寻找,如果比中间的数据小就在左边的数据区间查找,如此不断递归查找,直到找到目标数据。
无重复数据的二分查找
#include<iostream>
using namespace std;
// 如何在大量排序好的数据中找到想要的数据?
// 二分查找:先选取中间的数据,如果要查找的数据就是中间的数据则直接返回,
// 如果比中间的数据大就在右边的数据区间寻找,
// 如果比中间的数据小就在左边的数据区间查找,
// 如此不断递归查找,直到找到目标数据
int besearchInternally(int* A, int low, int high, int val)
{
// int mid = (low + high) / 2;
// 因为当low和high过大时两者之和就有可能溢出,所以可以改进为
// int mid = low + (high - low) / 2;
// 如果要将性能优化到极致(因为计算机处理位运算比处理除法要快的多),可以将除以2转化成位运算
// >>n表示将二进制数整体向右移n位,当n=1时就相当于除以二
if (high < low)
return -1;
int mid = low + ((high - low) >> 1);
if (val == A[mid])
return mid;
else if (val < A[mid])
return besearchInternally(A, low, mid - 1, val);
else
return besearchInternally(A, mid + 1, high, val);
}
// A是要查找的有序数据集,n是数据集的大小,val是要查找的目标数据,
// 返回值是目标数据在数组中的位置,若没有则返回-1
int besearch(int* A, int n, int val)
{
if (val > A[n-1] || val < A[0])
return -1;
return besearchInternally(A, 0, n - 1, val);
}
int main()
{
int size = 14;
int x[] = { 1,52,100,101,102,103,104,105,109,120,135,200,800,900};
int val;
int n;
while (true)
{
cout << "请输入要查找的数据:";
cin >> val;
n = besearch(x, size, val);
if (n == -1)
cout << val << "数据不存在" << endl;
else
cout << val << ":" << besearch(x, size, val) << endl;
}
}
存在相同数据
二分查找(查找第一个和要查找数据相同的元素的位置)
#include<iostream>
using namespace std;
// 二分查找变形(查找第一个值等于给定值的元素)
// 主要思路:按照之前的查找方法,先查找到某个等于给定值的元素,
// 然后不断往前,直到前一个元素不等于给定值,就返回
int besearchInternally(int* A, int low, int high, int val)
{
// int mid = (low + high) / 2;
// 因为当low和high过大时两者之和就有可能溢出,所以可以改进为
// int mid = low + (high - low) / 2;
// 如果要将性能优化到极致(因为计算机处理位运算比处理除法要快的多),可以将除以2转化成位运算
// >>n表示将二进制数整体向右移n位,当n=1时就相当于除以二
if (high < low)
return -1;
int mid = low + ((high - low) >> 1);
if (val == A[mid])
{
while (mid>0&&A[mid - 1] == val)
{
mid--;
}
return mid;
}
else if (val < A[mid])
return besearchInternally(A, low, mid - 1, val);
else
return besearchInternally(A, mid + 1, high, val);
}
// A是要查找的有序数据集,n是数据集的大小,val是要查找的目标数据,
// 返回值是目标数据在数组中的位置,若没有则返回-1
int besearch(int* A, int n, int val)
{
if (val > A[n - 1] || val < A[0])
return -1;
return besearchInternally(A, 0, n - 1, val);
}
int main()
{
int size = 17;
int x[] = { 1,1,52,100,100,101,102,103,104,105,109,120,135,200,800,900,900 };
int val;
int n;
while (true)
{
cout << "请输入要查找的数据:";
cin >> val;
n = besearch(x, size, val);
if (n == -1)
cout << val << "数据不存在" << endl;
else
cout << val << ":" << besearch(x, size, val) << endl;
}
}
二分查找(查找最后一个和目标数据相同的元素)
#include<iostream>
using namespace std;
// 二分查找变形(查找最后一个值等于给定值的元素)
// 主要思路:按照之前的查找方法,先查找到某个等于给定值的元素,
// 然后不断往后,直到后一个元素不等于给定值或已经到达数组的末尾,就返回
// 区别于之前的二分查找,需要多一个size参数,来控制查找的上限
int besearchInternally(int* A, int low, int high, int val,int size)
{
// int mid = (low + high) / 2;
// 因为当low和high过大时两者之和就有可能溢出,所以可以改进为
// int mid = low + (high - low) / 2;
// 如果要将性能优化到极致(因为计算机处理位运算比处理除法要快的多),可以将除以2转化成位运算
// >>n表示将二进制数整体向右移n位,当n=1时就相当于除以二
if (high < low)
return -1;
int mid = low + ((high - low) >> 1);
if (val == A[mid])
{
while (mid < size-1 && A[mid + 1] == val)
{
mid++;
}
return mid;
}
else if (val < A[mid])
return besearchInternally(A, low, mid - 1, val,size);
else
return besearchInternally(A, mid + 1, high, val,size);
}
// A是要查找的有序数据集,n是数据集的大小,val是要查找的目标数据,
// 返回值是目标数据在数组中的位置,若没有则返回-1
int besearch(int* A, int n, int val)
{
if (val > A[n - 1] || val < A[0])
return -1;
return besearchInternally(A, 0, n - 1, val,n);
}
int main()
{
int size = 17;
int x[] = { 1,1,52,100,100,101,102,103,104,105,109,120,135,200,800,900,900 };
int val;
int n;
while (true)
{
cout << "请输入要查找的数据:";
cin >> val;
n = besearch(x, size, val);
if (n == -1)
cout << val << "数据不存在" << endl;
else
cout << val << ":" << besearch(x, size, val) << endl;
}
}
二分查找(查找第一个大于等于给定值的元素)
#include<iostream>
using namespace std;
// 二分查找变形(查找第一个大于等于给定值的元素)
// 主要思路:按照之前的查找方法,如果能查找到就直接返回,
// 如果没有这个数据,当high>low时,也就是查找结束时,返回low
// (此时low就是第一个大于给定值元素的下标)
int besearchInternally(int* A, int low, int high, int val)
{
// 当hign<low时,表示数组中没有这个数据,这个时候low就是第一个大于给定值的元素
if (high < low)
{
return low;
}
// int mid = (low + high) / 2;
// 因为当low和high过大时两者之和就有可能溢出,所以可以改进为
// int mid = low + (high - low) / 2;
// 如果要将性能优化到极致(因为计算机处理位运算比处理除法要快的多),可以将除以2转化成位运算
// >>n表示将二进制数整体向右移n位,当n=1时就相当于除以二
int mid = low + ((high - low) >> 1);
if (val == A[mid])
return mid;
else if (val < A[mid])
return besearchInternally(A, low, mid - 1, val);
else
return besearchInternally(A, mid + 1, high, val);
}
// A是要查找的有序数据集,n是数据集的大小,val是要查找的目标数据,
// 返回值是目标数据在数组中的位置,若没有则返回-1
int besearch(int* A, int n, int val)
{
// 如果要查找的数据已经比数组中最大的数都大了就直接返回-1
if ( val > A[n-1])
return -1;
return besearchInternally(A, 0, n - 1, val);
}
int main()
{
int size = 4;
int x[] = { 1,2,3,5 };
int val;
int n;
while (true)
{
cout << "请输入要查找的数据:";
cin >> val;
n = besearch(x, size, val);
if (n == -1)
cout << "大于等于" << val << "的数据不存在" << endl;
else
cout << val << ":" << besearch(x, size, val) << endl;
}
}
二分查找(查找第一个小于等于给定值的元素)
#include<iostream>
using namespace std;
// 二分查找变形(查找第一个小于等于给定值的元素)
// 主要思路:按照之前的查找方法,如果能查找到就直接返回,
// 如果没有这个数据,当high>low时,也就是查找结束时,返回high
// (此时high就是第一个小于给定值元素的下标)
int besearchInternally(int* A, int low, int high, int val)
{
// 当hign<low时,表示数组中没有这个数据,这个时候low就是第一个大于给定值的元素
if (high < low)
{
return high;
}
// int mid = (low + high) / 2;
// 因为当low和high过大时两者之和就有可能溢出,所以可以改进为
// int mid = low + (high - low) / 2;
// 如果要将性能优化到极致(因为计算机处理位运算比处理除法要快的多),可以将除以2转化成位运算
// >>n表示将二进制数整体向右移n位,当n=1时就相当于除以二
int mid = low + ((high - low) >> 1);
if (val == A[mid])
return mid;
else if (val < A[mid])
return besearchInternally(A, low, mid - 1, val);
else
return besearchInternally(A, mid + 1, high, val);
}
// A是要查找的有序数据集,n是数据集的大小,val是要查找的目标数据,
// 返回值是目标数据在数组中的位置,若没有则返回-1
int besearch(int* A, int n, int val)
{
// 如果要查找的数据已经比数组中最小的数都小了就直接返回-1
if (val < A[0])
return -1;
return besearchInternally(A, 0, n - 1, val);
}
int main()
{
int size = 4;
int x[] = { 1,2,3,5 };
int val;
int n;
while (true)
{
cout << "请输入要查找的数据:";
cin >> val;
n = besearch(x, size, val);
if (n == -1)
cout << "小于等于"<<val << "的数据不存在" << endl;
else
cout << val << ":" << besearch(x, size, val) << endl;
}
}
二分查找的思路很好理解,但是代码不是很好写,尤其是后四个变式,主要要关注递归结束的条件,查找范围的选取,不同情况下返回值的选择,还有最开始的条件判断(如果很明显数据不存在就直接返回)。