就是常见的先序、中序和后序遍历框架,如下:
/* 二叉树遍历框架 */
void traverse(TreeNode root) {
// 前序遍历
traverse(root.left)
// 中序遍历
traverse(root.right)
// 后序遍历
}
写递归算法的关键是要明确函数的「定义」是什么,然后相信这个定义,利用这个定义推导最终结果,绝不要试图跳入递归。
快排首先找到一个分界点,所有小于分界点的值在其左边,所有大于分界点的值在其右边,然后再在左半区间和右半区间进行快排,直到区间不可划分为止。
其实就是先序遍历的思想,代码框架如下:
void sort(int[] nums, int lo, int hi) {
/****** 前序遍历位置 ******/
// 通过交换元素构建分界点 p
int p = partition(nums, lo, hi);
/************************/
sort(nums, lo, p - 1);
sort(nums, p + 1, hi);
}
归并排序最初在相邻两个元素排序,然后合并。再将合并的较大区间作为一个整体,在这样相邻两个整体间排序,再合并。依次到全部合并完为止。
如果将两个元素或区间看成左子树和右子树,其实就是后序遍历,代码框架如下:
void sort(int[] nums, int lo, int hi) {
int mid = (lo + hi) / 2;
sort(nums, lo, mid);
sort(nums, mid + 1, hi);
/****** 后序遍历位置 ******/
// 合并两个排好序的子数组
merge(nums, lo, mid, hi);
/************************/
}
翻转一棵二叉树。
示例:
输入:
4
/ \
2 7
/ \ / \
1 3 6 9
输出:
4
/ \
7 2
/ \ / \
9 6 3 1
使用递归的思想,假定这个函数的功能已经是翻转以root为结点的方法。先调用函数翻转左子树,然后翻转右子树,再交换根节点下的左右子树即可。
class Solution {
public TreeNode invertTree(TreeNode root) {
// base case
if (root == null) { return null; }
// 后序遍历
TreeNode leftNode = invertTree(root.left);
TreeNode rightNode = invertTree(root.right);
// 交换左右子树
root.left = rightNode;
root.right = leftNode;
return root;
}
}
给定一个完美二叉树,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL
。
初始状态下,所有 next 指针都被设置为 NULL
。
跟上一题基本思路一致,利用后序遍历处理子树连接问题。但是本题还是有些不一样,除了左子树的next连接到右子树外,还需要左子树的最右侧所有结点的next都要连接到右子树最左侧的所有右结点。
class Solution {
public Node connect(Node root) {
// base case
if (root == null) { return null; }
if (root.left == null) { return root; }
// 后序遍历
Node leftNode = connect(root.left);
Node rightNode = connect(root.right);
// 连接
while (leftNode != null) {
leftNode.next = rightNode;
leftNode = leftNode.right;
rightNode = rightNode.left;
}
return root;
}
}
给定一个二叉树,原地将它展开为一个单链表。
例如,给定二叉树
1
/ \
2 5
/ \ \
3 4 6
将其展开为:
1
\
2
\
3
\
4
\
5
\
6
使用递归思想(没有实现原地),分别将左右都展开成一个单链表,将左子树接到根节点的右子树上,右子树接到原先左子树的末尾。
class Solution {
public void flatten(TreeNode root) {
// base case
if (root == null) { return; }
// 后序遍历
flatten(root.left);
flatten(root.right);
/**** 后序遍历逻辑位置 ****/
// 1. 左右子树已被拉成一条直线
TreeNode leftNode = root.left;
TreeNode rightNode = root.right;
// 2. 将左子树作为右子树
root.left = null;
root.right = leftNode;
// 3. 将原先的右子树接到当前右子树的末端
TreeNode p = root;
while (p.right != null) {
p = p.right;
}
p.right = rightNode;
}
}