树基本概念及二叉搜索树

二叉树和多叉树

基本概念

节点: 树上的每个元素

根节点: 每棵树只有一个根节点

父节点: 上一层为下一层的父节点

子节点: 与上条相反

兄弟节点: 同一父节点的子节点

节点的度: 子树的个数, 例如1 的度为 5

树的度: 所有节点度中的最大值, 例如1 的度为5, 树中的最大值, 即为树的度

叶子节点: 度为0 的节点, 例如, 221, 222, 223

非叶子节点: 度不为0 的节点

层数: 根节点在第一层, 根节点的子节点在第二层

节点的深度: 从根节点到当前节点的唯一路径上的节点总数

节点的高度: 从当前节点到最远叶子节点的路径上的节点总数

树的深度: 所有节点深度中的最大值

树的高度: 所有节点高度中的最大值

树的深度等于树的高度

有序树: 树中的任意节点的自己诶但之间有顺序关系

无序树: 树中任意节点的子节点之间没有顺序关系, 也称为自由树

树的结构
各种树结构

二叉树(Binary Tree)

二叉树的特点

每个节点的度最大为2

左右子树时有顺序的

即使某节点只有一颗子树, 也要区分左右子树

二叉树为有序树

二叉树的性质

非空二叉树的第i 层, 最多有2^(i-1) 个节点(i >= 1)

在高度为h 的二叉树上最多有2^h - 1 个节点(h >= 1)

对于任何一颗非空二叉树, 如果叶子节点个数为n0, 度为2 的节点个数为n2, 则, n0 = n2 + 1

假设度为1 的节点个数为n1, 那么二叉树的节点总数n = n0 + n1 + n2

二叉树的边数T = n1 + 2*n2 = n - 1= n0 + n1 + n2 - 1

因此n0 = n2 +1

真二叉树(Proper Binary Tree)

所有节点的度要么为0, 要么为2

真二叉树

满二叉树(Full Binary Tree)

所有节点的度要么为0, 要么为2, 最后一层节点的度为0, 其他节点的度都为2

同样高度的二叉树中, 满二叉树的叶子节点数量最多, 总结点数量最多

满二叉树一定是真二叉树, 真二叉树不一定是满二叉树

第i 层节点数量: 2^(i-1)

叶子节点数量: 2^(h-1)

总节点数量n

n = 2^h - 1

h = log2(n+1)

满二叉树

完全二叉树(Complete Binary Tree)

对节点从上至下, 左至右开始编号, 其所有编号都能与相同高度的满二叉树中的编号对应

叶子节点只会出现最后2层, 最后1 层的叶子节点都靠左对齐

完全二叉树从根节点至倒数第2层是一颗满二叉树

满二叉树一定是完全二叉树, 完全二叉树不一定是满二叉树

度为1 的节点只有左子树

度为1 的节点要么是1 个, 要么是0 个

同样节点数量的二叉树, 完全二叉树的高度最小

假设完全二叉树的高度为h, 那么

至少有2^(h-1) 个节点

对多有2^h - 1 个节点

总结点数量为n

2^(h-1) <= n < 2^h

h - 1 <= log2(n) < h

h = floor(log2(n)) + 1

完全二叉树和满二叉树

二叉搜索树(Binary Search Tree)

二叉搜索树时二叉树的一种, 是应用非常广泛的一种二叉树, 简称 BST

又被称为二叉查找树, 二叉排序树

任意一个节点的值都大于其左子树所有节点的值

任意一个节点的值都小于其右子树所有节点的值

它的左右子树也是一颗二叉搜索树

二叉搜索树存储的元素必须具备可比较性

如果是自定义的类型, 需要指定的比较方式, 不允许为null

二叉树的遍历

根据节点访问顺序不同, 二叉树的常见遍历方式有4 种

  • 前序遍历(Preorder Traversal)
  • 中序遍历(Inorder Traversal)
  • 后序遍历(Postorder Traversal)
  • 层序遍历(Level Order Traversal)

