0%

问题

你有 4 张写有 1 到 9 数字的牌。你需要判断是否能通过 *,/,+,-,(,) 的运算得到 24。

示例 1:

输入: [4, 1, 8, 7]
输出: True
解释: (8-4) * (7-1) = 24

示例 2:

输入: [1, 2, 1, 2]
输出: False
注意:

除法运算符 / 表示实数除法,而不是整数除法。例如 4 / (1 - 2/3) = 12 。
每个运算符对两个数进行运算。特别是我们不能用 - 作为一元运算符。例如,[1, 1, 1, 1] 作为输入时,表达式 -1 - 1 - 1 - 1 是不允许的。
你不能将数字连接在一起。例如,输入为 [1, 2, 1, 2] 时,不能写成 12 + 12 。

链接:https://leetcode-cn.com/problems/24-game

解答

递归暴力搜寻。。。

judgePoint24(cards)函数意义为 cards 数组 是否满足 24 点。

存在递归关系 , 从cards中随便挑选两个进行任何运算,把运算出来的结果放回cards,调用递归算下去即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution:

def judgePoint24(self, cards: List[int]) -> bool: # 支持 n 个 卡的24点游戏
size = len(cards)

if len(cards) == 1:
return abs(cards[0] - 24) < 0.0001

for i in range(size):
for j in range(size): # 挑选两个数
if i == j: continue
for sign in "+-*/":
newCards = cards[:]
newCards.remove(cards[i])
newCards.remove(cards[j]) # 取出来
try:
newCards.append(eval("%f%s%f" % (cards[i], sign, cards[j]))) # 加减乘除算一下放回去
if self.judgePoint24(newCards): # 递归调用
return True
except ZeroDivisionError:
pass
return False

输出表达式

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
from typing import List


class Solution:
def dfs(self, cards, formulas):
size = len(cards)
if len(cards) == 1:

ans = abs(cards[0] - 24) < 0.0001
if ans:
print(formulas[0] + " = 24")
return True

for i in range(size):
for j in range(size):
if i == j: continue

for sign in "+-*/":
newCards = cards[:]
newCards.remove(cards[i])
newCards.remove(cards[j])
newFormulas = formulas[:]
newFormulas.remove(formulas[i])
newFormulas.remove(formulas[j])
try:
formula = "%f%s%f" % (cards[i], sign, cards[j])
newCards.append(eval(formula))
newFormulas.append('(' + formulas[i] + sign + formulas[j] + ')')
if self.dfs(newCards, newFormulas):
return True

except ZeroDivisionError:
continue

return False

def judgePoint24(self, cards: List[int]) -> bool:
return self.dfs(cards, list(map(str, cards)))


if __name__ == '__main__':
sol = Solution()
# cards = [4, 1, 8, 7]
# cards = [1, 2, 1, 2]
cards = [8, 1, 6, 6]
ret = sol.judgePoint24(cards)
print(ret)

# 输出

# (6/(1-(6/8))) = 24
# True

力扣题

首先看一道高频经典力扣题146. LRU 缓存机制,题干不再赘述。直接放出我的题解

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

class ListNode:
def __init__(self, key=0, val=0, prev=None, next=None):
self.key = key
self.val = val
self.prev = prev
self.next = next


class LRUCache:

def __init__(self, capacity: int):
self.capacity = capacity
self.currentSize = 0
self.dic = {}
self.head = ListNode()
self.tail = ListNode()
self.head.next = self.tail
self.tail.prev = self.head

def get(self, key) -> int:
if key in self.dic:
self.move_to_end(key)
return self.dic[key].val
else:
return -1

def put(self, key, value: int) -> None:

if self.capacity == 0: return

if key in self.dic: # 存在不用考虑size
self.dic[key].val = value
self.move_to_end(key)
else: # 不存在
if self.currentSize == self.capacity: # 满了
self.delete(self.head.next.key)
self.currentSize -= 1
self.append(key, value)
self.currentSize += 1

def append(self, key, value: int) -> None:
nodePrev = self.tail.prev
nodeNext = self.tail

node = ListNode(key, value, nodePrev, nodeNext)
self.dic[key] = node

nodePrev.next = node
nodeNext.prev = node

def delete(self, key) -> ListNode:
node = self.dic.pop(key)

nodePrev = node.prev
nodeNext = node.next

nodePrev.next = nodeNext
nodeNext.prev = nodePrev

return node

def move_to_end(self, key) -> None:
popped = self.delete(key)
self.append(popped.key, popped.val)

嗯。hashmap + 双向链表 搞定!

Python装饰器

