之前博客主要参考比特的免费课程和一部分百度、MSDN等资料。
后面一阶段会进行C语言练习,先在牛客网刷一点简单的题目。
描述
有一个长度为 n 的非降序数组,比如[1,2,3,4,5],将它进行旋转,即把一个数组最开始的若干个元素搬到数组的末尾,变成一个旋转数组,比如变成了[3,4,5,1,2],或者[4,5,1,2,3]这样的。请问,给定这样一个旋转数组,求数组中的最小值。
数据范围:1≤n≤10000,数组中任意元素的值: 0≤val≤10000
要求:空间复杂度:O(1) ,时间复杂度:O(logn)
空间复杂度O(1):算法在执行过程中,无论输入数据规模如何变化,所占用的额外空间保持恒定,即不随输入数据规模的增加而增加。
时间复杂度O(logn):算法的运行时间与输入规模n的对数成正比。
遍历查找:
//遍历查找
int min(int* nums, int numsLen) //函数所需参数为数组地址和数组元素数量
{
int i = 0;
int tmp = nums[0]; //创建临时变量,存储最小值,初始数值为首元素的值
for (i = 0; i < numsLen; i++) //遍历查找,遇到更小数值则存入tmp变量中
{
if (nums[i] < tmp)
{
tmp = nums[i];
}
}
return tmp; //查找结束后,返回tmp,即为最小值。
}
//遍历查找编程很简单,但是如果遇到数据量庞大,则会非常耗时。不符合时间复杂度O(logn)
遍历查找的程序写起来很简单,但是当数据量很庞大时,它所花费的时间和资源也会非常巨大。
二分查找:
//二分查找
//二分查找就需要考虑有重复数值的情况
int min(int* nums, int numsLen)
{
if (numsLen == 1) //如果数组只有一个数,直接返回该数
return nums[0];
int left = 0; //定义左边元素坐标
int right = numsLen - 1; //定义右边元素坐标
int mid = 0; //定义中间元素坐标
while (left < right)
{
mid = (left + right) / 2; //中间元素坐标为 左右两边中间,除不尽时靠近左边
if (nums[mid] < nums[right]) //如果中间值小于右边值,则最小值一定坐标在 [left,mid]之间
right = mid; //将右边界坐标改为mid
else if (nums[mid] > nums[right]) //如果中间值大于右边值,则最小值一定在[mid+1,right]之间
left = mid + 1; //将左边界改为 mid+1 。防止 mid = (left + right) / 2 除不尽陷入死循环
else
{
right--; //如果中间值等于右边值,则最小值可能在任何地方,此时采用遍历的思路逐个对比
}
if (left == right) //如果left = mid+1 后 left = right 了,则需要及时更新,防止mid除不尽输出nums[left]
mid = left;
}
return nums[mid]; //找出最小值坐标,返回其在数组中的值。
}
当中间值等于右边值时,最小值可能是下面任何一个1变成0(除中间值),所以要通过遍历的方式找到它的坐标,然后再按照二分法的节奏快速定位。
11111111111111111111
二分法虽然程序写起来麻烦一点,但数据庞大时,可以节省时间。