题目:
144、94、145:给定一个二叉树,返回它的 前、中、后序 遍历。。
思路:
-
使用递归;
-
使用循环;
思路详细分析:
-
递归方法非常简单,无需说明,在这几个题目中,因为十分简单,除了函数的参数外也没有用到额外的局部变量,并没有浪费太多的空间;
-
使用循环则需要借助Stack,需要注意的是后续遍历每个节点需要入栈出栈两次,第一次访问他的左节点,然后入栈,第二次访问的右节点,然后入栈,最后出栈时才能访问该节点,所以需要节点有一个标志位,方便记录是否已经访问了右节点,以便知道是应该访问右节点还是访问该节点,左节点不需要标志为记录是否已经访问,因为没到一个节点,第一时间就是去访问其左节点,所以从栈中拿出来的元素,其左节点肯定是访问过的;
代码实现:
1,前、中、后递归实现,代码非常简单易懂:
/**
* Definitionfor a binary tree node.
*public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
List<Integer> list = new ArrayList<Integer>();
//仅仅只是利用到增加元素的功能,可以考虑使用LinkedList,这里没有改,在循环版本中已经修改为LinkedList;
public List<Integer> preorderTraversal(TreeNode root) {
if (root == null) return list;
list.add(root.val);
preorderTraversal(root.left);
preorderTraversal(root.right);
return list;
}
}
class Solution {
List<Integer> list = new ArrayList<Integer>();
public List<Integer> inorderTraversal(TreeNode root) {
if (root == null) return list;
inorderTraversal(root.left);
list.add(root.val);
inorderTraversal(root.right);
return list;
}
}
class Solution {
List<Integer> list = new ArrayList<Integer>();
public List<Integer> postorderTraversal(TreeNode root) {
if (root == null) return list;
postorderTraversal(root.left);
postorderTraversal(root.right);
list.add(root.val);
return list;
}
}
2 循环实现:
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> list = new LinkedList<>();
if (root == null) return list;
LinkedList<TreeNode> stack = new LinkedList<>(); //存放已经访问过的节点,便于以后查找该节点的右子节点;
TreeNode node =root;
stack.push(root);
while (!stack.isEmpty()){
node = stack.pop();
list.add(node.val);
if (node.right != null){
stack.push(node.right);
}
if (node.left != null){
stack.push(node.left);
}
}
return list;
}
}
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> list = new LinkedList<Integer>();
if(root == null) return list;
LinkedList<TreeNode> stack = new LinkedList<>();
TreeNode node = root;
while(node != null || !stack.isEmpty()){
if(node != null){
stack.push(node);
node = node.left;
} else {
node = stack.pop();
list.add(node.val);
node = node.right;
}
}
return list;
}
}
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> list = new LinkedList<Integer>();
if(root == null) return list;
LinkedList<TreeNode> stack = new LinkedList<>();
LinkedList<Boolean> bool = new LinkedList<>(); //每个TreeNode入栈都对应着一个标志为入栈,每个TreeNode出栈也会对应一个标志位出栈,该标志为标识对应的TreeNode的右子节点是否已经被访问,如果为true,则表示该节点可以被访问了,否则应该访问它的右子节点;
TreeNode node = root;
while(node != null || !stack.isEmpty()){
if(node != null){
stack.push(node);
bool.push(false);
node = node.left;
}else{
node = stack.pop();
if(bool.pop()){
list.add(node.val);
node = null;
}else{
tack.push(node);
bool.push(true);
node = node.right;
}
}
}
return list;
}
}
另外在LeeCode上看到一个比较有意思的解法,获取元素的顺序是反着来的,先获取最后一个元素,但是每次获取到时候都插入list的最前端,LinkedList插入是O(1)的,所以也没有额外的开销,但却免掉了顺序找元素的复杂逻辑和相对较小的空间开销(类似于前序遍历,当前节点直接访问,无需入栈及标志位所占用的空间),代码如下:
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
LinkedList<Integer> out = new LinkedList<Integer>();
LinkedList<TreeNode> st = new LinkedList<>();
if (root == null)
return out;
st.add(root);
while(!st.isEmpty()){
TreeNode curr = st.pollLast();
out.addFirst(curr.val);
if(curr.left != null)
st.add(curr.left);
if(curr.right != null)
st.add(curr.right);
}
return out;
}
}