前序遍历

访问顺序: 根节点 -> 前序遍历左子树 -> 前序遍历右子树

前序遍历
    /**
     * 前序遍历
     */
    public void preorderTraversal() {
        preorderTraversal(root);
    }

    private void preorderTraversal(Node node) {
        if (node == null) return;
       // 此处可以做要做的一些操作
        System.out.println(node.element);
        preorderTraversal(node.left);
        preorderTraversal(node.right);
    }

中序遍历

中序遍历左子树 -> 根节点 -> 中序遍历右子树

二叉搜索树的中序遍历结果是升序或者降序

中序遍历
    /**
     * 中序遍历
     */
    public void inorderTraversal() {
        inorderTraversal(root);
    }

    private void inorderTraversal(Node node) {
        if (node == null) return;
        inorderTraversal(node.left);
       // 此处可以做要做的一些操作
        System.out.println(node.element);
        inorderTraversal(node.right);
    }

后序遍历

后序遍历左子树 -> 后序遍历右子树 -> 根节点

后序遍历
    /**
     * 后序遍历
     */
    public void postorderTraversal() {
        postorderTraversal(root);
    }

    private void postorderTraversal(Node node) {
        if (node == null) return;
        postorderTraversal(node.left);
        postorderTraversal(node.right);
       // 此处可以做要做的一些操作
        System.out.println(node.element);
    }

层序遍历

从上到下 -> 从左到右, 以此访问每一个节点

层序遍历
    /**
     * 层序遍历
     */
    public void levelorderTraversal() {
        if (root == null) return;
        Queue> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            Node node = queue.poll();
          // 此处可以做要做的一些操作
            System.out.println(node.element);
            if (node.left != null) {
                queue.offer(node.left);
            }
            if (node.right != null) {
                queue.offer(node.right);
            }
        }
    }

遍历的应用

  • 前序遍历

    展示树状结构

  • 中序遍历

    二叉搜索树的中序遍历按升序或者降序处理

  • 后序遍历

    适用于一些先子后父的操作

  • 层序遍历

    计算二叉树的高度

    判断一棵树是否为完全二叉树

树高度计算

    // 使用递归计算
    public int height2() {
        return height(root);
    }
    // 递归调用
    private int height(Node node) {
        if (node == null) return 0;
        return 1 + Math.max(height(node.left), height(node.right));
    }
    
    // 使用层序遍历, 使用队列记录每一次的元素数量, 每取出头, 减一, 为零时则说明, 这一层已经遍历完成, 高度加一
    public int height() {
        if (root == null) return 0;
        // 高度
        int height = 0;
        // 存储每一层的元素数量
        int levelSize = 1;
        Queue> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            Node node = queue.poll();
            levelSize--;
            if (node.left != null) {
                queue.offer(node.left);
            }
            if (node.right != null) {
                queue.offer(node.right);
            }
            if (levelSize == 0) {
                levelSize = queue.size();
                height++;
            }
        }
        return height;
    }

是否是完全二叉树

  1. 如果树为空, 则返回false
  2. 如果不为空, 开始层序遍历二叉树, 使用队列
    1. 如果node.left != null, 将node.left 入队
    2. 如果node.left == null && node.right != null, 返回false
    3. 如果node.right != null, 将node.right 入队
    4. 如果node.right == null, 之后的遍历节点都是叶子结点, 才保证是完全二叉树, 否则false
  3. 遍历完成, 返回true
    /**
     * 判断是否完全二叉树
     * @return
     */
    public boolean isComplete() {
        if (root == null)
            return false;
        Queue> queue = new LinkedList<>();
        queue.offer(root);
        boolean leaf = false;
        while (!queue.isEmpty()) {
            Node node = queue.poll();
            if (leaf == true && !node.isLeaf()) {
                return false;
            }
            if (node.left != null) {
                queue.offer(node.left);
            } else if (node.right != null) {
                return false;
            }
            
            if (node.right != null) {
                queue.offer(node.right);
            } else {// node.right == null
                leaf = true;
            }
        }
        return true;
    }

