java数据结构--二叉树

目录

一.概念

 二.构建二叉树节点类TreeNode

 三.二叉树的遍历

 1.前序遍历preOrder

2.中序遍历medOrder

3.后序遍历postOrder

 4.非递归遍历

三.深度

1.概念

2.递归求最大深度

 3.层序遍历加队列求最大深度

4.测试

5.递归求最小深度

6.层序遍历加队列求最小深度

7.测试

四.对称二叉树

 五.翻转二叉树

六.后缀表达式构造树

七.根据前序遍历和中序遍历还原树

八.根据后序和中序遍历还原树


一.概念

二叉树是一种树状结构,其中每个节点最多有两个子节点,被称为左子节点右子节点。二叉树通常具有以下特点:

  1. 根节点:二叉树的最顶层节点被称为根节点。它没有父节点,是整个树的起点。

  2. 子节点:每个节点最多有两个子节点,分别称为左子节点和右子节点。左子节点在树结构中位于父节点的左侧,右子节点在右侧。

  3. 叶节点:没有子节点的节点被称为叶节点,也被称为终端节点。叶节点位于树的最底层。

  4. 父节点:每个节点的上一层节点被称为父节点。每个节点除了根节点都有一个父节点。

  5. 兄弟节点:拥有相同父节点的节点被称为兄弟节点。

  6. 深度:节点的深度是指从根节点到该节点的路径上的节点数。

  7. 高度:节点的高度是指从该节点到树的最底层叶节点的最长路径。

  8. 子树:节点及其子节点以及与之相关的边所构成的树称为子树。

二叉树在计算机科学中具有广泛的应用,例如在搜索树排序算法中的使用。因为二叉树具有简单的结构和快速的搜索能力,所以它被广泛应用于许多领域

 二.构建二叉树节点类TreeNode

