树的常见知识点及代码整理

本文详细介绍了树的基本概念,如结点层次、深度和度,以及二叉树的特性和存储结构。重点讲解了二叉树的遍历方法,包括前序、中序和后序遍历,以及层次遍历,并提供了相应的递归和栈实现代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、什么是树

树是n(n>=0)个结点的有限集合,它:
(1)或者是一棵空树(n=0),空树不包括任何结点;
(2)或者是一棵非空树(n>0),此时有且仅有一个特定的根结点;当n>1时,其余结点可分为m个互不相交的有限集T1,…,Tm,其中每一个本身又是一棵树,称为根的子树。

注意:有且仅有一个根结点!

(一)基本概念

1.结点的层次:从根开始定义,根节点的层次为0,其子树的根节点层次为1,依次递加。
2.树的深度:树中结点的最大层次树为树的深度。
3.结点的度:结点拥有的子树的数目(即子结点的个数)称为结点的度。度为0的结点为叶子结点。
性质1:树中的结点数=树的边数+1=所有结点度数之和+1
性质2:树中的任意两点之间都存在唯一的路径。
性质3:树中所有结点最大度数为m的有序树称为m叉树

二、二叉树

(一)基本性质

每个结点的度数不超过2的有序树称为二叉树
性质1:二叉树的第i层最多有2^i个结点
性质2:高度为h的二叉树最多有2^(h+1) -1个结点
性质3:如果二叉树的终端结点数为n0,度为2的结点数为n2,则n0=n2+1

满二叉树:每层的结点数都达到最大(然而这个问题是有歧义的,有些人认为,所有节点要么是叶子结点,要么有两个子节点,则这个树是满二叉树)
完全二叉树:相较于满二叉树而言,在最下层从最右侧起有部分结点不满

性质4:有n个结点的完全二叉树的高度为[log n]
性质5:含有n>=1个结点的二叉树,高度最大为n-1,最小为[log n]
性质6:若根节点编号为1,从左到右、从上到下的顺序依次编号,则结点i的父结点为[i/2],左子节点为2i,右子结点为2i+1。

(二)存储结构

方法一:顺序存储
将元素逐一存放到数组中,根据性质6来在O(1)的时间内直接找到父结点、左右结点。
特点:适合满二叉树或完全二叉树,对于普通二叉树而言需要用虚结点补成完全二叉树后存储,易造成空间浪费

方法二:链式存储
通过链表来存储树中结点,结点的定义往往有两种:
(1)Data+lChild+rChild
(2)Data+lChild+rChild+parent

(三)常见的遍历方法

前序遍历:
(1)若二叉树为空,则进行空操作
(2)访问根结点
(3)访问左子结点
(4)访问右子结点
后序遍历:
(1)若二叉树为空,则进行空操作
(2)访问左子结点
(3)访问右子结点
(4)访问根结点
中序遍历:
(1)若二叉树为空,则进行空操作
(2)访问左子结点
(3)访问根结点
(4)访问右子结点

三种遍历的常用实现方法:
(1)递归
(2)栈

(1)递归实现代码
前序遍历

void helpFun(TreeNode root, List<Integer> list){
        if(root ==null ){//如果root为空结点
            return ;
        }else{
            list.add(root.val);
            helpFun(root.left,list);
            helpFun(root.right,list);
        }
    }
    public List<Integer> preorderTraversal(TreeNode root) {
    //前序遍历代码,将结果存储在list中
        List<Integer> list = new ArrayList<>();
        if(root == null){
            return list;//root为空结点
        }else{
            helpFun(root, list);
            return list;
        }
    }

(2)栈
前序遍历:

public List<Integer> preorderTraversal(TreeNode root){
        //前序遍历:根-左-右(先打印再访问左右子树)
        List<Integer> res = new LinkedList<>();
        TreeNode p = root;
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while(!stack.isEmpty() || p != null){
            if(p != null){
            	stack.push(p);
                res.add(p.val);
                p = p.left;
            }else{
                p=stack.pop();
                p=p.right;
            }
        }
        return res;
    }

中序遍历:

public List<Integer> inorderTraversal(TreeNode root) {
        //中序遍历:左-根-右
        List<Integer> list = new LinkedList<>();
        Stack<TreeNode> stack = new Stack<>();
        TreeNode p = root;
        while(p != null || !stack.empty()){//当p为空且栈为空(没有需要检查的点了),就结束
            if(p != null){//非空先一直向左遍历
                stack.push(p);//将经过的结点都入栈,为的是访问右侧的结点
                p = p.left;
            }else{
                TreeNode t = stack.pop();
                list.add(t.val);
                p=t;
                // stack.push(p);
                p=p.right;
            }

        }
        return list;
    }

主要思想:
(1)无脑入栈
(2)遇到null就弹出上一个
(3)弹出时访问右结点

前序遍历于中序遍历的基本思想基本相同,但是由于后序遍历在出栈后,无法保存根节点的,方法有所不同。
但是换个思路,左-右-根反过来就是根-右-左,所以只需要在最后将得到的list反过来就行。

public List<Integer> postorderTraversal(TreeNode root) {
        //左、右、根   1 2 3
         List<Integer> list = new LinkedList<>();
        Stack<TreeNode> stack = new Stack<>();
        TreeNode p = root;
        while(!stack.isEmpty() || p != null){
            if(p != null){
                list.add(p.val);
                stack.push(p);
                p = p.right;
            }else{
                p = stack.pop();
                p = p.left;
            }
        }
        Collections.reverse(list);
        return list;

    }

(四)层次遍历(用的比较少)

依次从上到下、从左到右访问结点
方法:通过队列来存储(先到先出)
在这里插入图片描述

public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> res = new LinkedList<>();
        if(root == null){
            return res;
        }
        
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()){
            int size = queue.size();
            List<Integer> help = new LinkedList<>();
            for(int i = 0; i < size; i++){
                TreeNode tn = queue.poll();
                help.add(tn.val);
                // System.out.println(tn.val);
                if(tn.left != null){
                    queue.offer(tn.left);
                }
                if(tn.right != null){
                    queue.offer(tn.right);
                }
                
            }
            // System.out.println(queue.size());
            res.add(help);
            
        }
        return res;

    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值