数据结构(七)——树结构(Tree) 之二叉树的常用操作

这篇博客详细介绍了二叉树的链式存储、顺序存储以及线索二叉树的操作。链式存储中涉及先序、中序、后序和层级遍历,结点查找与删除;顺序存储主要针对完全二叉树,阐述了遍历方法;线索二叉树则通过线索化实现更高效的遍历和查找父节点。
摘要由CSDN通过智能技术生成

一.链式存储的二叉树的操作

1.遍历二叉树

  • 先序遍历:根-->左-->右
  • 中序遍历:左-->根-->右
  • 后序遍历:左-->右-->根
  • 层级遍历:逐层从左到输出

2.二叉树结点的查找 

结点的查找也可以分为先序查找,中序查找和后序查找。方式和遍历方式相似。

3.删除二叉树结点

  • 二叉树抽象类代码
package cn.kimtian.tree;

/**
 * 链式存储的二叉树
 *
 * @author kimtian
 */
public class BinaryTree {
    /**
     * 根结点
     */
    TreeNode root;

    /**
     * 设置根结点
     *
     * @param root 根结点
     */
    public void setRoot(TreeNode root) {
        this.root = root;
    }

    /**
     * 获取根结点
     *
     * @return TreeNode 根结点
     */
    public TreeNode getRoot() {
        return root;
    }

    /**
     * 先序遍历
     */
    public void frontShow() {
        if (root != null) {
            root.frontShow();
        }
    }

    /**
     * 中序遍历
     */
    public void middleShow() {
        if (root != null) {
            root.middleShow();
        }
    }

    /**
     * 后序遍历
     */
    public void behindShow() {
        if (root != null) {
            root.behindShow();
        }
    }

    /**
     * 查找二叉树的结点--先序查找
     *
     * @param i 查找的结点的值
     */
    public TreeNode frontSearch(int i) {
        return root.frontSearch(i);
    }

    /**
     * 查找二叉树的结点--中序查找
     *
     * @param i 查找的结点的值
     */
    public TreeNode middleSearch(int i) {
        return root.middleSearch(i);
    }

    /**
     * 查找二叉树的结点--后序查找
     *
     * @param i 查找的结点的值
     */
    public TreeNode behindSearch(int i) {
        return root.behindSearch(i);
    }

    /**
     * 删除二叉树的结点/子树
     *
     * @param i 查找的结点的值
     */
    public void deleteTreeNode(int i) {
        if (root.value == i) {
            root = null;
        } else {
            root.deleteTreeNode(i);
        }
    }
}
  • 二叉树的结点
package cn.kimtian.tree;

/**
 * 树里面的结点
 *
 * @author kimtian
 */
public class TreeNode {
    /**
     * 树里面的值,结点的权
     */
    int value;
    /**
     * 左儿子
     */
    TreeNode leftNode;
    /**
     * 右儿子
     */
    TreeNode rightNode;

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

    public void setLeftNode(TreeNode leftNode) {
        this.leftNode = leftNode;
    }

    public void setRightNode(TreeNode rightNode) {
        this.rightNode = rightNode;
    }

    /**
     * 先序遍历--递归思想
     */
    public void frontShow() {
        //先遍历当前结点的内容
        System.out.print(value + " ");
        //左结点
        if (leftNode != null) {
            leftNode.frontShow();
        }
        //右结点
        if (rightNode != null) {
            rightNode.frontShow();
        }
    }

    /**
     * 中序遍历
     */
    public void middleShow() {
        //左结点
        if (leftNode != null) {
            leftNode.middleShow();
        }
        System.out.print(value + " ");
        //右结点
        if (rightNode != null) {
            rightNode.middleShow();
        }
    }

    /**
     * 后序遍历
     */
    public void behindShow() {
        //左结点
        if (leftNode != null) {
            leftNode.behindShow();
        }
        //右结点
        if (rightNode != null) {
            rightNode.behindShow();
        }
        System.out.print(value + " ");
    }
    /**
     * 层级遍历
     */
    public void levelShow(TreeNode treeNode) {
        //如果根结点为空,直接返回
        if (treeNode == null) {
            return;
        } else {
            //新建一个队列,存放树的结点
            Queue<TreeNode> q = new ArrayDeque<TreeNode>();
            //将根结点加入队列中
            q.add(treeNode);
            TreeNode cur;
            //如果队列不为空,循环输出
            while (!q.isEmpty()) {
                // 获取当前队列的队头
                cur = q.peek();
                //输出队头的值
                System.out.print(cur.value + " ");
                //将队头结点的左结点入队
                if (cur.leftNode != null) {
                    q.add(cur.leftNode);
                }
                //将队头结点的右结点入队
                if (cur.rightNode != null) {
                    q.add(cur.rightNode);
                }
                //将队头出队列
                q.poll();
            }
        }
    }