package 树.二叉树;/*** 普通二叉树*/
public class TreeNode {int value;TreeNode left;TreeNode right;public TreeNode(int value,TreeNode left,TreeNode right){this.value = value;this.left = left;this.right = right;}@Overridepublic String toString() {return "TreeNode{" +"value=" + value +", left=" + left +", right=" + right +'}';}
}

 三.二叉树的遍历

 1.前序遍历preOrder

    /*** 前序遍历** @param treeNode*/static void preOrder(TreeNode treeNode) {if (treeNode == null) {return;}System.out.print(treeNode.value + "\t");preOrder(treeNode.left);    //左preOrder(treeNode.right);   //右}

2.中序遍历medOrder

    /*** 中序遍历** @param treeNode*/static void medOrder(TreeNode treeNode) {if (treeNode == null) {return;}medOrder(treeNode.left);    //左System.out.print(treeNode.value + "\t");medOrder(treeNode.right);   //右}

3.后序遍历postOrder

    /*** 后序遍历** @param treeNode*/static void postOrder(TreeNode treeNode) {if (treeNode == null) {return;}postOrder(treeNode.left);    //左postOrder(treeNode.right);   //右System.out.print(treeNode.value + "\t");}

分别打印一下:

 

 public static void main(String[] args) {/***            1*           / \*          2   3*         /   / \*        6   9   8*/TreeNode root = new TreeNode(1,new TreeNode(2, new TreeNode(6, null, null), null),new TreeNode(3, new TreeNode(9, null, null), new TreeNode(8, null, null)));System.out.println("前序遍历");preOrder(root);System.out.println();System.out.println("中序遍历");medOrder(root);System.out.println();System.out.println("后序遍历");postOrder(root);}

运行:

前序遍历
1	2	6	3	9	8	
中序遍历
6	2	1	9	3	8	
后序遍历
6	2	9	8	3	1	
进程已结束,退出代码0

 4.非递归遍历

 我们可以使用栈(stack)来实现二叉树的遍历,栈可以模拟递归

 /*** 通过迭代实现三种遍历*/@Testpublic void testAllOrder(){/***            1*           / \*          2   3*         /   / \*        6   9   8*/TreeNode root = new TreeNode(1,new TreeNode(2, new TreeNode(6, null, null), null),new TreeNode(3, new TreeNode(9, null, null), new TreeNode(8, null, null)));//创建栈LinkedListStack<TreeNode> stack = new LinkedListStack<>(10);//定义指针变量,初始指向root节点TreeNode node = root;TreeNode pop = null;//遍历,只要node不为空,并且栈中有元素while (node != null || !stack.isEmpty()) {//先找左孩子if (node != null) {//压入栈stack.push(node);System.out.println("前序:"+node.value);//指向左孩子node = node.left;} else {//弹出元素TreeNode peek = stack.peek();// 没有右子树if (peek.right == null ) {System.out.println("中序遍历:"+peek.value);//弹出pop = stack.pop();System.err.println("后序:"+pop.value);}//右子树处理完成else if ( peek.right == pop) {pop = stack.pop();System.err.println("后序:"+pop.value);}//待处理右子树else {System.out.println("中序遍历:"+peek.value);//指向栈顶元素的右孩子node = peek.right;}}}}

测试一下:

前序:1
前序:2
前序:6
后序:6
中序遍历:6
后序:2
中序遍历:2
后序:9
中序遍历:1
后序:8
前序:3
前序:9
中序遍历:9
后序:3
中序遍历:3
后序:1
前序:8
中序遍历:8

三.深度

1.概念

  二叉树的深度是指从根节点最远叶子节点的路径上的节点个数。可以通过递归迭代的方式来计算二叉树的深度。

递归方法:

  1. 如果二叉树为空,返回深度为 0。
  2. 否则,分别计算左子树和右子树的深度。
  3. 树的深度为左子树深度和右子树深度中的较大值加 1。

迭代方法(层次遍历):

  1. 如果根节点为空,返回深度为 0。
  2. 创建一个队列,并将根节点入队。
  3. 初始化深度为 0。
  4. 循环执行以下步骤直到队列为空:
    • 获取当前层的节点个数,记为 count。
    • 将当前层的节点依次出队,并将它们的左子节点和右子节点依次入队。
    • 将 count 减去当前层节点个数,如果 count 大于 0,则深度加 1。
  5. 返回深度的值。

无论使用递归还是迭代的方式,都可以得到二叉树的深度。

2.递归求最大深度

/*** 递归调研求最大深度* @param node* @return*/static int maxDepth(TreeNode node){if(node == null){return 0;}if(node.left == null && node.right == null){return 1;}int d1 = maxDepth(node.left);int d2 = maxDepth(node.right);return Integer.max(d1,d2) + 1;}

 3.层序遍历加队列求最大深度

/***  通过层序遍历 加 队列 来实现求最大深度*  思路:*     每次将一层的节点加入到队列中,然后循环,到下一层取出队列中的元素,如果该*     元素右左右子树,那么继续加入到队列中去,* @param root* @return*/static  int maxDepthByQueue(TreeNode root){if(root==null){return 0;}//创建队列Queue<TreeNode> queue = new LinkedList<>();//先把根节点加入到队列中去queue.offer(root);//初始深度为0int depth = 0;//当队列不为空时循环while (!queue.isEmpty()){//相当于每一层的个数for (int i =0;i < queue.size(); i++){//从队头取出元素TreeNode poll = queue.poll();//如果有左孩子,把左孩子加入到队列中if(poll.left != null){queue.offer(poll.left);}//如果有右孩子,把右孩子加入到队列中if(poll.right != null){queue.offer(poll.right);}}//每遍历完一层,让深度加一depth++;}return depth;}

4.测试

    /***          1*         / \*        2   3*      /      \*     4        5*               \*                6**    左子树最大深度为3,右子树为4,所以最大深度为4*/public static void main(String[] args) {TreeNode root = new TreeNode(1,new TreeNode(2,new TreeNode(4,null,null),null),new TreeNode(3,null,new TreeNode(5,null,new TreeNode(6,null,null))));System.out.println("递归法:");System.out.println(maxDepth(root));System.out.println("层序队列法:");System.out.println(maxDepthByQueue(root));}

 运行:

递归法:
4
层序队列法:
4进程已结束,退出代码0

5.递归求最小深度

    /*** 递归求最小深度* @param root* @return*/static int minDepth(TreeNode root){if(root == null){return 0;}int d1 = minDepth(root.left);int d2 = minDepth(root.right);//为了防止单边树,如果一边为null,那么应该返回另一边的深度if(d1 == 0){return d2 + 1;}if(d2 == 0){return d1 + 1;}return Integer.min(d1,d2) + 1;}

6.层序遍历加队列求最小深度

 /*** 层序遍历找最小深度,*  思路:*    如果找到第一个叶子节点,那么该叶子节点所在的层就是最小层* @param root* @return*/static int minDepthFloorByQueue(TreeNode root){if(root==null){return 0;}LinkedList<TreeNode> queue = new LinkedList<>();int depth = 0;queue.offer(root);while (!queue.isEmpty()){int size = queue.size();depth++;for (int i = 0; i < size; i++) {TreeNode poll = queue.poll();if(poll.left==null && poll.right==null){return depth;}if(poll.left!=null){queue.offer(poll.left);}if(poll.right != null){queue.offer(poll.right);}}}return depth;}
}

7.测试

 

 public static void main(String[] args) {/***          1*         / \*        2   3*      /      \*     4        5*               \*                6**    左子树最大深度为3,右子树为4,所以最大深度为4*/TreeNode root = new TreeNode(1,new TreeNode(2,new TreeNode(4,null,null),null),new TreeNode(3,null,new TreeNode(5,null,new TreeNode(6,null,null))));System.out.println("递归法:");System.out.println(minDepth(root));System.out.println("层序队列法:");System.out.println(minDepthFloorByQueue(root));}

运行:

递归法:
3
层序队列法:
3进程已结束,退出代码0

四.对称二叉树

/*** 对称二叉树*/
public class D_Eq_Tree {/***   左的左和右的右相等,*   左的右和右的左相等** @param args*/public static void main(String[] args) {/***           1*          / \*         2   2*        /\   /\*       3  4 4  3*/TreeNode root =   new TreeNode(1,new TreeNode(2,new TreeNode(3,null,null),new TreeNode(4,null,null)),new TreeNode(2,new TreeNode(4,null,null),new TreeNode(3,null,null)));boolean flag = isD_EqTree(root.left, root.right);System.out.println(flag);}static boolean isD_EqTree(TreeNode left,TreeNode right){//如果左子树和右子树都为null,则对称if(left==null && right==null){return true;}//如果左子树或者右子树为Null,则不对称if(left==null || right==null){return false;}//如果左右两边值不相等,则不对称if(left.value != right.value){return false;}//左右递归return isD_EqTree(left.left,right.right) && isD_EqTree(left.right,right.left);}}

测试运行:

true进程已结束,退出代码0

 五.翻转二叉树

/*** 翻转二叉树*/
public class L226_ReversedTree {public static void main(String[] args) {/***         1                       1*        / \                     / \*       2   3        =>         3   2*      / \ / \                 / \ / \*     4  5 6  7               7  6 5 4*/TreeNode root = new TreeNode(1,new TreeNode(2,new TreeNode(4,null,null),new TreeNode(5,null,null)),new TreeNode(3,new TreeNode(6,null,null),new TreeNode(7,null,null)));preOrder(root);System.out.println();reversed(root);preOrder(root);}/*** 翻转二叉树* @param root*/static void reversed(TreeNode root){if(root==null){return;}TreeNode node = root.left;root.left = root.right;root.right = node;//递归左子树reversed(root.left);//递归右子树reversed(root.right);}static void preOrder(TreeNode root){if(root==null){return;}System.out.print(root.value+"\t");preOrder(root.left);preOrder(root.right);}}

运行:

1	2	4	5	3	6	7	
1	3	7	6	2	5	4	
进程已结束,退出代码0

六.后缀表达式构造树

/*** 后缀表达式构造树*/
public class Last_Expression {/***     中缀: (2-1)*3*     后缀:  21-3***     树:*              **             / \*            -   3*           / \*          2   1**    后序遍历就能获得 : 21-3**** @param args*/public static void main(String[] args) {String[] str = {"2","1","-","3","*"};LinkedList<TreeStrNode> stack = new LinkedList<>();for (String c : str) {switch (c){case "+":case "-":case "*":case "/"://先弹出的为右孩子TreeStrNode right = stack.pop();//后弹出的为左孩子TreeStrNode left = stack.pop();//创建树TreeStrNode parent = new TreeStrNode(c);parent.left = left;parent.right = right;//最后把父节点压入栈stack.push(parent);break;default://如果不是运算符,就压入栈stack.push(new TreeStrNode(c));}}//最终栈中的节点为树的根节点TreeStrNode root = stack.peek();//对他做一个后序遍历postOrder(root);}/*** 后序遍历** @param treeNode*/static void postOrder(TreeStrNode treeNode) {if (treeNode == null) {return;}postOrder(treeNode.left);    //左postOrder(treeNode.right);   //右System.out.print(treeNode.str + "\t");}}

 运行:

2	1	-	3	*	
进程已结束,退出代码0

七.根据前序遍历和中序遍历还原树

/*** 根据前序遍历和中序遍历 还原树*/
public class Pre_In_ToTree {/***            1*           / \*          2   3*         /   / \*        6   9   8**        preOrder = 1, 2, 6, 3, 9, 8*        inOrder = 6, 2, 1, 9 , 3, 8**  思路:*     前序遍历的第一个是根节点 root = 1*     然后找到中序遍历中 root所在的位置*     ->左边的是左子树*     ->右边的是右子树*     中序:*      左: 6,2*      右: 9,3,8*     前序:*      左: 2,6*      右: 3,9,8**     这样根据中序的左和前序的左,可以知道 6 是左子树, 2 是父节点*         根据中序的右和前序的右,可以知道 9是左子树,8是右子树,3是父节点**     最后递归调用继续分割左右数组***/public static void main(String[] args) {int[] preOrder = {1, 2, 6, 3, 9, 8};int[]  inOrder = {6, 2, 1, 9 , 3, 8};TreeNode root = getTree(preOrder, inOrder);preOrder(root);}static TreeNode getTree(int[] preOrder,int[] inOrder){//结束递归条件if(preOrder.length == 0){return null;}//先找到根节点int rootValue = preOrder[0];TreeNode root = new TreeNode(rootValue, null, null);//在中序中找到根节点的位置for (int i = 0; i < inOrder.length ; i++) {if(inOrder[i] == rootValue){//找到之后切割左子树和右子树//inOrder.left = 0 ~ i-1//inOrder.right = i+1 ~ inOrder.length-1;int[] inLeft = Arrays.copyOfRange(inOrder, 0, i);int[] inRight = Arrays.copyOfRange(inOrder, i + 1, inOrder.length);//继续切割preOrder//preOrder.left = 1 ~ i+1//preOrder.right = i+2 ~ preOrder.length-1int[] preLeft = Arrays.copyOfRange(preOrder, 1, i+1);int[] preRight = Arrays.copyOfRange(preOrder, i + 1, preOrder.length);//递归调用root.left = getTree(preLeft,inLeft);root.right = getTree(preRight,inRight);break;}}return root;}/*** 前序遍历** @param treeNode*/static void preOrder(TreeNode treeNode) {if (treeNode == null) {return;}System.out.print(treeNode.value + "\t");preOrder(treeNode.left);    //左preOrder(treeNode.right);   //右}}

运行:

1	2	6	3	9	8	
进程已结束,退出代码0

 

八.根据后序和中序遍历还原树

/*** 根据后序和中序遍历还原树*/
public class Post_In_ToTree {/***            1*           / \*          2   3*         /   / \*        6   9   8**        postOrder = 6,2,9,8,3,1*        inOrder = 6, 2, 1, 9 , 3, 8***/public static void main(String[] args) {int[] postOrder = {6,2,9,8,3,1};int[]  inOrder = {6, 2, 1, 9 , 3, 8};TreeNode root = buildTree(postOrder, inOrder);preOrder(root);}static TreeNode buildTree(int[] postOrder,int[] inOrder){if(postOrder.length==0){return null;}//先找根节点int rootValue = postOrder[postOrder.length - 1];TreeNode root = new TreeNode(rootValue, null, null);for (int i = 0; i < inOrder.length; i++) {if(inOrder[i] == rootValue){//切分int[] inLeft = Arrays.copyOfRange(inOrder, 0, i);int[] inRight = Arrays.copyOfRange(inOrder, i + 1, inOrder.length);int[] postLeft = Arrays.copyOfRange(postOrder, 0, i);int[] postRight = Arrays.copyOfRange(postOrder, i , postOrder.length - 1);//递归root.left = buildTree(postLeft,inLeft);root.right = buildTree(postRight,inRight);break;}}return root;}}

运行:

1	2	6	3	9	8	
进程已结束,退出代码0

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/142264.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【保姆级教程】Linux安装JDK8

本文以centos7为例&#xff0c;一步一步进行jdk1.8的安装。 1. 下载安装 官网下载链接&#xff1a; https://www.oracle.com/cn/java/technologies/downloads/#java8 上传jdk的压缩包到服务器的/usr/local目录下 在当前目录解压jdk压缩包&#xff0c;如果是其它版本&#xf…

设计模式 -- 代理模式(Proxy Pattern)

代理模式&#xff1a;一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。 在代理模式中&#xff0c;我们创建具有现有对象的对象&#xff0c;以便向外界提供功能接口。 介绍 意图&#xff1a;为其他对象提供一种代理以控制对这个对象的访问。主要解决问题&#xf…

基于LoongArch指令集-五级流水线CPU 乘除法指令的添加

调用Xilinx IP实现乘除法运算部件 调用Xilinx IP实现乘法运算部件 wire [31:0] src1,src2; wire [63:0] unsigned_prod; wire [63:0] signed_prod;assign unsigned_prod src1*src2; assign signed_prod $signed(src1)*$signed(src2);调用 Xilinx IP 实现除法运算部件 在工程…

Trie 前缀树 字典树

PreFix Tree 二叉树 的每个节点只有两个孩子,那如果每个节点可以有多个孩子呢?这就形成了「多叉树」。多叉树的子节点数目一般不是固定的,所以会用变长数组来保存所有的子节点的指针。 「前缀树」是一种特殊的多叉树,它的 TrieNode 中 chidren 是一个大小为 26 的一维数组…

自压缩llm 为 超长记忆

自压缩llm 为 超长记忆 解释数据处理实际例子解释 # 自压缩llm 为 超长记忆 # prompt 格式 # <|细颗粒词表|><|粗颗粒词表|><|细颗粒词表|> # 细颗粒词表 = 词1,词2,词3,词4,词5,词6,词7,词8,词9,词10, # 组颗粒词表id1, 组颗粒词表id2, 组颗粒…

一篇博客读懂队列——Queue

目录 一、队列的概念和结构 ​二、队列的实现 2.1队列的初始化QueueInit 2.2队列的摧毁QueueDestroy 2.3插入结点QueuePush 2.4删除结点QueuePop 2.5返回队头QueueFront 2.6返回队尾QueueBack 2.7判断队列为空QueueEmpty 2.8统计队列数目QueueSize 一、队列的概念和…

C#动态数据类型

static void Main(string[] args){dynamic point new {x 15,y 10};DrwaPoint(point);System.Console.Read();}static void DrwaPoint(dynamic point) >System.Console.WriteLine($"x:{point.x},y:{point.y}");

【nlp】1.4 文本特征处理(n-gram特征、文本长度规范:补齐与截断)

文本特征处理 1 什么是n-gram特征2 文本长度规范及其作用文本特征处理的作用: 文本特征处理包括为语料添加具有普适性的文本特征,如:n-gram特征,以及对加入特征之后的文本语料进行必要的处理, 如: 长度规范。这些特征处理工作能够有效的将重要的文本特征加入模型训练中,增强…

设计模式介绍

写在前面的话 以前也学习过设计模式&#xff0c;但是总感觉学不懂&#xff0c;最近开发项目深刻感受到设计模式的重要性&#xff0c;系统学习一些。 我是观看油管上李建忠老师的视频学习做的总结&#xff0c;会不断补充&#xff0c;欢迎大家指出错误&#xff0c;学习进步。 …

k8s ----对外暴露

目录 一、Ingress 简介 1、Ingress 组成 2、Ingress 工作原理 二、部署Ingress 1、部署 nginx-ingress-controller 2、暴露ingress 4.1 DaemonSetHostNetworknodeSelector模式的service 4.2 DeploymentNodePort模式的Service 三、Ingress HTTP 代理访问 四、Ingress …

数据结构与算法【递归】Java实现

递归 递归是一种解决计算问题的方法&#xff0c;其中解决方案取决于同一类问题的更小子集。 特点&#xff1a; 自己调用自己&#xff0c;如果说每个函数对应着一种解决方案&#xff0c;自己调用自己意味着解决方案是一样的&#xff08;有规律的&#xff09;每次调用&#xf…

计算机毕业设计选题推荐-体育赛事微信小程序/安卓APP-项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

图像分类:对google/vit-large-patch32-384模型进行微调

背景&#xff1a; 图像分类是很常见的场景&#xff0c;比如日常的物体识别&#xff0c;可很多时候我们还需要根据自己的数据来训练自己的分类&#xff0c;大模型出现以后&#xff0c;我们不需要再重头来训练我们的模型&#xff0c;直接根据已经训练好的大模型进行微调即可&…

Oracle(2-1) Networking Overview

文章目录 一、基础知识1、Network Environ Challenges 网络环境挑战2、Simple Network :2-Tier 简单的两层网络3、Simple to Complex : N-Tier 简单到复杂&#xff1a;N层网络4、Oracle Network Solutions Oracle网络解决方案5、Key Features of Oracle Net Oracle Net的主要功…

JavaWeb篇_09——Tomcat运行过程以及Servlet继承结构

Tomcat运行过程 用户访问localhost:8888/test/helloword.do&#xff0c;请求被发送到Tomcat&#xff0c;被监听8888端口并处理 HTTP/1.1 协议的Connector获得。Connector把该请求交给它所在的Service的Engine来处理&#xff0c;并等待Engine的回应。Engine获得请求localhost/t…

酷柚易汛ERP-购货订单操作指南

1、应用场景 先下购货订单&#xff0c;收货入库后生成购货单。 2、主要操作 2.1 新增购货订单 打开【购货】-【购货订单】新增购货订单。&#xff08;*为必填项&#xff0c;其他为选填&#xff09; ① 录入供应商&#xff1a;点击供应商字段框的 &#xff0c;在弹框中选择供…

买房和租房哪个划算?

目录 1、考虑因素 1. 1费用比较 1.2 资产增值 1.3 税收影响 1.4 灵活性 1.5 贷款利率 1.6 长期计划 1.7 当地市场条件 2、买房计算 2.1等额本息 2.2等额本金 3、租房计算 1、考虑因素 在比较买房和租房哪个更划算时&#xff0c;需要考虑多个因素。以下是一些可以考…

客户下单时如何自动匹配到最近的门店

有些商家有多个门店&#xff0c;当客户下单时&#xff0c;希望能够将客户下的订单分配给最近的门店。下面就具体介绍一下在采云小程中是如何实现的。 首先&#xff0c;为了简便起见&#xff0c;请确定门店高级设置保持着默认设定。因为单独的商品管理模式以及独享的商品信息模…

【milkv】0、duo编译环境搭建

一、开发资料整理 Docker https://hub.docker.com/repository/docker/dreamcmi/cv1800-docker/general GitHub https://github.com/milkv-duo/duo-buildroot-sdk CV181x/CV180x MMF SDK 开发文档汇总 https://developer.sophgo.com/thread/471.html cv181x芯片使用的交叉…

CCF 备忘

一、不错的网站 CCF CCSP 竞赛历年资料 官网 http://118.190.20.162/home.page 二、基础套路 循环输入 数组标记法&#xff08;数组下标-数值 的映射&#xff09; 两个矩阵相乘 map<long long, map<long long, long long> > ans; for(int i1;i<d;i){for(int…