学习使用工具
剑指Offer http://itmyhome.com/sword-means-offer/sword-means-offer.pdf
LeetCode的剑指Offer题库 https://leetcode.cn/problemset/all/
剑指 Offer 36. 二叉搜索树与双向链表
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。
为了让您更好地理解问题,以下面的二叉搜索树为例:
我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。
下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。
特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。
解法:
中序遍历二叉搜索树得到的结果就是一个升序序列。得到中序遍历结果后,修改指针即可。
def treeToDoublyList(self, root: 'Node') -> 'Node':
if not root:
return None
ans = []
def travel(root: 'Node'):
if root.left:
travel(root.left)
ans.append(root)
if root.right:
travel(root.right)
travel(root)
if len(ans) == 1:
root.left = root
root.right = root
return root
ans[0].left = ans[len(ans) - 1]
ans[0].right = ans[1]
ans[len(ans) - 1].left = ans[len(ans) - 2]
ans[len(ans) - 1].right = ans[0]
for i in range(1, len(ans) - 1):
ans[i].left = ans[i - 1]
ans[i].right = ans[i + 1]
return ans[0]
剑指 Offer 37. 序列化二叉树
请实现两个函数,分别用来序列化和反序列化二叉树。
你需要设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。
提示:输入输出格式与 LeetCode 目前使用的方式一致,详情请参阅 LeetCode 序列化二叉树的格式。你并非必须采取这种方式,你也可以采用其他的方法解决这个问题。
示例:
输入:root = [1,2,3,null,null,4,5]
输出:[1,2,3,null,null,4,5]
解法:
作者:Krahets
来源:力扣(LeetCode)
通常使用的前序、中序、后序、层序遍历记录的二叉树的信息不完整,即唯一的输出序列可能对应着多种二叉树可能性。题目要求的序列化和反序列化是可逆操作。因此,序列化的字符串应携带完整的二叉树信息。
设 m
为列表区间 [0,n]
中的 null
节点个数,则可总结出根节点、左子节点、右子节点的列表索引的递推公式:
node.val | node的索引 | node.left的列表索引 | node.right的列表索引 |
---|---|---|---|
≠ null | n | 2(n-m)+1 | 2(n-m)+2 |
= null | n | 无 | 无 |
序列化使用层序遍历实现。反序列化通过以上递推公式反推各节点在序列中的索引,进而实现。
class Codec:
def serialize(self, root):
if not root: return "[]"
queue = collections.deque()
queue.append(root)
res = []
while queue:
node = queue.popleft()
if node:
res.append(str(node.val))
queue.append(node.left)
queue.append(node.right)
else: res.append("null")
return '[' + ','.join(res) + ']'
def deserialize(self, data):
if data == "[]": return
vals, i = data[1:-1].split(','), 1
root = TreeNode(int(vals[0]))
queue = collections.deque()
queue.append(root)
while queue:
node = queue.popleft()
if vals[i] != "null":
node.left = TreeNode(int(vals[i]))
queue.append(node.left)
i += 1
if vals[i] != "null":
node.right = TreeNode(int(vals[i]))
queue.append(node.right)
i += 1
return root
剑指 Offer 38. 字符串的排列
输入一个字符串,打印出该字符串中字符的所有排列。
你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。
示例:
输入:s = "abc"
输出:["abc","acb","bac","bca","cab","cba"]
限制:
1 <= s 的长度 <= 8
解法:
新的一天,新的Offer消失术:
def permutation(self, s: str) -> List[str]:
return list(set(["".join(i) for i in permutations(s,len(s))]))
正常的解法应当是DFS+回溯剪枝。
def permutation(self, s: str) -> List[str]:
if not s:
return []
res = []
def swap(s, i, j):
sl = list(s)
sl[i], sl[j] = sl[j], sl[i]
return ''.join(sl)
def dfs(s:str, pos: int):
if pos == len(s):
res.append(s)
for i in range(pos, len(s)):
flag = True
for j in range(pos, i):
if s[i] == s[j]:
flag = False
if flag:
s = swap(s, pos, i)
dfs(s, pos + 1)
s = swap(s, pos, i)
dfs(s, 0)
return res
剑指 Offer 39. 数组中出现次数超过一半的数字
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入: [1, 2, 3, 2, 2, 2, 5, 4, 2]
输出: 2
限制:
1 <= 数组长度 <= 50000
解法:
数组中有一个数字出现的次数超过数组长度的一半,那么对数组进行排序后该数字必然出现在中间位置。
def majorityElement(self, nums: List[int]) -> int:
nums.sort()
return nums[len(nums) // 2]