数据结构——树

概念

树是一种重要的非线性数据结构,直观地看,它是数据元素(在树中称为结点)按分支关系组织起来的结构,很象自然界中的树那样。

为什么要使用树结构?

在说为什么之前,我们先来看一下数组和链表的结构有哪些优缺点

数组的优缺点

  • 优点:通过下标方式访问元素,速度非常的快。对于有序数组,还可以使用二分查找、斐波那契查找等算法来提高性能
  • 缺点:如果我们需要按顺序插入一个元素时,数组的缺点就很明显了,他会整体移动,效率非常低

有小伙伴可能就会说可以使用集合的形式来存储数据,我们来ArrayList来举例

//空参构造器
public ArrayList() {
  this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//ArrayList也是一个Object数组的形式
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

//当我们调用add方法时,他会调用grow方法来扩容数组,其底层原理其实是两个数组的copy
private void grow(int minCapacity) {
  // overflow-conscious code
  int oldCapacity = elementData.length;
  int newCapacity = oldCapacity + (oldCapacity >> 1);
  if (newCapacity - minCapacity < 0)
    newCapacity = minCapacity;
  if (newCapacity - MAX_ARRAY_SIZE > 0)
    newCapacity = hugeCapacity(minCapacity);
  // minCapacity is usually close to size, so this is a win:
  elementData = Arrays.copyOf(elementData, newCapacity);
}

链表的优缺点

链表的优缺点跟数组正好相反

  • 优点:在对元素的删除、插入等操作时,只需要将对应的节点指向改变,效率很高
  • 缺点:在查找等操作时,需要从头开始遍历链表,效率非常低

树存储方式分析

能提高数据存储、读取的效率,比如使用二叉排序树,既可以保证数据的检索速度,同时也可以保证数据的插入、删除、修改的速度

在这里插入图片描述

树的常用术语

可以查看这位博主的文章,写的非常通俗易懂 点击跳转

二叉树

概念

二叉树(Binary tree)是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重要。二叉树特点是每个节点最多只能有两棵子树,且有左右之分。

二叉树的遍历

二叉树的遍历分为前序遍历、中序遍历以及后序遍历,通过以下这张图来了解一下三种遍历

图解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fgB5JOdB-1649677865671)(/Users/yellowstar/Library/Application Support/typora-user-images/image-20220411152456244.png)]

代码实现
public class BinaryTreeDemo {
    public static void main(String[] args) {
        //创建一棵树
        BinaryTree binaryTree = new BinaryTree();

        HeroNode root = new HeroNode(1, "宋江");
        HeroNode node1 = new HeroNode(2, "吴用");
        HeroNode node2 = new HeroNode(3, "卢俊");
        HeroNode node3 = new HeroNode(4, "林冲");
        //暂时使用手动的方式创建树,之后会学习递归的方式
        root.setLeft(node1);
        root.setRight(node2);
        node2.setRight(node3);
        binaryTree.setRoot(root);

        System.out.println("前序遍历");
        binaryTree.preOrder(); // 1,2,3,4
        System.out.println("中序遍历");
        binaryTree.midOrder(); // 2,1,3,4
        System.out.println("后序遍历");
        binaryTree.postOrder(); // 2,4,3,1
    }
}

//二叉树
class BinaryTree{
    private HeroNode root;

    public BinaryTree() {
    }

    public BinaryTree(HeroNode root) {
        this.root = root;
    }

    public HeroNode getRoot() {
        return root;
    }

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

    //前序遍历
    public void preOrder(){
        if (this.root != null){
            this.root.preOrder();
        }else {
            System.out.println("二叉树为空,无法遍历");
        }
    }
    //中序遍历
    public void midOrder(){
        if (this.root != null){
            this.root.midOrder();
        }else {
            System.out.println("二叉树为空,无法遍历");
        }
    }
    //后序遍历
    public void postOrder(){
        if (this.root != null){
            this.root.postOrder();
        }else {
            System.out.println("二叉树为空,无法遍历");
        }
    }
}

//创建节点
class HeroNode{
    private int id;
    private String name;
    //左子节点,默认为空
    private HeroNode left;
    //右子节点,默认为空
    private HeroNode right;

