《剑指offer》 Day3

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/EngineerHe/article/details/100069057

《剑指offer》 Day3

旋转数组的最小数字

题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转数组,该数组的最小值为1

使用O(n)遍历解法

def min_num(nums):
    n = len(nums)
    if n <= 0:
        return None
    res = nums[0]
    
    for i in range(1, n):
        if res > nums[i]:
            res = nums[i]
    return res

上述方法这样做的话是可以的,但是题目意思提及到了旋转数组,显然虽然结果是对的,但是方法应该有其他的解法。采用二分查找法,使用双指针来不断的去逼近二分法的两个区间,最后当两个指针相差1的时候,右区间的那个指针指向的就是最小值,特殊的,当一个数组里的有多个重复的值,造成无法区分是左区间还是右区间的时候,通过遍历的方法确定

def min_rotate_array(nums):
    n = len(nums)
    
    if n <= 0:
        return None
    l = 0
    r = n-1
    mid = l
    while nums[l] >= nums[mid]:
        if r-l == 1:
            mid = r
            break
        mid = (l+r)//2
        
        if (nums[mid] == nums[l]) and (nums[mid] == nums[r]):
            return min_in_order(nums, l, r)
        
        if nums[mid] >= nums[l]:
            l = mid
        elif nums[mid] <= nums[r]:
            r = mid
        
    return nums[mid]
        
def min_in_order(nums, l, r):
    min_num = nums[l]
    
    for i in range(l+1, r+1):
        if min_num > nums[i]:
            min_num = nums[i]
            
    return min_num

二进制中1的个数

题目:请实现一个函数,输入一个整数,输出该整数二进制表示中1的个数。例如,把9表示成二进制是1001,有2位是1.因此,如果输入是9,则该函数输出2。

采用右移的操作,这种方法可能会陷入死循环,因为如果是一个负数,做高位一直是1,右移的过程中,就会产生0xFFFFFFFF的结果(在python中,这种方法是可以实现的,主要是python中没有明确的区分整数的类型,再者是因为python可以支持无限大的数,假如是0x80000000,在其他语言中是负数,但是在python中却是一个大数)

def number_of_1(num):
    cnt = 0    
    
    while num:
        if num & 1 != 0:
            cnt += 1
        num >>= 1
    return cnt

常规的,一个32位的数,判断里面有多少个1,定义一个flag,使其左移32次,每次都和对应位上的数字相与,那么久可以统计出这个数有多少个1

def number_of_1(num):
    flag = 1
    cnt = 0
    for i in range(32):
        if (num & flag):
            cnt += 1
        flag = flag << 1
    
    return cnt

高级解放:这个在我的博客中写过,还花了一个图,可以看我的这个博客 Leetcode191–位1的个数(含多种解法和图解分析),这种方法的话,数字里有几个1就循环几次

def number_of_1(num):
    
    cnt = 0
    
    while num:
        cnt += 1
        num = num & (num-1)
    
    return cnt

剪绳子

题目:给你一根长度为n的绳子,请把绳子剪成m段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k [ 0 ] , k [ 1 ] , &ThinSpace; , k [ m ] k\left[ 0 \right],k\left[ 1 \right], \cdots ,k\left[ m \right] 。请问 k [ 0 ] × k [ 0 ] × × k [ m ] k\left[ 0 \right] \times k\left[ 0 \right] \times \cdots \times k\left[ m \right] 可能的最大乘积是多少?例如当绳子的长度为8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18.

动态规划解法

动态规划的写法,分块处理,假如最大值是 f ( n ) f(n) ,那么如果分为两段可以有 n-1 中可能的选择。从这些分段中可以得到一个最大的值,即 f ( n ) = m a x ( f ( n i ) × f ( i ) ) f(n) = max(f(n-i) \times f(i)) ,假如我们的目标是 f ( 6 ) f(6) ,那么是不是可以通过得到 f ( 3 ) f(3) f ( 3 ) f(3) 的组合最大,然后在通过类似的求 f ( 3 ) f(3) 就可以了。然后对于动态规划,我们可以通过几个基层的元素,去寻找目标长度的最大值,当绳子长度小于3的时候,可以直接进行求解,当绳子大于3的时候,如绳子长6,就可以先求长度是4是的最大值,是4,然后吧他作为基元素,然后求长为5,最大就是 下标为2和下标为4的基元素之和,然后在把长度为5的添加进来;求6的时候就是判断这几个基元素的组合,可以得到 下标为3的基元素的平方是最大值,具体理解看下列代码

def max_product_dp(length):
    if length < 2:
        return 0
    if length == 2:
        return 1
    if length == 3:
        return 2
    
    products = [0]*(length+1)
    products[0] = 0
    products[1] = 1
    products[2] = 2
    products[3] = 3
    
    for i in range(4, length+1):
        max = 0
        for j in range(1, (i//2)+1):
            product = products[j]*products[i-j]
            if max < product:
                max = product
        products[i] = max
    
    return products[-1]

贪婪算法

贪心算法,首先可以知道对于一个长为n的绳子,如何才能使它们的乘积最大,那就是要尽可能的分成长度为3的子段,然后在剩下的长度中尽可能的分成长度为2的子段,那么这样的乘积就会最大。需要注意的是,如果剩余的长度是1,那么就要减少3的个数,因为3 * 1 < 2 * 2 .

def max_product_ga(length):
    if length < 2:
        return 0
    if length == 2:
        return 1
    if length == 3:
        return 2
    
    number_of_3 = length//3
    if (length - number_of_3*3) == 1:
        number_of_3 -= 1
    number_of_2 = (length - number_of_3*3)//2
    
    return (3**number_of_3)*(2**number_of_2)

猜你喜欢

转载自blog.csdn.net/EngineerHe/article/details/100069057