0%

给定一个长度为 n 的非负整数数组 nums,你最初位于数组的第一个下标。数组中的每个元素代表你在该下标可以跳跃的最大长度。

你的目标是使用最少的跳跃次数到达数组的最后一个下标。

先确定跳跃的范围,假设当前下标为 i,那么可以跳跃到的下标范围是 [i+1, i+nums[i]]。如果 i+nums[i] 大于等于最后一个下标 n-1,那么就可以直接到达最后一个下标。这个时候更新最后的下标为 i,并且跳跃次数加一。接下来继续从新的下标 i 开始,直到最后一个下标为 0。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution:
def jump(self, nums: List[int]) -> int:
# 递归
# n = len(nums)
# if n == 1:
# return 0
# for i in range(0, n-1):
# jump_len = nums[i]
# if jump_len + i >= n-1:
# return self.jump(nums[:i+1]) + 1

# 迭代
n = len(nums)
last, count = n-1, 0
while last > 0:
for i in range(0, last):
if i + nums[i] >= last:
count += 1
last = i
break
return count

给你一个非负整数数组 nums,你最初位于数组的第一个下标。数组中的每个元素代表你在该下标可以跳跃的最大长度。
判断你是否能够到达最后一个下标。

一开始想了一个比较笨的方法,就是从后往前推,递归的判断每一个元素能否到达最后一个元素。

后来发现从前往后推更简单,直接用一个变量right来记录当前能到达的最远位置。每次遍历到一个元素时,如果当前元素的下标小于等于right,就更新rightmax(right, i+nums[i])。如果在遍历过程中发现right已经大于等于最后一个元素的下标,就可以返回True。

阅读全文 »

在西门子实习的时候,当时突发奇想想设计一个pre-training加post-training的架构,当时是希望在后面接一个projection head,然后前面用预训练好的模型冻结住不改变就可以了。原本以为只需要写一串这样的代码就完全OK:

1
2
for param in model.xxxpart.parameters():
param.require_grad = False

结果训练完后面的projection head,前面的模型输出的重建图片的结果和原来差别巨大,我就感觉并没有冻结住所有的变量,肯定是对前面也进行了训练。查看训练前后模型的参数变化:

阅读全文 »

博客是2021年就搭建好的,可惜也没有写一些技术类的文章。主要是自己也没什么本事,肚子里也没啥墨水。那还不如在互联网上记录过去一年生活与学习的痕迹,以后还能留个纪念。写流水账

实习

一月份结束了在ChaoWei Xiao老师那里的远程实习,是我第一份科研向实习工作,主要做的是大模型评测代码相关benchmark的搭建。

正如同deepseek宣传的那样,AGI的实现是算力x算法x数据,数据在其中其实扮演着很重要的角色,所以这个工作大部分时间也就在洗数据,测模型,找bad case等。据说benchmark相关的工作就是这样,但是我个人实在是觉得这样的工作没有什么含金量,也没有去继续下去。

虽然这段实习经历就三个月的时间,并没有继续延续下去,但是我觉得这倒是我受益比较多的实习经历。在zqz师兄 (虽然我觉得他情商非常低下) 的指导之下,倒是形成了自己的代码规范,对于面向对象代码的规范,方法的封装,类的继承等都比之前要写的好了很多,技术力upup。同时在做实验的时候还学到了很多分析bad case的方法,以及分析问题的角度与思路,还是蛮不错的。

寒假在家倒是挺爽的,和朋友吃吃喝喝,爽摸一个月。

阅读全文 »

给定一个只包括 '('')''{''}''['']' 的字符串,判断字符串是否有效。

有效字符串需满足:

  • 左括号必须用相同类型的右括号闭合。
  • 左括号必须以正确的顺序闭合。
  • 每个右括号必须闭合对应的左括号。

用一个栈来存储左括号,遇到右括号时,判断栈顶元素是否与之匹配,若匹配则弹出栈顶元素,否则将右括号压入栈中。最后判断栈是否为空即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution:
def isValid(self, s: str) -> bool:
stack = []
matched = ["()", "{}", "[]"]