    public HeroNode(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public HeroNode getLeft() {
        return left;
    }

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

    public HeroNode getRight() {
        return right;
    }

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

    @Override
    public String toString() {
        return "HeroNode{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }


    //前序遍历
    public void preOrder(){
        System.out.println(this);
        if (this.left != null){
            this.left.preOrder();
        }
        if (this.right != null){
            this.right.preOrder();
        }
    }

    //中序遍历
    public void midOrder(){
        if (this.left != null){
            this.left.midOrder();
        }
        System.out.println(this);
        if (this.right != null){
            this.right.midOrder();
        }
    }

    //后序遍历
    public void postOrder(){
        if (this.left != null){
            this.left.postOrder();
        }
        if (this.right != null){
            this.right.postOrder();
        }
        System.out.println(this);
    }
}

二叉树的查找

二叉树的查找也分为前序查找、中序查找以及后序查找

图解

在这里插入图片描述

代码实现

在HeroNode添加查找方法

 		//前序查找
    public HeroNode preOrderSearch(int no){
        System.out.println("前序遍历次数");
        //判断当前节点id是否为相同,如果相同,直接返回
        if (this.id == no){
            return this;
        }
        //如果左子节点不为空,继续递归前序查找
        HeroNode resNode = null;
        if (this.left != null){
            resNode = this.left.preOrderSearch(no);
        }
        //判断左子节点有没有找到,如果找到,直接返回
        if (resNode != null){
            return resNode;
        }
        //如果右子节点不为空,继续递归前序查找
        if (this.right != null){
            resNode = this.right.preOrderSearch(no);
        }
        return resNode;
    }

    //中序查找
    public HeroNode midOrderSearch(int no){
        //如果左子节点不为空,继续递归前序查找
        HeroNode resNode = null;
        if (this.left != null){
            resNode = this.left.midOrderSearch(no);
        }
        //判断左子节点有没有找到,如果找到,直接返回
        if (resNode != null){
            return resNode;
        }
        System.out.println("中序查找次数");
        //判断当前节点id是否为相同,如果相同,直接返回
        if (this.id == no){
            return this;
        }
        //如果右子节点不为空,继续递归前序查找
        if (this.right != null){
            resNode = this.right.midOrderSearch(no);
        }
        return resNode;
    }

    //后序查找
    public HeroNode postOrderSearch(int no){
        //如果左子节点不为空,继续递归前序查找
        HeroNode resNode = null;
        if (this.left != null){
            resNode = this.left.postOrderSearch(no);
        }
        //判断左子节点有没有找到,如果找到,直接返回
        if (resNode != null){
            return resNode;
        }
        //如果右子节点不为空,继续递归前序查找
        if (this.right != null){
            resNode = this.right.postOrderSearch(no);
        }
        //判断右子节点有没有找到,如果找到,直接返回
        if (resNode != null){
            return resNode;
        }
        System.out.println("后序查找次数");
        //判断当前节点id是否为相同,如果相同,直接返回
        if (this.id == no){
            return this;
        }
        return resNode;
    }

在二叉树中添加调用方法

    //前序查找
    public HeroNode preOrderSearch(int no){
        if (this.root != null){
            return this.root.preOrderSearch(no);
        }else {
            return null;
        }
    }

    //中序查找
    public HeroNode midOrderSearch(int no){
        if (this.root != null){
            return this.root.midOrderSearch(no);
        }else {
            return null;
        }
    }

    //后序查找
    public HeroNode postOrderSearch(int no){
        if (this.root != null){
            return this.root.postOrderSearch(no);
        }else {
            return null;
        }
    }

测试

    public static void main(String[] args) {
        //创建一棵树
        BinaryTree binaryTree = new BinaryTree();

        HeroNode root = new HeroNode(1, "宋江");
        HeroNode node1 = new HeroNode(2, "吴用");
        HeroNode node2 = new HeroNode(3, "卢俊");
        HeroNode node3 = new HeroNode(4, "林冲");
        HeroNode node4 = new HeroNode(5, "关胜");
        //暂时使用手动的方式创建树,之后会学习递归的方式
        root.setLeft(node1);
        root.setRight(node2);
        node2.setRight(node3);
        node2.setLeft(node4);
        binaryTree.setRoot(root);

        System.out.println("前序查找~~~");
        HeroNode resNode1 = binaryTree.preOrderSearch(5);
        if (resNode1 != null){
            System.out.println("找到英雄:" + resNode1);
        }else {
            System.out.println("不存在该英雄");
        }

        System.out.println("中序查找~~~");
        HeroNode resNode2 = binaryTree.midOrderSearch(5);
        if (resNode2 != null){
            System.out.println("找到英雄:" + resNode2);
        }else {
            System.out.println("不存在该英雄");
        }

        System.out.println("后序查找~~~");
        HeroNode resNode3 = binaryTree.postOrderSearch(5);
        if (resNode3 != null){
            System.out.println("找到英雄:" + resNode3);
        }else {
            System.out.println("不存在该英雄");
        }
    }

二叉树的删除

图解

在这里插入图片描述

代码实现

在HeroNode中添加以下代码

    /** 删除节点
     *  规定:如果当前节点是叶子节点,直接删除
     *  如果当前节点是子节点,删除该节点的子树
     */
    public void delHero(int no){
        //如果当前节点的左子节点不为空,并且满足删除条件,则将this.left置空,返回(结束递归)
        if (this.left != null && this.left.id == no){
            this.left = null;
            return;
        }
        //如果当前节点的右子节点不为空,并且满足删除条件,则将this.right置空,返回(结束递归)
        if (this.right != null && this.right.id == no){
            this.right = null;
            return;
        }
        //如果1,2两步都没有删除节点,那么需要向左子树递归删除
        if (this.left != null){
            this.left.delHero(no);
        }
        //如果3步也没有删除节点,那么需要向右子树递归删除
        if (this.right != null){
            this.right.delHero(no);
        }
    }

对于root的操作,需要放在二叉树中进行

    //删除
    public void delHero(int no){
        //判断root节点是否为空,如果为空则不需要删除,判断root节点是否有子节点,如果没有子节点,并且满足条件,则将root置空
        if (this.root != null){
            if (this.root.getLeft() != null || this.root.getRight() != null){
                this.root.delHero(no);
            }else {
                if (this.root.getId() == no){
                    root = null;
                }
            }
        }else {
            System.out.println("二叉树为空");
        }
    }

测试

public class BinaryTreeDemo {
    public static void main(String[] args) {
        //创建一棵树
        BinaryTree binaryTree = new BinaryTree();

        HeroNode root = new HeroNode(1, "宋江");
        HeroNode node1 = new HeroNode(2, "吴用");
        HeroNode node2 = new HeroNode(3, "卢俊");
        HeroNode node3 = new HeroNode(4, "林冲");
        HeroNode node4 = new HeroNode(5, "关胜");
        //暂时使用手动的方式创建树,之后会学习递归的方式
        root.setLeft(node1);
        root.setRight(node2);
        node2.setRight(node3);
        node2.setLeft(node4);
        binaryTree.setRoot(root);

        System.out.println("删除前");
        binaryTree.preOrder();
        binaryTree.delHero(3);
        System.out.println("删除后");
        binaryTree.preOrder();
    }
}

顺序存储二叉树

概念

从数据存储的角度来看,数组存储方式和树的存储方式可以相互转换,即数组可以转化为树,树也可以转化成数组

在这里插入图片描述

代码实现

我们还是来实现顺序存储二叉树的前序遍历、中序遍历以及后序遍历

public class ArrayBinaryTreeDemo {
    public static void main(String[] args) {
        int[] arr = {1,2,3,4,5,6,7};
        ArrayBinaryTree arrayBinaryTree = new ArrayBinaryTree(arr);
        arrayBinaryTree.postOrder();
    }
}

class ArrayBinaryTree{
    public int[] arr;

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

    public void preOrder(){
        preOrder(0);
    }

    /**
     * 前序遍历 遍历结果因为
     * @param index 表示数组的下标
     */
    public void preOrder(int index){
        if (arr == null || arr.length == 0){
            System.out.println("数组为空");
            return;
        }
        //输出当前节点
        System.out.println(arr[index]);
        //递归向左遍历
        if (arr.length > (2 * index) + 1){
            preOrder((2 * index) + 1);
        }
        //递归向右遍历
        if (arr.length > (2 * index) + 2){
            preOrder((2 * index) + 2);
        }
    }

    public void midOrder(){
        midOrder(0);
    }

    public void midOrder(int index){
        if (arr == null || arr.length == 0){
            System.out.println("数组为空");
            return;
        }
        //递归向左遍历
        if (arr.length > (2 * index) + 1){
            midOrder((2 * index) + 1);
        }
        //输出当前节点
        System.out.println(arr[index]);
        //递归向右遍历
        if (arr.length > (2 * index) + 2){
            midOrder((2 * index) + 2);
        }
    }

    public void postOrder(){
        postOrder(0);
    }

    public void postOrder(int index){
        if (arr == null || arr.length == 0){
            System.out.println("数组为空");
            return;
        }
        //递归向左遍历
        if (arr.length > (2 * index) + 1){
            postOrder((2 * index) + 1);
        }
        //递归向右遍历
        if (arr.length > (2 * index) + 2){
            postOrder((2 * index) + 2);
        }
        //输出当前节点
        System.out.println(arr[index]);
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
在Keil MDK中使用可以通过定义节点结构体和使用指针来实现。下面是一个简单的示例代码: ```c #include <stdio.h> #include <stdlib.h> // 定义节点结构体 typedef struct node { int data; struct node *left; struct node *right; } TreeNode; // 创建节点 TreeNode* create_node(int data) { TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode)); node->data = data; node->left = NULL; node->right = NULL; return node; } // 插入节点 TreeNode* insert_node(TreeNode* root, int data) { if (root == NULL) { return create_node(data); } if (data < root->data) { root->left = insert_node(root->left, data); } else { root->right = insert_node(root->right, data); } return root; } // 计算的深度 int tree_depth(TreeNode* root) { if (root == NULL) { return 0; } int left_depth = tree_depth(root->left); int right_depth = tree_depth(root->right); return (left_depth > right_depth ? left_depth : right_depth) + 1; } int main() { // 创建根节点 TreeNode* root = create_node(5); // 插入节点 root = insert_node(root, 3); root = insert_node(root, 7); root = insert_node(root, 1); root = insert_node(root, 4); root = insert_node(root, 6); root = insert_node(root, 8); // 计算的深度并输出 int depth = tree_depth(root); printf("Tree Depth: %d\n", depth); return 0; } ``` 在这个示例代码中,我们首先定义了一个节点结构体,包含了节点数据和左右子节点指针。然后我们定义了创建节点和插入节点的函数,使用递归的方式实现。最后我们定义了计算深度的函数,并在main函数中使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值