算法10:从一个非递减数组的旋转数组中找其最小元素值

#算法:求取一个旋转数组的最小元素
#旋转数组定义:一个数组中若干元素移到数组的末尾,称之为数组的旋转
#例:array = [1,2,3,4];数组的一个旋转为:array = [3,4,1,2]
#移动若干元素,若干包括1个,2个,3个...
#问题:若给定一个非递减数组的旋转,如何找到该数组中的最小元素?
#不知道什么情况下,会出现这么个问题!
#分析:该旋转数组原来是有序,且非递减,则其旋转后有特点:
#其被移动的若干位构成递增数列
#剩余的数列构成另一个递增数列,且这个数列的所有大于等于前一个数列
#因此该旋转数组特点:先递增后减到最小值,然后停止(只移动了一位)或者递增(移动了1位以上)
#例子:原数组 = [1,2,3,4,5]
#旋转数组1 = [2,3,4,5,1](移动了一位,先增加后减到最小值,停止)
#旋转数组2 = [3,4,5,1,2](移动了两位[多于一位],先增加后减小到最小值,再递增)
#因此利用"二分法"的思想,我们可以通过中点划分构成的左,右两数列的递增情况,来确定最小值所在的数列区间范围
#如果一数列一直递增,则最小数值便在另外一数列中;反之,若该数列有递减情况,
#则说明最小值在此数列范围之内。
#那么如何判断数列的递增与否呢?
#由旋转矩阵的特点可知:若为递增,其首尾两个元素值大小保持递增顺序不变;反之,其数列首尾两个元素大小值递减。
#因此,通过划分的两个数列的首尾元素值的大小关系便可以确定最小元素所在区间范围。

#1.在数组中元素没有冗余情况时,且有效率要求时;
def MinNumInRotateArray(arr):
    #判断数组元素是否为空,空的话返回值为0
    if len(arr) == 0:
        return 0
    #设定数组索引起点和终点
    l = 0
    h = len(arr) - 1
    #循环结束条件为区间范围合理:下限小于上限
    while l<h:
        #计算区间中心点
        m = int(l+(h-l)/2)
        #如果中心点小于数列右端点,说明从中心点到右端点的数组元素构成的数列是递增数列
        #旋转数组的最小元素不在此数列中,需将右端点进行更新,设置其为中心点
        #反之,如果中心点处值大于右端点处值,说明该数列包含被"旋转"的元素
        #即该数列中含有旋转数组的最小元素,将区间下限设置为该中心点的上一位
        #(因为求最小值,既然中心点大于右端点,则其不可能是最小值了,有理由将其排除出去)
        #即,在区间是递增情况下时候,更新右端点为中心点值(中心点处是小于区间右端点值的小值点,不能排除);
        #在区间不是递增的情况时,更新左端点为中心点的下一个点值(中心点处是大于区间右端点值的大值点,理应排除)
        if arr[m] <= arr[h]:
            h = m
        else:
            l = m + 1
    return arr[l]


#2.在数组中有冗余元素时,可能会遇到的情况:中心点,左端点,右端点三处值相同,无法确定
#旋转数组最小元素所在区间时,需要用到顺序查找。
def MinNumInRotateArrayWithSearch(arr):
    if len(arr) == 0:
        return 0
    l = 0
    h = len(arr) - 1
    while l<h:
        mid = int(l + (h-l)/2)
        if arr[l] == arr[h] and arr[h] == arr[mid]:
            SpecialSequentialSearch(arr,l,h)
        elif arr[mid] <= arr[h]:
            h = mid
        else:
            l = mid + 1
    return arr[l]


#一般顺序查找函数
def SequentialSearch(arr,l,h):
    minValue = arr[l]
    for i in range(l+1,h+1):
        if arr[i]<minValue:
            minValue = arr[i]
    return minValue


#特殊查找函数
def SpecialSequentialSearch(arr,l,h):
    #因为旋转数组的特殊性
    #一旦某个数组元素比其后面的元素大,便知道该处点是旋转点
    #其后跟随的是另一个被移动的递增数列,因此在循环中找到减小点
    #即找到了最小值
    #若循环中没有找到最小点,即该数组一直不减小,说明该数组已经有序
    #开始点即为最小点,即返回arr[l]
    for i in range(l,h):
        if arr[i]>arr[i+1]:
            return arr[i+1]
    return arr[l]


#1.处理没有冗余元素,且追求高效率的一般情况,用MinNumInRotateArray(arr)函数
a = [3,4,1,2]
print(MinNumInRotateArray(a))
#2.处理可能因为冗余元素导致无法确定最小值所在区间的情况,用MinNumInRotateArrayWithSearch(arr)函数
print(MinNumInRotateArrayWithSearch(a))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值