既然LRU类都整出来了,自己可以自己实现 python functools里的 @lru_cache呀。

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
from functools import wraps

# 自己定义一个lru装饰器
def lru_cache(size: [int, None] = 128):
"""
size 如果是 None, 则缓存大小不受限制。但是递归深度python 是有限制的,
使用这个装饰器并不能解决所有动态规划问题
"""
if size is None:
size = float('inf')

def lru_cache_decorator(f):
lru = LRUCache(size)

@wraps(f)
def decorated(*args, **kwargs):
cached = lru.get(args)
if cached == -1:
ret = f(*args, **kwargs)
lru.put(args, ret)
return ret
else:
return cached

return decorated

return lru_cache_decorator

如何使用呢?

1
2
3
4
5
6
7
8
9
@lru_cache()
def feb(n):
if n <= 1: return 1
return feb(n - 1) + feb(n - 2)


if __name__ == '__main__':
print(feb(1000)) # 跑起来飞快 去掉 装饰器慢如乌龟

参考链接

146. LRU 缓存机制

Python 函数装饰器 | 菜鸟教程 (runoob.com)

2021.8.31 某同学的字节一面手撕算法题

给出类似于 HM2(H2ON3A)3N2 这样的类化学表达式

输出每个原子的个数

看起来有点意思

思路:

遇到左括号亚栈 有括号弹出栈

栈中存放固定大小为元素总数的数组,其记录每个元素的出现次数

同时主要到字母后面与右括号的数字,讲栈顶记录的出现次数与其相乘即可。

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# 写上 所有 化学元素和 原子序数的对应 。这里比较懒先写几个用得到的
DIC = {
'H': 0,
'He': 1,
'Li': 2,
'C': 5,
'O': 7,
'S': 15,
'Al': 12,
'Cu': 29,
}

RE_DIC = {
0: 'H',
1: 'He',
2: 'Li',
5: 'C',
7: 'O',
15: 'S',
12: 'Al',
29: 'Cu',
}


def solve(s):
stack = [[0] * 100] # 化学元素先设定一百个吧。每个栈帧是个数组 数组按照元素顺序存放着元素的出现次数
n = len(s)
for idx, item in enumerate(s):
if item.isupper(): # 化学元素名字总是一个大写字母开头后面都是小写字母

# 支持多字符的元素,识别出类似于 Al Cu 这样的元素
end = idx + 1
while end <= n - 1 and s[end].islower():
end += 1
name = s[idx:end]

# 记录右下缀 ,如 读出 Al2 中数字 2
times = 1
if end <= n - 1 and s[end].isdecimal():
times = int(s[end])
stack[-1][DIC[name]] += times

elif item == '(': # 压栈
stack.append([0] * 100)

elif item == ')': # 出栈
times = 1
if idx != n - 1 and s[idx + 1].isdecimal(): # 如果 ) 后面是数字 要加倍
times = int(s[idx + 1])
popped = stack.pop()
# 把弹出的信息,合并到栈顶中去
for i in range(len(popped)):
stack[-1][i] += popped[i] * times

return stack


if __name__ == '__main__':
# s = "Al2(SO4)3"
s = "Cu2(OH)2CO3"
ret = solve(s)
for i in range(100):
if ret[0][i] != 0:
print(f"原子{RE_DIC[i]} : {ret[0][i]}")

输出

1
2
3
4
原子H : 2
原子C : 1
原子O : 5
原子Cu : 2

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

参加美团笔试的其中一个笔试题,题干是以角色扮演类游戏为背景,计算角色对标靶的总攻击和。
没有记录下详细的题干,只有自己的代码。

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
class Warrior:
def __init__(self, a, d, c2, c3, c4):
self.INITIAL_ATTACK = a # 初始攻击力
self.MAX_ATTACK_BUFF_TIME = c2 # 进攻buff 持续时间
self.MAX_AIM_DEFENCE = d # 目标最大防御力
self.MAX_DEFENCE_BUFF_TIME = c3 # 减防buff持续时间
self.PERMANENCY_ATTACK_INCREASE = c4 # 永久提升额

self.baseAttack = self.INITIAL_ATTACK # 基础攻击力
self.aimDefence = self.MAX_AIM_DEFENCE # 目标防御力
self.defenceBuffTime = 0 # 减防buff剩余时间
self.attackBuffTimes = [] # 攻击buff剩余时间

self.totalAttack = 0

def do1(self):
# 进攻
attack = (len(self.attackBuffTimes) + 1) * self.baseAttack # 计算攻击力加成

defence = self.MAX_AIM_DEFENCE
if self.defenceBuffTime > 0:
defence = 0

