剑指offer (8) 旋转数组

1. 查找和排序

查找:顺序查找、二分查找、二叉搜索树、哈希表

顺序查找:T(n) = O(n)           std::find

二分查找:T(n) = O(log n)      std::binary_search
 std::lower_bound  std::upper_bound

哈希表:   T(n) = O(1)          
std::unordered_map

排序:快速排序、归并排序、堆排序、计数排序、冒泡排序


 1 int Partition(std::vector<int>& vec, int first, int last)
2 {
3 if (first < 0 || last >= vec.size()) {
4 throw invalid_argument("invalid_argument");
5 }
6
7 int index = RandomInRange(first, last);
8 std::swap(vec.at(index), vec.at(first));
9 int midIndex = first;
10 for (int i = first + 1; i <= last; ++i) {
11 if (vec.at(i) < vec.at(first)) {
12 swap(vec.at(++midIndex), vec.at(i));
13 }
14 }
15 std::swap(vec.at(first), vec.at(midIndex));
16 return midIndex;
17 }
18
19 int RandomInRange(int first, int last)
20 {
21 assert(first <= last);
22 int res = rand() % (last - first + 1) + first;
23 return res;
24 }

面试题8:旋转数组

题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转,例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转

现有一个非降序数组的一个旋转

a. 输出旋转数组的最小元素。

b. 输出旋转数组的最大元素。

c. 查找旋转数组的某个元素。

a. 输出旋转数组的最小元素。

旋转之后的数组实际上可以划分为两个排序的子数组,第一个子数组的元素都 >= 第二个子数组的元素

最小和最大 这两个元素刚好组成了 这两个子数组的分界线

step1. 我们用两个指针分别指向元素的第一个元素和最后一个元素

step2. 找到数组中间的元素,如果中间元素 >= 第一个指针指向元素,则其位于 第一个子数组,此时 最小元素应该位于
该中间元素的后面,我们可以将第一个指针指向 该中间元素

step3. 如果中间元素 <= 第二个指针指向元素,则其位于 第二个子数组,此时 最小元素应该位于
该中间元素的前面,我们可以将第二个指针指向 该中间元素

每次迭代都可以缩小搜索范围,如此往复,第一个指针总是指向
第一个子数组的元素,第二个指针总是指向 第二个子数组的元素

最终,两个指针会指向两个相邻的元素

第一个指针指向 第一个子数组的最后一个元素,即该数组的最大值

第二个指针指向 第二个子数组的第一个元素,即该数组的最小值

注意两点:

(1) 数组本身有序的情况,即旋转了0位

(2) 数组中存在重复元素的情况:

当 vec.at(low) == vec.at(mid) == vec.at(high) 我们无法确认 中间元素是属于第一个子数组
还是第二个子数组,只能采取线性遍历


 1 int FindRotateMin(const std::vector<int>& vec)
2 {
3 if (vec.size() == 0) {
4 throw std::invalid_argument("invalid_argument");
5 }
6
7 int low = 0;
8 int high = vec.size() - 1;
9 int mid = low; // 如果数组原本有序,返回vec.at(mid)正确
10 while (vec.at(low) >= vec.at(high)) { // 保证 low 总是指向 第一个子数组, high 总是指向 第二个子数组
11 mid = low + (high - low) / 2;
12 if (high - low == 1) return vec.at(high);
13
14 if (vec.at(low) == vec.at(mid) && vec.at(mid) == vec.at(high)) {
15 return *(std::min_element(vec.begin() + low, vec.begin() + high + 1));
16 }
17
18 if (vec.at(low) <= vec.at(mid)) {
19 low = mid;
20 }
21
22 if (vec.at(mid) <= vec.at(high)) {
23 high = mid;
24 }
25 }
26 return vec.at(mid);
27 }

b. 找出旋转数组的最大值


 1 int FindRotateMax(const std::vector<int>& vec)
2 {
3 if (vec.size() == 0) {
4 throw std::invalid_argument("invalid_argument");
5 }
6
7 int low = 0;
8 int high = vec.size() - 1;
9 int mid = high;
10 while (vec.at(low) >= vec.at(high)) {
11 mid = low + (high - low) / 2;
12 if (high - low == 1) return vec.at(low);
13
14 if (vec.at(low) == vec.at(mid) && vec.at(mid) == vec.at(high)) {
15 return *(std::max_element(vec.begin() + low, vec.begin() + high + 1));
16 }
17
18 if (vec.at(low) <= vec.at(mid)) {
19 low = mid;
20 }
21
22 if (vec.at(mid) <= vec.at(high)) {
23 high = mid;
24 }
25 }
26 return vec.at(mid);
27 }

