力扣牛客每日刷题(持续更新)
初试结束第15天, 之前简单的处理了部分毕设方面的任务, 对接下来的学习做了个简单的规划
决定每天开始刷几道力扣题提高一下算法的理解,不能让之前学的数据结构都忘记了
每道题发一篇有点水文章了,就打算持续更新在这篇文章里记录做题过程
day1: 两数之和
1月9日
解法1(字典): 20 ms
python解法思路:
根据enumerate来遍历nums,自动记录下每个数值以及其对应的索引
在遍历的过程中将数值与索引分别存储为字典的键值,
通过已知的target和当前的value值可以计算出剩下的number是多少(number + value = target)
再判断number是否出现在字典的键中即可, 如果出现则返回两个数字的索引
注意: 这里 判断的 if 语句要放在
dic[value] = index
的前面,不然在第一轮且 value + value = target的情况下会产生错误
如[3,4,2],6
为测试案例时
第一轮遍历中 先插入字典,此时字典为{3:0}
,因为第一个value
为3
,而target
刚好是6
,得到的other_number
也为3, 就会错误的返回0,0
的结果.
class Solution(object):
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
dic = {}
for index, value in enumerate(nums):
other_number = target - value
if other_number in dic:
return dic[other_number], index
dic[value] = index // 先判断后插入,避免了当index=dic[other_number]的情况
解法2(暴力解): 1884 ms
这种解法基本上不用什么思路, 直接两次遍历就够了
class Solution(object):
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
l = len(nums)
for i in range(l):
for j in range(i + 1, l):
if nums[i] + nums[j] == target:
return i, j
return None
day2: 两数相加
1月10日
解法1(遍历): 72 ms
链表是在数据结构中学习到的, 这里第一反应没有想到什么特别适合python特性的解法,就先按最大宗的解法求解,可能更契合C的大众思路。
python解法思路:
本题相当于两个链表逐位相加,保留其个位数,并将产生的进位并入后面的计算中
可以采取遍历链表的形式,用carry
记录进位的信息,用%
和//
来求得个位数和进位数
要注意链表的长度不一定相等,因此在后面要分别对两个链表进行检查,将其多出来的长度按照与0相加来计算,而有些链表相加的结果如999
与9
会一直产生进位导致得到的结果总长度比最长链表的总长度还多1
,所以又在最后补上对carry
的检查
总体感觉这种解法太长,也不是很简洁,因此会在后面几种解法中做出改进
result = ListNode()
head = result
carry = 0
con = 0
while l1 and l2:
num = l1.val + l2.val + carry
ones = num % 10
carry = num // 10
if con == 0:
result.val = ones
else:
result.next = ListNode(ones)
result = result.next
l1 = l1.next
l2 = l2.next
con += 1
while l1:
num = l1.val + carry
ones = num % 10
carry = num // 10
result.next = ListNode(ones)
result = result.next
l1 = l1.next
while l2:
num = l2.val + carry
ones = num % 10
carry = num // 10
result.next = ListNode(ones)
result = result.next
l2 = l2.next
if carry:
result.next = ListNode(carry)
return head
解法2:(递归): 56 ms
python解法思路: 每次将进位加到l1.val
上, 并根据l1
,l2
,carry
的信息决定是否往result
中继续添加节点
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
num = l1.val + l2.val
ones = num % 10
carry = num // 10
result = ListNode(ones)
if l1.next or l2.next or carry:
if l1.next:
l1 = l1.next
else:
l1 = ListNode(0)
if l2.next:
l2 = l2.next
else:
l2 = ListNode(0)
l1.val += carry
result.next = self.addTwoNumbers(l1, l2)
return result
本题测试用例
# 创建对象Solution
sol = Solution()
# 定义l1链表
l1 = ListNode(9)
l1.next = l11 = ListNode(9)
l11.next = l12 = ListNode(9)
# 定义l2链表
l2 = ListNode(9)
l2.next = l21 = ListNode(9)
# 获取返回值的链表
head = sol.addTwoNumbers(l1, l2)
错解: TypeError: object of type 'ListNode' has no len()
第一遍做题的时候没有看清楚是链表,把l1,和 l2当做列表来做了, 最后出现了错误
而在Leecode的回答上给出了定义的ListNode类
carry = 0
len1 = len(l1)
len2 = len(l2)
if len1 > len2:
l1.extend([0 for i in range(len1 - len2)])
if len2 > len1:
l2.extend([0 for i in range(len2 - len1)])
con = 0
for i, j in zip(l1, l2):
sum = i + j + carry
ones = sum % 10
carry = sum // 10
if con == 0:
result = ListNode(ones)
con += 1
else:
result.next = ListNode(ones)
if carry:
result.next = ListNode(carry)
return result
day3 无重复字符的最长子串
1月11日
解法1(滑动窗口): 64ms
python解法思路, 利用滑动窗口的思想,用li
来记录当前窗口的值,用num
来记录窗口滑动中的最大值
何为滑动窗口: 如当窗口为dabc
时,下一个遍历到的字符为a
,此时窗口变为dabca
将会含有重复的a
,我们要把第一个出现的a
与其之前的元素剔除,新窗口为bca
继续相后移动
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
li = []
num = 0
if len(s) == 0:
return 0
for word in s:
if word in li:
index = li.index(word)
li = li[index + 1:]
li.append(word)
num = num if len(li) < num else len(li)
return num
解法2(暴力循环): 1816 ms
python解法思路,两层遍历,用list
记录最长子串
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
num = 0
for i in range(len(s)):
li = []
for j in range(i, len(s)):
if s[j] in li:
break
li.append(s[j])
num = num if num > len(li) else len(li)
return num
解法3(暴力循环): 468 ms
python解法思路,两层遍历,用set
记录最长子串
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
num = 0
for i in range(len(s)):
li = set()
for j in range(i, len(s)):
if s[j] in li:
break
li.add(s[j])
num = num if num > len(li) else len(li)
return num
day4 无重复字符的最长子串
1月12日
解法1: (普通思路) : 56 ms
python解法思路: 运用list
自带的拼接操作与排序操作完成本题
class Solution:
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
li = list(sorted(nums1 + nums2))
length = len(li)
if length % 2 == 0:
return (li[length // 2 - 1] + li[length // 2]) / 2
else:
return li[length // 2]
解法2: (numpy一行解): 144 ms
python解法思路: 用numpy
自带的求中位数的函数完成一行解题的效果
import numpy as np
class Solution:
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
return np.median(list(sorted(nums1 + nums2)))
day5: 167. 两数之和 II - 输入有序数组
1月13日梳理了一下python数据结构的知识
1月14日
解法1: (字典): 36ms
dic = {}
for index, value in enumerate(numbers):
other_number = target - value
if other_number in dic:
return dic[other_number], index + 1
dic[value] = index + 1
解法2: (二分查找): 56ms
class Solution:
def binary_search_no_recursion(self, num, low, high, target):
'''
二分查找: 非递归版
:param num: 有序数组
:param low: 下标: 起始位置
:param high: 上标: 结束位置
:param target: 目标值
:return: 目标值的索引
'''
while low <= high:
mid = (low + high) // 2
if num[mid] == target:
return mid
elif num[mid] > target: # 在左边
high = mid - 1
else: # 在右边
low = mid + 1
return -1
def twoSum(self, numbers: List[int], target: int) -> List[int]:
length = len(numbers)
for index, number in enumerate(numbers):
other = target - number
tindex = self.binary_search_no_recursion(numbers, index, length, other) + 1
if tindex:
return index + 1, tindex
解法3: (双指针):
python解法思路: low
从最左边开始, high
从最右边开始,因为是有序数组并且题目说了只对应唯一的答案,所以不用考前numbers=[2,2,4,2],target=4
这种情况, 双指针法利用一次遍历,判断当前两指针指向的数字之和与target
的对比,根据偏差分别移动low
和high
, 相比于二分查找法只需要O(n)
的时间复杂度
class Solution:
def twoSum(self, numbers: List[int], target: int) -> List[int]:
length = len(numbers)
low = 0
high = length - 1
while low < high:
s = numbers[low] + numbers[high]
if s == target:
return low + 1, high + 1
elif s > target:
high -= 1
else:
low += 1
day6: 653. 两数之和 IV - 输入 BST
1月15日
解法1: 中序+二分查找: 68ms
解题思路: 先通过中序生成一个有序数组, 转为之前做过的题 167
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def findTarget(self, root: Optional[TreeNode], k: int) -> bool:
if not root:
return False
tmp = []
def in_order(root):
if not root:
return False
in_order(root.left)
tmp.append(root.val)
in_order(root.right)
in_order(root)
left, right = 0, len(tmp) - 1
while left < right:
lr_sum = tmp[left] + tmp[right]
if lr_sum == k:
return True
elif lr_sum < k:
left += 1
else:
right -= 1
return False
解法2: 中序+Hash: 80ms
解题思路: 在递归的过程中检查目标值是否在set中,若存在则返回
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def findTarget(self, root: Optional[TreeNode], k: int) -> bool:
s = set()
def order(node):
if not node:
return False
if k - node.val in s:
return True
s.add(node.val)
return order(node.left) or order(node.right)
return order(root)
day7: 15. 三数之和
1月16日
解法1: 暴力:(超时)
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
result = []
length = len(nums)
if length < 3:
return result
for i in range(length):
for j in range(i + 1, length):
target = 0 - nums[i] - nums[j]
if target in nums[j + 1:]:
tmp = sorted([nums[i], nums[j], target])
if tmp not in result:
result.append(tmp)
return result
解法2: 双指针: 4776 ms
先用sort使nums化为有序数组,然后再遍历nums,固定一个数字,剩下的数组中化为两数之和问题
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
result = []
length = len(nums)
if length < 3:
return result
nums.sort()
for i in range(length):
low = i + 1
high = length - 1
target = -1 * nums[i]
while low < high:
if nums[low] + nums[high] == target:
if sorted([nums[i], nums[low], nums[high]]) not in result:
result.append([nums[i], nums[low], nums[high]])
low += 1
elif nums[low] + nums[high] < target:
low += 1
else:
high -= 1
return result
解法3: 双指针优化去重: 504 ms
if i > 0 and nums[i] == nums[i - 1]: continue
因为nums已经变为有序数组,因此对于重复的数字一定是相邻的,我们可以跳过这种已经搜索过的数字. 在搜索过程中我们可以跳过. 在两数之和中, 使用while
去重,跳过一些重复的数字,增加效率
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
if len(nums) < 3:
return []
nums.sort()
res = []
for i in range(0, len(nums) - 2):
if i > 0 and nums[i] == nums[i - 1]: continue
target = -nums[i]
left, right = i + 1, len(nums) - 1
while left < right:
s = nums[left] + nums[right]
if s == target:
res.append([nums[i], nums[left], nums[right]])
# 去重
while left < right:
left = left + 1
if nums[left - 1] != nums[left]: break
while left < right:
right = right - 1
if nums[right + 1] != nums[right]: break
elif s < target:
left = left + 1
else:
right = right - 1
return res
day8: 18. 三数之和
1月17日
python解法: 644 ms
与三数之和类似, O(n3)
排序后先用两层循环固定两个数, 然后在剩下的数组里使用双指针
def fourSum(nums, target):
length = len(nums)
result = []
if length < 4:
return result
nums.sort()
for i in range(length - 3):
if i > 0 and nums[i] == nums[i - 1]:
continue
for j in range(i + 1, length - 2):
if j > i + 1 and nums[j] == nums[j - 1]:
continue
expect = target - (nums[i] + nums[j])
low = j + 1
high = length - 1
while low < high:
recent = nums[low] + nums[high]
if expect == recent:
result.append([nums[i], nums[j], nums[low], nums[high]])
while low < high:
low += 1
if nums[low] != nums[low - 1]:
break
while low < high:
high -= 1
if nums[high + 1] != nums[high]:
break
elif expect > recent:
low += 1
else:
high -= 1
return result
day9: 989. 数组形式的整数加法
1月18日
解法1: python特性: 88ms
利用python的int函数可以将str转换为int的特性, 将list中的数据转为int再相加,最后转为list输出
class Solution:
def addToArrayForm(self, num: List[int], k: int) -> List[int]:
s = ''
for i in num:
s += str(i)
return [int(i) for i in str(int(s) + k)]
解法2: 遍历: 76ms
与两数相加类似, 循环遍历num与k, 因为k给的是整数,因此用k//10 k%10
来起到遍历的作用,要考虑到num与k的长度问题,已经最高位进位问题
class Solution:
def addToArrayForm(self, num: List[int], k: int) -> List[int]:
result = []
carry = 0
length = len(num) - 1
while length >= 0 or k != 0:
if length <= -1:
number = 0
else:
number = num[length]
one = k % 10
k = k // 10
now_number = number + carry + one
result.append(now_number % 10)
carry = now_number // 10
length -= 1
if carry:
result.append(carry)
result.reverse()
return result
day10: 66.加一
1月19日
本题与上一题类似,更简单
python解法: 8ms
class Solution(object):
def plusOne(self, digits):
"""
:type digits: List[int]
:rtype: List[int]
"""
s = ''
for digit in digits:
s += str(digit)
return [int(i) for i in str(int(s) + 1)]
python解法: 12ms
class Solution(object):
def plusOne(self, digits):
"""
:type digits: List[int]
:rtype: List[int]
"""
carry = 1
result = []
for digit in digits[::-1]:
num = digit + carry
carry = num // 10
result.append(num % 10)
if carry:
result.append(carry)
result.reverse()
return result
python解法: 16 ms
这边digits.insert(0, 1)
也可以改为digits=[1] + digits
def plusOne(digits):
"""
:type digits: List[int]
:rtype: List[int]
"""
for i in range(len(digits) - 1, -1, -1):
digits[i] += 1
if digits[i] == 10:
digits[i] = 0
else:
return digits
digits.insert(0, 1)
return digits
day11:415. 字符串相加
1月20日
python解法(python特性): 32 ms
这里题目要求不能直接将输入的字符串转为整形
先给出一种python特性的一行解法,使用了eval函数
class Solution(object):
def addStrings(self, num1, num2):
"""
:type num1: str
:type num2: str
:rtype: str
"""
return str(eval(num1 + '+' + num2))
python解法(遍历): 24ms
l1 = len(num1) - 1
l2 = len(num2) - 1
carry = 0
result = ''
while l1 >= 0 or l2 >= 0:
x = ord(num1[l1]) - ord('0') if l1 >= 0 else 0
y = ord(num2[l2]) - ord('0') if l2 >= 0 else 0
s = x + y + carry
result += str(s % 10)
carry = s // 10
l1 -= 1
l2 -= 1
if carry:
result += str(carry)
return result[::-1]
day12:67. 二进制求和
1月24日
解法1: 遍历求和:24ms
与之前题目的思路都一样
class Solution(object):
def addBinary(self, a, b):
"""
:type a: str
:type b: str
:rtype: str
"""
l1 = len(a) - 1
l2 = len(b) - 1
carry = 0
result = ''
while l1 >= 0 or l2 >= 0:
x = ord(a[l1]) - ord('0') if l1 >= 0 else 0
y = ord(b[l2]) - ord('0') if l2 >= 0 else 0
s = x + y + carry
result += str(s % 2)
carry = s // 2
l1 -= 1
l2 -= 1
if carry:
result += str(carry)
return result[::-1]
解法2:(python特性):12ms
python中给出了二进制与十进制的转换,利用bin
与int
来求解
class Solution(object):
def addBinary(self, a, b):
"""
:type a: str
:type b: str
:rtype: str
"""
return bin(int(a, 2) + int(b, 2))[2:]
day13:912. 排序数组
1月24日
解法1: 归并: 348 ms
class Solution(object):
def sortArray(self, nums):
"""
:type nums: List[int]
:rtype: List[int]
"""
return self.merge_sort(nums)
def merge(self, left, right):
'''
合并操作,将两个有序数组left[]和right[]合并成一个大的有序数组
'''
# left与right的下标指针
l, r = 0, 0
result = []
while l < len(left) and r < len(right):
if left[l] < right[r]:
result.append(left[l])
l += 1
else:
result.append(right[r])
r += 1
result += left[l:]
result += right[r:]
return result
def merge_sort(self, alist):
if len(alist) <= 1:
return alist
# 二分分解
num = len(alist) // 2
left = self.merge_sort(alist[:num])
right = self.merge_sort(alist[num:])
# 合并
return self.merge(left, right)
解法2: 快排: 348 ms
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
# 快速排序
def quick_sort(start, end):
# 只剩一个元素时,不用再进行排序
if start >= end: return
# 任意找到个基准放到前面
idx = random.randint(start, end)
nums[start], nums[idx] = nums[idx], nums[start]
pivot = nums[start]
l, r = start, end
while l < r:
# 从右向左找到小的
while l < r and nums[r] >= pivot:
r -= 1
# 从左向右找到大的
while l < r and nums[l] <= pivot:
l += 1
nums[l], nums[r] = nums[r], nums[l]
# 确定基准点的位置
nums[start], nums[l] = nums[l], nums[start]
quick_sort(start, l-1)
quick_sort(l+1, end)
quick_sort(0, len(nums) - 1)
return nums
解法3: 内置函数 348 ms
class Solution(object):
def sortArray(self, nums):
"""
:type nums: List[int]
:rtype: List[int]
"""
return sorted(nums)
day14:283. 移动零
1月25日
解法 (直接赋值)28ms
遍历一遍数组, 使用快慢指针的思想, 如果没有遇到0
的时候low=i
,那么nums[low] = nums[i]
相当于原地赋值, 如果遇到了0
,会导致low
指针落后与i
, 即low
指向0
的位置,而i
比low
快一个位置, 遍历结束之后,所有的非零元素都稳定地移动到了数组的前方,low
停止的位置往后都是原来0
的位置
也可以理解为i
比low
领先的元素个数都表示原来数组中0
的位置
class Solution(object):
def moveZeroes(self, nums):
"""
:type nums: List[int]
:rtype: None Do not return anything, modify nums in-place instead.
"""
low = 0
length = len(nums)
for i in range(length):
if nums[i] == 0:
continue
nums[low] = nums[i]
low += 1
while low < length:
nums[low] = 0
low += 1
return nums
day14:56. 合并区间
解法:32ms
解题思路: 先对intervals
按每组区间的第一个数排序, 这样之后只需要比较前一区间的右边界与后一区间的左边界即可,在遍历的过程中判断区间是否覆盖,有覆盖则修改res
中区间的右边界,无覆盖则将当前区间加入res
中
class Solution(object):
def merge(self, intervals):
"""
:type intervals: List[List[int]]
:rtype: List[List[int]]
"""
if len(intervals) == 1:
return intervals
intervals.sort(key=lambda x: x[0])
res = [intervals[0]]
for i in range(1, len(intervals)):
data = intervals[i]
if data[0] <= res[-1][1]:
res[-1][1] = max(data[1],res[-1][1])
else:
res.append(data)
return res
day15:179. 最大数
1月27日
解法1: pythoncmp_to_key
自定义排序: 40ms
cmp_to_key
自定义排序规则, 这里int(x + y) - int(y + x))
代表前后两项, 如 nums = [3,30,34,5,9]
时, x
,y
为3 30
的时候开始比较 int(x+y) = int(330) = 330
, int(y+x)=int(303)
因此前者大, 3
要在30的前面
. 依此类推。
from functools import cmp_to_key
class Solution:
def largestNumber(self, nums: List[int]) -> str:
return str(int(''.join(sorted(map(str, nums), key=cmp_to_key(lambda x, y: (int(x + y) - int(y + x))), reverse=True))))
from functools import cmp_to_key
class Solution:
def largestNumber(self, nums: List[int]) -> str:
nums.sort(key=cmp_to_key(lambda x,y: int(str(y)+str(x)) - int(str(x)+str(y))))
ans = ''.join([str(num) for num in nums])
return str(int(ans))
解法1: 优化32ms
from typing import List
class LargerNumKey(str):
def __lt__(x, y):
return x+y > y+x
class Solution:
def largestNumber(self, nums: List[int]) -> str:
largest_num = ''.join(sorted(map(str, nums), key = LargerNumKey))
return "0" if largest_num[0] == '0' else largest_num
解法2: 快排: 40ms
def quick_sort(alist, start, end):
"""快速排序"""
if start >= end:
return
mid = alist[start]
tmp = ''
low = start
high = end
while low < high:
while low < high and int(alist[high] + mid) >= int(mid + alist[high]):
high -= 1
alist[low] = alist[high]
while low < high and int(alist[low] + mid) < int(mid + alist[low]):
low += 1
alist[high] = alist[low]
alist[low] = mid
quick_sort(alist, start, low - 1)
quick_sort(alist, low + 1, end)
li = list(map(str, nums))
quick_sort(li, 0, len(nums) - 1)
li = li[::-1]
return str(int(''.join(li)))
day16:JZ6 从尾到头打印链表
1月29日
解法1: 链表+列表逆序: 65ms
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
#
# @param listNode ListNode类
# @return int整型一维数组
#
class Solution:
def printListFromTailToHead(self , listNode: ListNode) -> List[int]:
res = []
# write code here
while listNode:
res.append(listNode.val)
listNode = listNode.next
return res[::-1]
解法2: 链表+栈 输出:71ms
class Stack(object):
"""栈"""
def __init__(self):
self.items = []
def is_empty(self):
"""判断是否为空"""
return self.items == []
def push(self, item):
"""加入元素"""
self.items.append(item)
def pop(self):
"""弹出元素"""
return self.items.pop()
def peek(self):
"""返回栈顶元素"""
return self.items[len(self.items)-1]
def size(self):
"""返回栈的大小"""
return len(self.items)
class Solution:
def printListFromTailToHead(self , listNode: ListNode) -> List[int]:
res = []
s = Stack()
# write code here
while listNode:
s.push(listNode.val)
listNode = listNode.next
while not s.is_empty():
res.append(s.pop())
return res
解法3: 链表逆置: 62ms
思路: reverse代表新链表的头, 将原链表中的节点断开,连入新链表中,完成逆置
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
#
# @param listNode ListNode类
# @return int整型一维数组
#
class Solution:
def printListFromTailToHead(self , listNode: ListNode) -> List[int]:
res = []
# 链表逆置
new_head = None
node = listNode
while node:
tmp = node.next
node.next = new_head
new_head = node
node = tmp
while new_head:
res.append(new_head.val)
new_head=new_head.next
return res
day17: JZ24 反转链表
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
#
# @param head ListNode类
# @return ListNode类
#
class Solution:
def ReverseList(self , head: ListNode) -> ListNode:
if not head or not head.next:
return head
new_head = None
while head:
p = head
head = head.next
p.next = new_head
new_head = p
return new_head