IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 【java数据结构】就是你爱的那颗二叉树 -> 正文阅读

[数据结构与算法]【java数据结构】就是你爱的那颗二叉树

目录

树的定义

树的特点

树的结点分类

二叉树

三种特殊的二叉树

?二叉树的性质(非常重要)

?二叉树的存储

二叉树的基本操作

二叉树的遍历

二叉树的前序遍历

二叉树的中序遍历

二叉树的后序遍历

获取树中节点的个数

获取叶子节点的个数

获取第K层节点的个数

获取二叉树的高度

检测值为value的元素是否存在

层序遍历

判断一棵树是不是完全二叉树


树的定义

树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。

树的特点

  • 有一个特殊的结点,称为根结点根结点没有前驱结点(通常叫它root节点)
  • 除根结点外,其余结点被分成M(M > 0)个互不相交的集合T1、T2、......、Tm,其中每一个集合 Ti (1 <= i<= m) 又是一棵与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继。(根节点:唯一,有且仅有一个根节点,中间结点:一个双亲可以有多个孩子节点,叶子结点:叶子结点度为0,无孩子可以有多个)。
  • 由于一棵树又可以划分为多个子树,所以二叉树的操作大部分都是由递归来实现的
  • 对于节点数为n,n>0时根节点是唯一的,有且仅有一个
  • 一棵树可以有多个节点,但是节点之间是不能相交的

树的结点分类

结点的度:一个结点含有子树的个数称为该结点的度;对于1节点它的度就是2,1节点共有两个子树
树的度:一棵树中,所有结点度的最大值称为树的度; 这颗树,一个结点最多子树为2个,所以树的度为2
叶子结点或终端结点:度为0的结点称为叶结点; 叶子结点在树的最后没有子树的节点,例如6,7,8,9,10节点.
双亲结点或父结点:若一个结点含有子结点这个结点称为其子结点的父结点;比如2的父亲就是1
孩子结点或子结点:一个结点含有的子树的根结点称为该结点的子结点; 对于2节点来说它的父亲是1,对于1节点来说它的孩子是2和3节点
根结点:一棵树中,没有双亲结点的结点;一般来说除了空树外,其他树有且仅有一个根节点root
结点的层次:从根开始定义起,根为第1层,根的子结点为第2层,以此类推
树的深度:相对于节点来说的,比如1节点深度为1。?2,3节点深度为2。4,5,6,7节点深度为3

树的高度:树中结点的最大层次;树的最大深度就是这一颗树的高度

树的祖先:从根到该结点所经分支上的所有结点,对于8节点来说,1,2,4节点都是8的公共祖先。

二叉树

二叉树的特点

  • 对于二叉树的每一个节点来说,它的度也就是子树的个数都是小于等于2的
  • 二叉树有左右子树区分。一定要区分它的左树和右树。

二叉树的五种形态

  • 空树(没有结点的树)
  • 只有左树没有右树
  • 只有右树没有左树
  • 既有左树又有右树
  • 只有一个根节点

三种特殊的二叉树

  • 斜树:所有树只有左子树的树或者只有右子树的树叫做斜树,只有左子树的树叫做左斜树,只有右子树的树叫做右斜树

  • 满二叉树:?一棵二叉树,如果每层的结点数都达到最大值,则这棵二叉树就是满二叉树。也就是说,如果一棵二叉树的层数为K,且结点总数是 ,则它就是满二叉树

  • ?完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从0至n-1的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。完全二叉树从左往右开始编号,如果不为空是一颗完全二叉树,完全二叉树可以有度为1的情况,如果从左往右开始编号为空,那就证明不是一颗完全二叉树

?二叉树的性质(非常重要)

  • 若规定根结点的层数为1,则一棵非空二叉树的第i层上最多有 2^i-1(i>0)个结点

  • ?若规定只有根结点的二叉树的深度为1,则深度为K的二叉树的最大结点数是2^k-1 (k>=0)

  • ?对任何一棵二叉树, 如果其叶结点个数为 n0, 度为2的非叶结点个数为 n2,则有n0=n2+1

  • ?具有n个结点的完全二叉树的深度k为 log2(n+1)上取整

  • ?. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的顺序对所有节点从0开始编号,则对于序号为i的结点有:
    若i>0,双亲序号:(i-1)/2;i=0,i为根结点编号,无双亲结点
    若2i+1<n,左孩子序号:2i+1,否则无左孩子
    若2i+2<n,右孩子序号:2i+2,否则无右孩子