self.totalAttack += max(1, attack - defence)
self.final()

def do2(self):
# 增加buff
self.attackBuffTimes.append(self.MAX_ATTACK_BUFF_TIME)
self.final()

def do3(self):
# 刷新减防
self.defenceBuffTime = self.MAX_DEFENCE_BUFF_TIME
self.final()

def do4(self):
# 增加永久攻击
self.baseAttack += self.PERMANENCY_ATTACK_INCREASE
self.final()

def final(self):
# buff结算
if 0 in self.attackBuffTimes: self.attackBuffTimes.remove(0)
for i in range(len(self.attackBuffTimes)):
self.attackBuffTimes[i] -= 1

if self.defenceBuffTime != 0:
self.defenceBuffTime -= 0


if __name__ == '__main__':
a = 3
d = 3
c2 = 6
c3 = 6
c4 = 2
opt = "121341"
warrior = Warrior(a, d, c2, c3, c4)
for c in opt:
if c == '1':
warrior.do1()
elif c == '2':
warrior.do2()
elif c == '3':
warrior.do3()
elif c == '4':
warrior.do4()
print(warrior.totalAttack)
# while True:
# try:
# n, a, d, c2, c3, c4 = [int(i) for i in input().split()]
# opt = input()
# ans = solve(a, d, c2, c3, c4, opt)
# print(ans)
# except:
# break

力扣1310子数组异或查询

问题

有一个正整数数组 arr,现给你一个对应的查询数组 queries,其中 queries[i] = [Li, Ri]。

对于每个查询 i,请你计算从 Li 到 Ri 的 XOR 值(即 arr[Li] xor arr[Li+1] xor … xor arr[Ri])作为本次查询的结果。

并返回一个包含给定查询 queries 所有结果的数组。

示例 1:

输入:arr = [1,3,4,8], queries = [[0,1],[1,2],[0,3],[3,3]]
输出:[2,7,14,8]
解释:
数组中元素的二进制表示形式是:
1 = 0001
3 = 0011
4 = 0100
8 = 1000
查询的 XOR 值为:
[0,1] = 1 xor 3 = 2
[1,2] = 3 xor 4 = 7
[0,3] = 1 xor 3 xor 4 xor 8 = 14
[3,3] = 8

示例 2:

输入:arr = [4,8,2,10], queries = [[2,3],[1,3],[0,0],[0,3]]
输出:[8,0,4,4]

提示:

1 <= arr.length <= 3 * 10^4
1 <= arr[i] <= 10^9
1 <= queries.length <= 3 * 10^4
queries[i].length == 2
0 <= queries[i][0] <= queries[i][1] < arr.length

来源:力扣(LeetCode)

链接:https://leetcode-cn.com/problems/xor-queries-of-a-subarray

解答

前缀和的思想。使用暴力法会超时

1
2
3
4
5
6
7
8
9
10
11
class Solution:
def xorQueries(self, arr: List[int], queries: List[List[int]]) -> List[int]:
"""XOR前缀和"""
pre = [0] # 前缀数组
for i in range(len(arr)):
pre.append(arr[i] ^ pre[-1])
res = []
for a, b in queries:
res.append(pre[a] ^ pre[b + 1])

return res

评论区神解答

1
2
3
4
class Solution:
def xorQueries(self, arr: List[int], queries: List[List[int]]) -> List[int]:
acc = list(accumulate([0] + arr, xor))
return [acc[a] ^ acc[b + 1] for a, b in queries]

199. 二叉树的右视图

问题

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

示例:

1
2
3
4
5
6
7
8
9
10

输入: [1,2,3,null,5,null,4]
输出: [1, 3, 4]
解释:

1 <---
/ \
2 3 <---
\ \
5 4 <---

来源:力扣(LeetCode)

链接:https://leetcode-cn.com/problems/binary-tree-right-side-view

解答

参考甜姐的解答

二、DFS
我们按照 「根结点 -> 右子树 -> 左子树」 的顺序访问, 就可以保证每层都是最先访问最右边的节点的。
(与先序遍历 「根结点 -> 左子树 -> 右子树」 正好相反,先序遍历每层最先访问的是最左边的节点)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
List<Integer> res = new ArrayList<>();

public List<Integer> rightSideView(TreeNode root) {
dfs(root, 0);
return res;
}

void dfs(TreeNode root, int depth) {
if (root == null) return;
if (depth == res.size()) res.add(root.val);
dfs(root.right, depth + 1);
dfs(root.left, depth + 1);
}
}

题目

