4.树、二叉树、二叉搜索树

1 实现和特性

链表的查询时间复杂度慢,所以有跳表优化
加速方式:升维
链表中next指向(存放)多个结点

出现树的原因

1.1 树

结点的定义

在这里插入图片描述

二叉树的遍历(Pre-order、In-order、Post-order)基于递归

  1. 前序遍历:根-左-右在这里插入图片描述
  2. 中序:左根右在这里插入图片描述
  3. 后序遍历:左右根在这里插入图片描述

在这里插入图片描述

区别:

Linked List(链表)就是特殊的Tree(树)
Tree是特殊化的Graph图(没有环)

1.2 二叉搜索树(Binary Search Tree)

也称为有序二叉树(Ordered Binary Tree)、排序二叉树(Sorted Binary Tree)
普通的树查找结点的时间复杂度为O(n)
搜索树就将二叉树变得有序
特性:

  1. 左子树所有结点均小于它的根节点
  2. 右子树所有结点均大于它的根节点
  3. 以此类推:左右子树也分别为二叉搜索树(重复性
    得出:中序遍历为升序排列

二叉搜索树常见操作

操作demo

  1. 查询(log n)
    在这里插入图片描述
  2. 插入(log n):插入时先查找,未找到的话那么当前搜索的这个位置就是需要插入的位置
    在这里插入图片描述
  3. 创建:把所有结点依次插入空二叉搜索树
  4. 删除:当删除的结点是根节点时,将此节点最接近的结点填补上去(一般是取第一个大于此结点的结点)
    删除65,第一个大于 的结点为72在这里插入图片描述
    在这里插入图片描述

特殊情况:树退化成只有右结点状态(链表),此时时间复杂度会增加

时间复杂度为(log n)

2. 算法题解

1)二叉树的中序遍历

  • 中序遍历:左根右
  1. 递归,
  2. 维护栈:遍历数组时将左结点的依次放入栈,直到没有结点后出栈并放入数组
    出栈的时候要判断出栈的结点是否有右结点,有的话需要继续入栈已出栈结点的右结点
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import javax.swing.tree.TreeNode;

/*
 * @lc app=leetcode.cn id=94 lang=java
 *
 * [94] 二叉树的中序遍历

 */

// @lc code=start
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
	// 1.
    // public List<Integer> inorderTraversal(TreeNode root) {
    //     List<Integer> res = new ArrayList<>();
    //     helper(root, res);
    //     return res;
    // }

    // public void helper(TreeNode root, List<Integer> res) {
    //     if (root != null){
    //         if (root.left != null) {
    //             helper(root.left, res); // 左子树存在就一直递归,直到不存在
    //         }
    //         res.add(root.val); // 判断左子结点不再存在后就添加当前结点,
    //         if (root.right != null) {
    //             helper(root.right, res); // 左子树递归完成后递归有右子树的结点的右子树
    //         }
    //     }
    // }

    // 2.
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        TreeNode curr = root;
        while (curr != null || !stack.isEmpty()){
            while (curr != null){ // 左子结点存在时入栈
                stack.push(curr);
                curr = curr.left;
            }
            // 左子结点不存在就出栈
            curr = stack.pop(); // 保存当前出栈的结点
            res.add(curr.val); // 将当前出栈的结点存入数组
            curr = curr.right; // 若curr.right为空,那么curr为空 继续pop()出栈
            // 只要满足cur !=null 或者 stack.isEmpty()则继续循环出栈
        }
        return res;
    }
}
// @lc code=end


2) 二叉树的前序遍历

前序遍历:根左右

  1. 递归
  2. 维护数组:按规则入栈
    和中序遍历不一样的是:
    中序被遍历后要从最深的那个左子结点开始存入数组
    遍历:根-根1-根1左-根1右 != 返回:根1左-根1-根1右-根
    而前序遍历时遍历到哪个结点,就存入哪个结点
    遍历:根-根1-根1左-根1右 == 返回:
import java.util.List;
import java.util.Stack;

import javax.swing.tree.TreeNode;

import jdk.nashorn.api.tree.Tree;



// @lc code=start
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    // // 1.递归
    // public List<Integer> preorderTraversal(TreeNode root) {
    //     List<Integer> res = new ArrayList<>();
    //     pre_order(root, res);
    //     return res;
    // }

    // public void pre_order(TreeNode root, List<Integer> res) {
    //     if (root != null) {
    //         res.add(root.val);
    //         if (root.left != null) {
    //             pre_order(root.left, res);
    //         }
    //         if (root.right != null) {
    //             pre_order(root.right, res);
    //         }
    //     }
    // }

    // 2. 维护数组
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;

        while (cur != null || !stack.isEmpty()) {
            while (cur != null) { // 当结点存在
                stack.push(cur); // 将此结点入栈
                res.add(cur.val); // 规则是被遍历就直接存入数组,和中序遍历不一样的是:中序被遍历后要从最深的那个左子结点开始存入数组
                cur = cur.left;
            }
            cur = stack.pop();
            cur = cur.right;
        }
        return res;
    }

}
// @lc code=end


