第一周LeetCode记录
9.7 1.两数之和
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
思路一
遍历数组,拿sum去减遍历的数,判断是否存在,存在返回下标
思路二
先排序,首尾相加,若大于target,right-1,小于target,left+1
我的解法:
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
new_nums = sorted(nums)
left = 0
right = len(nums) - 1
while new_nums[left] + new_nums[right] != target:
if left == right:
print("无结果")
break
elif new_nums[left] + new_nums[right] > target:
right -= 1
else:
left += 1
if new_nums[left] == new_nums[right]:
start = nums.index(new_nums[left])
end = nums.index(new_nums[right], left + 1, len(new_nums))
else:
start = nums.index(new_nums[left])
end = nums.index(new_nums[right])
return sorted([start,end])
错误总结:
1. 在list.index()方法中,忽略了两个相等的值下标一样。就要从后面找end。
2. 结尾排序,重要!
最优解:
为了对运行时间复杂度进行优化,我们需要一种更有效的方法来检查数组中是否存在目标元素。如果存在,我们需要找出它的索引。保持数组中的每个元素与其索引相互对应的最好方法是什么?哈希表。
通过以空间换取速度的方式,我们可以将查找时间从 O(n)O(n) 降低到 O(1)O(1)。哈希表正是为此目的而构建的,它支持以 近似 恒定的时间进行快速查找。我用“近似”来描述,是因为一旦出现冲突,查找用时可能会退化到 O(n)O(n)。但只要你仔细地挑选哈希函数,在哈希表中进行查找的用时应当被摊销为 O(1)O(1)。
一个简单的实现使用了两次迭代。在第一次迭代中,我们将每个元素的值和它的索引添加到表中。然后,在第二次迭代中,我们将检查每个元素所对应的目标元素(target - nums[i]target−nums[i])是否存在于表中。注意,该目标元素不能是 nums[i]nums[i] 本身!
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
map.put(nums[i], i);
}
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (map.containsKey(complement) && map.get(complement) != i) {
return new int[] { i, map.get(complement) };
}
}
throw new IllegalArgumentException("No two sum solution");
}
}
最优解总结:
- 利用hash存储index和value,在原list中直接找
- 因为题设中暗含了不可能有3个同样的数,所以此解法巧妙的不用考虑重复键值对的问题。
9.8 2. 快乐数
编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果 可以变为 1,那么这个数就是快乐数。
思路
递归,每次调用本身,直至符合条件。并且每次存储算出来的数,如果出现过返回false。
我的解法:
class Solution:
all_list = []
@classmethod
def isHappy(cls, n: int) -> bool:
if n in Solution.all_list:
return False
li = list(str(n))
sum = 0
for i in li:
sum += int(i) * int(i)
if sum != 1:
Solution.all_list.append(n)
boolean = Solution.isHappy(sum)
return boolean
else:
return True
错误总结:
- 解法貌似没问题,但是leetcode不支持。
- 因为用的是递归,不知道怎么在方法里记录已有数据
最优解:
class Solution:
def isHappy(self, n: int) -> bool:
seen = {1}
while n not in seen:
seen.add(n)
n = sum(int(i) ** 2 for i in str(n))
return n == 1
总结:
没有用递归调用此方法,所以不会出现无法记录的情况。另外,用set来记录,达到记录的目的。
9.9 3.排列硬币
你总共有 n 枚硬币,你需要将它们摆成一个阶梯形状,第 k 行就必须正好有 k 枚硬币。
给定一个数字 n,找出可形成完整阶梯行的总行数。
n 是一个非负整数,并且在32位有符号整型的范围内。
n = 5
硬币可排列成以下几行:
¤
¤ ¤
¤ ¤
因为第三行不完整,所以返回2
思路
观察棋子总数和行数的关系
我的解法
class Solution:
def arrangeCoins(self, n: int) -> int:
if n == 0:
return 0
elif n == 1:
return 1
else:
for i in range(1,n+1):
if (1 + i) * i / 2 == n:
return i
elif (1 + i) * i / 2 < n:
continue
else:
return i-1
错误总结
当等于n的时候可以直接返回行数。
0,1是特殊情况没有考虑到
最优解
class Solution:
def arrangeCoins(self, n: int) -> int:
return int((2*n+1/4)**0.5-1/2)
n列一共需要(1+n)*n/2个棋子,y = (1+n)*n/2 => 2y + 1/4 = n^2 + n + 1/4 => 2y + 1/4 = (n+1/2)^2
=> 换成n=f(y)表示
总结:
x, y之间有联系,可以先建立函数关系式,通过关系式来完成相互之间转化。
9.12 4. 猜数字
小A 和 小B 在玩猜数字。小B 每次从 1, 2, 3 中随机选择一个,小A 每次也从 1, 2, 3 中选择一个猜。他们一共进行三次这个游戏,请返回 小A 猜对了几次?
输入:guess = [1,2,3], answer = [1,2,3]
输出:3
解释:小A 每次都猜对了。
思路
两个数组索引的数字比较
我的解法:
def game(self, guess: list, answer: list) -> int:
sum = 0
for i in range(3):
if guess[i] == answer[i]:
sum += 1
return sum
最优解
return sum(guess[i]==answer[i] for i in range(len(guess)))
总结
可以对bool求和。
9.12 5.珠玑妙算
计算机有4个槽,每个槽放一个球,颜色可能是红色(R)、黄色(Y)、绿色(G)或蓝色(B)。例如,计算机可能有RGGB 4种(槽1为红色,槽2、3为绿色,槽4为蓝色)。作为用户,你试图猜出颜色组合。打个比方,你可能会猜YRGB。要是猜对某个槽的颜色,则算一次“猜中”;要是只猜对颜色但槽位猜错了,则算一次“伪猜中”。注意,“猜中”不能算入“伪猜中”。
给定一种颜色组合solution和一个猜测guess,编写一个方法,返回猜中和伪猜中的次数answer,其中answer[0]为猜中的次数,answer[1]为伪猜中的次数。
输入: solution="RGBY",guess="GGRR"
输出: [1,1]
解释: 猜中1次,伪猜中1次。
思路
两个列表取交集可以得到伪猜中数量,用猜数字的算法得到猜中数量,伪猜中减去猜中
我的解法(未解出来):
伪猜中没有想到要利用hash来记录,值作为键,出现的次数作为值。取交集可以获取。
错误总结:
- 伪猜中的算法错误。
最优解
class Solution:
def masterMind(self, solution: str, guess: str):
from collections import Counter
total = sum((Counter(solution) & Counter(guess)).values())
right = sum(1 for (i, j) in zip(solution, guess) if i == j)
return [right, total - right]
总结:
Couter可以返回每个元素出现了几次,并且可以取交集等操作,获得伪猜中次数。要复习一下cookbook里面的常用工具类方法。
9.13 6.判断能否形成等差数列
给你一个数字数组 arr 。
如果一个数列中,任意相邻两项的差总等于同一个常数,那么这个数列就称为 等差数列 。
如果可以重新排列数组形成等差数列,请返回 true ;否则,返回 false 。
思路
先将数组排序,遍历后一项减前一项得到差,差值不变返回true。
我的解法
if len(arr) == 2:
return True
if len(arr) < 2:
return False
arr.sort()
a = arr[1] - arr[0]
for i in range(len(arr)-1):
b = arr[i+1] - arr[i]
if b != a:
return False
return True
最优解
class Solution:
def canMakeArithmeticProgression(self, arr: List[int]) -> bool:
arr.sort()
a=[arr[i+1]-arr[i] for i in range(len(arr)-1)]
if len(set(a))==1:
return True
else:
return False
总结:
利用列表解析式可以得到一个列表,里面元素是差值,判断差值是否一样就用set去重。