一只青蛙想要过河。 假定河流被等分为若干个单元格,并且在每一个单元格内都有可能放有一块石子(也有可能没有)。 青蛙可以跳上石子,但是不可以跳入水中。

给你石子的位置列表 stones(用单元格序号 升序 表示), 请判定青蛙能否成功过河(即能否在最后一步跳至最后一块石子上)。

开始时, 青蛙默认已站在第一块石子上,并可以假定它第一步只能跳跃一个单位(即只能从单元格 1 跳至单元格 2 )。

如果青蛙上一步跳跃了 k 个单位,那么它接下来的跳跃距离只能选择为 k - 1、k 或 k + 1 个单位。 另请注意,青蛙只能向前方(终点的方向)跳跃。

示例 1:

输入:stones = [0,1,3,5,6,8,12,17]
输出:true
解释:青蛙可以成功过河,按照如下方案跳跃:跳 1 个单位到第 2 块石子, 然后跳 2 个单位到第 3 块石子, 接着 跳 2 个单位到第 4 块石子, 然后跳 3 个单位到第 6 块石子, 跳 4 个单位到第 7 块石子, 最后,跳 5 个单位到第 8 个石子(即最后一块石子)。

示例 2:

输入:stones = [0,1,2,3,4,8,9,11]
输出:false
解释:这是因为第 5 和第 6 个石子之间的间距太大,没有可选的方案供青蛙跳跃过去。

提示:

2 <= stones.length <= 2000
0 <= stones[i] <= 231 - 1
stones[0] == 0

来源:力扣(LeetCode)

链接:https://leetcode-cn.com/problems/frog-jump

解答

用户 bryannliu 的解答:

这题和 55. 跳跃游戏 有点像,但是55 满足贪心选择性,所以直接用贪心算法做,这道题目因为有多种走法,所以得一个个试,用记忆化递归来做比较合适。

因为每次有三种走法,要尝试每种可能,肯定有重复计算,所以加上备忘录。

  1. 状态定义和参数:s(i, step) 表示当前是第i块时候,通过step步过来的,
  2. 决策和状态转移:接下来就是根据走的步数做状态变换,这里状态变换只有三种 step-1, step, step + 1, 尝试每种可能的走法,看能不能走到最后一块。

我这里的 recur(start, k) 中的 start 表示 stones数组顺序下标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution:
def canCross(self, stones: List[int]) -> bool:
n = len(stones)

@lru_cache(None)
def recur(start, k):
if start >= n - 1: return True
for i in range(start + 1, n):
if stones[start] + k - 1 == stones[i]: # 往后遍历找到 能以 k-1 步跳到的石子
if recur(i, k - 1): return True
if stones[start] + k == stones[i]: # 往后遍历找到 能以 k 步跳到的石子
if recur(i, k): return True
if stones[start] + k + 1 == stones[i]: # 往后遍历找到 能以 k+1 步跳到的石子
if recur(i, k + 1): return True
if stones[start] + k + 1 < stones[i]: break # 因stones的单调性 提早结束遍历
return False

return recur(0, 0)

问题

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

示例 1:

输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。

示例 2:

输入:nums = [0,1,0,3,2,3]
输出:4

示例 3:

输入:nums = [7,7,7,7,7,7,7]
输出:1

提示:

1 <= nums.length <= 2500
-104 <= nums[i] <= 104

进阶:

你可以设计时间复杂度为 O(n2) 的解决方案吗?
你能将算法的时间复杂度降低到 O(n log(n)) 吗?

来源:力扣(LeetCode)

链接:https://leetcode-cn.com/problems/longest-increasing-subsequence

解答

引用 甜姨的评论

相当于维护一个结果数组,如果当前元素比结果数组的值都大的的话,就追加在结果数组后面(相当于递增序列长度加了1);否则的话用当前元素覆盖掉第一个比它大的元素(这样做的话后续递增序列才有可能更长,即使并没有更长,这个覆盖操作也并没有副作用哈,当然这个覆盖操作可能会让最终的结果数组值并不是最终的递增序列值,这无所谓)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public int lengthOfLIS(int[] nums) {
int[] res = new int[nums.length];
int len = 0;
for (int num: nums) {
int idx = Arrays.binarySearch(res, 0, len, num);
idx = idx < 0? -idx - 1: idx;
res[idx] = num;
if (idx == len) {
len++;
}
}
return len;
}
}

这里给出我的使用二分法API的Python版

1
2
3
4
5
6
7
8
9
10
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
res = []
for x in nums:
loc = bisect.bisect_left(res, x)
if loc == len(res):
res.append(x)
else:
res[loc] = x
return len(res)