这是我参与2022首次更文挑战的第15天,活动详情查看:2022首次更文挑战。
二叉树的遍历
面试之前,对实现中序、后序和前序遍历,你要做到轻车熟路,其中在面试中最常见的是中序遍历。
中序遍历
中序遍历是指先访问(通常也会打印)左子树,然后访问当前节点,最后访问右子树。
1. void inOrderTraversal(TreeNode node) {
2. if (node != null) {
3. inOrderTraversal(node.left);
4. visit(node);
5. inOrderTraversal(node.right);
6. }
7. }
当在二叉搜索树上执行遍历时,它以升序访问节点。因此命名为“中序遍历”。
前序遍历
前序遍历先访问当前节点,再访问其子节点。因此命名为“前序遍历”。
1. void preOrderTraversal(TreeNode node) {
2. if (node != null) {
3. visit(node);
4. preOrderTraversal(node.left);
5. preOrderTraversal(node.right);
6. }
7. }
前序遍历中,根节点永远第一个被访问。
后序遍历
后序遍历于访问子节点之后访问当前节点。因此命名为“后序遍历”。
1. void postOrderTraversal(TreeNode node) {
2. if (node != null) {
3. postOrderTraversal(node.left);
4. postOrderTraversal(node.right);
5. visit(node);
6. }
7. }
后序遍历中,根节点永远最后一个被访问。
二叉堆(小顶堆与大顶堆)
本书只讨论小顶堆。大顶堆实际上是一样的,只是其元素是以降序排列而不是升序排列的。
一个小顶堆是一棵完整二叉树(也就是说,除了底层最右边的元素,树的每层都被填满了),其中每个节点都小于其子节点。因此,根是树中的最小元素。
在最小堆中有两个关键操作:insert
和extract_min
。
插入操作
当我们向一个最小堆插入元素时,总是从底部开始。从最右边的节点开始插入操作以保持树的完整性。
然后,通过与其祖先节点进行交换来“修复”树,直到找到新元素的适当位置。我们基本上是在向上传递最小的元素。
此操作时间复杂度为,其中
是堆中节点的个数。
提取最小元素
找到小顶堆的最小元素是小菜一碟:它总是在顶部。颇为棘手的是如何删除该元素(其实也不是那么棘手)。
首先,删除最小元素并将其与堆中的最后一个元素(位于最底层、最右边的元素)进行交换。然后,向下传递这个元素,不断使其与自身子节点之一进行交换,直到小顶堆的属性得以恢复。
是和左边的孩子节点还是右边的孩子节点进行交换取决于它们的值。左右元素之间没有固定的顺序,但是为了保持小顶堆的元素有序,你需要选择两者中较小的元素。
该算法的时间复杂度同样为。