数据结构复习——二叉树

树的概念

定义树的方式是使用递归的方式。

一棵树就是一些点的集合。这个集合可以是空集;也可以是非空集,若不是空集,则树由称作根的节点r以及0个或多个非空子树T1,T2…组成这些子树中的每一棵的根都被来自根r的一条有向边所连结。

每一棵子树的根叫做根r的儿子,而r是每一棵子树的根的父亲。

没有儿子的节点被称为叶子。

对于节点n,n的深度即为从根到n的唯一的路径长度。

树的实现

实现树的一种方式可以是在每一个节点除数据外还要有一些链,使得该结点的每一个儿子都有一个链指向他。

二叉树的节点:

/**
 * @program: 手搓代码
 * @description: 树的节点
 * @author: 郑畅道
 * @create: 2020-04-14 17:03
 **/
public class TreeNode<E> {

    /**
     * 当前节点存储的元素
     */
    private E element;

    /**
     * 父节点
     */
    TreeNode<E> previousNode;

    /**
     * 左孩子
     */
    TreeNode<E> nextLeftNode;

    /**
     * 右孩子
     */
    TreeNode<E> nextRightNode;

    public TreeNode() {
    }

    public TreeNode(E element, TreeNode<E> previousNode, TreeNode<E> nextLeftNode, TreeNode<E> nextRightNode) {
        this.element = element;
        this.previousNode = previousNode;
        this.nextLeftNode = nextLeftNode;
        this.nextRightNode = nextRightNode;
    }

    public E getElement() {
        return element;
    }

    public void setElement(E element) {
        this.element = element;
    }

    public TreeNode<E> getPreviousNode() {
        return previousNode;
    }

    public void setPreviousNode(TreeNode<E> previousNode) {
        this.previousNode = previousNode;
    }

    public TreeNode<E> getNextLeftNode() {
        return nextLeftNode;
    }

    public void setNextLeftNode(TreeNode<E> nextLeftNode) {
        this.nextLeftNode = nextLeftNode;
    }

    public TreeNode<E> getNextRightNode() {
        return nextRightNode;
    }

    public void setNextRightNode(TreeNode<E> nextRightNode) {
        this.nextRightNode = nextRightNode;
    }

    @Override
    public String toString() {
        return "TreeNode{" +
                "element=" + element +
                ", previousNode=" + previousNode +
                ", nextLeftNode=" + nextLeftNode +
                ", nextRightNode=" + nextRightNode +
                '}';
    }
}

树的遍历及应用

以此树为例:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7RCFDo6O-1587370759729)(E:\宝库\markdown笔记\面试题V2.0\数据结构\1586855795265.png)]

树的遍历方法有三种:

先中后是相对根节点的。

  • 先序遍历

    先序遍历遵循根→左→右的顺序遍历;

    则遍历顺序为A→B→C;

  • 中序遍历

    中序遍历遵循左→根→右的顺序遍历;

    则遍历顺序为B→A→C;

  • 后序遍历

    中序遍历遵循左→右→根的顺序遍历;

    则遍历顺序为B→C→A;

二叉树

二叉树是一棵每个节点的儿子都不多于2个的树。

表达式树

在表达式树中叶子节点为操作数,而其它节点为操作符。因为此处的所有操作都是二元的,所以此树恰为二叉树。

对于表达式树常常使用中序遍历的方式。

节点示例代码:

/**
 * @program: 手搓代码
 * @description: 树的节点
 * @author: 郑畅道
 * @create: 2020-04-14 17:03
 **/
public class TreeNode<E> {

    /**
     * 当前节点名字
     */
    private String name;

    /**
     * 当前节点存储的元素
     */
    private E element;

    /**
     * 父节点
     */
    private TreeNode<E> previousNode;

    /**
     * 左孩子
     */
    private TreeNode<E> nextLeftNode;

    /**
     * 右孩子
     */
    private TreeNode<E> nextRightNode;

    public TreeNode() {
    }

