额外题目
- 1365. 有多少小于当前数字的数字
- 借着本题,学习一下各种排序
- 未看解答自己编写的青春版
- 重点
- 代码随想录的代码
- 我的代码(当天晚上理解后自己编写)
- 941.有效的山脉数组
- 未看解答自己编写的青春版
- 重点
- 代码随想录的代码
- 我的代码(当天晚上理解后自己编写)
- 1207. 独一无二的出现次数
- 未看解答自己编写的青春版
- 重点
- 代码随想录的代码
- 我的代码(当天晚上理解后自己编写)
- 283. 移动零
- 重点
- 代码随想录的代码
- 我的代码(当天晚上理解后自己编写)
- 189. 轮转数组
- 未看解答自己编写的青春版
- 重点
- 代码随想录的代码
- 我的代码(当天晚上理解后自己编写)
- 724. 寻找数组的中心下标
- 重点
- 代码随想录的代码
- 我的代码(当天晚上理解后自己编写)
- 34. 在排序数组中查找元素的第一个和最后一个位置
- 未看解答自己编写的青春版
- 重点
- 看了思路之后,自己写写试试?
- 这个题目暴露了我二分法不会写
- 代码随想录的代码
- 我的代码(当天晚上理解后自己编写)
- 目前的欠债:排序学习,二分法学习
- 922. 按奇偶排序数组 II
- 未看解答自己编写的青春版
- 重点
- 发现了我自己写的代码的问题了,我只要对奇数or偶数,操作就可以了,不需要全部操作。我写的代码太复杂了,没有利用好题目的特性。
- 代码随想录的代码
- 我的代码(当天晚上理解后自己编写)
- 35.搜索插入位置
- 未看解答自己编写的青春版
- 重点
- 代码随想录的代码
- 我的代码(当天晚上理解后自己编写)
- 24. 两两交换链表中的节点
- 未看解答自己编写的青春版
- 重点
- 代码随想录的代码
- 我的代码(当天晚上理解后自己编写)
- 234. 回文链表
- 未看解答自己编写的青春版
- 重点
- 代码随想录的代码
- 我的代码(当天晚上理解后自己编写)
- 143. 重排链表
- 未看解答自己编写的青春版
- 重点
- 代码随想录的代码
- 方法三,稍微练了一下
- 我的代码(当天晚上理解后自己编写)
- 141. 环形链表
- 未看解答自己编写的青春版
- 重点
- 代码随想录的代码
- 我的代码(当天晚上理解后自己编写)
- 面试题 02.07. 链表相交
- 未看解答自己编写的青春版
- 重点
- 代码随想录的代码
- 我的代码(当天晚上理解后自己编写)
- 205. 同构字符串
- 未看解答自己编写的青春版
- 重点
- 代码随想录的代码
- 用Python中最基础的{}作为字典的自己写版本
- 我的代码(当天晚上理解后自己编写)
- 1002. 查找共用字符
- 未看解答自己编写的青春版
- 重点
- 代码随想录的代码
- 我的代码(当天晚上理解后自己编写)
- 925. 长按键入
- 未看解答自己编写的青春版
- 重点
- 代码随想录的代码
- 我的代码(当天晚上理解后自己编写)
- 844. 比较含退格的字符串
- 未看解答自己编写的青春版
- 重点
- 代码随想录的代码
- 我的代码(当天晚上理解后自己编写)
- 129. 求根节点到叶节点数字之和
- 未看解答自己编写的青春版
- 重点
- 代码随想录的代码
- 我的代码(当天晚上理解后自己编写)
- 1382.将二叉搜索树变平衡
- 未看解答自己编写的青春版
- 重点
- 代码随想录的代码
- 我的代码(当天晚上理解后自己编写)
- 100. 相同的树
- 未看解答自己编写的青春版
- 重点
- 代码随想录的代码
- 我的代码(当天晚上理解后自己编写)
- 116. 填充每个节点的下一个右侧节点指针
- 未看解答自己编写的青春版
- 重点
- 代码随想录的代码
- 这道题是经典,之前没做过类似的!
- 我的代码(当天晚上理解后自己编写)
- 52. N皇后II
- 未看解答自己编写的青春版
- 重点
- 代码随想录的代码
- 我的代码(当天晚上理解后自己编写)
- 649. Dota2 参议院
- 未看解答自己编写的青春版
- 重点
- 这道题真的很妙
- 代码随想录的代码
- 我的代码(当天晚上理解后自己编写)
- 1221. 分割平衡字符串
- 未看解答自己编写的青春版
- 重点
- 代码随想录的代码
- 我的代码(当天晚上理解后自己编写)
- 5. 最长回文子串
- 这种题到现在还是不会写,看着答案写一写
- 重点
- 本题的双指针法也值得学习
- 代码随想录的代码
- 我的代码(当天晚上理解后自己编写)
- 132. 分割回文串 II
- 自己看着解答写的不优秀代码
- 重点
- 代码随想录的代码
- 我的代码(当天晚上理解后自己编写)
- 673. 最长递增子序列的个数
- 未看解答自己编写的青春版
- 这题记录一下吧,没心情看了,思路还没看懂,本题也很重要
- 重点
- 代码随想录的代码
- 我的代码(当天晚上理解后自己编写)
- 657. 机器人能否返回原点
- 未看解答自己编写的青春版
- 重点
- 代码随想录的代码
- 我的代码(当天晚上理解后自己编写)
- 剩下的几道题都很难,都是没接触过的题,后面再单开一个文章记录。
1365. 有多少小于当前数字的数字
借着本题,学习一下各种排序
未看解答自己编写的青春版
class Solution:
def smallerNumbersThanCurrent(self, nums: List[int]) -> List[int]:
temp = sorted(nums)
n = len(temp)
haxi = [0]*101
for i in range(n-1,-1,-1) :
haxi[temp[i]] = i
res = [0]*n
count = 0
for i in nums :
res[count] = haxi[i]
count += 1
return res
重点
排序之后加哈希,一开始加哈希是没想到的,光排序是不够的,在哈希赋值中,利用倒序遍历的小技巧,就可以避免值相同的情况。
代码随想录的代码
class Solution:
def smallerNumbersThanCurrent(self, nums: List[int]) -> List[int]:
res = nums[:]
hash = dict()
res.sort() # 从小到大排序之后,元素下标就是小于当前数字的数字
for i, num in enumerate(res):
if num not in hash.keys(): # 遇到了相同的数字,那么不需要更新该 number 的情况
hash[num] = i
for i, num in enumerate(nums):
res[i] = hash[num]
return res
我的代码(当天晚上理解后自己编写)
941.有效的山脉数组
未看解答自己编写的青春版
条件判断大法。
class Solution:
def validMountainArray(self, arr: List[int]) -> bool:
flag = 0
n = len(arr)
if n < 3 :
return False
for i in range(1,n):
next_diff = arr[i]-arr[i-1]
if flag == 0 :
if next_diff > 0 :
continue
elif next_diff < 0 and i != 1 :
flag = 1
else :
return False
else :
if next_diff < 0 :
continue
else :
return False
if flag == 0 :
return False
return True
重点
代码随想录给出了双指针的思路,确实一开始没想到!这种判断序列的整体趋势的,都可以思考双指针法。
代码随想录的代码
class Solution:
def validMountainArray(self, arr: List[int]) -> bool:
left, right = 0, len(arr)-1
while left < len(arr)-1 and arr[left+1] > arr[left]:
left += 1
while right > 0 and arr[right-1] > arr[right]:
right -= 1
return left == right and right != 0 and left != len(arr)-1
我的代码(当天晚上理解后自己编写)
1207. 独一无二的出现次数
未看解答自己编写的青春版
class Solution:
def uniqueOccurrences(self, arr: List[int]) -> bool:
haxi = [0]*2001
n = len(arr)
for i in range(n):
haxi[arr[i]] += 1
index = [0]*n
for i in haxi :
if i != 0 :
if index[i-1] != 0 :
return False
index[i-1] = 1
return True
重点
两次哈希,我的想法和代码随想录的思路一模一样。
代码随想录的代码
# 方法 1: 数组在哈西法的应用
class Solution:
def uniqueOccurrences(self, arr: List[int]) -> bool:
count = [0] * 2002
for i in range(len(arr)):
count[arr[i] + 1000] += 1 # 防止负数作为下标
freq = [False] * 1002 # 标记相同频率是否重复出现
for i in range(2001):
if count[i] > 0:
if freq[count[i]] == False:
freq[count[i]] = True
else:
return False
return True
# 方法 2: map 在哈西法的应用
class Solution:
def uniqueOccurrences(self, arr: List[int]) -> bool:
ref = dict()
for i in range(len(arr)):
ref[arr[i]] = ref.get(arr[i], 0) + 1
value_list = sorted(ref.values())
for i in range(len(value_list) - 1):
if value_list[i + 1] == value_list[i]:
return False
return True
我的代码(当天晚上理解后自己编写)
283. 移动零
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
n = len(nums)
slow = 0
fast = 0
while fast < n :
if nums[fast] != 0 :
nums[slow] = nums[fast]
fast += 1
slow += 1
else :
fast += 1
for i in range(slow,n):
nums[i] = 0
重点
一开始其实没啥思路,没想到用双指针,还是水平太菜。
代码随想录的代码
def moveZeroes(self, nums: List[int]) -> None:
slow = 0
for fast in range(len(nums)):
if nums[fast] != 0:
nums[slow] = nums[fast]
slow += 1
for i in range(slow, len(nums)):
nums[i] = 0
交换前后变量,避免补零
def moveZeroes(self, nums: List[int]) -> None:
slow, fast = 0, 0
while fast < len(nums):
if nums[fast] != 0:
nums[slow], nums[fast] = nums[fast], nums[slow]
slow += 1 # 保持[0, slow)区间是没有0的
fast += 1
我的代码(当天晚上理解后自己编写)
189. 轮转数组
未看解答自己编写的青春版
我自己想的额外申请空间版:
class Solution:
def rotate(self, nums: List[int], k: int) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
n = len(nums)
k = k % n
temp = nums[n-k:n]
if n > k :
for i in range(n-k-1,-1,-1):
nums[i+k] = nums[i]
nums[:k] = temp
重点
这题还是得看代码随想录的解答。
旋转子串我是没想到的,当时左旋转字符串那道题,就没想到,隔了这么久又该复习了。
代码随想录的代码
方法一:局部翻转 + 整体翻转
class Solution:
def rotate(self, A: List[int], k: int) -> None:
def reverse(i, j):
while i < j:
A[i], A[j] = A[j], A[i]
i += 1
j -= 1
n = len(A)
k %= n
reverse(0, n - 1)
reverse(0, k - 1)
reverse(k, n - 1)
方法二:利用余数
申请copy数组了,而且比我的额外空间还大一些。
class Solution:
def rotate(self, nums: List[int], k: int) -> None:
copy = nums[:]
for i in range(len(nums)):
nums[(i + k) % len(nums)] = copy[i]
return nums
# 备注:这个方法会导致空间复杂度变成 O(n) 因为我们要创建一个 copy 数组。但是不失为一种思路。
我的代码(当天晚上理解后自己编写)
724. 寻找数组的中心下标
class Solution:
def pivotIndex(self, nums: List[int]) -> int:
total = sum(nums)
leftsum = 0
index = 0
for i in nums :
rightsum = total - i - leftsum
if rightsum == leftsum :
return index
else :
leftsum += i
index += 1
return -1
重点
先算总和,再遍历数组,和代码随想录的思路一模一样。
代码随想录的代码
class Solution:
def pivotIndex(self, nums: List[int]) -> int:
numSum = sum(nums) #数组总和
leftSum = 0
for i in range(len(nums)):
if numSum - leftSum -nums[i] == leftSum: #左右和相等
return i
leftSum += nums[i]
return -1
我的代码(当天晚上理解后自己编写)
34. 在排序数组中查找元素的第一个和最后一个位置
未看解答自己编写的青春版
一开始没思路。想到二分法了,但是觉得二分法,无法处理有相同值的情况。
重点
看了解答,两次二分法啊!先确定左边界,再确定右边界。
看了思路之后,自己写写试试?
没写出来,二分法不会写了,下面是没写完的代码:
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
n = len(nums)
minima = nums[0]
maxima = nums[-1]
if target >= minima and target <= maxima :
left_edge = -2
left = 0
right = n-1
while left < right :
middle = left + (right-left)//2
if target > nums[middle] :
left = middle
else :
right = middle
left_edge = left
right_edge = -2
left = 0
right = n-1
while left < right :
middle = left + (right-left)//2
if target < nums[middle] :
left = middle
else :
right = middle
right_edge = right
if
else :
return [-1,-1]
这个题目暴露了我二分法不会写
代码随想录的代码
本题的其他方法先暂时不看,先搞懂这一种方法。
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
def getRightBorder(nums:List[int], target:int) -> int:
left, right = 0, len(nums)-1
rightBoder = -2 # 记录一下rightBorder没有被赋值的情况
while left <= right:
middle = left + (right-left) // 2
if nums[middle] > target:
right = middle - 1
else: # 寻找右边界,nums[middle] == target的时候更新left
left = middle + 1
rightBoder = left
return rightBoder
def getLeftBorder(nums:List[int], target:int) -> int:
left, right = 0, len(nums)-1
leftBoder = -2 # 记录一下leftBorder没有被赋值的情况
while left <= right:
middle = left + (right-left) // 2
if nums[middle] >= target: # 寻找左边界,nums[middle] == target的时候更新right
right = middle - 1
leftBoder = right
else:
left = middle + 1
return leftBoder
leftBoder = getLeftBorder(nums, target)
rightBoder = getRightBorder(nums, target)
# 情况一
if leftBoder == -2 or rightBoder == -2: return [-1, -1]
# 情况三
if rightBoder -leftBoder >1: return [leftBoder + 1, rightBoder - 1]
# 情况二
return [-1, -1]
我的代码(当天晚上理解后自己编写)
目前的欠债:排序学习,二分法学习
922. 按奇偶排序数组 II
未看解答自己编写的青春版
不需要额外存储空间,但是时间只打败5%
class Solution:
def sortArrayByParityII(self, nums: List[int]) -> List[int]:
n = len(nums)
for i in range(n):
if nums[i]%2 == 0 and i%2 == 0:
continue
elif nums[i]%2 == 1 and i%2 == 1:
continue
elif nums[i]%2 == 0 and i%2 == 1:
j = i+1
while j < n :
if nums[j]%2 == 1 and j%2 == 0 :
break
j += 1
nums[i],nums[j] = nums[j],nums[i]
else :
j = i+1
while j < n :
if nums[j]%2 == 0 and j%2 == 1 :
break
j += 1
nums[i],nums[j] = nums[j],nums[i]
return nums
申请一块额外数组的方法很好写。
重点
三种方法。
发现了我自己写的代码的问题了,我只要对奇数or偶数,操作就可以了,不需要全部操作。我写的代码太复杂了,没有利用好题目的特性。
修改加速版,方法三真牛逼。
class Solution:
def sortArrayByParityII(self, nums: List[int]) -> List[int]:
n = len(nums)
index = 1
for i in range(0,n,2):
if nums[i] % 2 == 1 :
while nums[index] %2 == 1 :
index += 2
nums[i],nums[index]=nums[index],nums[i]
return nums
代码随想录的代码
过。
我的代码(当天晚上理解后自己编写)
35.搜索插入位置
未看解答自己编写的青春版
还行,这是最基础的二分法,写出来了
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
nums.sort()
n = len(nums)
mini = nums[0]
maxi = nums[-1]
if target < mini :
return 0
elif target > maxi :
return n
left = 0
right = n-1
while left <= right :
middle = left + (right-left)//2
if nums[middle] > target :
right = middle-1
elif nums[middle] < target :
left = middle + 1
else :
return middle
# 这里直接return left就行
return max(left,right)
重点
以后大家只要看到面试题里给出的数组是有序数组,都可以想一想是否可以使用二分法。
同时题目还强调数组中无重复元素,因为一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的。
代码随想录的代码
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
left, right = 0, len(nums) - 1
while left <= right:
middle = (left + right) // 2
if nums[middle] < target:
left = middle + 1
elif nums[middle] > target:
right = middle - 1
else:
return middle
return right + 1
我的代码(当天晚上理解后自己编写)
24. 两两交换链表中的节点
未看解答自己编写的青春版
这次写,竟然有些磕磕绊绊,不过稍微调了调,还是AC了。
class Solution:
def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
if head==None :
return head
if head.next == None :
return head
virtual = ListNode(0,head)
cur = virtual
while head and head.next :
temp = head.next.next
cur.next = head.next
head.next.next = head
head.next = temp
cur = head
head = cur.next
return virtual.next
重点
这道题只要画图,逻辑就能明白。
代码随想录的代码
递归版本还是不太好理解的,我感觉。
# 递归版本
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
if head is None or head.next is None:
return head
# 待翻转的两个node分别是pre和cur
pre = head
cur = head.next
next = head.next.next
cur.next = pre # 交换
pre.next = self.swapPairs(next) # 将以next为head的后续链表两两交换
return cur
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def swapPairs(self, head: ListNode) -> ListNode:
dummy_head = ListNode(next=head)
current = dummy_head
# 必须有cur的下一个和下下个才能交换,否则说明已经交换结束了
while current.next and current.next.next:
temp = current.next # 防止节点修改
temp1 = current.next.next.next
current.next = current.next.next
current.next.next = temp
temp.next = temp1
current = current.next.next
return dummy_head.next
我的代码(当天晚上理解后自己编写)
234. 回文链表
未看解答自己编写的青春版
失败失败,用链表不行,想错了,只考虑了“1221”的情况,没考虑“12121”的情况。错误代码如下:
class Solution:
def isPalindrome(self, head: Optional[ListNode]) -> bool:
stack = []
while head :
if stack == []:
stack.append(head.val)
else :
node = stack[-1]
if node == head.val :
stack.pop()
else :
stack.append(head.val)
head = head.next
n = len(stack)
if n == 0 :
return True
else :
return False
重点
翻转链表的方法细节很多,还是用数组承接是最简单的方法,不做翻转链表可以作为代码练习。
代码随想录的代码
#数组模拟
class Solution:
def isPalindrome(self, head: Optional[ListNode]) -> bool:
list=[]
while head:
list.append(head.val)
head=head.next
l,r=0, len(list)-1
while l<=r:
if list[l]!=list[r]:
return False
l+=1
r-=1
return True
#反转后半部分链表
class Solution:
def isPalindrome(self, head: Optional[ListNode]) -> bool:
fast = slow = head
# find mid point which including (first) mid point into the first half linked list
while fast and fast.next:
fast = fast.next.next
slow = slow.next
node = None
# reverse second half linked list
while slow:
slow.next, slow, node = node, slow.next, slow
# 这个翻转链表的代码也太复杂了,太难理解了,还是展开写,感觉翻转操作很好写啊
'''
while slow:
temp = slow.next
slow.next = node
node = slow
slow = temp
'''
# compare reversed and original half; must maintain reversed linked list is shorter than 1st half
while node:
if node.val != head.val:
return False
node = node.next
head = head.next
return True
我的代码(当天晚上理解后自己编写)
143. 重排链表
未看解答自己编写的青春版
没什么思路
重点
主要有三个思路:数组模拟、双向队列模拟、直接分割链表。
把链表放进数组中,然后通过双指针法,一前一后,来遍历数组,构造链表。
把链表放进双向队列,然后通过双向队列一前一后弹出数据,来构造新的链表。这种方法比操作数组容易一些,不用双指针模拟一前一后了。
代码随想录的代码
# 方法二 双向队列
class Solution:
def reorderList(self, head: ListNode) -> None:
"""
Do not return anything, modify head in-place instead.
"""
d = collections.deque()
tmp = head
while tmp.next: # 链表除了首元素全部加入双向队列
d.append(tmp.next)
tmp = tmp.next
tmp = head
while len(d): # 一后一前加入链表
tmp.next = d.pop()
tmp = tmp.next
if len(d):
tmp.next = d.popleft()
tmp = tmp.next
tmp.next = None # 尾部置空
# 方法三 反转链表
class Solution:
def reorderList(self, head: ListNode) -> None:
if head == None or head.next == None:
return True
slow, fast = head, head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
right = slow.next # 分割右半边
slow.next = None # 切断
right = self.reverseList(right) #反转右半边
left = head
# 左半边一定比右半边长, 因此判断右半边即可
while right:
curLeft = left.next
left.next = right
left = curLeft
curRight = right.next
right.next = left
right = curRight
def reverseList(self, head: ListNode) -> ListNode:
cur = head
pre = None
while(cur!=None):
temp = cur.next # 保存一下cur的下一个节点
cur.next = pre # 反转
pre = cur
cur = temp
return pre
方法三,稍微练了一下
class Solution:
def reorderList(self, head: Optional[ListNode]) -> None:
"""
Do not return anything, modify head in-place instead.
"""
fast = slow = head
# find mid point which including (first) mid point into the first half linked list
while fast and fast.next:
fast = fast.next.next
slow = slow.next
# 下面两句代码别忘了,一个是取右半边,这样取的右半边一定是短的那一方
# 这样连接也符合题目要求
right = slow.next # 获取后半天的头
slow.next = None # 切断!这句话很重要,不然就成环了
node = None
while right:
temp = right.next
right.next = node
node = right
right = temp
head2 = node
head1 = head
while head1 and head2:
temp1 = head1.next
temp2 = head2.next
head1.next = head2
head2.next = temp1
head1 = temp1
head2 = temp2
我的代码(当天晚上理解后自己编写)
141. 环形链表
未看解答自己编写的青春版
只判断是否有环,简单
注意判断条件,当循环里需要用到 fast.next.next 时,while 的判断条件要是:while fast and fast.next 。切记切记。
class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
if head == None :
return False
if head.next == None:
return False
slow = fast = head
# 注意判断条件,当循环里需要用到 fast.next.next 时,while 的判断条件要是:
# while fast and fast.next 。切记切记。
while fast and fast.next :
slow = slow.next
fast = fast.next.next
if slow == fast :
return True
return False
重点
如何找入口?自己画一个图,分成三段,x y z ,则有: 2(x+y) = x + y + z + y ,所以 x = z 。那么从头结点出发一个slow,从相遇节点出发一个fast,两个指针每次都移动一格,相遇节点即为环的入口!
代码随想录的代码
class Solution:
def hasCycle(self, head: ListNode) -> bool:
if not head: return False
slow, fast = head, head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if fast == slow:
return True
return False
我的代码(当天晚上理解后自己编写)
面试题 02.07. 链表相交
未看解答自己编写的青春版
我的这个代码,内存占用很高。
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
count1 = count2 = 0
cur1 = headA
cur2 = headB
while cur1 :
count1 += 1
cur1 = cur1.next
while cur2 :
count2 += 1
cur2 = cur2.next
if count1 >= count2 :
diff = count1 - count2
while diff > 0 :
headA = headA.next
diff -= 1
while headA :
if headA == headB :
return headA
headA = headA.next
headB = headB.next
return None
else :
diff = count2 - count1
while diff > 0 :
headB = headB.next
diff -= 1
while headA :
if headA == headB :
return headA
headA = headA.next
headB = headB.next
return None
重点
代码随想录的最冗余版本都比我的简洁!这题一定要学习他的写法。
代码随想录的代码
(版本一)求长度,同时出发
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
lenA, lenB = 0, 0
cur = headA
while cur: # 求链表A的长度
cur = cur.next
lenA += 1
cur = headB
while cur: # 求链表B的长度
cur = cur.next
lenB += 1
curA, curB = headA, headB
if lenA > lenB: # 让curB为最长链表的头,lenB为其长度
curA, curB = curB, curA
lenA, lenB = lenB, lenA
for _ in range(lenB - lenA): # 让curA和curB在同一起点上(末尾位置对齐)
curB = curB.next
while curA: # 遍历curA 和 curB,遇到相同则直接返回
if curA == curB:
return curA
else:
curA = curA.next
curB = curB.next
return None
(版本二)求长度,同时出发 (代码复用)
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
lenA = self.getLength(headA)
lenB = self.getLength(headB)
# 通过移动较长的链表,使两链表长度相等
if lenA > lenB:
headA = self.moveForward(headA, lenA - lenB)
else:
headB = self.moveForward(headB, lenB - lenA)
# 将两个头向前移动,直到它们相交
while headA and headB:
if headA == headB:
return headA
headA = headA.next
headB = headB.next
return None
def getLength(self, head: ListNode) -> int:
length = 0
while head:
length += 1
head = head.next
return length
def moveForward(self, head: ListNode, steps: int) -> ListNode:
while steps > 0:
head = head.next
steps -= 1
return head
(版本三)求长度,同时出发 (代码复用 + 精简)
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
dis = self.getLength(headA) - self.getLength(headB)
# 通过移动较长的链表,使两链表长度相等
if dis > 0:
headA = self.moveForward(headA, dis)
else:
headB = self.moveForward(headB, abs(dis))
# 将两个头向前移动,直到它们相交
while headA and headB:
if headA == headB:
return headA
headA = headA.next
headB = headB.next
return None
def getLength(self, head: ListNode) -> int:
length = 0
while head:
length += 1
head = head.next
return length
def moveForward(self, head: ListNode, steps: int) -> ListNode:
while steps > 0:
head = head.next
steps -= 1
return head
最后这个方法,理解不了,不知道在干什么。
(版本四)等比例法
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
# 处理边缘情况
if not headA or not headB:
return None
# 在每个链表的头部初始化两个指针
pointerA = headA
pointerB = headB
# 遍历两个链表直到指针相交
while pointerA != pointerB:
# 将指针向前移动一个节点
pointerA = pointerA.next if pointerA else headB
pointerB = pointerB.next if pointerB else headA
# 如果相交,指针将位于交点节点,如果没有交点,值为None
return pointerA
我的代码(当天晚上理解后自己编写)
205. 同构字符串
未看解答自己编写的青春版
自己的思路是错的!
因为题目中没有给出:字符串一定由英文字母组成,所以不能使用哈希!
重点
字符串没有说都是小写字母之类的,所以用数组不合适了,用map来做映射。
使用两个map 保存 s[i] 到 t[j] 和 t[j] 到 s[i] 的映射关系,如果发现对应不上,立刻返回 false
代码随想录的代码
class Solution:
def isIsomorphic(self, s: str, t: str) -> bool:
default_dict1 = defaultdict(str)
default_dict2 = defaultdict(str)
if len(s) != len(t): return false
for i in range(len(s)):
if not default_dict1[s[i]]:
default_dict1[s[i]] = t[i]
if not default_dict2[t[i]]:
default_dict2[t[i]] = s[i]
if default_dict1[s[i]] != t[i] or default_dict2[t[i]] != s[i]:
return False
return True
用Python中最基础的{}作为字典的自己写版本
class Solution:
def isIsomorphic(self, s: str, t: str) -> bool:
dic1 = {}
dic2 = {}
n = len(s)
m = len(t)
if m!=n :
return False
for i in range(n):
if not dic1.get(s[i],0):
dic1[s[i]] = t[i]
if not dic2.get(t[i],0):
dic2[t[i]] = s[i]
if dic1[s[i]]!=t[i] or dic2[t[i]]!=s[i]:
return False
return True
我的代码(当天晚上理解后自己编写)
1002. 查找共用字符
未看解答自己编写的青春版
这道题在编写能力上要求很高,因为如果是N个字符串,要有N个哈希表。
重点
哈希哈希,看到字符串全部由小学字母组成没有?这么明显的提示,不用哈希用什么?
主要注意的点是,如果两个字符串为 [ ‘ll’ , ‘ll’ ] , 结果应该是: [ ‘l’ , ‘l’ ] , 而不是 [ ‘l’ ] 。
所以要取每个哈希中的最小值。
注意:思路是取三个哈希,不能只取一个计数!(我一开始是这样想的),因为这样会有问题,假如第一个字符串出现两个
‘a’ , 如果只有一个哈希表,会计数为2 , 和两个字符串中都出现一个 ‘a’ 的结果一样。
代码随想录的代码
看懂了,但应该还没掌握。
class Solution:
def commonChars(self, words: List[str]) -> List[str]:
if not words: return []
result = []
hash = [0] * 26 # 用来统计所有字符串里字符出现的最小频率
for i, c in enumerate(words[0]): # 用第一个字符串给hash初始化
hash[ord(c) - ord('a')] += 1
# 统计除第一个字符串外字符的出现频率
for i in range(1, len(words)):
hashOtherStr = [0] * 26
for j in range(len(words[i])):
hashOtherStr[ord(words[i][j]) - ord('a')] += 1
# 更新hash,保证hash里统计26个字符在所有字符串里出现的最小次数
for k in range(26):
hash[k] = min(hash[k], hashOtherStr[k])
# 将hash统计的字符次数,转成输出形式
for i in range(26):
while hash[i] != 0: # 注意这里是while,多个重复的字符
result.extend(chr(i + ord('a')))
hash[i] -= 1
return result
class Solution:
def commonChars(self, words: List[str]) -> List[str]:
tmp = collections.Counter(words[0])
l = []
for i in range(1,len(words)):
# 使用 & 取交集
tmp = tmp & collections.Counter(words[i])
# 剩下的就是每个单词都出现的字符(键),个数(值)
for j in tmp:
v = tmp[j]
while(v):
l.append(j)
v -= 1
return l
我的代码(当天晚上理解后自己编写)
925. 长按键入
未看解答自己编写的青春版
class Solution:
def isLongPressedName(self, name: str, typed: str) -> bool:
n = len(name)
m = len(typed)
i = j =0
while i < n and j < m :
if name[i] == typed[j] :
i += 1
j += 1
else :
if j == 0 :
return False
while j < m and typed[j]==typed[j-1] :
j += 1
# 这个判断是要加的,不加会报 index 的错误
# 笑死了,C++不加就不会报错,python不加就会报错
# 不同语言的测试用例还不一样
# 不过应该是需要加的我认为
if j == m :
return False
# 移动后要再次匹配,关键
if typed[j] != name[i] :
return False
else :
i += 1
j += 1
# 排除最后一个字符重复的情况,移动j
if j < m :
while j < m and typed[j]==typed[j-1]:
j+=1
if i < n or j < m:
return False
else :
return True
重点
代码随想录的代码
代码随想录这题给的代码不好,看我上面自己写的就行,思路和代码随想录的完全一致。
我的代码(当天晚上理解后自己编写)
844. 比较含退格的字符串
未看解答自己编写的青春版
用栈模拟很简单,只要注意,只有当栈非空时,再对栈进行操作就可以避免bug。
重点
这道题要学习使用双指针法的思路。
代码随想录的代码
class Solution:
def get_string(self, s: str) -> str :
bz = []
for i in range(len(s)) :
c = s[i]
if c != '#' :
bz.append(c) # 模拟入栈
elif len(bz) > 0: # 栈非空才能弹栈
bz.pop() # 模拟弹栈
return str(bz)
def backspaceCompare(self, s: str, t: str) -> bool:
return self.get_string(s) == self.get_string(t)
pass
class Solution:
def backspaceCompare(self, s: str, t: str) -> bool:
s_index, t_index = len(s) - 1, len(t) - 1
s_backspace, t_backspace = 0, 0 # 记录s,t的#数量
while s_index >= 0 or t_index >= 0: # 使用or,以防长度不一致
while s_index >= 0: # 从后向前,消除s的#
if s[s_index] == '#':
s_index -= 1
s_backspace += 1
else:
if s_backspace > 0:
s_index -= 1
s_backspace -= 1
else:
break
while t_index >= 0: # 从后向前,消除t的#
if t[t_index] == '#':
t_index -= 1
t_backspace += 1
else:
if t_backspace > 0:
t_index -= 1
t_backspace -= 1
else:
break
if s_index >= 0 and t_index >= 0: # 后半部分#消除完了,接下来比较当前位的值
if s[s_index] != t[t_index]:
return False
elif s_index >= 0 or t_index >= 0: # 一个字符串找到了待比较的字符,另一个没有,返回False
return False
s_index -= 1
t_index -= 1
return True
我的代码(当天晚上理解后自己编写)
129. 求根节点到叶节点数字之和
未看解答自己编写的青春版
挺简单的,但是递归结束条件,一开始写错了。
class Solution:
def sumNumbers(self, root: Optional[TreeNode]) -> int:
self.res = 0
if root == None :
return 0
if root.left == None and root.right == None :
return root.val
level = [str(root.val)]
self.digui(root,level)
return self.res
def digui(self,root,level):
# 注意这里的递归结束条件,是叶子节点,不是None
if root.left == None and root.right == None:
self.res += int(''.join(level))
return
if root.left :
level.append(str(root.left.val))
self.digui(root.left,level)
level.pop()
if root.right :
level.append(str(root.right.val))
self.digui(root.right,level)
level.pop()
重点
本题主要注意的就是:递归结束条件,遇到叶子节点则返回。
代码随想录的代码
class Solution:
def sumNumbers(self, root: TreeNode) -> int:
res = 0
path = []
def backtrace(root):
nonlocal res
if not root: return # 节点空则返回
path.append(root.val)
if not root.left and not root.right: # 遇到了叶子节点
res += get_sum(path)
if root.left: # 左子树不空
backtrace(root.left)
if root.right: # 右子树不空
backtrace(root.right)
path.pop()
def get_sum(arr):
s = 0
for i in range(len(arr)):
s = s * 10 + arr[i]
return s
backtrace(root)
return res
我的代码(当天晚上理解后自己编写)
1382.将二叉搜索树变平衡
未看解答自己编写的青春版
先将二叉搜索树,中序遍历为有序数组,再利用有序数组,构造二叉平衡搜索树
class Solution:
def balanceBST(self, root: TreeNode) -> TreeNode:
if root == None :
return None
if root.left == None and root.right == None :
return root
self.nums = []
self.get_num(root)
head = self.compute_tree(self.nums)
return head
def get_num(self,root):
if root == None:
return
self.get_num(root.left)
self.nums.append(root.val)
self.get_num(root.right)
def compute_tree(self,nums):
if len(nums) == 0:
return None
n = len(nums)
middle = n//2
value = nums[middle]
node = TreeNode(value)
left = self.compute_tree(nums[:middle])
right = self.compute_tree(nums[middle+1:])
node.left = left
node.right = right
return node
重点
代码随想录的代码
class Solution:
def balanceBST(self, root: TreeNode) -> TreeNode:
res = []
# 有序树转成有序数组
def traversal(cur: TreeNode):
if not cur: return
traversal(cur.left)
res.append(cur.val)
traversal(cur.right)
# 有序数组转成平衡二叉树
def getTree(nums: List, left, right):
if left > right: return
mid = left + (right -left) // 2
root = TreeNode(nums[mid])
root.left = getTree(nums, left, mid - 1)
root.right = getTree(nums, mid + 1, right)
return root
traversal(root)
return getTree(res, 0, len(res) - 1)
我的代码(当天晚上理解后自己编写)
100. 相同的树
未看解答自己编写的青春版
写是写出来了,但是内存占用过高?
class Solution:
def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
if p == None and q == None :
return True
elif p == None and q != None :
return False
elif p != None and q == None :
return False
else :
if p.val != q.val :
return False
left = self.isSameTree(p.left,q.left)
right = self.isSameTree(p.right,q.right)
return left and right
重点
本题的迭代法,有坑,要多练
代码随想录的代码
class Solution:
def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
if not p and not q: return True
elif not p or not q: return False
elif p.val != q.val: return False
return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right)
本题的迭代法注意:不能像遍历那里一样,空节点不入栈,这里空节点必须入栈,然后加判断加continue
# 迭代法
class Solution:
def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
if not p and not q: return True
if not p or not q: return False
que = collections.deque()
que.append(p)
que.append(q)
while que:
leftNode = que.popleft()
rightNode = que.popleft()
# 这句话太关键了,必须加判断空节点和continue
if not leftNode and not rightNode: continue
if not leftNode or not rightNode or leftNode.val != rightNode.val: return False
# 不能像之前的写法一样,空节点不入栈
que.append(leftNode.left)
que.append(rightNode.left)
que.append(leftNode.right)
que.append(rightNode.right)
return True
我的代码(当天晚上理解后自己编写)
116. 填充每个节点的下一个右侧节点指针
未看解答自己编写的青春版
这道题和之前做过的那道题略有不同,这道题要求只能使用常量级的存储空间,说白了就是明着告诉你:给老子用递归写!不能像之前的题目一样使用层序遍历了。
但是用递归,就没什么思路了,明显就是层序遍历的模板题,怎么用递归?
重点
if (cur->left) cur->left->next = cur->right; // 操作1
if (cur->right) {
if (cur->next) cur->right->next = cur->next->left; // 操作2
else cur->right->next = NULL;
}
代码随想录的代码
# 递归法
class Solution:
def connect(self, root: 'Node') -> 'Node':
def traversal(cur: 'Node') -> 'Node':
if not cur: return []
if cur.left: cur.left.next = cur.right # 操作1
if cur.right:
if cur.next:
cur.right.next = cur.next.left # 操作2
else:
cur.right.next = None
traversal(cur.left) # 左
traversal(cur.right) # 右
traversal(root)
return root
这道题是经典,之前没做过类似的!
我的代码(当天晚上理解后自己编写)
52. N皇后II
未看解答自己编写的青春版
class Solution:
def totalNQueens(self, n: int) -> int:
if n == 1 :
return 1
self.res = 0
level = []
self.backtracking(level,n)
return self.res
def backtracking(self,level,n):
m = len(level)
if m >= n :
print(level)
self.res += 1
return
row = m
for i in range(n):
col = i
level.append([row,col])
# 下面那里,参数要传入 m+1 , 因为level已经加入一个数值了,Debug了好久
if self.is_right(level,n,m+1):
self.backtracking(level,n)
level.pop()
else :
level.pop()
def is_right(self,level,n,m):
for i in range(m):
for j in range(i+1,m):
if level[i][1]==level[j][1] :
return False
if abs((level[i][1]-level[j][1])/(level[i][0]-level[j][0])) == 1 :
return False
return True
重点
同N皇后。
代码随想录的代码
没有Python的代码。
我的代码(当天晚上理解后自己编写)
649. Dota2 参议院
未看解答自己编写的青春版
没写出来,这道题还是很有难度的。
重点
没想到用一个循环来控制轮数。
这道题真的很妙
代码随想录的代码
class Solution:
def predictPartyVictory(self, senate: str) -> str:
# R = true表示本轮循环结束后,字符串里依然有R。D同理
R , D = True, True
# 当flag大于0时,R在D前出现,R可以消灭D。当flag小于0时,D在R前出现,D可以消灭R
flag = 0
senate = list(senate)
while R and D: # 一旦R或者D为false,就结束循环,说明本轮结束后只剩下R或者D了
R = False
D = False
for i in range(len(senate)) :
if senate[i] == 'R' :
if flag < 0: senate[i] = '0' # 消灭R,R此时为false
else: R = True # 如果没被消灭,本轮循环结束有R
flag += 1
if senate[i] == 'D':
if flag > 0: senate[i] = '0'
else: D = True
flag -= 1
# 循环结束之后,R和D只能有一个为true
return "Radiant" if R else "Dire"
我的代码(当天晚上理解后自己编写)
1221. 分割平衡字符串
未看解答自己编写的青春版
class Solution:
def balancedStringSplit(self, s: str) -> int:
res = 0
s = list(s)
n = len(s)
i = 0
count = 0
while i < n :
if s[i] == 'R':
count += 1
else :
count -= 1
if count == 0 :
res += 1
i += 1
return res
重点
贪心就好,这道题一开始被示例迷惑了,都是对称的字符串,但是一般情况是,不对称的字符串,比如 “RRLRLL”
代码随想录的代码
class Solution:
def balancedStringSplit(self, s: str) -> int:
diff = 0 #右左差值
ans = 0
for c in s:
if c == "L":
diff -= 1
else:
diff += 1
if diff == 0:
ans += 1
return ans
我的代码(当天晚上理解后自己编写)
5. 最长回文子串
这种题到现在还是不会写,看着答案写一写
class Solution:
def longestPalindrome(self, s: str) -> str:
n = len(s)
if n <= 1 :
return s
dp = [[False]*n for _ in range(n)]
maxd = 0
left = 0
right = 0
for i in range(n-1,-1,-1):
# 注意这里 j 的取值范围,是从i开始的,包括i,因为dp[i][j]定义的是[i,j]闭区间
# 所以 i=j 是可行的,也是必须要考虑的
for j in range(i,n):
if s[j]==s[i]:
if j-i <= 1 :
dp[i][j]=True
# 一定注意这里是 elif 而不是 if , if 会出现数组越界
elif dp[i+1][j-1] == True :
dp[i][j]=True
if dp[i][j] and j-i+1 > maxd :
maxd = j-i+1
left = i
right = j
return s[left:right+1]
class Solution:
def longestPalindrome(self, s: str) -> str:
n = len(s)
if n <= 1 :
return s
start = 0
end = 0
for i in range(n):
left,right = self.find_point(i,i,s)
start,end = self.compare(end,start,right,left)
left,right = self.find_point(i,i+1,s)
# 注意这里,第二次比较,其实这次比较的,已经是前面,以单字符为中心的最大结果了
# 即是上一次的 left 和 right 结果,和这次的 left right 进行比较
start,end = self.compare(end,start,right,left)
return s[start:end]
def find_point(self,i,j,s):
while i >= 0 and j < len(s) and s[i]==s[j] :
i -= 1
j += 1
return i+1,j
def compare(self,a,b,c,d):
if a-b > c-d :
return b,a
else :
return d,c
重点
对于回文子串的题,要学会这种动态规划的写法,dp[i][j],代表 [ i , j ] 的子串,是否为回文串。这里一定要注意,这里有一个循环不变量,关系着代码的编写,注意这里是,左闭右闭区间!
注意处理三种情况,数值相等,那么下标相等或者相邻,都是回文;数值相等,如果里面也是回文,则是回文。
一定要注意,第三种情况,和前两种情况的关系,一定是 elif ,而不是 if , if 会出现数组越界。
本题的双指针法也值得学习
代码随想录的代码
class Solution:
def longestPalindrome(self, s: str) -> str:
dp = [[False] * len(s) for _ in range(len(s))]
maxlenth = 0
left = 0
right = 0
for i in range(len(s) - 1, -1, -1):
for j in range(i, len(s)):
if s[j] == s[i]:
if j - i <= 1 or dp[i + 1][j - 1]:
dp[i][j] = True
if dp[i][j] and j - i + 1 > maxlenth:
maxlenth = j - i + 1
left = i
right = j
return s[left:right + 1]
class Solution:
def longestPalindrome(self, s: str) -> str:
def find_point(i, j, s):
while i >= 0 and j < len(s) and s[i] == s[j]:
i -= 1
j += 1
return i + 1, j
def compare(start, end, left, right):
if right - left > end - start:
return left, right
else:
return start, end
start = 0
end = 0
for i in range(len(s)):
left, right = find_point(i, i, s)
start, end = compare(start, end, left, right)
left, right = find_point(i, i + 1, s)
start, end = compare(start, end, left, right)
return s[start:end]
我的代码(当天晚上理解后自己编写)
132. 分割回文串 II
自己看着解答写的不优秀代码
class Solution:
def minCut(self, s: str) -> int:
n = len(s)
if n <= 1 :
return 0
dp = [[False]*n for _ in range(n)]
for i in range(n-1,-1,-1):
for j in range(i,n):
if s[i]==s[j]:
if j-i <= 1:
dp[i][j]=True
elif dp[i+1][j-1]==True:
dp[i][j]=True
DP = [inf]*n
DP[0]=0
for i in range(1,n):
# 这里的if在我这里,应该是不能去掉的
# 这里我是根据debug才发现的,解答里也是这么写的,要先进行一个判断,不然DP[i]最小就是 1 了。
if dp[0][i] == True :
DP[i] = 0
else :
# 这里的下标也有一些混乱,我一直想着,dp的含义是[i,j]
# DP的含义是[0,j]是回文串的最小分割
# 始终明确数组的区间是否是闭的
for j in range(i):
if dp[j+1][i] == True :
DP[i] = min(DP[i],DP[j]+1)
return DP[n-1]
重点
本题的重点主要有两个:DP数组的含义,dp[i] : 范围是 [ 0 , i ] 的回文子串,最小分割次数是 dp[i] 。递推公式的话,就是去遍历每一个当前index前面的值,如果 [ i , j ] 是回文串,就是 dp[j] = dp[i] + 1。
那么怎么知道 [ i , j ] 是不是回文串呢 ? 其实就是前一题的代码,一模一样,先用前一题的DP方法,把记录所有子串的回文情况的DP数组,先计算出来。
代码随想录的代码
要学习代码的细节!
class Solution:
def minCut(self, s: str) -> int:
isPalindromic=[[False]*len(s) for _ in range(len(s))]
for i in range(len(s)-1,-1,-1):
for j in range(i,len(s)):
if s[i]!=s[j]:
isPalindromic[i][j] = False
elif j-i<=1 or isPalindromic[i+1][j-1]:
isPalindromic[i][j] = True
# print(isPalindromic)
dp=[sys.maxsize]*len(s)
dp[0]=0
for i in range(1,len(s)):
if isPalindromic[0][i]:
dp[i]=0
continue
for j in range(0,i):
if isPalindromic[j+1][i]==True:
dp[i]=min(dp[i], dp[j]+1)
return dp[-1]
我的代码(当天晚上理解后自己编写)
673. 最长递增子序列的个数
未看解答自己编写的青春版
不会。
这题记录一下吧,没心情看了,思路还没看懂,本题也很重要
重点
本题是第一次接触,同时维护两个数组的动态规划的题目。
代码随想录的代码
class Solution:
def findNumberOfLIS(self, nums: List[int]) -> int:
size = len(nums)
if size<= 1: return size
dp = [1 for i in range(size)]
count = [1 for i in range(size)]
maxCount = 0
for i in range(1, size):
for j in range(i):
if nums[i] > nums[j]:
if dp[j] + 1 > dp[i] :
dp[i] = dp[j] + 1
count[i] = count[j]
elif dp[j] + 1 == dp[i] :
count[i] += count[j]
if dp[i] > maxCount:
maxCount = dp[i];
result = 0
for i in range(size):
if maxCount == dp[i]:
result += count[i]
return result;
我的代码(当天晚上理解后自己编写)
657. 机器人能否返回原点
未看解答自己编写的青春版
重点
用坐标模拟就好。
代码随想录的代码
# 时间复杂度:O(n)
# 空间复杂度:O(1)
class Solution:
def judgeCircle(self, moves: str) -> bool:
x = 0 # 记录当前位置
y = 0
for i in range(len(moves)):
if (moves[i] == 'U'):
y += 1
if (moves[i] == 'D'):
y -= 1
if (moves[i] == 'L'):
x += 1
if (moves[i] == 'R'):
x -= 1
return x == 0 and y == 0