3 ) [590] N叉树的后序遍历

  1. 递归
    子结点是存储在List中的,并且从小到大排序
    所以通过依次递归这个列表中的结点实现结果
  2. 迭代,通过一个链表实现一个栈 入栈为add() 出栈为pollLast();
    另一个链表存放结果,出栈的元素加入结果链表的头部:1-4-2-3-6-5 —> 5-6-3-2-4-1
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import org.w3c.dom.Node;

/*
 * @lc app=leetcode.cn id=590 lang=java
 *
 * [590] N叉树的后序遍历

// @lc code=start
/*
// Definition for a Node.
class Node {
    public int val;
    public List<Node> children;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, List<Node> _children) {
        val = _val;
        children = _children;
    }
};
*/
class Solution {
    // // 1.递归
    // List<Integer> res = new ArrayList<>();
    // public List<Integer> postorder(Node root) {
    //     if (root != null){
    //         for (Node node: root.children) postorder(node); // node = root.children[i] retrun;
    //         res.add(root.val); // 5 - 6 - 3
    //     }
    //     return res;
    // }
    
    // 2. 迭代
    public List<Integer> postorder(Node root) {
        LinkedList<Node> stack = new LinkedList<>();
        LinkedList<Integer> res = new LinkedList<>();

        if (root != null) { // 可能给一个空node
            stack.add(root);
            while (!stack.isEmpty()) {
                Node node = stack.pollLast(); // 保存结点后出栈
                res.addFirst(node.val); // 出栈的顺序是根-右-左,所以反过来存入结果res就是后序遍历
                for (Node item : node.children) { 
                    // 若有子结点,将子结点放入栈,此步骤将入栈所有子结点
                    // 没有子结点了就继续while循环
                    // 例如:判断1子结点,入栈3-2-4,判断4-2无子,3有子就将其子5-6入栈
                    if (item != null) stack.add(item); 
                }
            }
        }
        return res;
    }
}
// @lc code=end


4) [589] N叉树的前序遍历

  1. 递归:根左右
  2. 迭代:要点:children的反向排序
    1. 通过一个链表LinkedList维护一个栈:Collections.reverse(node.children);反转指定列表中元素的顺序。
    2. 通过Stack维护栈 for (i=children.lenth)实现
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import org.w3c.dom.Node;

/*
 * @lc app=leetcode.cn id=589 lang=java
 *

 */

// @lc code=start
/*
// Definition for a Node.
class Node {
    public int val;
    public List<Node> children;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, List<Node> _children) {
        val = _val;
        children = _children;
    }
};
*/
class Solution {
    // // 1. 递归
    // List<Integer> res = new ArrayList<>();
    // public List<Integer> preorder(Node root) {
    //     if (root != null) {
    //         res.add(root.val); // 先加根
    //         for (Node node : root.children) preorder(node); 
    //     }
    //     return res;
    // }

    // // 2. 维护栈LinkedList
    // public List<Integer> preorder(Node root) {
    //     LinkedList<Node> stack = new LinkedList<>();
    //     LinkedList<Integer> res = new LinkedList<>();

    //     if (root != null) {
    //         stack.add(root);
    //         while (!stack.isEmpty()) {
    //             Node node = stack.pollLast();
    //             res.add(node.val);
    //             Collections.reverse(node.children); // 反转链表顺序为4-2-3,6-5
    //             for (Node item : node.children) {
    //                 stack.add(item); // 入栈顺序为4-2-3,3出栈,入栈6,5;
    //             }
                
    //         }
    //     }
    //     return res;
    // }
    // 第二种方法
    // Stack维护栈
    public List<Integer> preorder(Node root) {
        List<Integer> list = new ArrayList<>(); // 
        if (root == null) return list;
        
        Stack<Node> stack = new Stack<>();
        stack.add(root);
        
        while (!stack.empty()) {
            root = stack.pop(); //
            list.add(root.val);
            // 反向将结点的children入栈:4,2,3;出栈3,入栈6,5;出栈5-6;出栈4-2;
            for (int i = root.children.size() - 1; i >= 0; i--)
                stack.add(root.children.get(i));
        }
        
        return list;
    }
}
// @lc code=end


发布了90 篇原创文章 · 获赞 4 · 访问量 1435

猜你喜欢

转载自blog.csdn.net/weixin_44145258/article/details/103622851