    public TreeNode(E element, TreeNode<E> previousNode, TreeNode<E> nextLeftNode, TreeNode<E> nextRightNode) {
        this.element = element;
        this.previousNode = previousNode;
        this.nextLeftNode = nextLeftNode;
        this.nextRightNode = nextRightNode;
    }

    public E getElement() {
        return element;
    }

    public void setElement(E element) {
        this.element = element;
    }

    public TreeNode<E> getPreviousNode() {
        return previousNode;
    }

    public void setPreviousNode(TreeNode<E> previousNode) {
        this.previousNode = previousNode;
    }

    public TreeNode<E> getNextLeftNode() {
        return nextLeftNode;
    }

    public void setNextLeftNode(TreeNode<E> nextLeftNode) {
        this.nextLeftNode = nextLeftNode;
    }

    public TreeNode<E> getNextRightNode() {
        return nextRightNode;
    }

    public void setNextRightNode(TreeNode<E> nextRightNode) {
        this.nextRightNode = nextRightNode;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    /**
     * 如果直接打印节点的String,则会导致爆栈
     * 假设当前节点是A的子节点是B,
     * toString时打印子节点,子节点的父节点又是A,此时又会打印A的信息,
     * 如此循环...爆栈
     */
    @Override
    public String toString() {
        return "TreeNode{" +
                "name='" + name + '\'' +
                ", element=" + element +
                ", previousNode=" + (previousNode != null ? previousNode.getName() : "null") +
                ", nextLeftNode=" + (nextLeftNode != null ? nextLeftNode.getName() : "null") +
                ", nextRightNode=" + (nextRightNode != null ? nextRightNode.getName() : "null") +
                '}';
    }
}

树相关操作示例代码:

/**
 * @program: 手搓代码
 * @description: 二叉树
 * @author: 郑畅道
 * @create: 2020-04-14 17:26
 **/
public class BinTree<E> {

    private int size;

    private TreeNode rootNode;

    private List<TreeNode> nodes = new ArrayList<>();

    private List<TreeNode> seq = new ArrayList<>();

    public BinTree() {
    }

    public BinTree(TreeNode rootNode) {
        this.rootNode = rootNode;
        nodes.add(rootNode);
    }

    /**
     * 添加左孩子
     * @param fatherNode 父节点
     * @param childNode 孩子节点
     * @return 是否添加成功
     */
    public boolean addLeft(TreeNode<E> fatherNode, TreeNode<E> childNode){
        if(fatherNode == null){
            if (rootNode == null){
                this.rootNode = childNode;
                nodes.add(childNode);
                return true;
            }
            throw new NullPointerException("父节点不可为空");
        }
        fatherNode.setNextLeftNode(childNode);
        childNode.setPreviousNode(fatherNode);
        nodes.add(childNode);
        return true;
    }

    /**
     * 添加右孩子
     * @param fatherNode 父节点
     * @param childNode 孩子节点
     * @return 是否添加成功
     */
    public boolean addRight(TreeNode<E> fatherNode, TreeNode<E> childNode){
        if(fatherNode == null){
            if (rootNode == null){
                this.rootNode = childNode;
                nodes.add(childNode);
                return true;
            }
            throw new NullPointerException("父节点不可为空");
        }
        fatherNode.setNextRightNode(childNode);
        childNode.setPreviousNode(fatherNode);
        nodes.add(childNode);
        return true;
    }

    /**
     * 先序遍历
     */
    private void preorderTraversal(TreeNode rootNode){
        if (!nodes.contains(rootNode)){
            return;
        }
        seq.add(rootNode);
        if (rootNode.getNextLeftNode() != null){
            this.preorderTraversal(rootNode.getNextLeftNode());
        }
        if (rootNode.getNextRightNode() != null){
            this.preorderTraversal(rootNode.getNextRightNode());
        }
    }

