二叉树的遍历一般有前序、中序、后序。
遍历二叉树几种常用的方法:
- 利于栈(stack)
- 利于递归
- 在保证空间利用率为O(1)时,使用Minors方法、
- 递归方法
class Solution { public List<Integer> inorderTraversal(TreeNode root) { //保存结果数组 List<Integer> res = new ArrayList<Integer>(); inorder(root, res); return res; } public void inorder(TreeNode root, List<Integer> res) { if (root == null) { return; } inorder(root.left, res); res.add(root.val); inorder(root.right, res); } }
递归可以说是二叉树相对来说最简单的掌握方式,前序、中序、后序遍历主要体现在以下代码上,如果 res.add(root.val);在中间即中序遍历,在首位即前序遍历、在末尾即后序遍历。
inorder(root.left, res);
res.add(root.val);
inorder(root.right, res);
2.利用栈
利用栈的先进后出的特性,首先让二叉树遍历到最左边的位置,然后再一个节点一个节点弹出。
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
//初始化栈
Deque<TreeNode> stk = new LinkedList<TreeNode>();
while (root != null || !stk.isEmpty()) {
while (root != null) {
//将节点压入栈中
stk.push(root);
//一直向左走
root = root.left;
}
//走到了最左边之后,将最后一个进栈的元素弹出
root = stk.pop();
res.add(root.val);
//接着向右走
root = root.right;
}
return res;
}
}
利用栈遍历二叉树时间复杂度和空间复杂度一般都为O(n);且前序、中序、后续遍历的代码类似,区别主要体现在一下代码段上。
root = stk.pop();
res.add(root.val);
root = root.right;
即如果 res.add(root.val);在中间就是中序遍历;前序遍历为以下代码
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
//初始化栈
Deque<TreeNode> stk = new LinkedList<TreeNode>();
while (root != null || !stk.isEmpty()) {
while (root != null) {
res.add(root.val);
//将节点压入栈中
stk.push(root);
//一直向左走
root = root.left;
}
//走到了最左边之后,将最后一个进栈的元素弹出
root = stk.pop();
//接着向右走
root = root.right;
}
return res;
}
}
后续遍历为一下代码
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
if (root == null) {
return res;
}
Deque<TreeNode> stack = new LinkedList<TreeNode>();
TreeNode prev = null;
while (root != null || !stack.isEmpty()) {
while (root != null) {
stack.push(root);
root = root.left;
}
root = stack.pop();
if (root.right == null || root.right == prev) {
res.add(root.val);
prev = root;
root = null;
} else {
stack.push(root);
root = root.right;
}
}
return res;
}
}
4.Morris 中序遍历
主要详细介绍下中序遍历的过程
- 首先第一步会有一个空节点predecessor该节点的主要作用就是将二叉树的叶子节点与当前遍历的root节点建立联系
- 在第一步时,predecessor节点先向左走一步,保证二叉树左子数先于右子数遍历,如果此时root的左孩子为空,则将当前root节点加入到结果集合中去,root节点向右走一步。
- 接着predecessor一直往右走,直到走到当前root的左子树最右的叶子节点,这一步要保证predecessor不为空,且它右孩子不指向当前的root
- 接着进行判断,由于predecessor位于当前root的左孩子的最右边的叶子节点,如果predecessor为空则表示为第一次访问该节点,将该叶子节点与root建立联系,即将该叶子节点的右孩子指向root,完成之后,将root节点向左走一位,接着从第一步开始,在当前root节点下与叶子节点建立联系。
- 如果此时predecessor不为空,则表明是第二次访问该节点,此时需要将他与当前root节点建立的联系断开,将他加入到遍历集合中去,接着root要向右走一步,方位他的右子树。
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
TreeNode predecessor = null;
while (root != null) {
if (root.left != null) {
// predecessor 节点就是当前 root 节点向左走一步
predecessor = root.left;
//接着一直向右走
while (predecessor.right != null && predecessor.right != root) {
predecessor = predecessor.right;
}
// 让 predecessor 的右指针指向 root,继续遍历左子树
if (predecessor.right == null) {
//最后一个节点的right指向当前子树的root
predecessor.right = root;
//接着root向左走一步
root = root.left;
}
// 说明左子树已经访问完了,我们需要断开链接
else {
//此时将root节点加入到集合中去
res.add(root.val);
//断开节点的链接
predecessor.right = null;
//开始访问当前root的right子数
root = root.right;
}
}
// 如果没有左孩子,则直接访问右孩子
else {
//走到了当前二叉树root的左节点
res.add(root.val);
root = root.right;
}
}
return res;
}
}
5. Morris 前序遍历
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
if (root == null) {
return res;
}
TreeNode p1 = root, p2 = null;
while (p1 != null) {
p2 = p1.left;
if (p2 != null) {
while (p2.right != null && p2.right != p1) {
p2 = p2.right;
}
if (p2.right == null) {
res.add(p1.val);
p2.right = p1;
p1 = p1.left;
continue;
} else {
p2.right = null;
}
} else {
res.add(p1.val);
}
p1 = p1.right;
}
return res;
}
}
6.Morris 后序遍历
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
if (root == null) {
return res;
}
TreeNode p1 = root, p2 = null;
while (p1 != null) {
p2 = p1.left;
if (p2 != null) {
while (p2.right != null && p2.right != p1) {
p2 = p2.right;
}
if (p2.right == null) {
p2.right = p1;
p1 = p1.left;
continue;
} else {
p2.right = null;
addPath(res, p1.left);
}
}
p1 = p1.right;
}
addPath(res, root);
return res;
}
public void addPath(List<Integer> res, TreeNode node) {
int count = 0;
while (node != null) {
++count;
res.add(node.val);
node = node.right;
}
int left = res.size() - count, right = res.size() - 1;
while (left < right) {
int temp = res.get(left);
res.set(left, res.get(right));
res.set(right, temp);
left++;
right--;
}
}
}