前驱后继节点

前驱结点: 中序遍历时的前一个节点

如果是二叉搜索树, 前驱节点是前一个比它小的值

后继节点: 中序遍历时的后一个节点

如果是二叉搜索树, 后继节点就是后一个比它大的节点

    // 前驱节点
    private Node predecessor(Node node) {
        Node p = node.left;
        // 前驱节点在左子树中, left.right.right.....
        if (p != null) {
            while (p.right != null) {
                p = p.right;
            }
            return p;
        }
        // 从父节点, 祖父节点中寻找前驱节点
        while (node.parent != null && node == node.parent.left) {
            node = node.parent;
        }
        return node.parent;
        
    }
    // 后继节点
    private Node successor(Node node) {
        Node p = node.right;
        // 后继节点在右子树中, right.left.left.....
        if (p != null) {
            while (p.left != null) {
                p = p.left;
            }
            return p;
        }
        // 从父节点, 祖父节点中寻找后继节点
        while (node.parent != null && node == node.parent.right) {
            node = node.parent;
        }
        return node.parent;
        
    }

删除节点

  • 叶子节点

    直接删除

    1. node == node.parent.left, node.parent.left = null

    2. node == node.parent.right, node.parent.right = null

    3. node.parent == null, root == null

  • 度为1 的节点

    1. 用子节点替代原节点的位置

      • child 是node.left 或者child 时node.right

        用child 替代node 的位置

        • 如果node 时左子节点

        child.parent = node.parent

        node.parent.left = child

        • 如果node 的右子节点

        child.parent = node.parent

        node.parent.right = child

        • 如果node 是根节点

        root = child

        child.parent = null

  • 删除节点, 度为2 的节点

    • 先用前驱或者后继节点的值覆盖原节点的值

    • 然后删除相应的前驱或者后继的节点

    • 如果一个节点的度为2 , 那么它的前驱或者后继节点的度只可能是1 或者0

删除节点
删除度为2的节点
    // 删除节点
    private void remove(Node node) {
        if (node == null) return;
        size--;
        
        if (node.hasTwoChildren()) {// 度为2 的节点
            // 找到后继节点
            Node s = successor(node);
            // 用后继节点的值覆盖度为2 的节点
            node.element = s.element;
            // 删除后继节点, 替换掉这个节点, 在下一步中删除它
            node = s;
        }
        // 删除node 的节点, 度为1 或者0
        Node replacement = node.left != null ? node.left : node.right;
        if (replacement != null) { // 度为1 的节点
            // 更改parent
            replacement.parent = node.parent;
            if (node.parent == null) {// node 是度为1 的节点且是根节点
                root = replacement;
            } else if (node == node.parent.left) {
                node.parent.left = replacement;
            } else {
                node.parent.right = replacement;
            }
        } else if (node.parent == null) {// 叶子节点, 且为根节点 
            root = null;
        } else {// node 是叶子节点, 但不是根节点
            if (node == node.parent.left) {
                node.parent.left = null;
            } else { // node == node.parent.right
                node.parent.right = null;
            }
        }
    }

二叉树搜索树

接口设计

bst接口设计

BinaryTree 代码

import java.util.LinkedList;
import java.util.Queue;

import com.yw.printer.BinaryTreeInfo;

@SuppressWarnings("unchecked")
public class BinaryTree implements BinaryTreeInfo {
    protected int size;
    protected Node root;
    
    public int size() {
        return size;
    }
    
    public boolean isEmpty() {
        return size == 0;
    }
    
    public void clear() {
        root = null;
        size = 0;
    }
    
    public void preOrder(Visitor visitor) {
        preOrder(root, visitor);
    }
    
    private void preOrder(Node node, Visitor visitor) {
        if (node == null || visitor == null) return;
        visitor.visit(node.element);
        preOrder(node.left, visitor);
        preOrder(node.right, visitor);
    }
    
