精品推荐:
《征服数据结构》专栏:50多种数据结构彻底征服
《经典图论算法》专栏:50多种经典图论算法全部掌握
一网友在面试快手之后,进度一直是录用评估,这都评估一个月了还没消息,结果该网友给邮件上的HR打电话询问情况,HR说她离职了。我以为这是个例,结果在评论区有很多网友都遇到这种情况。这又是什么套路?为什么秋招的HR都喜欢离职,难道对接的HR都是实习的?
如果遇到这种情况,继续投简历找工作就是了,不用等了,HR离职有可能是真的离职,也有可能是你面试没通过,又懒得发邮件,随便找个借口应付了事。就像之前找工作面试结束之后HR会说回去等通知,如果过了肯定会收到通知,如果没过就算等一百年也不会给你通知。
--------------下面是今天的算法题--------------
来看下今天的算法题,这题是LeetCode的第106题:从中序与后序遍历序列构造二叉树。
问题描述
输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]
输入:inorder = [-1], postorder = [-1]
输出:[-1]
1 <= inorder.length <= 3000
postorder.length == inorder.length
-3000 <= inorder[i], postorder[i] <= 3000
inorder 和 postorder 都由 不同 的值组成
postorder 中每一个值都在 inorder 中
inorder 保证是树的中序遍历
postorder 保证是树的后序遍历
问题分析
根据这个特性我们可以把中序数组和后序数组划分两部分,然后每部分继续按照上面的方法划分,直到只有一个节点,不能划分为止。比如示例 1 的数组划分如下图所示。
public TreeNode buildTree(int[] inorder, int[] postorder) {
// 为了方便后续进行查找,先把中序数组的所有值存储到map中
Map<Integer, Integer> map = new HashMap<>();
int length = inorder.length;
for (int i = 0; i < length; i++)
map.put(inorder[i], i);
return build(postorder, map, length - 1, 0, length - 1);
}
private TreeNode build(int[] postorder, Map<Integer, Integer> map,
int postEnd, int inStart, int inEnd) {
if (inStart > inEnd) return null;// 表示数组被访问完了。
// 使用后序数组的最后一个元素创建根节点
TreeNode root = new TreeNode(postorder[postEnd]);
// 查找根节点在中序数组中位置
int index = map.get(root.val);
int rightCount = inEnd - index;// 右子树的所有节点个数
// 后序数组区间划分:
// [……, postEnd-rightCount-1]左子树
// [postEnd-rightCount, postEnd-1]右子树
// [postEnd, postEnd]根节点
// 中序数组区间划分:
// [inStart, index - 1]左子树
// [index, index]根节点
// [index + 1, inEnd]右子树
root.left = build(postorder, map, postEnd - rightCount - 1, inStart, index - 1);
root.right = build(postorder, map, postEnd - 1, index + 1, inEnd);
return root;
}
public:
TreeNode *buildTree(vector<int> &inorder, vector<int> &postorder) {
// 为了方便后续进行查找,先把中序数组的所有值存储到map中
unordered_map<int, int> m;
int length = inorder.size();
for (int i = 0; i < length; i++)
m[inorder[i]] = i;
return build(postorder, m, length - 1, 0, length - 1);
}
TreeNode *build(vector<int> &postorder, unordered_map<int, int> &m,
int postEnd, int inStart, int inEnd) {
if (inStart > inEnd)
return nullptr;// 表示数组被访问完了。
// 使用后序数组的第一个元素创建根节点
TreeNode *root = new TreeNode(postorder[postEnd]);
// 使用后序数组的最后一个元素创建根节点
int index = m[root->val];
int rightCount = inEnd - index;// 右子树的所有节点个数
// 后序数组区间划分:
// [……, postEnd-rightCount-1]左子树
// [postEnd-rightCount, postEnd-1]右子树
// [postEnd, postEnd]根节点
// 中序数组区间划分:
// [inStart, index - 1]左子树
// [index, index]根节点
// [index + 1, inEnd]右子树
root->left = build(postorder, m, postEnd - rightCount - 1, inStart, index - 1);
root->right = build(postorder, m, postEnd - 1, index + 1, inEnd);
return root;
}
def buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]:
def build(postEnd: int, inStart: int, inEnd: int):
if inStart > inEnd:
return None # 表示数组被访问完了。
# 使用后序数组的最后一个元素创建根节点
root = TreeNode(postorder[postEnd])
# 查找根节点在中序数组中位置
index = m[root.val]
rightCount = inEnd - index # 右子树的所有节点个数
'''
后序数组区间划分:
[……, postEnd-rightCount-1]左子树
[postEnd-rightCount, postEnd-1]右子树
[postEnd, postEnd]根节点
中序数组区间划分:
[inStart, index - 1]左子树
[index, index]根节点
[index + 1, inEnd]右子树
'''
root.left = build(postEnd - rightCount - 1, inStart, index - 1)
root.right = build(postEnd - 1, index + 1, inEnd)
return root
# 为了方便后续进行查找,先把中序数组的所有值存储到map中
m = {element: i for i, element in enumerate(inorder)}
return build(len(postorder) - 1, 0, len(postorder) - 1)
数组,稀疏表(Sparse Table),单向链表,双向链表,块状链表,跳表,队列和循环队列,双端队列,单调队列,栈,单调栈,双端栈,散列表,堆,字典树(Trie树),ArrayMap,SparseArray,二叉树,二叉搜索树(BST),笛卡尔树,AVL树,树堆(Treap),FHQ-Treap,哈夫曼树,滚动数组,差分数组,LRU缓存,LFU缓存
……
图的介绍,图的表示方式,邻接矩阵转换,广度优先搜索(BFS),深度优先搜索(DFS),A*搜索算法,迭代深化深度优先搜索(IDDFS),IDA*算法,双向广度优先搜索,迪杰斯特拉算法(Dijkstra),贝尔曼-福特算法(Bellman-Ford),SPFA算法,弗洛伊德算法(Floyd),卡恩(Kahn)算法,基于DFS的拓扑排序,约翰逊算法(Johnson)
……