    /**
     * 先序查找
     *
     * @param i 查找的结点的值
     */
    public TreeNode frontSearch(int i) {
        TreeNode target = null;
        //对比当前结点的值
        if (value == i) {
            return this;
        }
        //当前结点的值不是要查找的结点
        else {
            //查找左儿子
            if (leftNode != null) {
                target = leftNode.frontSearch(i);
            }
            if (target != null) {
                return target;
            }
            //查找右儿子
            if (rightNode != null) {
                target = rightNode.frontSearch(i);
            }
            if (target != null) {
                return target;
            }
        }
        return target;
    }

    /**
     * 中序查找
     *
     * @param i 查找的结点的值
     */
    public TreeNode middleSearch(int i) {
        TreeNode target = null;
        //对比左儿子的值
        if (leftNode != null) {
            target = leftNode.middleSearch(i);
        }
        if (target != null) {
            return target;
        }
        //对比当前结点的值
        if (value == i) {
            return this;
        }
        //查找右儿子
        if (rightNode != null) {
            target = rightNode.middleSearch(i);
        }
        if (target != null) {
            return target;
        }

        return target;
    }

    /**
     * 后序查找
     *
     * @param i 查找的结点的值
     */
    public TreeNode behindSearch(int i) {
        TreeNode target = null;
        //对比左儿子的值
        if (leftNode != null) {
            target = leftNode.behindSearch(i);
        }
        if (target != null) {
            return target;
        }
        //查找右儿子
        if (rightNode != null) {
            target = rightNode.behindSearch(i);
        }
        if (target != null) {
            return target;
        }

        //对比当前结点的值
        if (value == i) {
            return this;
        }
        return target;
    }

    /**
     * 删除二叉树的结点/子树
     *
     * @param i 要删除的结点的值
     */
    public void deleteTreeNode(int i) {
        TreeNode parent = this;
        //判断左儿子
        if (parent.leftNode != null && parent.leftNode.value == i) {
            parent.leftNode = null;
            return;
        }
        //判断右边儿子
        if (parent.rightNode != null && parent.rightNode.value == i) {
            parent.rightNode = null;
            return;
        }
        //递归检查并删除左儿子
        parent = leftNode;
        if (parent != null) {
            parent.deleteTreeNode(i);
        }
        //递归检查并删除右儿子
        parent = rightNode;
        if (parent != null) {
            parent.deleteTreeNode(i);
        }

    }
}

(3)测试类。

package cn.kimtian.tree;

/**
 * 测试链式存储的二叉树
 *
 * @author kimtian
 */
public class TestBinaryTree {
    public static void main(String[] args) {
        //创建一棵树
        BinaryTree binaryTree = new BinaryTree();
        //创建一个根结点
        TreeNode root = new TreeNode(1);
        //将根结点赋给树
        binaryTree.setRoot(root);
        //创建一个子左结点
        TreeNode leftTwo = new TreeNode(2);
        //创建一个子右结点
        TreeNode rightTwo = new TreeNode(3);
        //给根的左结点赋值
        root.setLeftNode(leftTwo);
        //给根的右结点赋值
        root.setRightNode(rightTwo);
        //第三层结点
        TreeNode llThree = new TreeNode(4);
        TreeNode lrThree = new TreeNode(5);
        TreeNode rlThree = new TreeNode(6);
        TreeNode rrThree = new TreeNode(7);
        leftTwo.setLeftNode(llThree);
        leftTwo.setRightNode(lrThree);
        rightTwo.setLeftNode(rlThree);
        rightTwo.setRightNode(rrThree);
        //先序遍历
        binaryTree.frontShow();
        System.out.println();
        //中序遍历
        binaryTree.middleShow();
        System.out.println();
        //后序遍历
        binaryTree.behindShow();
        System.out.println();
        System.out.println("====================");
        //先序查找
        TreeNode result = binaryTree.frontSearch(5);
        System.out.println(result);
        System.out.println(result == lrThree);
        TreeNode result1 = binaryTree.frontSearch(10);
        System.out.println(result1);

        System.out.println();
        System.out.println("====================");
        //中序查找
        TreeNode result2 = binaryTree.middleSearch(5);
        System.out.println(result2);
        System.out.println(result2 == lrThree);
        TreeNode result3 = binaryTree.middleSearch(10);
        System.out.println(result3);
        System.out.println();
        System.out.println("====================");

        //后序查找
        TreeNode result4 = binaryTree.behindSearch(5);
        System.out.println(result4);
        System.out.println(result4 == lrThree);
        TreeNode result5 = binaryTree.behindSearch(10);
        System.out.println(result5);
        //先序遍历
        binaryTree.frontShow();
        System.out.println();
        //删除一个子树
        binaryTree.deleteTreeNode(1);
        //先序遍历
        binaryTree.frontShow();
        System.out.println();

    }
}