给定子节点下标求父节点下标:(i-1)/2

给定父节点下标求子节点下标:左孩子2i+1 右孩子:2i+2

这个结论在堆的学习经常会用到所以务必要记住

一些习题:

?

?二叉树的存储

我们在平时刷题时和平常学的一些二叉树基础都是利用链式存储,也就是说一个节点有节点值和它的左孩子和右孩子。

public class TreeNode {
    int val; // 数据域
    TreeNode left; // 左孩子的引用,常常代表左孩子为根的整棵左子树
    TreeNode right; // 右孩子的引用,常常代表右孩子为根的整棵右子树
}

二叉树的基本操作

首先我们创建一颗二叉树

 static class TreeNode{
        public int val;
        public TreeNode left;
        public TreeNode right;
        public TreeNode(int val){
            this.val = val;
        }
    }

    //根节点
    public TreeNode root;

    public TreeNode createTree(){
        TreeNode node1 = new TreeNode(1);
        TreeNode node2 = new TreeNode(2);
        TreeNode node3 = new TreeNode(3);
        TreeNode node4 = new TreeNode(4);
        TreeNode node5 = new TreeNode(5);
        TreeNode node6 = new TreeNode(6);
        TreeNode node7 = new TreeNode(7);

        node1.left = node2;
        node1.right = node4;
        node2.left = node3;
        node2.right = node7;
        node4.left = node5;
        node4.right= node6;
        root =node1;
        return root;

    }

二叉树的遍历

二叉树的前序遍历

二叉树的前序遍历:先访问根在访问左子树在访问右子树? 根-左子树-右子树

?对于这样一颗二叉树它的先序遍历时 A - B - D - E -H - C - F - G

?我们仔细分析一下这个前序遍历:

对于A这棵树来说要先访问根也就是A,然后去访问A的左子树,而A的左子树又是一棵树,先访问根B,在访问B这棵树的左子树D,对于D这棵树而言又是一棵树,所以先访问根D,再去访问它的左子树,发现D的左子树为null,再去访问D的右子树为null,所以D这棵树访问完了,也证明B的左树访问完了,然后接着访问B的右树E,而E又是一棵树,先访问根节点E,在去访问它的左子树,发现它的左子树为null,再去访问它的右子树H,所以E这棵树访问完了,也说明B的右树访问完了,也说明A的左树访问完了,然后再去访问A的右树C,而C又是一棵树,先访问根C,在去访问她的左树F,而F又是一棵树,访问它的左树为空,右树为空,F这棵树访问完了,所以C的左树访问完,同理再去访问G,好最后返回到A证明A这棵树走完了,这才是真正的结束一个二叉树的前序遍历。


根据我上面的分析,每一个树的逻辑都是一样的,都是先访问根节点,在访问左子树,在访问右子树,在访问左子树或者右子树,又是同样的道理,所以每一棵树可以用相同的操作,在使用递归操作重复调用,最终这棵树就遍历完成。

代码实现:

  • 前序遍历-->递归版本无list
 // 前序遍历-->递归版本无list
    void preOrder(TreeNode root){
        if(root==null) return;
        System.out.print(root.val+" ");
        preOrder(root.left);
        preOrder(root.right);
    }
  • 使用list遍历思路去存储-->前序遍历
 //使用list遍历思路去存储-->前序遍历
    List<Integer> list = new ArrayList<>();
    public List<Integer> preorderTraversal(TreeNode root) {
        if(root==null) return list;
        list.add(root.val);
        preorderTraversal(root.left);
        preorderTraversal(root.right);
        return list;
    }
  • 使用list子问题思路进行前序遍历

