计算机基础--->数据结构(2)【树的概念、二叉搜索树的创建】

特点

  1. 任何一个非空树只有一个根节点
  2. 一棵树的任意两个节点有且仅有唯一的一条路径连通
  3. 一棵树如果有n个节点,那么它一定恰好有n-1条边
  4. 一棵树不包含回路

元素
根节点、子结点、叶子节点,层数(从上到下)、高度(从下到上)、深度(从上到下从0开始)

二叉树

概念: 每个结点最多只能有两个子结点的叫做二叉树
满二叉树

二叉树所有的叶子结点都在最后一层,并且结点总数=2^(n-1),则称为满二叉树,也就是每一层节点都达到了最大值。

完全二叉树

该二叉树的所有叶子结点都在最后一层或者倒数第二层,而且最后一层的叶子结点在左边连续,倒数第二层的叶子结点在左边连续,倒数第二层的叶子结点在右边连续,称之为完全二叉树
其余层都是满节点,最后一层或满或右边空缺,特点是父结点与子结点的序号有对应关系(父:i,左子:2i,右子:2i+1)

平衡二叉树

是一颗二叉排序树

  • 特点:可以是一棵空树,左右两个子树的高度差绝对值不超过1
  • 常用实现方法:红黑树、AVL树、替罪羊树、加权平均树、伸展树

二叉搜索树

在这里插入图片描述

特点:

  • 每个结点最多拥有两个子结点,即每个结点都是一个小的二叉树
  • 比父结点小的放左边
  • 比父结点大的放右边

优点: 拥有链表的快速删除,增加的特性,同时具有快速查找的优势
缺点: 由于数据原因,可能会造成左右两边深度过大,不平衡。甚至可能变成斜树,即退化成链表。

二叉树创建和构成

二叉树的存储

  1. 链式存储:通过链表将各个节点串联
    每个节点:数据data,左节点指针left,有结点指针right
private class Node {
        // 结点内容
        T val;
        // 左右孩子
        Node left;
        Node right;

        public Node(T val) {
            this.val = val;
            this.left = null;
            this.right = null;
        }
    }
  1. 顺序存储:通过数组将二叉树一层一层的存储,如果有空位,那就空着,每个位置只存储data,那么左子节点为父结点的2i,右节点为2i+1

	private class Node {
        // 结点内容
        T val;
        // 左右孩子
        Node left;
        Node right;

        public Node(T val) {
            this.val = val;
            this.left = null;
            this.right = null;
        }
    }

    Node root; // 根结点
    int size; // 树中结点的个数

    public Tree() {
        this.root = null;
        this.size = 0;
    }

    // 获取树中结点的个数
    public int getSize() {
        return this.size;
    }

    // 判断是否为空树
    public boolean isEmpty() {
        return this.root == null;
    }

    // 向树中添加结点
    public void add(T val) {
        this.root = add(root, val);
    }

    private Node add(Node node, T val) {
        // 递归终止条件
        if (node == null) {
            this.size++;
            return new Node(val);
        }
        // 递归操作
        if (node.val.compareTo(val) > 0) {
            node.left = add(node.left, val);
        } else {
            node.right = add(node.right, val);
        }
        return node;
    }
    
    /**
     * 查询val在是否存在于树中
     *
     * @param val 查询的值
     * @return boolean
     */
    public boolean find(T val) {
        return find(root, val);
    }

    private boolean find(Node node, T val) {
        // 1、递归终止条件
        if (node == null) {
            return false;
        }
        // 2、 递归操作
        if (node.val.compareTo(val) == 0) {
            return true;
        } else if (node.val.compareTo(val) < 0) {
            return find(node.right, val);
        } else {
            return find(node.left, val);
        }
    }

 // 从二分搜索树中寻找最小元素所在的结点
    public Node findMixNode() {
        if (root == null) {
            return null;
        }
        Node cur = root;
        while (cur.left != null) {
            cur = cur.left;
        }
        return cur;
    }

    public Node findMixNodeDG() {
        if (root == null) {
            return null;
        }
        return findMixNodeDG(root);
    }

    // 从以node为根的二分搜索树中查找最小元素所在的结点
    private Node findMixNodeDG(Node node) {
        // 1、递归终止的条件
        if (node.left == null) {
            return node;
        }
        // 2、递归操作
        return findMixNodeDG(node.left);
    }

    // 从二分搜索树中删除最小元素所在的结点
    public void removeMinNode() {
        Node minNode = findMixNodeDG();
        if (minNode == null) {
            return;
        }
        // 进行删除
        root = removeMinNode(root);
    }

    // 从以node 为根的二分搜索树中删除最小结点
    private Node removeMinNode(Node node) {
        // 1、递归终止的条件
        if (node.left == null) {
            Node rightNode = node.right;
            node.right = null;
            this.size--;
            return rightNode;
        }
        // 2、递归操作
        node.left = removeMinNode(node.left);
        return node;
    }