    /**
     * 先序遍历
     * @return 遍历结果
     */
    public List<TreeNode> getPreorderTraversal(TreeNode treeNode){
        this.checkAndClearList();
        this.preorderTraversal(treeNode);
        return seq;
    }

    /**
     * 中序遍历
     */
    private void sequentialTraversal(TreeNode rootNode){
        if (!nodes.contains(rootNode)){
            return;
        }
        if (rootNode.getNextLeftNode() != null){
            this.sequentialTraversal(rootNode.getNextLeftNode());
        }
        seq.add(rootNode);
        if (rootNode.getNextRightNode() != null){
            this.sequentialTraversal(rootNode.getNextRightNode());
        }
    }

    /**
     * 中序遍历
     * @return 遍历结果
     */
    public List<TreeNode> getSequentialTraversal(TreeNode node){
        this.checkAndClearList();
        this.sequentialTraversal(node);
        return seq;
    }

    /**
     * 后序遍历
     */
    private void postOrderTraversal(TreeNode rootNode){
        if (!nodes.contains(rootNode)){
            return;
        }
        if (rootNode.getNextLeftNode() != null){
            this.postOrderTraversal(rootNode.getNextLeftNode());
        }
        if (rootNode.getNextRightNode() != null){
            this.postOrderTraversal(rootNode.getNextRightNode());
        }
        seq.add(rootNode);
    }

    /**
     * 后序遍历
     * @return 遍历结果
     */
    public List<TreeNode> getPostOrderTraversal(TreeNode node){
        this.checkAndClearList();
        this.postOrderTraversal(node);
        return seq;
    }

    public int getSize() {
        return size;
    }

    public List<TreeNode> getNodes() {
        return nodes;
    }

    private void checkAndClearList(){
        if (seq.size() != 0){
            seq.clear();
        }
    }
}

测试代码:

/**
 * @program: 手搓代码
 * @description: 测试
 * @author: 郑畅道
 * @create: 2020-04-18 21:39
 **/
public class BinTreeTest {

    public static void main(String[] args) {
        TreeNode<String> root = new TreeNode<>();
        root.setName("root");
        root.setElement("A");
        TreeNode<String> c1 = new TreeNode<>();
        c1.setName("B");
        c1.setElement("B");
        TreeNode<String> c2 = new TreeNode<>();
        c2.setName("D");
        c2.setElement("D");
        TreeNode<String> c3 = new TreeNode<>();
        c3.setName("E");
        c3.setElement("E");
        TreeNode<String> c4 = new TreeNode<>();
        c4.setName("C");
        c4.setElement("C");
        TreeNode<String> c5 = new TreeNode<>();
        c5.setName("F");
        c5.setElement("F");

        BinTree<String> binTree = new BinTree<>(root);
        binTree.addLeft(root, c1);
        binTree.addRight(root, c4);
        binTree.addLeft(c1, c2);
        binTree.addRight(c1, c3);
        binTree.addLeft(c4, c5);

        System.out.println("所有节点:");
        for (TreeNode cursor :
                binTree.getNodes()) {
            System.out.println("节点:" + cursor);
        }
        System.out.println("*********************************************************");
        System.out.println("先序遍历:");
        for (TreeNode cursor :
                binTree.getPreorderTraversal(root)) {
            System.out.print(cursor.getElement() + "  ");
        }
        System.out.println();
        System.out.println("*********************************************************");
        System.out.println("中序遍历:");
        for (TreeNode cursor :
                binTree.getSequentialTraversal(root)) {
            System.out.print(cursor.getElement() + "  ");
        }
        System.out.println();
        System.out.println("*********************************************************");
        System.out.println("后序遍历:");
        for (TreeNode cursor :
                binTree.getPostOrderTraversal(root)) {
            System.out.print(cursor.getElement() + "  ");
        }
        System.out.println();
        System.out.println("*********************************************************");
    }

}

输出:

所有节点:
节点:TreeNode{name='root', element=A, previousNode=null, nextLeftNode=B, nextRightNode=C}
节点:TreeNode{name='B', element=B, previousNode=root, nextLeftNode=D, nextRightNode=E}
节点:TreeNode{name='C', element=C, previousNode=root, nextLeftNode=F, nextRightNode=null}
节点:TreeNode{name='D', element=D, previousNode=B, nextLeftNode=null, nextRightNode=null}
节点:TreeNode{name='E', element=E, previousNode=B, nextLeftNode=null, nextRightNode=null}
节点:TreeNode{name='F', element=F, previousNode=C, nextLeftNode=null, nextRightNode=null}
*********************************************************
先序遍历:
A  B  D  E  C  F  
*********************************************************
中序遍历:
D  B  E  A  F  C  
*********************************************************
后序遍历:
D  E  B  F  C  A  
*********************************************************

二叉查找树

使二叉树成为二叉查找树的性质是,对于树中的每个节点X,它的左子树中所有项的值小于X中的值,而它的右子树中所有项的值大于X中的值。这意味着该树所有的元素可以用某种一致的方式排序。

节点示例代码:

/**
 * @program: 手搓代码
 * @description: 树的节点
 * @author: 郑畅道
 * @create: 2020-04-14 17:03
 **/
public class TreeNode<E>{