二.顺序存储的二叉树的操作

顺序存储的二叉树通常情况只考虑完全二叉树。

  • 第n个元素的左子结点是2*n+1
  • 第n个元素的右子结点是2*n+2
  • 第n个元素的父结点是(n-1)/2

1.遍历二叉树

  • 先序遍历:根-->左-->右
  • 中序遍历:左-->根-->右
  • 后序遍历:左-->右-->根
package cn.kimtian.tree.arraybinarytree;

/**
 * 顺序存储的二叉树
 *
 * @author kimtian
 */
public class ArrayBinaryTree {
    /**
     * 一个存储树的数组
     */
    int[] data;

    public ArrayBinaryTree(int[] data) {
        this.data = data;
    }

    /**
     * 前序遍历
     *
     * @param index 从哪个结点开始遍历
     */
    public void frontShow(int index) {
        if (data == null || data.length == 0) {
            return;
        }
        //先遍历当前结点的内容
        System.out.print(data[index] + " ");
        //处理左子结点
        if (2 * index + 1 < data.length) {
            frontShow(2 * index + 1);
        }
        //处理右子结点
        if (2 * index + 2 < data.length) {
            frontShow(2 * index + 2);
        }
    }

    /**
     * 前序遍历 重载方法
     */
    public void frontShow() {
        frontShow(0);
    }

    /**
     * 中序遍历
     *
     * @param index 从哪个结点开始遍历
     */
    public void middleShow(int index) {
        if (data == null || data.length == 0) {
            return;
        }
        //处理左子结点
        if (2 * index + 1 < data.length) {
            middleShow(2 * index + 1);
        }
        //先遍历当前结点的内容
        System.out.print(data[index] + " ");
        //处理右子结点
        if (2 * index + 2 < data.length) {
            middleShow(2 * index + 2);
        }
    }

    /**
     * 中序遍历
     */
    public void middleShow() {
        middleShow(0);
    }

    /**
     * 后序遍历
     *
     * @param index 从哪个结点开始遍历
     */
    public void behindShow(int index) {
        if (data == null || data.length == 0) {
            return;
        }
        //处理左子结点
        if (2 * index + 1 < data.length) {
            behindShow(2 * index + 1);
        }
        //处理右子结点
        if (2 * index + 2 < data.length) {
            behindShow(2 * index + 2);
        }
        //先遍历当前结点的内容
        System.out.print(data[index] + " ");
    }

    /**
     * 后序遍历
     */
    public void behindShow() {
        behindShow(0);
    }
}

  三.线索二叉树

线索二叉树能线性地遍历二叉树,从而比递归的中序遍历更快。使用线索二叉树也能够方便的找到一个节点的父节点,这比显式地使用父亲节点指针或者栈效率更高。

先序线索化二叉树:如果没有左结点,则指向先序遍历的前一个结点。如果没有右结点,则指向先序遍历的后一个结点。

中序线索化二叉树:如果没有左结点,则指向中序遍历的前一个结点。如果没有右结点,则指向中序遍历的后一个结点。

后序线索化二叉树:如果没有左结点,则指向后序遍历的前一个结点。如果没有右结点,则指向后序遍历的后一个结点。

线索化二叉树时,一个结点的前一个节点,叫前驱结点。一个结点的后一个结点,叫后继结点。

1.线索二叉树的实现

可以找一个标记,来标记二叉树的左右结点,究竟指定的是左、右子树还是前驱结点和后继结点。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值