折半查找又称二分查找,它是一种效率较高的查找方法。但是折半查找要求查找序列中的元素是有序的,为了简单,假设序列是递增有序。
折半查找的非递归实现代码如下所示:
#include "iostream"
using namespace std;
//非递归实现
int binarySearch(int a[], int low, int high, int key) {
while (low <= high) {
int mid = (low + high) / 2;
if (a[mid] == key) {
return mid;
} else if (a[mid] > key) {
high = mid - 1;
} else {
low = mid + 1;
}
}
return -1;
}
int main() {
int len = 10;
int a[10] = {1,2,3,4,5,6,7,8,9,10};
int key = 3;
int pos = binarySearch(a, 0, len - 1, key);
cout << "position: " << pos + 1 << endl;
}
递归实现代码如下所示:
#include "iostream"
using namespace std;
//递归实现
int binarySearch(int a[], int low, int high, int key) {
if (low <= high) {
int mid = low + high >> 1;
//递归出口
if (a[mid] == key) {
return mid;
} else if (a[mid] > key) {
return binarySearch(a, low, mid - 1, key);
} else {
return binarySearch(a, mid + 1, high, key);
}
}
else {
return -1;
}
}
int main() {
int len = 10;
int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int key = 10;
int pos = binarySearch(a, 0, len - 1, key);
cout << "position: " << pos + 1 << endl;
}
折半查找的相关应用:
【查找最大和次大元素,最小和次小元素】
问题求解:
1、基本情况处理: 如果区间只包含一个元素(low == high),则直接将最大值和最小值都设置为该元素,并将次大值和次小值设为极端值(INT32_MIN和INT32_MAX)。
2、两个元素的情况处理: 如果区间包含两个元素(high - low == 1),则比较这两个元素的大小,分别设置最大值和最小值。
3、区间长度大于2时: 对于长度大于2的区间,将其划分为两个子区间,分别递归调用 solve 函数求解左右两个子区间的最大值、次大值、最小值和次小值。
4、合并子问题解: 根据左右子区间的解,合并得到整个区间的最大值、次大值、最小值和次小值。
5、如果左区间的最大值大于右区间的最大值,则整体最大值为左区间的最大值,次大值为左区间的次大值和右区间的最大值中的较大者。
6、如果左区间的最大值小于等于右区间的最大值,则整体最大值为右区间的最大值,次大值为右区间的次大值和左区间的最大值中的较大者。
类似地,根据左右区间的最小值、次小值合并得到整体最小值、次小值。
实现代码如下所示:
#include "iostream"
using namespace std;
void solve(int arr[], int low, int high, int &max1, int &max2, int &min1, int &min2) {
if (low == high) {
max1 = min1 = arr[low];
max2 = INT32_MIN;
min2 = INT32_MAX;
} else if (high - low == 1) {
//区间包含两个元素
max1 = min2 = max(arr[low], arr[high]);
min1 = max2 = min(arr[low], arr[high]);
} else {
//区间长度小于2,使用分治算法
int mid = low + high >> 1;
//分别求得左右两边得最值
int lmax1, lmax2, lmin1, lmin2;
int rmax1, rmax2, rmin1, rmin2;
solve(arr, low, mid, lmax1, lmax2, lmin1, lmin2);
solve(arr, mid + 1, high, rmax1, rmax2, rmin1, rmin2);
if (lmax1 > rmax1) {
max1 = lmax1;
max2 = max(rmax1, lmax2);
} else {
max1 = rmax1;
max2 = max(lmax1, rmax2);
}
if (lmin1 < rmin1) {
min1 = lmin1;
min2 = min(rmin1, lmin2);
} else {
min1 = rmin1;
min2 = min(rmin2, lmin1);
}
}
}
int main() {
int len = 8;
int a[8] = {4, 12, 32, 49, 21, 43, 14, 5};
int max1, max2, min1, min2;
solve(a, 0, len - 1, max1, max2, min1, min2);
printf("max1:%d\nmax2:%d\nmin1:%d\nmin2:%d", max1, max2, min1, min2);
}
运行结果如下所示:
【寻找一个序列中第k小的元素】
使用分治法编程解决在n个数当中找到第k小元素问题:
问题求解:
通过分治法查找数组中的元素我们很容易联想到常用的快速排序算法,每次快速排序时,需要将数组一分为二,将枢轴元素小的放在其左边,否则放右边,显然,枢轴的位置就是排序后的最终位置,并且不再发生改变。因此,快速排序的核心思想就在于将每个区间中的枢轴元素放在其对应的索引位置,而这个索引就是我们所要查找的k。
实现代码如下所示;
#include "iostream"
using namespace std;
int partition(int arr[], int low, int high) {
int pivot = arr[low];
while (low < high) {
while (low < high && arr[high] >= pivot) high--;
arr[low] = arr[high];
while (low < high && arr[low] <= pivot) low++;
arr[high] = arr[low];
}
arr[low] = pivot;
return low;
}
int quickSort(int arr[], int low, int high, int key) {
if (low < high) {
int pivot = partition(arr, low, high);
if (pivot == key) return arr[key];
else if (pivot > key) {
return quickSort(arr, low, pivot - 1, key);
}
return quickSort(arr, pivot + 1, high, key);
}
return arr[low];
}
int main() {
int arr[] = {32, 2, 4, 6, 12, 43, 54, 65, 76, 87};
int key = 9;
key--;
int ans = quickSort(arr, 0, 9, key);
cout << "the NO." << key << " smallest num is:" << ans;
}