    /**
     * 当前节点名字
     */
    private String name;

    /**
     * 当前节点存储的元素
     */
    private E element;

    /**
     * 父节点
     */
    private TreeNode<E> previousNode;

    /**
     * 左孩子
     */
    private TreeNode<E> nextLeftNode;

    /**
     * 右孩子
     */
    private TreeNode<E> nextRightNode;

    public TreeNode() {
    }

    public TreeNode(E element) {
        this.element = element;
        this.name = element.toString();
    }

    public TreeNode(E element, TreeNode<E> previousNode, TreeNode<E> nextLeftNode, TreeNode<E> nextRightNode) {
        this.element = element;
        this.previousNode = previousNode;
        this.nextLeftNode = nextLeftNode;
        this.nextRightNode = nextRightNode;
    }

    public E getElement() {
        return element;
    }

    public void setElement(E element) {
        this.element = element;
        if (this.name == null){
            this.name = element.toString();
        }
    }

    public TreeNode<E> getPreviousNode() {
        return previousNode;
    }

    public void setPreviousNode(TreeNode<E> previousNode) {
        this.previousNode = previousNode;
    }

    public TreeNode<E> getNextLeftNode() {
        return nextLeftNode;
    }

    public void setNextLeftNode(TreeNode<E> nextLeftNode) {
        this.nextLeftNode = nextLeftNode;
    }

    public TreeNode<E> getNextRightNode() {
        return nextRightNode;
    }

    public void setNextRightNode(TreeNode<E> nextRightNode) {
        this.nextRightNode = nextRightNode;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    /**
     * 如果直接打印节点的String,则会导致爆栈
     * 假设当前节点是A的子节点是B,
     * toString时打印子节点,子节点的父节点又是A,此时又会打印A的信息,
     * 如此循环...爆栈
     */
    @Override
    public String toString() {
        return "TreeNode{" +
                "name='" + name + '\'' +
                ", element=" + element +
                ", previousNode=" + (previousNode != null ? previousNode.getName() : "null") +
                ", nextLeftNode=" + (nextLeftNode != null ? nextLeftNode.getName() : "null") +
                ", nextRightNode=" + (nextRightNode != null ? nextRightNode.getName() : "null") +
                '}';
    }
}

二叉查找树示例代码:

/**
 * @program: 手搓代码
 * @description: 二叉查找树
 * @author: 郑畅道
 * @create: 2020-04-18 22:54
 **/
public class BinSearchTree<T extends Comparable<? super T>> {
    /**
     * 根节点
     */
    private TreeNode<T> root;

    public BinSearchTree() {
        root = null;
    }

    /**
     * 清空树
     */
    public void mackEmpty(){
        root = null;
    }

    /**
     * 是否为空
     * @return 结果
     */
    public boolean isEmpty(){
        return root == null;
    }