//使用list子问题思路进行前序遍历
    public List<Integer> preorderTraversal1(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        if(root==null) return list;
        list.add(root.val);
        List<Integer> leftTree = preorderTraversal(root.left);
        if(leftTree!=null){
            list.addAll(leftTree);
        }
        List<Integer> rightTree = preorderTraversal(root.right);
        if(rightTree!=null){
            list.addAll(rightTree);
        }
        return list;
    }
  • 前序遍历-->非递归版本

//前序遍历-->非递归版本
    public List<Integer> preorderTraversal2(TreeNode root) {
        List<Integer> ret = new ArrayList<>();
        if(root==null) return ret;
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        while(cur!=null||(!stack.isEmpty())){
            while(cur!=null){
                stack.push(cur);
                ret.add(cur.val);
                cur = cur.left;
            }
            TreeNode top = stack.pop();
            if(top.right!=null){
                cur = top.right;
            }
        }
        return ret;
    }

二叉树的中序遍历

根据二叉树的前序遍历,中序遍历也是一样的道理,中序遍历是先访问左子树在访问根节点在访问右子树。

对于上面那棵树而言中序遍历为:D - B - E - H - A - F - C - G

代码实现:

// 中序遍历
    void inOrder(TreeNode root){
        if(root==null) return;
        inOrder(root.left);
        System.out.print(root.val+" ");
        inOrder(root.right);
    }
  • 使用list-->子问题进行中序遍历
//使用list-->子问题进行中序遍历
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> ret = new ArrayList<>();
        if(root==null) return ret;
        List<Integer> leftTree = inorderTraversal(root.left);
        if(leftTree!=null){
            ret.addAll(leftTree);
        }
        ret.add(root.val);
        List<Integer> rightTree = inorderTraversal(root.right);
        if(rightTree!=null){
            ret.addAll(rightTree);
        }
        return ret;
    }
  • 使用list进行非递归实现--->中序遍历
//使用list进行非递归实现--->中序遍历
    public List<Integer> inorderTraversalNor(TreeNode root) {
        List<Integer> ret = new ArrayList<>();
        if(root==null) return ret;
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        while(cur!=null||!stack.isEmpty()){
            while (cur != null) {
                stack.push(cur);
                cur = cur.left;
            }
            TreeNode top = stack.pop();
            ret.add(top.val);
            if (top.right != null) {
                cur = top.right;
            }
        }
        return ret;
    }

二叉树的后序遍历

根据二叉树的前序遍历,中序遍历,后序遍历也是一样的道理,后序遍历是先访问左子树在访问右子树在访问根节点。

对于上面那棵树而言后序遍历为:D - H - E - B - F - G - C - A

代码实现:

    // 后序遍历
    void postOrder(TreeNode root){
        if(root==null) return;
        postOrder(root.left);
        postOrder(root.right);
        System.out.print(root.val+" ");
    }
  • 使用非递归来实现二叉树的后序遍历
//使用非递归来实现二叉树的后序遍历
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> ret = new ArrayList<>();
        if(root==null) return ret;
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        TreeNode prev = null;
        while(cur!=null||(!stack.isEmpty())){
            while(cur!=null){
                stack.push(cur);
                cur = cur.left;
            }
            TreeNode top = stack.peek();
            if(top.right==null||top.right==prev){
                stack.pop();
                ret.add(top.val);
                prev = top;
            }else {
                cur = top.right;
            }
        }
        return ret;
    }
  • 最终我们还可以得出一个结论要想有想构建一颗二叉树,要么给出二叉树的递归序(也就是包括null节点也给出),否则要想得到一颗二叉树,必须知道中序遍历,要么前序遍历和中序遍历,要么后序遍历和中序遍历,如果没有中序遍历,就只能根据前序或者后序确定根节点,不能确定左子树和右子树。

获取树中节点的个数

对于获取节点个数:普通遍历思路,就是遍历二叉树中的每一个节点然后计数器++,可以使用前序中序后序都可以。

这里的代码实现给出的是子问题的思路,我们可以思考一下,对于一颗二叉树来说,就是根节点,左子树和右子树,那一颗二叉树节点的个数是不是等于左子树节点的个数+右子树节点的个数+根节点本身(也就是+1)。

int size(TreeNode root){
    if(root==null) return 0;
    int left = size(root.left);//算出左子树的节点个数
    int right = size(root.right);//算出右子树的节点个数
    return left+right+1;
}

