树 是一种经常用到的数据结构,用来模拟具有树状结构性质的数据集合。
树里的每一个节点有一个值和一个包含所有子节点的列表。从图的观点来看,树也可视为一个拥有N 个节点和N-1 条边的一个有向无环图。
二叉树是一种更为典型的树状结构。如它名字所描述的那样,二叉树是每个节点最多有两个子树的树结构,通常子树被称作“左子树”和“右子树”。
前序遍历
前序遍历首先访问根节点,然后遍历左子树,最后遍历右子树。
中序遍历
中序遍历是先遍历左子树,然后访问根节点,然后遍历右子树。
通常来说,对于二叉搜索树,我们可以通过中序遍历得到一个递增的有序序列。
后序遍历
后序遍历是先遍历左子树,然后遍历右子树,最后访问树的根节点。
给定一个二叉树,返回它的前序遍历。
示例:
输入: [1,null,2,3]
1
\
2
/
3
输出: [1,2,3]
2种解法
解法1:递归
递归的方法很容易实现,也很容易理解:我们先访问根节点,然后递归访问左子树,再递归访问右子树,即实现了根->左->右的访问顺序,因为使用的是递归方法,所以每一个子树都实现了这样的顺序。
public List preorderTraversal(TreeNode root) {
List res = new ArrayList<>();
preorder(root, res);
return res;
}
private void preorder(TreeNode root, List res) {
if (root == null) {
return;
}
res.add(root.val);
preorder(root.left, res);
preorder(root.right, res);
}
解法2:迭代法
在迭代法中,我们使用栈来实现。由于出栈顺序和入栈顺序相反,所以每次添加节点的时候先添加右节点,再添加左节点。这样在下一轮访问子树的时候,就会先访问左子树,再访问右子树:
public List preorderTraversal(TreeNode root) {
List output = new LinkedList<>();
if (root == null) {
return output;
}
Stack stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
output.add(node.val);
if (node.right != null) {
stack.push(node.right);
}
if (node.left != null) {
stack.push(node.left);
}
}
return output;
}
给定一个二叉树,返回它的中序遍历。
示例:
输入: [1,null,2,3]
1
\
2
/
3
输出: [1,2,3]
2种解法
解法1:递归
递归的方法很容易实现,也很容易理解:先访问左子树,再访问根节点,再访问右子树,即 左->根->右
public List inorderTraversal(TreeNode root) {
List res = new ArrayList<>();
inorder(root, res);
return res;
}
private void inorder(TreeNode root, List res) {
if (root == null) {
return;
}
inorder(root.left, res);
res.add(root.val);
inorder(root.right, res);
}
解法2:迭代法
中序遍历的迭代法要稍微复杂一点,因为最先遇到的根节点不是最先访问的,我们需要先访问左子树,再回退到根节点,再访问根节点的右子树,这里的一个难点是从左子树回退到根节点的操作,虽然可以用栈来实现回退,但是要注意在出栈时保存根节点的引用,因为我们还需要通过根节点来访问右子树:
public List inorderTraversal(TreeNode root) {
List res = new ArrayList<>();
Stack stack = new Stack<>();
TreeNode curr = root;
while (curr != null || !stack.isEmpty()) {
while (curr != null) {
stack.push(curr);
curr = curr.left;
}
curr = stack.pop();
res.add(curr.val);
curr = curr.right;
}
return res;
}
给定一个二叉树,返回它的后序遍历。
示例:
输入: [1,null,2,3]
1
\
2
/
3
输出: [1,2,3]
2种解法
解法1:递归
递归的方法很容易实现,也很容易理解:对于后序遍历,就是先访问左子树,再访问右子树,再访问根节点,即 左->右->根:
public List postorderTraversal(TreeNode root) {
List res = new ArrayList<>();
postorder(root, res);
return res;
}
private void postorder(TreeNode root, List res) {
if (root == null) {
return;
}
postorder(root.left, res);
postorder(root.right, res);
res.add(root.val);
}
解法2:迭代法
从根节点开始依次迭代,弹出栈顶元素输出到输出列表中,然后依次压入它的所有孩子节点,按照从上到下、从左至右的顺序依次压入栈中。
因为深度优先搜索后序遍历的顺序是从下到上、从左至右,所以需要将输出列表逆序输出。
public List postorderTraversal(TreeNode root) {
LinkedList stack = new LinkedList<>();
LinkedList output = new LinkedList<>();
if (root == null) {
return output;
}
stack.add(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pollLast();
output.addFirst(node.val);
if (node.left != null) {
stack.add(node.left);
}
if (node.right != null) {
stack.add(node.right);
}
}
return output;
}