    /**
     * 查找值是否存在
     * @param nodeElement 值
     * @return 结果
     */
    public boolean contains(T nodeElement){
        return this.contains(nodeElement, root);
    }


    /**
     * 寻找最小值
     * @return 最小值
     * @throws Exception 树为空
     */
    public T findMin() throws Exception {
        if (isEmpty()){
            throw new Exception("当前树为空");
        }
        return findMin(root).getElement();
    }

    /**
     * 寻找最大值
     * @return 最大值
     * @throws Exception 树为空
     */
    public T findMax() throws Exception{
        if (isEmpty()){
            throw new Exception("当前树为空");
        }
        return findMax(root).getElement();
    }

    /**
     * 将数值插入到树中
     * @param nodeElement 值
     */
    public void insert(T nodeElement){
        root = insert(nodeElement, root);
    }

    /**
     * 将数值从树中移除
     * @param nodeElement 值
     */
    public void remove(T nodeElement){
        root = remove(nodeElement, root);
    }

    /**
     * 打印整棵树
     */
    public void printTree(){
        printTreeNode(root);
    }

    /**
     * 比较是否为目标值节点
     * @param nodeElement 目标值
     * @param node 节点
     * @return 结果
     */
    private boolean contains(T nodeElement, TreeNode<T> node){
        //空节点检测
        if(node == null){
            return false;
        }
        //获取当前节点的值与目标值的关系
        int res = nodeElement.compareTo(node.getElement());
        //根据关系判断递归左子树、右子树还是当前节点
        if (res < 0){
            return contains(nodeElement, node.getNextLeftNode());
        }else if (res > 0){
            return contains(nodeElement, node.getNextRightNode());
        }else {
            return true;
        }
    }

    /**
     * 返回最小值节点
     * @param node 根节点
     * @return 节点
     */
    private TreeNode<T> findMin(TreeNode<T> node){
        if (node != null){
            //左子树一定是最小值
            while (node.getNextLeftNode() != null){
                node = node.getNextLeftNode();
            }
            return node;
        }else {
            return null;
        }
    }

    /**
     * 返回最大值节点
     * @param node 根节点
     * @return 节点
     */
    private TreeNode<T> findMax(TreeNode<T> node){
        if (node != null){
            //右子树一定是最大值
            while (node.getNextRightNode() != null){
                node = node.getNextRightNode();
            }
            return node;
        }else {
            return null;
        }
    }

    /**
     * 插入新节点
     * @param nodeElement 节点值
     * @param node 根节点
     * @return 插入的新节点
     */
    private TreeNode<T> insert(T nodeElement, TreeNode<T> node){
        //检查节点是否为空
        if (node == null){
            return new TreeNode<>(nodeElement);
        }
        //检查节点值与当前节点的大小
        int res = nodeElement.compareTo(node.getElement());
        //递归和替换
        if (res > 0){
            node.setNextRightNode(
                    insert(nodeElement, node.getNextRightNode())
            );
        }else if (res < 0){
            node.setNextLeftNode(
                    insert(nodeElement, node.getNextLeftNode())
            );
        }
        return node;
    }

    /**
     * 移除节点
     * @param nodeElement 节点值
     * @param node 根节点
     * @return 移除的节点
     */
    private TreeNode<T> remove(T nodeElement, TreeNode<T> node){
        //检查节点是否为空
        if (node == null){
            return null;
        }
        //检查节点值与当前节点的大小
        int res = nodeElement.compareTo(node.getElement());
        //递归和替换
        if (res < 0){
            node.setNextLeftNode(remove(nodeElement, node.getNextLeftNode()));
        }else if (res > 0){
            node.setNextRightNode(remove(nodeElement, node.getNextRightNode()));
        }else if (node.getNextRightNode() != null && node.getNextLeftNode() != null){
            //如果目标节点有孩子,则找出最小的右子树中最小的孩子替换该节点并删除孩子节点
            node.setElement(findMax(node.getNextRightNode()).getElement());
            node.setNextRightNode(remove(node.getElement(), node.getNextRightNode()));
        }else {
            //绕过该节点
            node = (node.getNextLeftNode() != null ? node.getNextLeftNode() : node.getNextRightNode());
        }
        return node;
    }

