540. 有序数组中的单一元素
给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。
示例 1:
输入: [1,1,2,3,3,4,4,8,8]
输出: 2
示例 2:
输入: [3,3,7,7,10,11,11]
输出: 10
思路:
利用逻辑运算XOR的性质AAB=B。
class Solution(object):
def singleNonDuplicate(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
i = 0
for num in nums:
i ^= num
return i
301. 删除无效的括号
给你一个由若干括号和字母组成的字符串 s ,删除最小数量的无效括号,使得输入的字符串有效。
返回所有可能的结果。答案可以按 任意顺序 返回。
示例 1:
输入:s = “()())()”
输出:[“(())()”,“()()()”]
示例 2:
输入:s = “(a)())()”
输出:[“(a())()”,“(a)()()”]
示例 3:
输入:s = “)(”
输出:[“”]
class Solution:
def removeInvalidParentheses(self, s: str) -> List[str]:
res = []
lremove, rremove = 0, 0
for c in s:
if c == "(":
lremove += 1
elif c == ")":
if lremove == 0:
rremove += 1
else:
lremove -= 1
def isValid(ss):
cnt = 0
for c in ss:
if c == "(":
cnt += 1
elif c == ")":
cnt -= 1
if cnt < 0:
return False
return cnt == 0
def dfs(s, start, lremove, rremove):
if lremove == 0 and rremove == 0:
if isValid(s):
res.append(s)
return
for i in range(start, len(s)):
if i > start and s[i] == s[i-1]:
continue
if lremove + rremove > len(s) - i: # 剪枝
break
if lremove > 0 and s[i] == "(":
dfs(s[:i]+s[i+1:], i, lremove-1,rremove)
if rremove > 0 and s[i] == ")":
dfs(s[:i]+s[i+1:], i, lremove, rremove-1)
dfs(s, 0, lremove, rremove)
return res
437. 路径总和 III
给定一个二叉树的根节点root,和一个整数targetSum,求该二叉树里节点值之和等于targetSum的路径的数目。
路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
示例 1:
输入:root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8
输出:3
解释:和等于 8 的路径有 3 条,如图所示。
示例 2:
输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
输出:3
提示:
二叉树的节点个数的范围是 [0,1000]
-10^9 <= Node.val <= 10^9
-1000 <= targetSum <= 1000
class Solution:
def pathSum(self, root: TreeNode, targetSum: int) -> int:
def rootSum(root, targetSum):
if root is None:
return 0
ret = 0
if root.val == targetSum:
ret += 1
ret += rootSum(root.left, targetSum - root.val)
ret += rootSum(root.right, targetSum - root.val)
return ret
if root is None:
return 0
ret = rootSum(root, targetSum) #以root为根节点进行计算,也就是在这个函数里面不管怎样计算sum,都是会带上root的值
ret += self.pathSum(root.left, targetSum) #注意这里是pathSum函数,相当于是另一棵树了
ret += self.pathSum(root.right, targetSum)
return ret
204. 计数质数
输入: 10
输出: 4
解释: 小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。
要注意的是1不是质数哦!!
先将2-N的各数放入表中,然后在2的上面画一个圆圈,然后划去2的其他倍数;第一个既未画圈又没有被划去的数是3,将它画圈,再划去3的其他倍数;现在既未画圈又没有被划去的第一个数 是5,将它画圈,并划去5的其他倍数……依次类推,一直到所有小于或等于N的各数都画了圈或划去为止。这时,表中画了圈的以及未划去的那些数正好就是小于 N的素数。
class Solution:
def countPrimes(self, n: int) -> int:
isPrimes = [1] * n
res = 0
for i in range(2, n):
if isPrimes[i] == 1: res += 1
j = i
while i * j < n:
isPrimes[i * j] = 0
j += 1
return res
补充题21. 字符串相减
如果你还没做过415. 字符串相加,建议先做一下。减法比加法稍微麻烦一点,但核心思路相似。
题目描述
给定两个字符串形式的非负整数 num1 和num2 ,计算它们的差。
注意:
num1 和num2 都只会包含数字 0-9
num1 和num2 都不包含任何前导零
你不能使用任何內建 BigInteger 库
题目分析
两个非负整数相减的结果可能为负。
因此,首先比较两个数的大小。
如代码所示,当小减大时,需将两个参数调换一下位置执行减法,在结果前填上负号即可
注意:结果为0时不加负号。
def sub(a, b):
res = ""
borrow = 0
i, j = len(a) - 1, len(b) - 1
while i >= 0 or j >= 0:
x = int(a[i]) if i >= 0 else 0
y = int(b[j]) if j >= 0 else 0
z = (x - borrow - y + 10) % 10
res += str(z)
borrow = 1 if x - borrow - y < 0 else 0
i -= 1
j -= 1
res = res[::-1]
# 删除前导0。循环条件是len(res)-1是为防止"0000"的情况
pos = 0
while pos < len(res) - 1 and res[pos] == '0':
pos += 1
return res[pos:]
def cmp(a, b):
if len(a) == len(b):
return a < b
return len(a) < len(b)
def sub_strings(num1, num2):
if cmp(num1, num2):
res = sub(num2, num1)
if res != "0":
res = "-" + res
else:
res = sub(num1, num2)
return res
if __name__ == "__main__":
a = input()
b = input()
print(sub_strings(a, b))
567. 字符串的排列
给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。
换句话说,第一个字符串的排列之一是第二个字符串的子串。
示例1:
输入: s1 = “ab” s2 = “eidbaooo”
输出: True
解释: s2 包含 s1 的排列之一 (“ba”).
示例2:
输入: s1= “ab” s2 = “eidboaoo”
输出: False
注意:
输入的字符串只包含小写字母
两个字符串的长度都在 [1, 10,000] 之间
思路
最开始使用的是全排列,然后比较的时候发现T了,然后就想到用排序的方式,emmmm又T了。 最后想到用滑动窗口 然后A了
每次取 s1 长度的窗口 然后排序比较,相等就返回True
class Solution(object):
def checkInclusion(self, s1, s2):
"""
:type s1: str
:type s2: str
:rtype: bool
"""
l1,l2 = len(s1),len(s2)
s1 = sorted(s1)
for i in range(l2-l1+1):
t = s2[i:i+l1]
if sorted(t) == s1:
return True
return False
剑指 Offer 46. 把数字翻译成字符串
给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。
一个数字可能有多个翻译。
请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。
示例 1:
输入: 12258
输出: 5
解释: 12258有5种不同的翻译,分别是"bccfi", “bwfi”, “bczi”, “mcfi"和"mzi”
1.将输入num从int型转为str snum,dpres保存结果,dp[i]表示snum[0:i-1]有多少种组合方式。
dp[0]=0,dp[1]=0(对应snum[0]一个字符,只有一种方法),要注意dpres的下标比snum大1。
2.对snum的第i-1个字符进行判断:
如果snum[i-2]+snum[i-1]<26并且snum[i-2]不为0(避免06 05这样不合法的情况),则dpres[i]=dpres[i-1]+dpres[i-2],因为第i-1个字符有自己单独翻译为一个字母以及和前面一个字符一起翻译两种选择。
如果不满足以上条件,说明snum[i-1]只有单独翻译一个选项,dpres[i]=dpres[i-1]
class Solution(object):
def translateNum(self, num):
dpres = []
dpres.append(1)
dpres.append(1)
snum = str(num)
if len(snum)<=1:
return 1
for i in range(2,len(snum)+1):
if snum[i-2]+snum[i-1]<"26" and snum[i-2]!='0':
dpres.append(dpres[i-1]+dpres[i-2])
else:
dpres.append(dpres[i-1])
return dpres[len(snum)]
面试题 08.12. 八皇后
171. Excel表列序号
class solution(object):
def titleToNumber(self, s: str) -> int:
# 理解为26进制的转化
# 初始化结果 ans = 0,遍历时将每个字母与 A 做减法,因为 A 表示 1,所以减法后需要每个数加 1,计算其代表的数值 num = 字母 - ‘A’ + 1. 因为有 26 个字母,所以相当于 26 进制,每 26 个数则向前进一位. 所以每遍历一位则ans = ans * 26 + num. 以 ZY 为例,Z 的值为 26,Y 的值为 25,则结果为 26 * 26 + 25=701.
ans = 0
for i in range(len(s)): #遍历每个字符
#ord()函数返回对应的十进制整数。
num = ord(s[i]) - ord('A') + 1 #将字符转化为数字 例如Z则为26
ans = ans * 26 + num
return ans
a = 'ZY'
c = solution()
b = c.titleToNumber(a)
print(b)
#时间复杂度O(n),空间复杂度:O(1)
525. 连续数组
给定一个二进制数组 nums , 找到含有相同数量的 0 和 1 的最长连续子数组,并返回该子数组的长度。
示例 1:
输入: nums = [0,1]
输出: 2
说明: [0, 1] 是具有相同数量0和1的最长连续子数组。
示例 2:
输入: nums = [0,1,0]
输出: 2
说明: [0, 1] (或 [1, 0]) 是具有相同数量0和1的最长连续子数组。
一位大佬的代码链接,理解了一下思路:建立字典是为了统计从头开始到当下某个坐标的0与1数量的差值,开始坐标为-1,差值为0。如果是1,加1;如果是0,减1。每次结果与字典里的比较,如果存在相同的,说明两者之间的0与1的数量是相同的;不存在,加入字典。
class Solution:
def findMaxLength(self, nums: List[int]) -> int:
# 前缀和字典: key为1的数量和0的数量的差值,value为对应坐标
hashmap = {0:-1}
# 当前1的数量和0的数量的差值
counter = ans = 0
for i,num in enumerate(nums):
# 每多一个1,差值+1
if num:
counter += 1
# 每多一个0,差值-1
else:
counter -= 1
# 如果存在1和0的数量差值相等的地方,那么说明后者到前者之前1和0的数量相等!
if counter in hashmap:
ans = max(ans, i - hashmap[counter])
else:
hashmap[counter] = i
return ans
80. 删除排序数组中的重复项 II
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定 nums = [1,1,1,2,2,3],
函数应返回新长度 length = 5, 并且原数组的前五个元素被修改为 1, 1, 2, 2, 3 。
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,0,1,1,1,1,2,3,3],
函数应返回新长度 length = 7, 并且原数组的前五个元素被修改为 0, 0, 1, 1, 2, 3, 3 。
你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以“引用”方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝 int len = removeDuplicates(nums);
// 在函数里修改输入数组对于调用者是可见的。 // 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。
思路一(从后往前遍历,使用pop)
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
for i in range(len(nums)-1,1,-1):
if nums[i] == nums[i-2]:
nums.pop(i)
return len(nums)
思路二(不适用pop的情况下,用双指针、滑动窗口)
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
if len(nums) < 2:
return len(nums)
s = 2
for i in range(2,len(nums)):
if nums[i] !=nums[s-2]:
nums[s] = nums[i]
s += 1
return s
最多出现K次时的代码(K>1)
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
if len(nums) < k:
return len(nums)
s = k
for i in range(k,len(nums)):
if nums[i] !=nums[s-k]:
nums[s] = nums[i]
s += 1
return s
149. 直线上最多的点数
给定一个二维平面,平面上有 n 个点,求最多有多少个点在同一条直线上。
思路:
首先如果点数小于 3 个,直接返回点数(因为肯定能组成直线)。
我们对所有点遍历,记录包含这个点在内的所有直线中,能组成的点数最多的直线的点数数量。
返回这些数量的最大值。
怎么遍历呢?
我们对一个点遍历的时候,再遍历所有点
维护两个变量
一个来记录和这个点相同的点(重复点)
一个来记录非重复点和这个点组成的各个直线以及它们拥有的点数
即使用哈希表,键为斜率,值是这个直线拥有的点数。这里使用 Counter 直接统计各个直线拥有的点数。
返回最多拥有点数的直线所拥有的点数与重复点之和。
可以参考分步代码
重复点的处理是难点。
from decimal import *
class Solution:
def maxPoints(self, points: List[List[int]]) -> int:
maxans = 0
def K(i,j):
return float('Inf') if i[1] - j[1] == 0 else Decimal(i[0] - j[0])/Decimal(i[1] - j[1])
## 两点确定一条直线
if len(points) <= 2:
return len(points)
## 遍历所有点
for i in points:
same = sum(1 for j in points if j == i)
hashmap = Counter([K(i,j) for j in points if j != i])
tempmax = hashmap.most_common(1)[0][1] if hashmap else 0
maxans = max(same + tempmax, maxans)
return maxans
994. 腐烂的橘子
在给定的网格中,每个单元格可以有以下三个值之一:
值 0 代表空单元格;
值 1 代表新鲜橘子;
值 2 代表腐烂的橘子。
每分钟,任何与腐烂的橘子(在 4 个正方向上)相邻的新鲜橘子都会腐烂。
返回直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1。
输入:[[2,1,1],[1,1,0],[0,1,1]]
输出:4
示例 2:
输入:[[2,1,1],[0,1,1],[1,0,1]]
输出:-1
解释:左下角的橘子(第 2 行, 第 0 列)永远不会腐烂,因为腐烂只会发生在 4 个正向上。
示例 3:
输入:[[0,2]]
输出:0
解释:因为 0 分钟时已经没有新鲜橘子了,所以答案就是 0 。
思路:
这种需要一圈一圈往外传播的一般用BFS解,
先找到起始所有腐烂的橘子,
然后循环处理,把新腐烂的橘子加入下一次循环的队列中,
当下一次循环的队列为空时,说明不能继续腐烂了,
判断一下还有没有新鲜的橘子,如果有,就返回-1,否则返回分钟数
class Solution(object):
def orangesRotting(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
dx = [1, -1, 0, 0]
dy = [0, 0, 1, -1]
rotlist = list()
for i in range(len(grid)):
for j in range(len(grid[0])):
if grid[i][j] == 2:
rotlist.append([i, j])
minute = 0
while(rotlist):
newrotlist = list()
for rotnode in rotlist:
x0 = rotnode[0]
y0 = rotnode[1]
for k in range(4):
x = x0 + dx[k]
y = y0 + dy[k]
if 0 <= x < len(grid) and 0 <= y < len(grid[0]) and grid[x][y] == 1:
grid[x][y] = 2
newrotlist.append([x,y])
if not newrotlist:
break
rotlist = newrotlist[:]
minute += 1
for row in grid:
for i in row:
if i == 1:#还有新鲜的
return -1
return minute