获取叶子节点的个数

遍历思路:叶子结点前面我也讲过就是度为0的节点,也就是当我们遍历树的左子树为空并且它的右子树为空,那他就是我们的叶子结点使用计数器++就可以。

子问题思路:这里我给出的也是子问题思路,一颗二叉树我们只要求出它的左子树叶子结点个数+右子树叶子结点个数就是我们这棵二叉树的叶子结点个数了。

int getLeafNodeCount(TreeNode root){
        if(root==null) return 0;
        if(root.left==null&&root.right==null){
            return 1;
        }
        int left = getLeafNodeCount(root.left);
        int right = getLeafNodeCount(root.right);
        return left+right;
    }

获取第K层节点的个数

对于一颗二叉树,我们要求第3层结点个数,那相对于根节点我们是不是求它的第二层结点。而相对于第二层结点来说我们是不是在求它的第一层结点,对于第三层,我们是求它的第0层。

号也就是说,如果我们求第k层结点的个数我们就求相对于每一层的k-1层结点,当k=1时候也就是求它本身结点个数1.

int getKLevelNodeCount(TreeNode root,int k){
        if(root==null) return 0;
        if(k==1) return 1;
        int left = getKLevelNodeCount(root.left,k-1);
        int right = getKLevelNodeCount(root.right,k-1);
        return left + right;
    }

获取二叉树的高度

对于一颗二叉树来说,树的高度就是这颗二叉树最大深度,我们只要求出左子树和右子树高度的最大值在+1就是这棵二叉树的高度。

int getHeight(TreeNode root){
        if(root==null) return 0;
        int left = getHeight(root.left);
        int right = getHeight(root.right);
        return left>right?left+1:right+1;
    }

检测值为value的元素是否存在

还是一样的,对于一颗二叉树我们要判断value是否在这颗二叉树中存在,我们首先会先去根节点看一看是不是这个value,如果不是value,再去左子树看一看有没有,再去看一看右子树有没有,如果在一棵树中找到value立马把这个值返回来。

TreeNode find(TreeNode root, int val){
        if(root==null) return null;
        if(root.val==val) return root;
        TreeNode leftTree = find(root.left,val);
        if(leftTree!=null){
            return leftTree;
        }
        TreeNode rightTree = find(root.right,val);
        if(rightTree!=null){
            return rightTree;
        }
        return null;
    }

层序遍历

层序遍历我们利用一个辅助栈来实现。

  • 先将根节点压入栈中
  • 每一次将栈顶元素弹出并打印,在弹出元素时要将弹出元素利用一个临时遍历存起来,然后同时将它的左子树和右子树一并压入栈中,如果为null我们就不压入栈中,反复进行最终就是层序遍历
void levelOrder(TreeNode root){
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()){
            TreeNode cur = queue.poll();
            System.out.print(cur.val+" ");
            if(cur.left!=null){
                queue.offer(cur.left);
            }
            if(cur.right!=null){
                queue.offer(cur.right);
            }
        }
    }

判断一棵树是不是完全二叉树

根据层序遍历思想

  • 先将根节点压入栈中
  • 每一次将栈顶元素弹出并打印,在弹出元素时要将弹出元素利用一个临时遍历存起来,然后同时将它的左子树和右子树一并压入栈中,此时与层序遍历不同的是如果树为null,我们依然要压入栈中
  • 最后我们将这棵树遍历完之后,栈中还有元素,如果栈中元素全为null,就说明这是一颗完全二叉树,否则不是。
  • 这也依据完全二叉树的定义,如果从左往右开始编号,有一个子树漏掉了并且它的右边还有子树那就不是一颗完全二叉树
boolean isCompleteTree(TreeNode root){
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()){
            TreeNode cur = queue.poll();
            if(cur==null){
                break;
            }
            queue.offer(cur.left);
            queue.offer(cur.right);
        }
        while(!queue.isEmpty()){
            TreeNode tmp =queue.peek();
            if(tmp!=null){
                return false;
            }
            queue.poll();
        }
        return true;
    }

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-07-04 23:13:18  更:2022-07-04 23:13:48 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/29 8:28:48-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计