C++程序 在排序和旋转的数组中搜索元素
通过二分查找,可以在O(log n)时间内找到排序数组中的元素。但是假设我们在某个未知的枢轴上旋转了一个升序排序的数组。例如,1 2 3 4 5可能变成3 4 5 1 2。设计一种在旋转数组中找到元素的方法,并在O(log n)时间内完成。
例子:
输入: arr[] = {5, 6, 7, 8, 9, 10, 1, 2, 3};
key = 3
输出: 在索引8处找到
输入: arr[] = {5, 6, 7, 8, 9, 10, 1, 2, 3};
key = 30
输出: 没有找到
输入: arr[] = {30, 40, 50, 10, 20}
key = 10
输出: 在索引3处找到
所有提供的解决方案都假定数组中的所有元素都是不同的。
基本解决方案:
方法:
- 想法是找到枢轴点,将数组分成两个子数组并进行二分搜索。
- 找到枢轴的主要思想是 – 对于排序后(按递增顺序)和枢轴变化的数组,枢轴元素是唯一的元素,其中下一个元素比它小。
- 可以使用上述语句和二分搜索找到枢轴。
- 找到枢轴之后,将数组分成两个子数组。
- 现在,单个子数组已排序,因此可以使用二分搜索来查找元素。
实现:
输入 arr[] = {3, 4, 5, 1, 2}
要搜索的元素 = 1
1) 找到枢轴点并将数组分成两个子数组。(枢轴=2)/索引为5/
2) 现在为两个子数组之一调用二分搜索。
(a) 如果 元素大于0th元素,则在左侧数组中搜索
(b) 否则 在右侧数组中搜索
(1将进入else,因为1小于0th元素(3))
3) 如果 在所选子数组中找到元素,则返回索引
否则 返回-1。
下面是上述方法的实现:
输出:
复杂度分析:
- 时间复杂度: O(log n)。 二分查找需要log n次比较才能找到元素。因此时间复杂度是O(log n)。
- 空间复杂度: O(1),不需要额外的空间。
改进的方案:
方法: 与其进行两次或更多次二分搜索,不如在一次二分搜索中找到结果。需要修改二分搜索来执行搜索。想法是创建一个递归函数,该函数将l和r作为输入范围,并且key作为输入。
1) 找到中间点mid=(l+h)/2
2) 如果 关键字存在于中间点,则返回中间点
3) 否则 , 如果arr[l..mid]已排序
a) 如果 要搜索的关键字位于arr[l]
到arr[mid]的范围内,请对arr[l..mid]进行递归。
b) 否则,请对arr[mid+1..h]进行递归。
4) 否则 (arr[mid+1..h]必须排序)
a) 如果 要搜索的关键字位于arr[mid+1]
到arr[h]的范围内,请对 arr[mid+1..h]进行递归。
b) 否则,请对arr[l..mid]进行递归。
下面是上述思路的实现:
输出:
复杂度分析:
- 时间复杂度: O(log n)。 二分查找需要log n次比较才能找到元素。因此时间复杂度为O(log n)。
- 空间复杂度: O(1)。 没有需要额外空间。
如何处理重复项?
当允许有重复项时,似乎不可能在所有情况下以O(Logn)时间搜索。例如,在{2,2,2,2,2,2,2,2,0,2}和{2,0,2,2,2,2,2,2,2,2,2,2}中搜索0。
似乎不可能通过在中间做恒定数量的比较来决定是递归左半边还是右半边。