c. 查找旋转数组的某个元素

不存在重复元素的情况

存在重复元素的情况

剑指offer (8) 旋转数组,布布扣,bubuko.com

时间: 2024-10-19 03:56:21

剑指offer (8) 旋转数组的相关文章

【剑指offer】旋转数组的最小数字

题目描述: 把一个数组最开始的若干个元素搬到数组的末尾,称之为数组的旋转.输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素.例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1. 分析描述: 求一个数组中的最小值,最简单的办法就是逐个比较数组中各个元素的值,遍历完整个数组,即可得数组中最小元素.但这种方法存在的问题是时间复杂度为O(n). 利用题目中给出的条件:递增排序数组的旋转.利用递增排序的特点,可以用二分查找方法实现时间复杂度为O(logn)的查找.

【剑指offer】旋转数组中的最小值

题目总结: 1.若没有进行旋转,或者说旋转后的效果跟没有旋转是一样的,那么index1指示的值小于index2指示的值,返回index1的值. 2.若是一般性的旋转,那么最小的值旋转后肯定在中间,那么我们就可以从两边向中间夹逼. 3.夹逼的过程中,若 [ index1, middle ] 是有序的,说明这部分子区间没被破坏,旋转所移动的元素都在middle 的后面,那么最小值可定也在后面的部分,令 index1 = middle,继续向后夹逼:同理,若 [ middle ,index2 ] 是有

剑指OFFER之旋转数组的最小数字(九度OJ1386)

题目描述: 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转.输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素.例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1. 输入: 输入可能包含多个测试样例,对于每个测试案例, 输入的第一行为一个整数n(1<= n<=1000000):代表旋转数组的元素个数. 输入的第二行包括n个整数,其中每个整数a的范围是(1<=a<=10000000). 输出: 对应每个测试案例, 输出旋转数组

剑指Offer:旋转数组的最小数字【11】

剑指Offer:旋转数组的最小数字[11] 题目描述 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素. 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1. NOTE:给出的所有元素都大于0,若数组大小为0,请返回0. 解题分析 我们用两个坐标,Left,Right分别表示左右两个递增序列的下标,刚开始L为0,R为4: 当Arr[Mid]>Arr[Left],可以说明,Mid及左边构

剑指Offer 11. 旋转数组的最小数字

12345678910111213141516171819202122232425262728293031323334353637383940414243 public class { public int minNumberInRotateArray(int[] array) { int index1 = 0; int index2 = array.length - 1; int indexMid = 0; if (array.length == 0) { return 0; } while

leetcode之Search in Rotated Sorted Array,剑指offer之旋转数组的最小数字

Search in Rotated Sorted Array Suppose a sorted array is rotated at some pivot unknown to you beforehand. (i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2). You are given a target value to search. If found in the array return its index, otherwise retu

【Java】 剑指offer(10) 旋转数组的最小数字

本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转.输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素.例如数组{3, 4, 5, 1, 2}为{1, 2, 3, 4, 5}的一个旋转,该数组的最小值为1. 思路 数组在一定程度上是排序的,很容易分析出:可以采用二分法来寻找最小数字. 但是这里面有一些陷阱: 1.递增排序数组的本身是自己的旋转,则最小数字

剑指OFFER之旋转数组的最小数字

题目描述: 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转.输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素.例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1. 输入: 输入可能包含多个测试样例,对于每个测试案例, 输入的第一行为一个整数n(1<= n<=1000000):代表旋转数组的元素个数. 输入的第二行包括n个整数,其中每个整数a的范围是(1<=a<=10000000). 输出: 对应每个测试案例, 输出旋转数组

【剑指offer】旋转数组的最小值

题目描述 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转.输入一个非递减序列的一个旋转,输出旋转数组的最小元素.例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1. 输入描述 一个非递减序列的一个旋转数组 输出描述 输出旋转数组的最小元素 题目分析 原数组最小的值必然是第一个,旋转后数组是两个非递减数组的拼接,只要找到第二个非递减数组的第一个元素就是最小值.具体思路就是用二分查找算法.然后考虑特殊情况[1,1,1,1,1]遍历完没有结果,在最后