其实每次看到新的数据结构我的内心是崩溃的哈哈哈哈,对java基础不够扎实,其实并不会对各种数据结构进行具体的应用,只是最多看懂了题解,对于如何创建这种数据结构、如何对该数据结构的元素赋值、如何具体使用,我都是懵逼的呜呜呜。今天第一次接触树,反正也是有点崩溃哈哈哈哈,不过加油吧~不能放弃~这是第一次嘛!后面越来越好!
一、递归法(前中后通用)
递归三要素:
-
确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。
-
确定终止条件: 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。
-
确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。
前中后序遍历因为访问的元素和处理元素一致,都是中间节点,因此只需修改一种遍历的代码顺序即可实现。
前序遍历
创建一个list集合,将遍历结果存放在集合中。
首先传入根节点,将根节点加入到res中国,然后递归左子树,最后递归右子树。
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res=new ArrayList<Integer>();
preorder(root,res);
return res;
}
public void preorder(TreeNode root,List<Integer> res){
if(root==null){
return;
}
res.add(root.val);
preorder(root.left,res);
preorder(root.right,res);
}
}
中序遍历
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);
}
}
后序遍历
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res=new ArrayList<Integer>();
postorder(root,res);
return res;
}
public void postorder(TreeNode root,List<Integer> res){
if(root==null){return;}
postorder(root.left,res);
postorder(root.right,res);
res.add(root.val);
}
}
二、迭代法
迭代方法统一使用栈存放节点,最后将遍历结果存放在List集合中。
前序遍历
首先根节点入栈,开始循环,弹出根节点,然后判断该节点是否有右孩子,有的话入栈,然后判断左孩子,有的话入栈。直至栈中没有元素了结束循环。
注意:先让右孩子入栈,再让左孩子入栈。(因为栈是先进后出,因此左孩子后入栈是先弹出的,才符合 根左右 的遍历顺序。)
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null){
return result;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()){
TreeNode node = stack.pop();
result.add(node.val);
if (node.right != null){
stack.push(node.right);
}
if (node.left != null){
stack.push(node.left);
}
}
return result;
}
}
中序遍历
定义cur指针,同样首先根节点入栈,当cur指针不为空时,一直使cur指针入栈,并指向当前节点的左孩子,直至cur指向为空。此时另cur指针指向当前栈顶元素,弹出该元素(没有左孩子就弹出,说明此时cur是最左子树)。然后cur指向其右孩子,若没有重复上述空指针操作。
就是说cur一直指向最左孩子,然后弹出。再判断弹出元素是否有右孩子,有的话入栈,cur再指向左孩子。没有的话弹出栈顶元素。(我只是大概能理解,但还不是很透彻)
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result=new ArrayList<>();
if(root==null){return result;}
Stack<TreeNode> stack=new Stack<>();
TreeNode cur=root;
while(cur!=null || !stack.isEmpty()){
if(cur != null){
stack.push(cur);
cur=cur.left;
}else{
cur=stack.pop();
result.add(cur.val);
cur=cur.right;
}
}
后序遍历
后序遍历只要交换一下前序遍历左右孩子入栈顺序,再颠倒结果集即可。
(中左右)->(中右左)->左右中
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null){
return result;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()){
TreeNode node = stack.pop();
result.add(node.val);
if (node.left != null){
stack.push(node.left);
}
if (node.right != null){
stack.push(node.right);
}
}
Collections.reverse(result);
return result;
}
}