题50:第一个只出现一次的字符
法一:我以为我想的是个牛气方法,结果😅了
虽然代码行数很少,但每次count都会遍历一遍s
class Solution:
def firstUniqChar(self, s: str) -> str:
for i in s:
if s.count(i)==1:
return i
return " "
法二:正解是用字典记录下每个字符出现的次数,再返回第一个只出现一次的字符
class Solution:
def firstUniqChar(self, s: str) -> str:
dic={}
for i in s:
if i not in dic:
dic[i]=1
else:
dic[i]+=1
for k in dic:
if dic[k]==1:
return k
return " "
简洁版:
太厉害了,如果字符已经在字典中了,那么其值为0,否则为1
class Solution:
def firstUniqChar(self, s: str) -> str:
dic={}
for i in s:
dic[i]=not i in dic
for k in dic:
if dic[k]==1:
return k
return " "
题52: 两个链表的第一个公共节点
法一:自己想的笨方法
将两个链表存储下来,然后从后往前比较,遇到不相等的节点时,返回该节点的前一个节点即可。需要注意的是,当两个链表从第一个节点开始相交时,最后需要多一个判断。
不要学这种方法,当链表长度无穷时,该方法必然占用极大空间。。还是学正解吧。。
# 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:
NodeA, NodeB = [],[] ## 用于存储AB两个链表的list
ind = headA
while ind!=None:
NodeA.append(ind)
ind = ind.next
ind = headB
while ind!=None:
NodeB.append(ind)
ind = ind.next
if min(len(NodeA),len(NodeB)) == 0: # 当链表为空时,直接返回null
return None
for i in range(1,min(len(NodeA),len(NodeB))+1): # 开始从后往前判断
if NodeA[-i]!= NodeB[-i]:
return NodeA[-i].next
# 对特殊情况做处理
if len(NodeA)==i:
return NodeA[0]
elif len(NodeB)==i:
return NodeB[0]
else:
return None
法二:正解——双指针法
当链表headA 和headB 都不为空时,创建两个指针pA 和pB,初始时分别指向两个链表的头节点 headA 和headB,然后将两个指针依次遍历两个链表的每个节点。具体做法如下:
- 每步操作需要同时更新指针 pA 和pB。
- 如果指针pA 不为空,则将指针pA 移到下一个节点;如果指针pB 不为空,则将指针 pB 移到下一个节点。
- 如果指针pA 为空,则将指针pA 移到链表headB 的头节点;如果指针pB 为空,则将指针 pB 移到链表headA 的头节点。
- 当指针pA 和pB 指向同一个节点或者都为空时,返回它们指向的节点或者null。
证明该做法的正确性:
情况一:两个链表相交
则,链表headA 和headB 的长度分别是 m 和 n。假设链表headA 的不相交部分有 a 个节点,链表 headB 的不相交部分有 b 个节点,两个链表相交的部分有 c 个节点,则有 a+c=m,b+c=n。
- 如果a=b,则两个指针会同时到达两个链表的第一个公共节点,此时返回两个链表的第一个公共节点;
- 如果 a != b ,则指针 pA 会遍历完链表headA,指针pB 会遍历完链表headB,两个指针不会同时到达链表的尾节点,然后指针pA 移到链表headB 的头节点,指针pB 移到链表headA 的头节点,然后两个指针继续移动,在指针pA 移动了a+c+b次、指针pB 移动了b+c+a次之后,两个指针会同时到达两个链表的第一个公共节点,该节点也是两个指针第一次同时指向的节点,此时返回两个链表的第一个公共节点。【喵啊!!!】
情况二:两个链表不相交
链表 headA 和headB 的长度分别是 m 和 nn。考虑当m=n 和 m!=n时,两个指针分别会如何移动:
- 如果 m=n,则两个指针会同时到达两个链表的尾节点,然后同时变成空值null,此时返回null;
- 如果 m!=n,则由于两个链表没有公共节点,两个指针也不会同时到达两个链表的尾节点,因此两个指针都会遍历完两个链表,在指针pA 移动了 m+n 次、指针 pB 移动了 n+mn+m 次之后,两个指针会同时变成空值null,此时返回 null。
【题解描述参考👉LeetCode-Solution】
# 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:
pA,pB = headA,headB
while pA != pB:
pA = headB if not pA else pA.next
pB = headA if not pB else pB.next
return pA
题55-II: 平衡二叉树
法一:自己想的方法
遍历套遍历:遍历二叉树,针对每个节点,计算其左子树和右子树的深度,判断深度差【根据题意得到的最直接的想法】
代码如下:
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def isBalanced(self, root: TreeNode) -> bool:
flag = 1 # 指示节点是否满足平衡这一要求
def TreeDepth(p: TreeNode) -> int: # 递归求解树的深度
LD,RD = 0,0
if not p:
return 0
else:
LD = TreeDepth(p.left)
RD = TreeDepth(p.right)
return (LD if LD>RD else RD)+1
def preorder(p: TreeNode) -> None: # 先序遍历
nonlocal flag,TreeDepth # 指明变量作用域
if (p is not None)&(flag==1):
# 判断该节点的左右子树深度差是否大于1
flag = 0 if abs(TreeDepth(p.left)-TreeDepth(p.right))>1 else 1
preorder(p.left)
preorder(p.right)
preorder(root)
return True if flag==1 else False
法二:官方题解
官方题解和我的方法在思路上是一样的,但是在代码表述上简洁很多,直接上代码:
class Solution:
def isBalanced(self, root: TreeNode) -> bool:
def height(root: TreeNode) -> int: # 该函数用于求解该节点的高度,也是递归,但比我的递归在表达上简单
if not root:
return 0
return max(height(root.left), height(root.right)) + 1
if not root:
return True
# 这个return直接就实现了遍历递归,不需要像我那样写个单独的函数
return abs(height(root.left) - height(root.right)) <= 1 and self.isBalanced(root.left) and self.isBalanced(root.right)
献上膝盖
要点回顾:如何求二叉树的深度、如何深度遍历【应用形式上要学会举一反三
题56-I: 数组中数字出现的次数
第一次见这种题,自然想不到,于是看了某人的题解,现只对代码进行详细注释:
class Solution:
def singleNumbers(self, nums: List[int]) -> List[int]:
res = nums[0] # res用于存储整个nums异或的结果,该值其实等于a^b
for n in nums[1:]:
res ^= n
# 现在,开始寻找res在二进制下为1的位
i = 1
while res&i==0: # 循环起始条件为 res与上1的结果,其实就是res从右往左数的第一位
i <<= 1 # 当第一位为0时,将i左移一位,直到res与上i的结果为1,这时i就指示了目标位
a,b = None,None # 初始化a和b
for n in nums:
if n&i: # 根据n在第i位上的值,将nums化为两部分
a = a^n if a else n # 这里的if else是为了正确初始化a和b的值
else:
b = b^n if b else n # 如果a或b为空,则赋值为n
return [a,b]
姿势:异或:a ^b^a=a
题63:股票的最大利润
哦吼,自己终于完整想出&写出了一道题。
思想:举例说明:[2,5,3,1,6,4]中,从2开始往后遍历,如果遇到比2大的值,则求差并维护最大的值,如果遇到比2小的值,则更新遍历起点,即从1开始接着遍历。
代码:
class Solution:
def maxProfit(self, prices: List[int]) -> int:
# front为遍历起点,plen为list的长度,res为result
front,plen,res = 0,len(prices),0
# 开始遍历
while front < plen-1:
rear = front + 1 # 初始化rear
while prices[rear]>prices[front]: # 当rear所指值比front大,则
tmp = prices[rear] - prices[front] # 求解当前的差值
res = max(res,tmp) # 维护正确答案
if rear == plen-1: # 遍历结束
break
rear += 1 # 更新rear,即往后遍历
front = rear # 当rear小于front,则更新遍历起点front
return res
题68-II:二叉树的最近公共祖先
想到了思路,卡在了代码上。。。。
思路:对于节点R而言,如果其左子树包含p,右子树包含q;或者其本身为p,左子树或右子树中包含q;则该节点就是p和q的最近公共祖先。
代码如下:
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
res = None
def findn(r:TreeNode)->int:
nonlocal p,q,res
if not r:
return 0
ind = 1 if r.val==p.val or r.val==q.val else 0 # 判断当前节点是否为p/q;
now = ind+findn(r.left)+findn(r.right) # 求以该节点为根的子树中是否包含p/q
if now==2: #由于递归的存在,一定会先遍历答案,再往上便利,所以 一旦now==2,则该节点比为正确答案,此时,为避免答案重复,需要return 0;
res=r
return 0
else:
return now
findn(root)
return res
这种递归形式需要学习,函数只完成了判断当前节点是否为答案的过程,其递归隐藏在加法中,所以,当now==2时,为了避免答案出现重复,需要return 0;
【献膝盖给HappySuperman——orz】