C++ 算法2-查找
文章目录
1. 线性查找(顺序查找/遍历查找)
1. 算法:从表头开始,依次将每一个值与目标元素进行比较,直到找到或者找不到为止。
2. 评价:平均时间复杂度O(N),对样本的有序性没有要求。
2. 二分查找(折半查找)
-
算法:首先,假设表中的元素按升序排列,将表的中间元素与查找目标比较,如果相等则查找成功,如果中间元素>查找目标则在中间元素的左侧子表中继续查找,否则在中间元素的右侧子表中继续查找。重复以上过程,直到找到满足条件的元素为止——查找成功,或子表不存在——查找失败。
- 核心思想:通过比较中间元素
data[mid]
与目标值key
,逐步缩小搜索范围。
- 核心思想:通过比较中间元素
-
评价:平均时间复杂度
O(logN)
,要求样本必须有序。
3. 示例程序
// 查找算法
#include <iostream>
using namespace std;
// 线性查找
int line (int data[], int size, int key) {
for (int i = 0; i < size; i++)
if (data[i] == key)
return i;
return -1;
}
// 二分查找(迭代实现)
// 参数:
// data[]: 已排序的数组
// size: 数组长度
// key: 要查找的目标值
// 返回值:
// 找到时返回目标值的索引,未找到时返回-1
int binary (int data[], int size, int key) {
int left = 0; // 初始化左边界为数组起始位置
int right = size - 1; // 初始化右边界为数组末尾位置
// 当左边界不超过右边界时循环
while (left <= right) {
int mid = (left + right) / 2; // 计算中间位置
if (key < data[mid])// 目标值在左半部分
right = mid - 1;// 调整右边界
else
if (key > data[mid])// 目标值在右半部分
left = mid + 1; // 调整左边界
else
return mid; // 找到目标值
}
return -1; // 未找到目标值
}
// 二分查找(递归实现)
// 参数:
// data[]: 已排序的数组
// left: 当前查找范围的左边界
// right: 当前查找范围的右边界
// key: 要查找的目标值
// 返回值:
// 找到时返回目标值的索引,未找到时返回-1
int binary (int data[], int left,int right,int key){
// 递归终止条件:左边界超过右边界
if (left > right)
return -1;
int mid = (left + right) / 2;// 计算中间位置
if (key < data[mid]) // 目标值在左半部分
return binary (data, left, mid - 1, key); // 递归查找左半部分
else
if (key > data[mid]) // 目标值在右半部分
return binary (data, mid + 1, right, key);// 递归查找右半部分
else
return mid; // 找到目标值
}
int main (void) {
int data[] = {13, 23, 44, 26, 77, 52, 8};
int size = sizeof (data) / sizeof (data[0]);
// int res = line (data, size, 26);
// int res = line (data, size, 55);
int data2[] = {8, 13, 23, 26, 44, 52, 77};
size = sizeof (data2) / sizeof (data2[0]);
// int res = binary (data2, size, 26);
// int res = binary (data2, size, 55);
// int res = binary (data2, 0, size - 1, 77);
int res = binary (data2, 0, size - 1, 0);
if (res == -1)
cout << "不存在" << endl;
else
cout << "找到了:" << res << endl;
return 0;
}
迭代 vs 递归
特性 | 迭代实现 | 递归实现 |
---|---|---|
空间复杂度 | O(1)(无需额外空间) | O(log n)(递归调用栈) |
适用场景 | 大规模数据(避免栈溢出) | 小规模数据(代码更简洁) |
性能 | 通常更快(无函数调用开销) | 稍慢(递归调用有额外开销) |
4 二分查找的变种及其应用场景
二分查找的核心思想是通过分治策略快速定位目标值,但在实际应用中,可能需要处理重复值、边界条件或非精确匹配等问题。
1. 查找第一个等于目标值的位置
问题:数组中有多个 target
,返回第一个出现的位置。
示例:[1, 2, 2, 2, 3]
中查找 2
,应返回索引 1
。
int binary_search_first(int arr[], int size, int target) {
int left = 0, right = size - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] >= target) {
right = mid - 1; // 继续向左查找
}
else {
left = mid + 1;
}
}
if (left < size && arr[left] == target) return left;
return -1;
}
2. 查找最后一个等于目标值的位置
问题:返回最后一个 target
的位置。
示例:[1, 2, 2, 2, 3]
中查找 2
,应返回索引 3
。
int binary_search_last(int arr[], int size, int target) {
int left = 0, right = size - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] <= target) {
left = mid + 1; // 继续向右查找
}
else {
right = mid - 1;
}
}
if (right >= 0 && arr[right] == target) return right;
return -1;
}
#3# 3. 查找第一个大于等于目标值的位置
问题:返回第一个≥ target的元素位置(可用于插入位置)。
示例:[1, 3, 5, 7]
中查找 4
,应返回索引 2
(5
的位置)。
int binary_search_first_gte(int arr[], int size, int target) {
int left = 0, right = size - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] >= target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return left; // 注意:可能返回 size(表示所有元素均小于 target)
}
4. 查找最后一个小于等于目标值的位置
问题:返回最后一个 ≤ target
的元素位置。
示例:[1, 3, 5, 7]
中查找 4
,应返回索引 1
(3
的位置)。
int binary_search_last_lte(int arr[], int size, int target) {
int left = 0, right = size - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] <= target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return right; // 注意:可能返回 -1(表示所有元素均大于 target)
}
5. 旋转有序数组中的查找
问题:在旋转后的有序数组(如 [4, 5, 6, 1, 2, 3]
)中查找目标值。
关键点:先找到旋转点,再在左右半部分分别二分查找。
示例: 找到 4,位置在索引 0 ,找到 6,位置在索引 2
int search_in_rotated_array(int arr[], int size, int target) {
int left = 0, right = size - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] == target) return mid;
// 判断左右哪一部分有序
if (arr[left] <= arr[mid]) { // 左半部分有序
if (arr[left] <= target && target < arr[mid]) {
right = mid - 1;
} else {
left = mid + 1;
}
} else { // 右半部分有序
if (arr[mid] < target && target <= arr[right]) {
left = mid + 1;
} else {
right = mid - 1;
}
}
}
return -1;
}
6. 查找峰值元素
问题:在无序数组中找到任意一个峰值(比相邻元素大)。
示例:[1, 3, 20, 4, 1]
的峰值是 20
或 4
。
int find_peak_element(int arr[], int size) {
int left = 0, right = size - 1;
while (left < right) {
int mid = left + (right - left) / 2;
if (arr[mid] < arr[mid + 1]) {
left = mid + 1; // 峰值在右侧
} else {
right = mid; // 峰值在左侧
}
}
return left; // 返回任意一个峰值的位置
}
7. 查找缺失的数字
问题:在 [0, 1, 3, 4]
中找到缺失的数字(如 2
)。
变种:数组按升序排列,且唯一缺失一个数字。
int find_missing_number(int arr[], int size) {
int left = 0, right = size - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] == mid) {
left = mid + 1; // 缺失在右侧
} else {
right = mid - 1; // 缺失在左侧
}
}
return left; // 返回缺失的数字
}