这是我参与8月更文挑战的第12天,活动详情查看:8月更文挑战
一、前言
大家好,本文章属于《剑指 Offer 每日一题》中的系列文章中的第 8 篇。
在该系列文章中我将通过刷题练手的方式来回顾一下数据结构与算法基础,同时也会通过博客的形式来分享自己的刷题历程。如果你刚好也有刷算法题的打算,可以互相鼓励学习。我的算法基础薄弱,希望通过这两三个月内的时间弥补这块的漏洞。本次使用的刷题语言为 Java ,预计后期刷第二遍的时候,会采用 Python 来完成。
- 刷题平台: leetcode 的剑指 Offer 专题
- 码云仓库地址
二、题目
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
示例1:
输入:[3,4,5,1,2]
输出:1
复制代码
示例2:
输入:[2,2,2,0,1]
输出:0
复制代码
三、解题
3.1 思路1:暴力遍历
题目讲解的花里胡哨的,总结就是旋转数组之后的新数组的特点是分两端,两段都是递增的,但是左边这端的元素都不小于右边这段的元素。所以找到右边这段的第一个元素就是最小值即可。不过,我们可以直接遍历求解,无需考虑其他的。
算法流程:
- 1、从下标为0的元素开始遍历
- 2、每次进行比较,如果当前元素比相邻的下一个元素大,则对应的下一元素即为最小值
- 3、如果查到最后一个元素都没有出现2这种情况,则下标为0 的元素为最小元素
3.1.1 代码
class Solution {
public int minArray(int[] numbers) {
for (int i = 1; i < numbers.length; i++) {
if(numbers[i-1]>numbers[i]){
return numbers[i];
}
}
return numbers[0];
}
}
复制代码
3.1.2 执行效果
3.1.3 时间复杂度
时间复杂度:最坏的情况下全部遍历完,O(n),最好的情况下O(1)
空间复杂度:O(1)
3.2 思路2:二分
这题可以采用二分查询,加快查询效率。
一般使用二分法需要满足两个要求:
- 1、顺序存储
- 2、元素有序
下面我们分情况来假设数组的可能:
- 1、假设没有重复数字,如果 a[mid] <a[right] 。说明后半部分有序,则最小值在mid 之前
- 2、假设没有重复数字,如果a[mid] > a[right] ,说明最小值在mid之后。
- 3、假设有重复数字,此时a[mid] == a[right],此时并不能确定最小值在mid 的前面还是后面,此时就需要把right左移一个然后继续之前的计算。无法二分的时候,就直接暴力循环查找即可。
3.2.1 代码
static class Solution2 {
public int minArray(int[] numbers) {
int start =0;
int end = numbers.length-1;
while(start!=end){
int mid =start+(end-start)/2;
if(numbers[mid]>numbers[end]){
start=mid+1;
}else if(numbers[mid]<numbers[end]){
end=mid;
}else{
int result = numbers[start];
for (int i = start; i <=end ; i++) {
if(numbers[i]<result) {result=numbers[i];}
}
return result;
}
}
return numbers[start];
}
}
复制代码
3.2.2 执行效果
3.2.3 时间复杂度
-
时间复杂度:平均时间复杂度为 O(\log n)O(logn),其中 nn 是数组 \it numbersnumbers 的长度。如果数组是随机生成的,那么数组中包含相同元素的概率很低,在二分查找的过程中,大部分情况都会忽略一半的区间。而在最坏情况下,如果数组中的元素完全相同,那么 \texttt{while}while 循环就需要执行 nn 次,每次忽略区间的右端点,时间复杂度为 O(n)O(n)。
-
空间复杂度:O(1)。
四、总结
本题变换了对二分查找的理解,同时这题中需要考虑相同数字的存在也不仅仅是普通的二分查找应用。
第一种的暴力法不推荐,第二种的二分法必须掌握。
今天的刷题就到这了,欢迎点赞评论交流,剑指 Offer 刷题之旅将继续展开!