零、基础知识:
①前序遍历
前序遍历首先访问根节点,然后遍历左子树,最后遍历右子树。
②中序遍历
中序遍历是先遍历左子树,然后访问根节点,然后遍历右子树。
③后序遍历
后序遍历是先遍历左子树,然后遍历右子树,最后访问树的根节点。
一、递归法
①前序遍历
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();//创建list接受遍历结果
treeSort(root,res);
return res;
}
public void treeSort(TreeNode root,List<Integer> res){
if(root==null){ //终止条件
return;
}
res.add(root.val);
treeSort(root.left,res);//递归,遍历当前结点的左结点的左结点的...
treeSort(root.right,res);
}
}
②中序遍历
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> list = new LinkedList<>();
treeSort(root,list);
return list;
}
public void treeSort(TreeNode root, List<Integer> list){
if(root==null){
return;
}
treeSort(root.left,list);
list.add(root.val);
treeSort(root.right,list);
}
}
③后序遍历
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
treeSort(root,list);
return list;
}
public void treeSort(TreeNode root, List<Integer> list){
if(root==null){
return;
}
treeSort(root.left,list);
treeSort(root.right,list);
list.add(root.val);
}
}
二、迭代法
利用栈先进后出的特性,记录结点。
①前序遍历
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
Deque<TreeNode> stack = new ArrayDeque<>();//栈,用来记录节点
while(root!=null || !stack.isEmpty()){
while(root!=null){
stack.push(root);
res.add(root.val);
root = root.left;
}
root = stack.pop().right;
}
return res;
}
}
②中序遍历
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> list = new LinkedList<>();
Deque<TreeNode> deque = new LinkedList<>();
while(root!=null || deque.size()!=0){
while(root!=null){
deque.push(root);
root = root.left;
}
root = deque.pop();
list.add(root.val);
root = root.right;
}
return list;
}
}
③后序遍历
后序遍历比较复杂,左→右→结点,在将中间结点的左结点存入list列表之后,此时需要存入右结点,所以中间结点出栈后不能立即存入list中,需要再次存入栈中。中间结点的右结点存入之后,中间第二次出栈再存入list中。
方法一:记录已经存入list中的最后结点,如果该结点是当前出栈的root右结点,说明root结点已经是第二次出栈了,它的右子树已经遍历过了,root可以存入list中了。
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
Deque<TreeNode> stack = new LinkedList<>();
TreeNode last = null;//存储上一个放入list中的节点
while(root!=null || !stack.isEmpty()){
while(root!=null){
stack.push(root);
root=root.left;
}
root = stack.pop();
if(root.right==null || root.right == last){ //比较list中最后存储的结点是不是root的右结点,如果是,说明root已经是第二次出栈,root的右侧子树已经遍历完了
list.add(root.val);
last = root;
root = null;
}else{
stack.push(root);//如果root有右节点,把root重新放入栈中
root = root.right;
}
}
return list;
}
}
方法二:断开结点的右连接,使得下次该结点出栈时没有右侧结点,直接存入list列表中。
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
Deque<TreeNode> stack = new LinkedList<>();
while(root!=null || !stack.isEmpty()){
while(root!=null){
stack.push(root);
root=root.left;
}
root = stack.pop();
if(root.right==null){
list.add(root.val);
root = null;
}else{ //如果root.right!=null,表明该节点还有右子树
TreeNode temp = root;
root = root.right;
temp.right = null;//断开右子树,相当于标记已经遍历过的结点,下次该节点出栈时,就不会再遍历右子树
stack.push(temp);//将该节点再次入栈
}
}
return list;
}
}
三、Morris 遍历
记作当前节点为root。
- 如果root无左子树,root向右移动
- 如果root有左子树,找到root左子树上最右的节点,记为temp
- 如果temp的right指针指向空,让其指向root,root向左移动;
- 如果temp的right指针指向root,让其指向null,root向右移动。
①前序
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<Integer>();
while(root!=null){
if(root.left==null){
list.add(root.val);
root = root.right;
}else{
TreeNode temp = root.left;
while(temp.right!=null && temp.right!=root){
temp=temp.right;
}
if(temp.right==null){
list.add(root.val);
temp.right=root;
root=root.left;
}else{
temp.right=null;
root=root.right;
}
}
}
return list;
}
}
②中序
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<Integer>();
while(root!=null){
if(root.left==null){
list.add(root.val);
root = root.right;
}else{
TreeNode temp = root.left;
while(temp.right!=null && temp.right!=root){
temp=temp.right;
}
if(temp.right==null){
temp.right=root;
root=root.left;
}else{
list.add(root.val);
temp.right=null;
root=root.right;
}
}
}
return list;
}
}
③后序
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
// 新建一个虚拟节点,将root挂在虚拟节点的left上
// 原因:下面输出节点的代码可以发现,输出左树是正确的,因此把root挂在虚拟节点的left上,转化为要输出的部分全部是左树
TreeNode virtual = new TreeNode(-1);
virtual.left = root;
root = virtual;
while(root!=null){
if(root.left==null){
root = root.right;
}else{
TreeNode temp = root.left;
while(temp.right!=null && temp.right!=root){
temp=temp.right;
}
if(temp.right==null){
temp.right=root;
root=root.left;
}else{
temp.right=null;
// 左子树输出(逆序输出)
List<Integer> tmpList = new ArrayList<>();
temp = root.left;
while(temp != null){
tmpList.add(0, temp.val);
temp = temp.right;
}
list.addAll(tmpList);
root=root.right;
}
}
}
return list;
}
}