递归方式就使用的就是分治的思想,要用什么方式遍历取决于先输出还是先遍历左子树或遍历右子树,终止条件就是到最后,递归结束就可以
对于1有子节点2,3来说,遍历顺序是122213331 每个节点经过三次。另两次是在左子树和右子树返回的时候
非递归
前序遍历
准备一个栈,压入头结点,进入循环,若栈为空则退出。弹出一个元素,输出(这是第一次进入头结点),如果右子树不为空,则压入。如果左不为空再压左
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res=new ArrayList<Integer>();
if(root==null)return res;
Stack<TreeNode> s=new Stack<TreeNode>();
s.push(root);
while(!s.empty()){
TreeNode node=s.pop();
res.add(node.val);
if(node.right!=null)s.push(node.right);
if(node.left!=null)s.push(node.left);
}
return res;
}
中序遍历
中序遍历也就是要先输出最左的节点,再输出中间的。所以进入循环也就要先压入所有的左子节点一直到没有左节点,这步的条件当然是node不为空。
准备一个栈,直接进入循环,压入所有左节点。
当走到左边最后一个节点后,进入else,下一步操作就是打印这个最左边的节点然后打印中间节点再打印右节点。所以循环的条件还得加一个栈不为空,当栈不为空时进入循环进入else,就拿出一个节点,打印,此时如果这个节点没有右节点,则它就是一个左节点,直接返回了,如果他有右节点,则说明中间节点被打印完了,则下面需要把这个节点变成右节点再进入循环。
注:一定要注意这里的node全程都是一个节点,不要创建多的节点了,而且这里while循环如果node不为null是无法退出的,所以为空加入是正确的,别判断,和别的不一样。
非常重要,因为二叉排序树,前驱节点,后继节点都是用中序排列的。
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res=new ArrayList<>();
if(root==null)return res;
Deque<TreeNode> dq=new LinkedList<>();
while(root!=null || !dq.isEmpty()){
if(root!=null){
dq.push(root);
root=root.left;
}else{
root=dq.pop();
res.add(root.val);
root=root.right;
}
}
return res;
}
后序遍历
左右中很难实现,那我们就先实现中右左,输出就是将它压入另一个栈中,结束后输出栈即可
层序遍历
也就是从上到下从左到右一次输出。(广度优先遍历)
准备一个队列,压入头结点,进入循环,若队列为空则退出。弹出一个元素,输出, 压入左压入右(类似于前序遍历)
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res=new ArrayList<>();
if(root==null)return res;
Deque<TreeNode> dq=new LinkedList<>();
dq.offer(root);
while(!dq.isEmpty()){
int size=dq.size();
List<Integer> list=new ArrayList<>();
for(int i=0;i<size;i++){
TreeNode cur=dq.poll();
list.add(cur.val);
if(cur.left!=null){
dq.offer(cur.left);
}
if(cur.right!=null){
dq.offer(cur.right);
}
}
res.add(list);
}
return res;
}