    public void inOrder(Visitor visitor) {
        inOrder(root, visitor);
    }
    
    private void inOrder(Node node, Visitor visitor) {
        if (node == null || visitor == null) return;
        inOrder(node.left, visitor);
        visitor.visit(node.element);
        inOrder(node.right, visitor);
    }
    
    public void postOrder(Visitor visitor) {
        postOrder(root, visitor);
    }
    
    private void postOrder(Node node, Visitor visitor) {
        if (node == null || visitor == null) return;
        postOrder(node.left, visitor);
        postOrder(node.right, visitor);
        visitor.visit(node.element);
    }
    
    public void levelOrder(Visitor visitor) {
        if (root == null || visitor == null) return;
        Queue> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            Node node = queue.poll();
            visitor.visit(node.element);
            if (node.left != null) {
                queue.offer(node.left);
            }
            if (node.right != null) {
                queue.offer(node.right);
            }
        }
    }
    
    /**
     * 判断是否完全二叉树
     * @return
     */
    public boolean isComplete() {
        if (root == null)
            return false;
        Queue> queue = new LinkedList<>();
        queue.offer(root);
        boolean leaf = false;
        while (!queue.isEmpty()) {
            Node node = queue.poll();
            if (leaf == true && !node.isLeaf()) {
                return false;
            }
            if (node.left != null) {
                queue.offer(node.left);
            } else if (node.right != null) {
                return false;
            }
            
            if (node.right != null) {
                queue.offer(node.right);
            } else {// node.right == null
                leaf = true;
            }
        }
        return true;
    }
    
//  /**
//   * 判断是否完全二叉树
//   * @return
//   */
//  public boolean isComplete() {
//      if (root == null) return false;
//      Queue> queue = new LinkedList<>();
//      queue.offer(root);
//      boolean leaf = false;
//      while (!queue.isEmpty()) {
//          Node node = queue.poll();
//          if (leaf == true && !node.isLeaf()) {
//              return false;
//          }
//          
//          if (node.hasTwoChildren()) {
//              queue.offer(node.left);
//              queue.offer(node.right);
//          } else if (node.left == null && node.right != null) {
//              return false;
//          } else {
//              leaf = true;
//              if (node.left != null) {
//                  queue.offer(node.left);
//              }
//          }
////            if (node.left != null) {
////                queue.offer(node.left);
////            }
////            if (node.right != null) {
////                queue.offer(node.right);
////            }
//      }
//      return true;
//  }
    
    public int height() {
        if (root == null) return 0;
        // 高度
        int height = 0;
        // 存储每一层的元素数量
        int levelSize = 1;
        Queue> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            Node node = queue.poll();
            levelSize--;
            if (node.left != null) {
                queue.offer(node.left);
            }
            if (node.right != null) {
                queue.offer(node.right);
            }
            if (levelSize == 0) {
                levelSize = queue.size();
                height++;
            }
        }
        return height;
    }
    
    public int height2() {
        return height(root);
    }
    // 递归调用
    private int height(Node node) {
        if (node == null) return 0;
        return 1 + Math.max(height(node.left), height(node.right));
    }
    
    protected Node predecessor(Node node) {
        Node p = node.left;
        // 前驱节点在左子树中, left.right.right.....
        if (p != null) {
            while (p.right != null) {
                p = p.right;
            }
            return p;
        }
        // 从父节点, 祖父节点中寻找前驱节点
        while (node.parent != null && node == node.parent.left) {
            node = node.parent;
        }
        return node.parent;
        
    }
    
    protected Node successor(Node node) {
        Node p = node.right;
        // 前驱节点在左子树中, right.left.left.....
        if (p != null) {
            while (p.left != null) {
                p = p.left;
            }
            return p;
        }
        // 从父节点, 祖父节点中寻找前驱节点
        while (node.parent != null && node == node.parent.right) {
            node = node.parent;
        }
        return node.parent;
        
    }
    
    public static interface Visitor {
        void visit(E element);
    }
    
    protected static class Node {
        E element;
        Node left;
        Node right;
        Node parent;
        
        public Node(E element, Node parent) {
            this.element = element;
            this.parent = parent;
        }
        
        public boolean isLeaf() {
            return left == null && right == null;
        }
        
        public boolean hasTwoChildren() {
            return left != null && right != null;
        }
    }
    
    @Override
    public Object root() {
        // TODO Auto-generated method stub
        return root;
    }

    @Override
    public Object left(Object node) {
        // TODO Auto-generated method stub
        return ((Node)node).left;
    }

    @Override
    public Object right(Object node) { 
        // TODO Auto-generated method stub
        return ((Node)node).right;
    }

    @Override
    public Object string(Object node) {
        // TODO Auto-generated method stub
        Node myNode = ((Node)node);
        String parentStr = "null";
        if(myNode.parent != null){
            parentStr = myNode.parent.element.toString();
        }
        return myNode.element + "_p(" + parentStr + ")";
    }
    
}

