10- I 斐波那契数列
思路一:打表,O(1)时间复杂度值得拥有;
思路二:每次计算出新的值判断是否大于100000007,若大于求余;
思路三:求余时间较长,可改为若大于100000007,则减掉100000007。因为前面的两个数也都小于100000007,所以新的数不会大于100000007的二倍,放心的做减法就可以了。
代码三:
class Solution:
def mod(self, val):
if val >= 1000000007:
return val - 1000000007
else:
return val
def fib(self, n: int) -> int:
if n == 0:
return 0
elif n == 1 or n == 2:
return 1
pre, cur, nex = 1, 1, 0
for i in range(2, n):
nex = self.mod(pre + cur)
pre = self.mod(cur)
cur = self.mod(nex)
return nex
10- II. 青蛙跳台阶问题
思路:与上题斐波那契非常相似,简单的动态规划问题。因为一次只能走1步或2步,所以某一个台阶 i 的的走法仅仅由 i-1 和 i-2决定,即 i-1 走一步到达 i,i-2 走两格到达 i,所有就构成了状态转移关系:dp[i]=dp[i-1]+dp[i-2]
代码:
class Solution:
def mod(self, val):
if val >= 1000000007:
return val - 1000000007
else:
return val
def numWays(self, n: int) -> int:
if n == 0:
return 1
elif n == 1 or n == 2:
return n
dp = [0 for _ in range(n+1)]
dp[1], dp[2] = 1, 2
for i in range(3, n+1):
dp[i] = self.mod(dp[i-1] + dp[i-2])
return dp[n]
11. 旋转数组的最小数字
思路一:遍历一遍,找到第一个打破「非递减」规则的元素即可,时间复杂度O(n);
思路二:二分法,时间复杂度为O(log n)。设最左侧的索引为l,右侧索引为r,中间的mid=(l+r)/2,输入数组为a。分以下几种情况:
- 如果 a[mid] < a[r],表明右半部分的数组是单调递增的,此时最小元素在mid或左半部分数组,此时应该让 r=mid;
- 如果 a[mid] > a[r],表明右侧数并非单调递增的,其实右侧可以看作两个单调递增的序列的拼接,从mid到最小值前面一个元素单调,最小值到最后单调,因此最小值在数组的右侧部分,此时令 l=mid+1(mid不可能是最小值)
- 如果 a[mid] == a[r],此时不能推断出 l,r,mid三者一定相等,因为数组中可能存在重复的元素。此时也存在两种情况:(1)最右侧元素不是最小值,最小值存在于数组左侧或右侧(非最后一个元素),此时做 r-- 筛除掉这个右侧元素即可;(2)最右侧元素是最小值,因为 a[mid] 等于 a[r],所以也可以直接筛掉 a[r]。综上,这种情况下直接做 r-- 筛除掉最右侧元素即可。
代码二:
class Solution:
def minArray(self, numbers: List[int]) -> int:
n = len(numbers)
left, right = 0, n-1
while left < right:
mid = int((left + right) / 2)
if numbers[mid] > numbers[right]:
left = mid + 1
elif numbers[mid] < numbers[right]:
right = mid
else:
right -= 1
return numbers[left]