二叉树

2022-08-27  本文已影响0人  bowen_wu

概述

二叉树

public class TreeNode<T> {
    T val;
    TreeNode<T> left;
    TreeNode<T> right;

    TreeNode(T rootData) {
        val = rootData;
    }
}

满二叉树

完全二叉树

属性

完全二叉树 满二叉树
深度k 如果根节点深度 k根节点 = 0,那么深度k = 树高h - 1 如果根节点深度 k根节点 = 1,那么深度k = 树高h
总节点n(根节点所在深度 k0=1) 2k-1 <= n <= 2k - 1 n = 2k - 1
树高h h = log2n + 1 h = log2(n + 1)

二叉树遍历

前序遍历 根左右

中序遍历 左根右

后序遍历 左右根

构造二叉树

二叉搜索树

public class BinarySearchTree {
    private TreeNode root;

    public BinarySearchTree(TreeNode root) {
        this.root = root;
    }

    public BinarySearchTree(int value) {
        this.root = new TreeNode(value);
    }

    public TreeNode getRoot() {
        return root;
    }

    public void setRoot(TreeNode root) {
        this.root = root;
    }

    public boolean find(int value) {
        // 时间复杂度:O(logn)
        // 空间复杂度:O(1)
        TreeNode node = root;
        while (node != null) {
            if (node.getVal() == value) {
                return true;
            } else if (node.getVal() > value) {
                node = node.getLeft();
            } else {
                node = node.getRight();
            }
        }
        return false;
    }

    public boolean add(int value) {
        // 时间复杂度:O(logn)
        // 空间复杂度:O(1)
        if (root == null) {
            setRoot(new TreeNode(value));
            return true;
        }

        TreeNode node = root;
        while (node != null) {
            if (node.getVal() == value) {
                return false;
            } else if (node.getVal() > value) {
                if (node.getLeft() == null) {
                    node.setLeft(new TreeNode(value));
                    return true;
                }
                node = node.getLeft();
            } else {
                if (node.getRight() == null) {
                    node.setRight(new TreeNode(value));
                    return true;
                }
                node = node.getRight();
            }
        }
        return false;
    }

    private TreeNode deleteNode(TreeNode root, int value) {
        if (root.val == value) {
            // remove
            if (root.getLeft() == null && root.getRight() == null) {
                // 删除的节点是叶子节点
                return null;
            } else if (root.getLeft() == null) {
                // 删除节点的左节点是 null
                return root.getRight();
            } else if (root.getRight() == null) {
                // 删除节点的右节点是 null
                return root.getLeft();
            } else {
                // 删除节点左右子树都有
                // 查找右子树最小节点
                TreeNode rightMinNode = root.getRight();
                while (rightMinNode.getLeft() != null) {
                    rightMinNode = rightMinNode.getLeft();
                }

                // 将右子树最小值赋给删除的节点
                root.setVal(rightMinNode.getVal());

                // 删除 rightMinNode 节点
                TreeNode treeNode = deleteNode(root.getRight(), rightMinNode.getVal());
                root.setRight(treeNode);
            }
        } else if (root.getVal() > value) {
            root.setLeft(deleteNode(root.left, value));
        } else {
            root.setRight(deleteNode(root.right, value));
        }
        return root;
    }

    public TreeNode remove(int value) {
        // 1. 查找删除节点,如果有,删除
        // 2. 调整树结构,使得删除节点n后的树仍然为二叉搜索树
        //      2-1: 节点n是叶子节点 => 直接删除,使用 null 代替节点n
        //      2-2: 节点n只有一个子树(左子树或者右子树) => 使用左子树或者右子树代替节点n
        //      2-3: 节点n有两个子树 => 左子树的最右节点或者右子树的最左节点代替节点n => n 的中序遍历的后一个或前一个节点
        if (root == null) {
            return null;
        }
        return deleteNode(root, value);
    }
}

static class TreeNode {
    private int val;
    private TreeNode left;
    private TreeNode right;

    public TreeNode(int val) {
        this.val = val;
    }

    public TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }

    public void setVal(int val) {
        this.val = val;
    }

    public int getVal() {
        return val;
    }

    public TreeNode getLeft() {
        return left;
    }

    public void setLeft(TreeNode left) {
        this.left = left;
    }

    public TreeNode getRight() {
        return right;
    }

    public void setRight(TreeNode right) {
        this.right = right;
    }
}

知识点

  1. 疑问:递归遍历的时间复杂度 => 感觉是范围值,因为树的结构不知道
    1. 如果是 BST,时间复杂度为何是 O(logn)
      1. 二叉树查找的时间复杂度和高度有关,T(n) = O(h)
      2. 二叉树的高度和总节点数有关。拥有最多的节点是满 BST,此时 h = log2(n + 1)
      3. 故如果此 BST 是满 BST => T(n) = O(log2(n + 1))
      4. 如果此 BST 不是满 BST => 节点数 < 满 BST 节点数 => T(n) < O(log2(n + 1))
      5. 根据时间复杂度计算原则 => T(n) <= O(logn) => 取最大值 T(n) = O(logn)
    2. 如果不是 BST,那么是 O(n)
  2. 树问题思路 => 递归较迭代更加容易,先想递归再想迭代
    1. 递归
    2. 迭代
上一篇下一篇

猜你喜欢

热点阅读