BST 代码

import java.util.Comparator;

@SuppressWarnings("unchecked")
public class BST extends BinaryTree {
    
    private Comparator comparator;
    
    public BST() {
        this(null);
    }
    
    public BST(Comparator comparator) {
        this.comparator = comparator;
    }
    
    public void add(E element) {
        elementNotNullCheck(element);
        // 只有一个根节点
        if (root == null) {
            root = new Node<>(element, null);
            size++;
            return;
        }
        
        // 其他节点
        // 找到父节点
        Node parent = root;
        Node node = root;
        int cmp = 0;
        while (node != null) {
            cmp = compare(element, node.element);
            parent = node;
            if (cmp > 0) {
                node = node.right;
            } else if (cmp < 0) {
                node = node.left;
            } else {
                node.element = element;
                return;
            }
        }
        Node newNode = new Node<>(element, parent);
        if (cmp > 0) {
            parent.right = newNode;
        } else {
            parent.left = newNode;
        }
        size++;
    }
    
    public void remove(E element) {
        remove(node(element));
    }
    
    public boolean contains(E element) {
        return node(element) != null;
    }
    
    private int compare(E e1, E e2) {
        if (comparator != null) {
            comparator.compare(e1, e2);
        }
        return ((Comparable)e1).compareTo(e2);
    }
    
    private void remove(Node node) {
        if (node == null) return;
        
        size--;
        
        if (node.hasTwoChildren()) {// 度为2 的节点
            // 找到后继节点
            Node s = successor(node);
            // 用后继节点的值覆盖度为2 的节点
            node.element = s.element;
            // 删除后继节点
            node = s;
        }
        // 删除node 的节点, 度为1 或者0
        Node replacement = node.left != null ? node.left : node.right;
        if (replacement != null) { // 度为1 的节点
            // 更改parent
            replacement.parent = node.parent;
            if (node.parent == null) {// node 是度为1 的节点且是根节点
                root = replacement;
            } else if (node == node.parent.left) {
                node.parent.left = replacement;
            } else {
                node.parent.right = replacement;
            }
        } else if (node.parent == null) {// 叶子节点, 且为根节点 
            root = null;
        } else {// node 是叶子节点, 但不是根节点
            if (node == node.parent.left) {
                node.parent.left = null;
            } else { // node == node.parent.right
                node.parent.right = null;
            }
        }
    }
    
    private Node node(E element) {
        Node node = root;
        while (node != null) {
            int cmp = compare(element, node.element);
            if (cmp == 0) return node;
            if (cmp > 0) {
                node = node.right;
            } else {
                node = node.left;
            }
        }
        return null;
    }
    
    private void elementNotNullCheck(E element) {
        if (element == null) {
            throw new IllegalArgumentException("element must not be null");
        }
    }
    
}

你可能感兴趣的:(树基本概念及二叉搜索树)