遍历二叉树常用的方法是使用递归或者堆栈迭代实现 堆栈实现遍历的时间复杂度是O(n) 空间复杂度为O(logn) 使用递归算法时,空间复杂度为O(logn) 在节点数目较多时,递归的开销很大,占用资源大,因为需要不断的开辟新的栈桢,并保护现场。
使用Morris算法遍历二叉树时,时间复杂度O(n) 空间复杂度O(1)。
Morris算法的原理是线索二叉树。
在线索二叉树中,使用线索来保存前驱和后继的信息,而这些线索是利用叶节点的空指针域来保存,知道了树中每个节点的前驱和后继的位置就可以有效降低遍历过程中对空间的需求,但是使用线索二叉树先通过一次二叉树遍历算法,为二叉树建立线索。
Morri算法使用二叉树叶节点的right指针域来保存后面将要访问节点的信息(线索),当这个right指针域使用完毕之后再重新置为null。但是在访问过程中有些节点会被访问两次。
中序遍历
算法步骤:
1 如果当前节点的左子节点为空,则输出当前节点,并将当前节点置为该节点的右子节点。
2如果当前节点的左子节点不为空时,则找到当前节点左子树的最右节点(该节点为当前节点中序遍历的前驱节点)
A 如果最右节点的右指针为空,则将最右节点的右指针指向当前节点,并把当前节点置为其左子节点。
B 如果最右节点的右指针不为空,则输出当前节点,并将最右节点的右指针重新置为空,将当前节点置为其右子节点。
3 重复1和2,直至当前节点为空。
(建议自己画图并结合程序)
“`
//中序遍历
public List morrisInOrder(TreeNode root) {
List result = new ArrayList();
if(root==null) return result; //返回[]
TreeNode cur = root;
while(cur!=null) {
if(cur.left==null) {
result.add(cur.val);
cur=cur.right;
} else {
TreeNode tmp = cur.left;
while(tmp.right!=null && tmp.right!=cur) {
tmp = tmp.right; //找到左子树的最右节点
}
if(tmp.right==null) {
tmp.right=cur;
cur=cur.left;
} else {
result.add(cur.val);
tmp.right=null;
cur=cur.right;
}
}
}
return result;
}
“`前序遍历
算法步骤:
1如果当前节点的左子节点为空,则输出当前节点,并将当前节点置为该节点的右子节点。
2 如果当前节点的左子节点不为空,则找到当前节点左子树的最右节点(该节点为当前节点中序遍历的前驱节点)
A 如果最右节点的右指针为空,则输出当前节点,并将最右节点的右指针指向当前节点,当前节点置为其左子节点
B 如果最右节点的右指针不为空,则将最右节点的右指针重新置为空,并将当前节点置为其右子节点。
3 重复1和2,直至当前节点为空
“`
//前序遍历
public List morrisPreOrder(TreeNode root) {
List result = new ArrayList();
if(root==null) return result; //返回[]
TreeNode cur = root;
while(cur!=null) {
if(cur.left==null) {
result.add(cur.val); //输出当前节点
cur=cur.right;
} else {
TreeNode tmp = cur.left;
//找左子树的最右节点
while(tmp.right!=null && tmp.right!=cur) {
tmp=tmp.right;
}
if(tmp.right==null){
result.add(cur.val); //输出当前节点
tmp.right = cur;
cur=cur.left;
} else {
tmp.right=null; //恢复二叉树
cur=cur.right;
}
}
}
return result;
}
“`后序遍历:需要建立一个临时节点,并令该节点的左子节点为root,并且需要一个子过程,倒序输出某两个节点之间路径上的各个节点。
算法步骤:
1 如果当前节点的左子节点为空,则将其右子节点作为当前节点。
2 如果当前节点的左子节点不为空,则找到当前节点左子树的最右节点(该节点为中序遍历的前驱节点)
A 如果最右节点的右指针为空,则将最右节点的右指针指向当前节点,当前节点置为其左子节点。
B 如果最右节点的右指针不为空,则将最右节点的右指针重新置为空,倒序输出从当前节点的左子节点到该最右节点路径上的所有节点,并将当前节点置为其右节点。
3 重复1和2,直至当前节点为空。
//后序遍历
public List<Integer> morrisPostOrder(TreeNode root) {
List<Integer> result = new ArrayList<Integer>();
if(root == null) return result;
TreeNode tmpNode = new TreeNode(-1); //建立一个临时节点
tmpNode.left = root;//临时节点的左子节点指向root
TreeNode cur = tmpNode;
while(cur!=null) {
if(cur.left==null) {
cur = cur.right;
} else {
TreeNode tmp = cur.left;
while(tmp.right!=null && tmp.right!=cur)
tmp = tmp.right;
if(tmp.right==null) {
tmp.right=cur;
cur=cur.left;
} else {
tmp.right=null;
TreeNode t = cur.left;
List<Integer> tmpList = new ArrayList<>();
while(t!=null) { //倒序输出
tmpList.add(0,t.val); //在指定位置上插入元素,向右移动该位置上原先的元素及后续元素
t = t.right;
}
result.addAll(tmpList);
cur=cur.right;
}
}
}
return result;
}