二叉树的遍历

  • 三种遍历方式:前序,中序,后序

前序遍历
先输出父结点,再遍历左子树和右子树

// 前序遍历
public List<T> preTraverse() {
    List<T> list = new ArrayList<>();
    preTraverse(root, list);
    return list;
}

private void preTraverse(Node node, List<T> list) {
    // 1、递归终止的条件
    if (node == null) {
        return;
    }
    // 2、递归操作
    list.add(node.val);
    preTraverse(node.left, list);
    preTraverse(node.right, list);
}

中序遍历
先遍历左子树,再输出父结点,再遍历右子树

//中续遍历
public List<T> middleTraverse() {
    List<T> list = new ArrayList<>();
    middleTraverse(root, list);
    return list;
}

private void middleTraverse(Node node, List<T> list) {
    // 1、递归终止的条件
    if (node == null) {
        return;
    }
    // 2、递归操作
    middleTraverse(node.left, list);
    list.add(node.val);
    middleTraverse(node.right, list);
}

后序遍历
先遍历左子树,再遍历右子树,然后输出父结点

//后续遍历
public List<T> afterTraverse() {
    List<T> list = new ArrayList<>();
    afterTraverse(root, list);
    return list;
}

private void afterTraverse(Node node, List<T> list) {
    // 1、递归终止的条件
    if (node == null) {
        return;
    }
    // 2、递归操作
    afterTraverse(node.left, list);
    afterTraverse(node.right, list);
    list.add(node.val);
}

层序遍历
利用队列的性质将每层的结点存储到一个集合中,然后分别判断其左右子树,直到循环结束

/**
 * 层序遍历---借助队列实现
 *
 * @return
 */
public List<T> levelTraver() {
    List<T> list = new ArrayList<>();

    Queue<Node> queue = new LinkedList<>();
    if (this.root != null) {
        queue.offer(this.root);
    }

    while (!queue.isEmpty()) {
        Node temp = queue.poll();
        list.add(temp.val);
        if (temp.left != null) {
            queue.offer(temp.left);
        }
        if (temp.right != null) {
            queue.offer(temp.right);
        }
    }
    return list;
}

平衡二叉树

特点:

  • 是一棵空树或它的左右两个子树的高度差的绝对值不超过1,超过1就会失衡。并且左右两个子树都是一棵平衡二叉树。

获取平衡因子

//  获取平衡因子
public int getBalance(Node node) {
    if (node == null) {
        return 0;
    }
    return getHeight(node.left) - getHeight(node.right);
}

添加节点:

  • 往平衡二叉树中添加节点很可能会导致二叉树失去平衡,所以我们需要在每次插入结点后进行平衡的维护操作。插入结点破坏平衡性有如下四种情况:
  • 右右旋转
  • 左左旋转
  • 左右旋转
  • 右左旋转
   // 向树中添加结点

public void add(T val) {
    if (find(val) != null) {
        return;
    }
    this.root = add(root, val);
}

// 在以node为根的二分搜索树中添加val
private Node add(Node node, T val) {
    // 1、递归终止的条件
    if (node == null) {
        this.size++;
        return new Node(val);
    }
    // 2、递归操作
    if (node.val.compareTo(val) > 0) {
        node.left = add(node.left, val);
    } else {
        node.right = add(node.right, val);
    }
    // 更新节点高度
    node.height = Math.max(getHeight(node.left), getHeight(node.right)) + 1;

    //以node节点为根的树不平衡,就要调整
    Node resultNode = node;
    if (getBalance(node) > 1 && getBalance(node.left) >= 0) {
        resultNode = rightRotate(node);
    } else if (getBalance(node) < -1 && getBalance(node.right) <= 0) {
        resultNode = leftRotate(node);
    } else if (getBalance(node) > 1 && getBalance(node.left) < 0) {
        node.left = leftRotate(node.left);
        resultNode = rightRotate(node);
    } else if (getBalance(node) < -1 && getBalance(node.right) > 0) {
        node.right = rightRotate(node.right);
        resultNode = leftRotate(node);
    }
    return resultNode;
}