for st in s:
if len(stack) == 0:
stack.append(st)
continue
if stack[-1]+st in matched:
stack.pop()
else:
stack.append(st)

if len(stack) == 0:
return True
else:
return False

给定一个经过编码的字符串,返回它解码后的字符串。

编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。

你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。

同时,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a2[4] 的输入。

用两个栈来分别存储数字和字符串,遇到数字就更新数字,遇到左括号就将数字和左括号分别入栈,遇到右括号就将栈顶的字符串出栈,直到遇到左括号,然后将字符串翻转后重复数字次入栈。最后将栈中的字符串拼接起来即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution:
def decodeString(self, s: str) -> str:
num_stack, str_stack = [], []
cur_num = 0
for st in s:
if st.isdigit():
print(cur_num)
cur_num = cur_num*10 + int(st)
elif st == '[':
num_stack.append(cur_num)
cur_num = 0
str_stack.append(st)
elif not st == ']':
str_stack.append(st)
else:
tmp_str = ''
top = str_stack.pop()
while top != '[':
tmp_str += top
top = str_stack.pop()
tmp_str = tmp_str[::-1]*int(num_stack.pop())
for tmp in tmp_str:
str_stack.append(tmp)
return ''.join(str_stack)

给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的路径的数目。 路径不需要从根节点开始,也不需要在叶节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。

首先用深度优先搜索遍历整个二叉树,将每个节点的父节点保存在字典中。然后遍历字典,对于每个节点,向上遍历到根节点,计算路径和,如果等于目标值,则计数器加一。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 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 pathSum(self, root: Optional[TreeNode], targetSum: int) -> int:
ans, parents = 0, {root: None}

def dfs(node):
if not node:
return

if node.left: parents[node.left] = node
if node.right: parents[node.right] = node

dfs(node.left)
dfs(node.right)

dfs(root)

for node, parent in parents.items():
cur_sum = 0
while node:
cur_sum += node.val
if cur_sum == targetSum:
ans += 1

node = parents[node]

return ans

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

最近公共祖先的定义为:对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。

分类讨论,如果给定的两个节点分别在左右子树中,那么根节点就是最近公共祖先,如果都在左子树中,那么递归左子树,如果都在右子树中,那么递归右子树。如果当前的根节点等于其中一个节点,那么返回当前节点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 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:
if not root or root == p or root == q: return root
left = self.lowestCommonAncestor(root.left, p, q)
right = self.lowestCommonAncestor(root.right, p, q)
if not left: return right
if not right: return left
return root

给定两个整数数组 preorderinorder ,其中 preorder 是前序遍历序列,inorder 是同一棵树的中序遍历序列。请构造并返回二叉树的根节点。

preorderinorder 均无重复元素。

经典的408考研题。按照前序遍历的顺序,根节点一定是第一个元素,然后在中序遍历中找到根节点的位置,左边的元素是左子树,右边的元素是右子树。递归的构建左右子树即可。

这里找到了根节点在中序遍历中的位置,然后递归的构建左右子树。根节点在中序遍历中的位置是root_index,这个位置左边的元素是左子树,右边的元素是右子树。所以可以用这个位置将前序遍历和中序遍历分成两部分,分别递归的构建左右子树。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 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 buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
if not preorder:
return None
root = preorder[0]
root_index = inorder.index(root)

return TreeNode(root, self.buildTree(preorder[1:root_index+1], inorder=inorder[:root_index]), self.buildTree(preorder[root_index+1:], inorder[root_index+1:]))

给定一个二叉树的 根节点 root,想象自己站在它的 右侧 ,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。

思路很简单,就是层序遍历,每次取每一层的最后一个节点的值即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 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 rightSideView(self, root: Optional[TreeNode]) -> List[int]:
if not root: return []
stack, ans = [root], []
while stack:
tmp = []
cur = stack[-1]
ans.append(cur.val)
for node in stack:
if node.left: tmp.append(node.left)
if node.right: tmp.append(node.right)
stack = tmp
return ans