LeetCode 第34题:在排序数组中查找元素的第一个和最后一个位置
LeetCode 第34题要求在一个排序的数组中找到给定目标值的第一个和最后一个位置。如果目标值不存在,则返回 [-1, -1]。在本篇文章中,我将为大家提供详细的 C 语言解法,并逐行解释代码。
题目分析
给定一个升序排列的整数数组 nums
和一个目标值 target
,我们需要找到目标值 target
在数组中首次和最后一次出现的位置。如果数组中没有目标值,则返回 [-1, -1]
。
解法思路
我们可以使用二分查找来加速搜索过程,利用排序数组的性质找到目标值的第一个和最后一个位置。
具体的思路如下:
- 使用二分查找找到目标值的位置。
- 找到目标值后,通过向左扩展和向右扩展的方式来确定目标值的最左和最右位置。
- 如果找不到目标值,返回
[-1, -1]
。
代码实现
代码解读
#include <stdio.h>
#include <stdlib.h>
/**
* searchRange 函数用于在已排序的数组中找到目标值的起始和结束位置。
* 如果目标值不存在,则返回 [-1, -1]。
*
* @param nums: 排序后的整数数组。
* @param numsSize: 数组的大小。
* @param target: 要查找的目标值。
* @param returnSize: 返回数组的大小,固定为2,表示包含目标值的范围。
* @return: 一个包含目标值范围的数组,范围为 [left, right],如果目标值不存在返回 [-1, -1]。
*/
int* searchRange(int* nums, int numsSize, int target, int* returnSize) {
// 设置返回数组的大小为2,表示结果为目标值的起始和结束位置
*returnSize = 2;
// 动态分配结果数组,用于存储目标值的起始和结束位置
int* result = (int*)malloc(sizeof(int) * 2);
// 检查内存分配是否成功
if (result == NULL) {
*returnSize = 0; // 如果内存分配失败,返回0大小的数组
return NULL;
}
// 初始化结果数组,默认值为 [-1, -1]
result[0] = -1;
result[1] = -1;
// 如果数组为空或目标值不在数组的范围内,直接返回 [-1, -1]
if (numsSize == 0 || nums[0] > target || nums[numsSize - 1] < target) {
return result;
}
int left = 0; // 左指针
int right = numsSize - 1; // 右指针
int middle = 0; // 中间指针
// 二分查找目标值的位置
while (left <= right) {
middle = left + (right - left) / 2; // 计算中间位置,避免溢出
if (nums[middle] == target) { // 找到目标值
// 找到目标值后,向左和向右扩展,找到最左和最右的目标值
int m_left = middle;
int m_right = middle;
// 向右扩展,直到不再等于目标值
while ((m_right + 1) < numsSize && nums[m_right + 1] == target) {
m_right += 1;
}
// 向左扩展,直到不再等于目标值
while ((m_left - 1) >= 0 && nums[m_left - 1] == target) {
m_left -= 1;
}
// 设置结果数组,返回最左和最右位置
result[0] = m_left;
result[1] = m_right;
return result;
}
// 根据中间值与目标值的大小关系调整左右指针
if (nums[middle] > target) {
right = middle - 1; // 如果目标值小于中间值,右指针左移
} else {
left = middle + 1; // 如果目标值大于中间值,左指针右移
}
}
// 如果没有找到目标值,返回 [-1, -1]
return result;
}
int main() {
// 示例数组和目标值
int nums[] = {5, 7, 7, 8, 8, 10};
int target = 8;
int returnSize;
// 调用 searchRange 函数
int* result = searchRange(nums, sizeof(nums) / sizeof(nums[0]), target, &returnSize);
// 输出结果
if (result != NULL) {
printf("Range: [%d, %d]\n", result[0], result[1]);
free(result); // 记得释放动态分配的内存
} else {
printf("Target not found.\n");
}
return 0;
}
代码分析
-
初始化和内存分配:我们首先动态分配了一个
result
数组,用于存储目标值的最左和最右位置。若内存分配失败,我们将returnSize
设置为 0 并返回NULL
。 -
边界检查:在开始二分查找之前,我们进行了边界检查。如果目标值不可能出现在数组中(如目标值小于数组中的最小值或大于数组中的最大值),我们直接返回
[-1, -1]
。 -
二分查找:我们通过
while
循环进行二分查找,在找到目标值后,进一步扩展搜索范围,找到目标值的最左和最右位置。 -
结果输出:最后,我们通过
printf
输出目标值的范围。如果目标值不存在,我们输出 “Target not found”。
时间复杂度分析
- 二分查找 的时间复杂度是 O(log n),在最坏情况下,我们只需要进行一次二分查找。
- 扩展查找最左和最右位置的过程是线性的 O(n),但这仅在找到目标值后进行,所以整体时间复杂度仍然是 O(n)。