删除操作

 public void remove(T ele) {
        Node delNode = find(ele);
        if (delNode == null) {
            return;
        }
        // 进行删除操作
        root = remove(root, ele);
    }

    // 从以node为根的二分搜索树中删除值为ele的结点
    private Node remove(Node node, T ele) {
        Node resultNode = null;
        if (node.val.compareTo(ele) == 0) {
            // 情况1: 只存在右子树
            if (node.left == null) {
                Node rightNode = node.right;
                node.right = null;
                this.size--;
                resultNode = rightNode;
            } else if (node.right == null) {
                Node leftNode = node.left;
                node.left = null;
                this.size--;
                resultNode = leftNode;
            } else {
                // 左右子树都不为空
                // 1>从右子树中查找最小元素所属结点
                Node suffixNode = findMixNodeDG(node.right);
                // 2> 后继结点的右子树:从原右子树中删除最小元素结点之后新树的根结点
                suffixNode.right = remove(node.right, suffixNode.val);
                // 3> 后继结点的左子树: 删除结点的左子树
                suffixNode.left = node.left;
                node.left = node.right = null;
                resultNode = suffixNode;
            }
        } else if (node.val.compareTo(ele) > 0) {
            node.left = remove(node.left, ele);
            resultNode = node;
        } else {
            node.right = remove(node.right, ele);
            resultNode = node;
        }

        // 删除的是叶子节点
        if (resultNode == null) {
            return null;
        }

        // 删除之后,可能改变了树的平衡,因此需要进行调整
        resultNode.height = Math.max(getHeight(resultNode.left), getHeight(resultNode.right)) + 1;

        Node result = resultNode;
        if (getBalance(resultNode) > 1 && getBalance(resultNode.left) >= 0) {
            result = rightRotate(resultNode);
        } else if (getBalance(resultNode) < -1 && getBalance(resultNode.right) <= 0) {
            result = leftRotate(resultNode);
        } else if (getBalance(resultNode) > 1 && getBalance(resultNode.left) < 0) {
            resultNode.left = leftRotate(resultNode.left);
            result = rightRotate(resultNode);
        } else if (getBalance(resultNode) < -1 && getBalance(resultNode.right) > 0) {
            resultNode.right = rightRotate(resultNode.right);
            result = leftRotate(resultNode);
        }
        return result;
    }

    // 判断方法: 判断是否为二分搜索树
    public boolean isBinarySearchTree() {
        if (this.root == null) {
            return true;
        }
        // 使用中序遍历的性质
        List<T> list = middleTraverse();
        for (int i = 1; i < list.size(); i++) {
            if (list.get(i - 1).compareTo(list.get(i)) > 0) {
                return false;
            }
        }
        return true;
    }

    // 判断方法: 判断是否为AVL树
    public boolean isBalanceTree() {
        return isBalanceTree(this.root);
    }

    private boolean isBalanceTree(Node node) {
        if (node == null) {
            return true;
        }
        int balance = Math.abs(getBalance(node));
        if (balance > 1) {
            return false;
        }
        return isBalanceTree(node.left) && isBalanceTree(node.right);
    }

    // 层序遍历,并打印平衡因子
    public void levelTraverse() {
        LinkedList<Node> queue = new LinkedList<>();
        if (this.root == null) {
            return;
        }
        queue.add(this.root);
        while (!queue.isEmpty()) {
            Node node = queue.poll();
            System.out.println(node.val + "<---->" + getBalance(node));
            if (node.left != null) {
                queue.add(node.left);
            }
            if (node.right != null) {
                queue.add(node.right);
            }
        }
    }

    /**
     * 右旋
     *     y
     *   x    ----->         x
     * z                  z     y
     */

    private Node rightRotate(Node y) {
        Node x = y.left;
        // 1、保存x的右子树
        Node t3 = x.right;

        // 2、将y作为x的右子树
        x.right = y;
        // 3、将t3作为y的左子树
        y.left = t3;
        // 更新高度
        y.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1;
        x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1;
        return x;
    }

    /**
     * 左旋
     * <p>
     * y
     *   x      ---->     x
     *     z            y   z
     */
    private Node leftRotate(Node y) {
        Node x = y.right;
        // 1、保存x的左孩子
        Node t2 = x.left;
        // 2、将y作为x的左树
        x.left = y;
        // 3、将t2作为y的右树
        y.right = t2;
        // 更新高度
        y.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1;
        x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1;
        return x;
    }
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值