    private void printTreeNode(TreeNode<T> node){
        if (node == null){
            return;
        }
        System.out.println(node);
        printTreeNode(node.getNextLeftNode());
        printTreeNode(node.getNextRightNode());
    }
}

测试代码:

/**
 * @program: 手搓代码
 * @description: 二叉查找树测试
 * @author: 郑畅道
 * @create: 2020-04-19 19:38
 **/
public class BSTreeTest {
    public static void main(String[] args) throws Exception {
        List<Integer> integers = new ArrayList<>();
        BinSearchTree<Integer> binSearchTree = new BinSearchTree<>();
        Random random = new Random();
        for (int i = 0; i < 10; i++) {
            Integer value = random.nextInt(100);
            binSearchTree.insert(value);
            integers.add(value);
        }
        System.out.println("插入的值;");
        for (int i = 0; i < integers.size(); i++) {
            System.out.print(integers.get(i) + "  ");
        }
        System.out.println();
        binSearchTree.printTree();
        System.out.println("*****************************************");
        System.out.println("最大值:" + binSearchTree.findMax());
        System.out.println("*****************************************");
        System.out.println("最小值:" + binSearchTree.findMin());
        System.out.println("*****************************************");
        int removeVal = integers.remove(integers.size()/2);
        System.out.println("移除" + removeVal + " 后:");
        binSearchTree.remove(removeVal);
        binSearchTree.printTree();
        System.out.println("*****************************************");
    }
}

输出:

插入的值;
17  81  87  26  37  20  24  84  32  92  
TreeNode{name='17', element=17, previousNode=null, nextLeftNode=null, nextRightNode=81}
TreeNode{name='81', element=81, previousNode=null, nextLeftNode=26, nextRightNode=87}
TreeNode{name='26', element=26, previousNode=null, nextLeftNode=20, nextRightNode=37}
TreeNode{name='20', element=20, previousNode=null, nextLeftNode=null, nextRightNode=24}
TreeNode{name='24', element=24, previousNode=null, nextLeftNode=null, nextRightNode=null}
TreeNode{name='37', element=37, previousNode=null, nextLeftNode=32, nextRightNode=null}
TreeNode{name='32', element=32, previousNode=null, nextLeftNode=null, nextRightNode=null}
TreeNode{name='87', element=87, previousNode=null, nextLeftNode=84, nextRightNode=92}
TreeNode{name='84', element=84, previousNode=null, nextLeftNode=null, nextRightNode=null}
TreeNode{name='92', element=92, previousNode=null, nextLeftNode=null, nextRightNode=null}
*****************************************
最大值:92
*****************************************
最小值:17
*****************************************
移除20 后:
TreeNode{name='17', element=17, previousNode=null, nextLeftNode=null, nextRightNode=81}
TreeNode{name='81', element=81, previousNode=null, nextLeftNode=26, nextRightNode=87}
TreeNode{name='26', element=26, previousNode=null, nextLeftNode=24, nextRightNode=37}
TreeNode{name='24', element=24, previousNode=null, nextLeftNode=null, nextRightNode=null}
TreeNode{name='37', element=37, previousNode=null, nextLeftNode=32, nextRightNode=null}
TreeNode{name='32', element=32, previousNode=null, nextLeftNode=null, nextRightNode=null}
TreeNode{name='87', element=87, previousNode=null, nextLeftNode=84, nextRightNode=92}
TreeNode{name='84', element=84, previousNode=null, nextLeftNode=null, nextRightNode=null}
TreeNode{name='92', element=92, previousNode=null, nextLeftNode=null, nextRightNode=null}
*****************************************
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值