【程序人生】leetcode(四)
说在前面
这篇博客中部分题目答案来自leetcode评论区,不是本人原创。感谢原作者的工作。这里记录在这篇博客中,方便日后回顾、学习
两数相除(转载)
给定两个整数,被除数dividend和除数divisor。将两数相除,要求不使用乘法、除法和mod运算符。
返回被除数dividend除以除数divisor得到的商。
整数除法的结果应当截去(truncate)其小数部分,例如:truncate(8.345)=8以及 truncate(-2.7335)=-2
示例:
输入: dividend = 10, divisor = 3
输出: 3
解释: 10/3 = truncate(3.33333…) = truncate(3) = 3
输入: dividend = 7, divisor = -3
输出: -2
解释: 7/-3 = truncate(-2.33333…) = -2
提示:
- 被除数和除数均为32位有符号整数。
- 除数不为0。
- 假设我们的环境只能存储32位有符号整数,其数值范围是 [ − 2 31 −2^{31} −231, 2 31 − 1 2^{31} − 1 231−1]。本题中,如果除法结果溢出,则返回 2 31 − 1 2^{31} − 1 231−1。
class Solution:
def divide(self, dividend: int, divisor: int) -> int:
result = 0
key =True
if str(dividend)[0] == "-" and str(divisor)[0] != "-":
key = False
if str(dividend)[0] != "-" and str(divisor)[0] == "-":
key = False
dividend = abs(dividend)
divisor = abs(divisor)
if divisor == 1:
if key is False:
result = -dividend
else:
result = dividend
if result > 2**31 - 1 or result < -2**31:
return 2**31 - 1
else:
return result
def my_divide(dividend_, divisor_, result_):
if dividend_ < divisor_:
return 0
divisor__ = divisor_
while dividend_ >= divisor_:
if result_ == 0:
result_ = result_ + 1
else:
result_ = result_ + result_
last = divisor_
divisor_ = divisor_ + divisor_
result_ = result_ + my_divide(dividend_ - divisor_ + last, divisor__, 0)
return result_
result = my_divide(dividend, divisor, result)
if key is False:
result = -result
if result > 2**31 - 1 or result < -2**31:
return 2**31 - 1
return result
思路:
举个例子,11除以3 。
首先11比3大,结果至少是1, 然后我让3翻倍,就是6,发现11比3翻倍后还要大,那么结果就至少是2了,那我让这个6再翻倍,得12,11不比12大,吓死我了,差点让就让刚才的最小解2也翻倍得到4了。但是我知道最终结果肯定在2和4之间。也就是说2再加上某个数,这个数是多少呢?我让11减去刚才最后一次的结果6,剩下5,我们计算5是3的几倍,也就是除法,看,递归出现了。
下一个排列(原创)
实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须原地修改,只允许使用额外常数空间。
以下是一些例子,输入位于左侧列,其相应输出位于右侧列。
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1
class Solution:
def nextPermutation(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
CHANGE = False
for i in range(len(nums) - 1):
idx = len(nums) - 1 - i
last = idx - 1
if nums[idx] > nums[last]:
CHANGE = True
for k in range(len(nums[idx:])):
k_ = len(nums) - 1 - k
if nums[k_] > nums[last]:
key = nums[k_]
nums[k_] = nums[last]
nums[last] = key
break
KEY = True
while KEY:
KEY = False
for j in range(len(nums[idx:]) - 1):
if nums[idx+j] > nums[idx +j +1]:
Key = nums[idx +j + 1]
nums[idx +j + 1] = nums[idx+j]
nums[idx+j] = Key
KEY = True
break
if CHANGE is False:
for i in range(int(len(nums) / 2)):
key = nums[i]
nums[i] = nums[len(nums) - 1 - i]
nums[len(nums) -1 -i] = key
搜索旋转排序数组(转载)
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组[0,1,2,4,5,6,7]可能变为[4,5,6,7,0,1,2])。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回-1 。
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是 O(logn) 级别。
示例:
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1
class Solution:
def search(self, nums: List[int], target: int) -> int:
if not nums:
return -1
# 左边第一个值的索引/右边第一个值的索引
l, r = 0, len(nums) - 1
# 当左指针大于右指针就跳出循环
while l <= r:
mid = (l + r) // 2
if nums[mid] == target:
return mid
# 如果第一个值小于等于mid的值,则左边有序,否则右边有序
if nums[0] <= nums[mid]:
# 左边有序,且target落在左边,右指针移动到mid前
if nums[0] <= target < nums[mid]:
r = mid - 1
# 否则,左指针,移动到mid后
else:
l = mid + 1
# 右边有序的情况
else:
# 如果target落在右边,左指针移动到mid后
if nums[mid] < target <= nums[len(nums) - 1]:
l = mid + 1
# 否则,右指针移动到mid前
else:
r = mid - 1
return -1
在排序数组中查找元素的第一个和最后一个位置(原创)
给定一个按照升序排列的整数数组nums,和一个目标值target。找出给定目标值在数组中的开始位置和结束位置。
你的算法时间复杂度必须是 O(logn) 级别。
如果数组中不存在目标值,返回[-1, -1]。
示例:
输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]
输入: nums = [5,7,7,8,8,10], target = 6
输出: [-1,-1]
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
# -----------------------------
if not nums:
return [-1, -1]
if target < nums[0]:
return [-1, -1]
if target > nums[-1]:
return [-1, -1]
# -----------------------------
result = []
l = 0
r = len(nums) - 1
while l <= r:
key = True
mid = (l + r) // 2
if (target == nums[mid]) and (mid == 0):
key = False
result.append(mid)
break
if (target == nums[mid]) and (nums[mid - 1] < target):
key = False
result.append(mid)
break
if target == nums[mid]:
r = mid - 1
# 落在左边
elif target < nums[mid]:
r = mid - 1
elif target > nums[mid]:
l = mid + 1
else:
r = mid - 1
if key is True:
return [-1, -1]
l = 0
r = len(nums) - 1
while l <= r:
mid = (l + r) // 2
if (target == nums[mid]) and (mid == (len(nums) - 1)):
result.append(mid)
break
if (target == nums[mid]) and (nums[mid + 1] > target):
result.append(mid)
break
if target == nums[mid]:
l = mid + 1
# 落在左边
elif target < nums[mid]:
r = mid - 1
elif target > nums[mid]:
l = mid + 1
else:
r = mid + 1
return result
有效的数独(原创)
判断一个9x9的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。
- 数字1-9在每一行只能出现一次。
- 数字1-9在每一列只能出现一次。
- 数字1-9在每一个以粗实线分隔的3x3宫内只能出现一次。
上图是一个部分填充的有效的数独。
数独部分空格内已填入了数字,空白格用 ‘.’ 表示。
示例一:
输入:
[
[“5”,“3”,".",".",“7”,".",".",".","."],
[“6”,".",".",“1”,“9”,“5”,".",".","."],
[".",“9”,“8”,".",".",".",".",“6”,"."],
[“8”,".",".",".",“6”,".",".",".",“3”],
[“4”,".",".",“8”,".",“3”,".",".",“1”],
[“7”,".",".",".",“2”,".",".",".",“6”],
[".",“6”,".",".",".",".",“2”,“8”,"."],
[".",".",".",“4”,“1”,“9”,".",".",“5”],
[".",".",".",".",“8”,".",".",“7”,“9”]
]
输出: true
示例二:
输入:
[
[“8”,“3”,".",".",“7”,".",".",".","."],
[“6”,".",".",“1”,“9”,“5”,".",".","."],
[".",“9”,“8”,".",".",".",".",“6”,"."],
[“8”,".",".",".",“6”,".",".",".",“3”],
[“4”,".",".",“8”,".",“3”,".",".",“1”],
[“7”,".",".",".",“2”,".",".",".",“6”],
[".",“6”,".",".",".",".",“2”,“8”,"."],
[".",".",".",“4”,“1”,“9”,".",".",“5”],
[".",".",".",".",“8”,".",".",“7”,“9”]
]
输出: false
class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
def remove(alist, astr):
while astr in alist:
alist.remove(astr)
return alist
# 判断行
board_ = []
for i in range(9):
board_.append(board[i].copy())
for i in range(9):
list0 = remove(board_[i], ".")
if len(list0) != len(list(set(list0))):
return False
# 判断列
board_ = []
for i in range(9):
board_.append(board[i].copy())
for i in range(9):
list0 = []
for j in range(9):
list0.append(board_[j][i])
list0 = remove(list0, ".")
if len(list0) != len(list(set(list0))):
return False
board_ = []
for i in range(9):
board_.append(board[i].copy())
amap = {0: [0, 1, 2], 1: [3, 4, 5], 2:[6, 7, 8]}
# 判断3x3
for i in range(3):
for j in range(3):
list0 = []
for k in amap[i]:
for h in amap[j]:
list0.append(board_[k][h])
list0 = remove(list0, ".")
if len(list0) != len(list(set(list0))):
print(3)
return False
return True
组合总和(原创)
给定一个无重复元素的数组candidates和一个目标数target,找出candidates中所有可以使数字和为target的组合。
candidates中的数字可以无限制重复被选取。
说明:
- 所有数字(包括 target)都是正整数。
- 解集不能包含重复的组合。
示例1:
输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]
示例2:
输入:candidates = [2,3,5], target = 8,
所求解集为:
[
[2,2,2,2],
[2,3,3],
[3,5]
]
DFS+剪枝
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
# 顺序排序
candidates.sort()
result = []
def dfs(alist, choice, temp):
if choice + sum(temp) > target:
temp.append(choice)
temp.append("back")
return
elif choice + sum(temp) == target:
temp.append(choice)
temp_ = temp.copy()
temp_.sort()
if temp_ in result:
pass
else:
result.append(temp_)
return
temp.append(choice)
for each in alist:
dfs(alist, each, temp)
if temp[-1] == "back":
temp.pop()
temp.pop()
return
temp.pop()
for i, each in enumerate(candidates):
dfs(candidates[i:], each, [])
return result
组合总和 II(原创)
给定一个数组candidates和一个目标数target,找出candidates中所有可以使数字和为target的组合。
candidates中的每个数字在每个组合中只能使用一次。
说明:
- 所有数字(包括目标数)都是正整数。
- 解集不能包含重复的组合。
示例1:
输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
示例2:
输入: candidates = [2,5,2,1,2], target = 5,
所求解集为:
[
[1,2,2],
[5]
]
class Solution:
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
candidates.sort()
result = []
# alist中包含choice
def dfs(alist, choice, temp):
if choice + sum(temp) > target:
temp.append(choice)
temp.append("back")
return
elif choice + sum(temp) == target:
temp.append(choice)
temp_ = temp.copy()
temp_.sort()
if temp_ in result:
pass
else:
result.append(temp_)
return
else:
temp.append(choice)
alist_ = alist.copy()
alist_.remove(choice)
for each in alist_:
dfs(alist_, each, temp)
if temp[-1] == "back":
temp.pop()
temp.pop()
return
else:
temp.pop()
for i, each in enumerate(candidates):
dfs(candidates[i:], each, [])
return result
字符串相乘(转载)
给定两个以字符串形式表示的非负整数num1和num2,返回num1和num2的乘积,它们的乘积也表示为字符串形式。
示例1:
输入: num1 = “2”, num2 = “3”
输出: “6”
示例2:
输入: num1 = “123”, num2 = “456”
输出: “56088”
不能使用任何标准库的大数类型(比如 BigInteger)或直接将输入转换为整数来处理。
乘法分配律
class Solution:
def multiply(self, num1: str, num2: str) -> str:
res = 0
for i in range(1,len(num1)+1):
for j in range(1, len(num2)+1):
res += int(num1[-i]) * int(num2[-j]) *10**(i+j-2)
return str(res)
全排列(原创)
给定一个没有重复数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
依然是DFS
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
result = []
# nums中不包含choice
def dfs(nums, choice, temp):
print(nums, choice, temp)
temp.append(choice)
temp_ = temp.copy()
if nums == []:
result.append(temp_)
return
for i in nums:
nums_ = nums.copy()
nums_.remove(i)
dfs(nums_, i, temp)
temp.pop()
for each in nums:
nums_ = nums.copy()
nums_.remove(each)
dfs(nums_, each, [])
return result
全排列 II(转载)
给定一个可包含重复数字的序列,返回所有不重复的全排列。
示例:
输入: [1,1,2]
输出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
重点就是剪枝:如果这个数和之前的数一样,并且之前的数还未使用过(说明已经回溯过)
以[1,1,2]为例,在递归到第1个元素(从0开始编号)时,与第0个元素数值相同,如果第0个元素是用过(即在目前的序列中),那么会产生 [1,1]这种序列;如果第0个元素是没有用过(不在目前的序列中),而第0个元素的元素值和第1个元素的元素值是相同的,第1个元素能够表达的情况在第0个元素在序列中时,都已经涵盖到了,所以为了去重就不需要再把第一个元素进一步递归了。
class Solution:
def permuteUnique(self, nums: List[int]) -> List[List[int]]:
n = len(nums)
nums.sort()
res = []
def backTrack(mark,temp_list):
if len(mark) == n:
res.append(temp_list)
for j in range(n):
if j in mark:
continue
if j > 0 and nums[j] == nums[j-1] and j - 1 not in mark:
continue
backTrack(mark + [j], temp_list + [nums[j]])
backTrack([],[])
return res
结语
如果您有修改意见或问题,欢迎留言或者通过邮箱和我联系。
手打很辛苦,如果我的文章对您有帮助,转载请注明出处。