《剑指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 ] , ⋯ , k [ m ] k\left[ 0 \right],k\left[ 1 \right], \cdots ,k\left[ m \right] k[0],k[1],⋯,k[m]。请问 k [ 0 ] × k [ 0 ] × ⋯ × k [ m ] k\left[ 0 \right] \times k\left[ 0 \right] \times \cdots \times k\left[ m \right] k[0]×k[0]×⋯×k[m]可能的最大乘积是多少?例如当绳子的长度为8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18.
动态规划解法
动态规划的写法,分块处理,假如最大值是
f
(
n
)
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(n)=max(f(n−i)×f(i)),假如我们的目标是
f
(
6
)
f(6)
f(6),那么是不是可以通过得到
f
(
3
)
f(3)
f(3) 和
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)
欢迎大家关注我的个人公众号,同样的也是和该博客账号一样,专注分享技术问题,我们一起学习进步