数据结构与算法--重建二叉树

二叉树

  • 树在实际编程中经常遇到地一种数据结构。上一篇中我们解释了二叉树及其原理,从中可以知道,树地操作会涉及到很多指针地操作,我们一般遇到地树相关地问题差不多都是二叉树。二叉树最重要地莫过于遍历,即按照某一顺序访问树中所有节点,通常有如下几种遍历方式:
    • 前序遍历:先根节点,左节点,右节点。用以上顺序来访问
    • 中序遍历:先左节点,中节点,右节点
    • 后续遍历:先左节点,右节点,中节点
  • 以上三种遍历都有递归和循环两种不同地实现方式,每一种遍历地递归实现都比循环实现要简单。在上一节中已经给出了三种递归遍历地实现方式。
  • 二叉树还有很多特例,二叉搜索树左子树中节点总数小于或者等于根节点。右子树地节点总数大于等于根节点。
  • 二叉树特例还包括堆和红黑树。堆分为最大堆和最小堆。
  • 一下我们利用二叉树地三种遍历方式地特性来解决如下算法问题:

重建二叉树

  • 题目:输入某个二叉树前序遍历,中序遍历地结果,请重建该二叉树。我们假设驶入地前序遍历,中序遍历不包含重复数字。例如:前序(1,2,4,7,3,5,6,8),中序遍历(4,7,2,1,5,3,8,6),如下图
    在这里插入图片描述
分析
  • 二叉树前序遍历中,第一个数字总数树地根节点。
  • 中序遍历中,根节点在序列中间。左子树在根节点左边,右子树在根节点右边
  • 我们通过前序遍历找到根节点
  • 在中序遍历中确认跟节点的位置后得到左右子树的长度
  • 接着将左子树,右子树分别执行以上流程(递归)
  • 如下图,在二叉树的前序遍历和中序遍历的序列中确定根节点的值,左子树的值和右子树的值

在这里插入图片描述

实现
  • 以上对二叉树节点地定义:

/*** 二叉树节点对象定义** @author liaojiamin* @Date:Created in 15:24 2020/12/11*/
public class BinaryNode implements Comparable {private Object element;private BinaryNode left;private BinaryNode right;/*** 树高度*/private int height;private int count;public BinaryNode(Object element, BinaryNode left, BinaryNode right) {this.element = element;this.left = left;this.right = right;this.count = 1;this.height = 0;}public int getHeight() {return height;}public void setHeight(int height) {this.height = height;}public Object getElement() {return element;}public void setElement(Object element) {this.element = element;}public BinaryNode getLeft() {return left;}public void setLeft(BinaryNode left) {this.left = left;}public BinaryNode getRight() {return right;}public void setRight(BinaryNode right) {this.right = right;}public int getCount() {return count;}public void setCount(int count) {this.count = count;}@Overridepublic int compareTo(Object o) {if (o == null) {return 1;}int flag;if (o instanceof Integer) {int myElement = (int) this.element - (int) o;flag = myElement > 0 ? 1 : myElement == 0 ? 0 : -1;} else {flag = this.element.toString().compareTo(o.toString());}if (flag == 0) {return 0;} else if (flag > 0) {return 1;} else {return -1;}}
}
  • 重构二叉树实现
package com.ljm.resource.math.binary;/***  已知前序遍历:1,2,4,7,3,5,6,8 中序遍历:4,7,2,1,5,3,8,6 重建二叉树* @author liaojiamin* @Date:Created in 17:22 2021/3/8*/
public class RebuildBinary {public static BinaryNode construct(int[] preOrder, int[] inOrder){if(preOrder == null || inOrder == null || preOrder.length != inOrder.length || preOrder.length <= 0){System.out.println("Invalid input");return null;}return constructCore(preOrder, inOrder, 0, preOrder.length -1, 0, inOrder.length -1);}public static BinaryNode constructCore(int[] preOrder, int[] inOrder,int preStart, int preEnd,int inStart, int inEnd){int rootValue = preOrder[preStart];BinaryNode rootNode = new BinaryNode(rootValue, null, null);//整棵树只有一个root节点的清空if(preStart == preEnd){if(inStart == inEnd && preOrder[preStart] == inOrder[inStart]){return rootNode;}else {System.out.println("Invalid input");return null;}}//找到root节点在 中序遍历中位置int rootInOrderIndex = inStart;while (rootInOrderIndex <= inEnd && inOrder[rootInOrderIndex] != rootValue){rootInOrderIndex ++;}//中序遍历中没有找到root节点if(rootInOrderIndex == inEnd && inOrder[rootInOrderIndex] != rootValue){System.out.println("Invalid input");return null;}int leftLength = rootInOrderIndex - inStart;//没有左子树情况start = endif(leftLength > 0){int leftInOrderStart = inStart;int leftInOrderEnd =  inStart + leftLength - 1;int leftPreOrderStart = preStart + 1;int leftPreOrderEnd = preStart + leftLength;rootNode.setLeft(constructCore(preOrder, inOrder, leftPreOrderStart, leftPreOrderEnd, leftInOrderStart, leftInOrderEnd));}//存在右节点if(leftLength < preEnd - preStart){int rightInOrderStart = rootInOrderIndex + 1;int rightInOrderEnd = inEnd;int rightPreOrderStart = preStart + 1 + leftLength;int rightPreOrderEnd = preEnd;rootNode.setRight(constructCore(preOrder, inOrder, rightPreOrderStart, rightPreOrderEnd, rightInOrderStart, rightInOrderEnd));}return rootNode;}public static void main(String[] args) {int preOrder[] = {1,2,4,7,3,5,6,8};int inOrder[] = {4,7,2,1,5,3,8,6};BinaryNode binaryNode = construct(preOrder, inOrder);PostfixExTOTreeEx.printMiddleFirstTree(binaryNode);}
}
  • 以上我们在函数constructCore中,我们先根据前序遍历序列地第一个数字,创建根节点。
  • 接下来在中序遍历中找到根节点位置
  • 这样就能确定左右子树地数量
  • 在前序遍历和中序遍历地序列中划分左右子树节点后,
  • 将左右子树递归调用函数constructCore,分别构建左右子树。得到最终地树

上一篇:数据结构与算法–二叉树实现原理
下一篇:数据结构与算法–二叉查找树实现原理

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

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

相关文章

3分钟掌握Quartz.net分布式定时任务的姿势

长话短说&#xff0c;今天聊一聊分布式定时任务&#xff0c;我的流水账笔记&#xff1a;ASP.NET CoreQuartz.Net实现web定时任务AspNetCore结合Redis实践消息队列细心朋友稍一分析&#xff0c;就知道还有问题&#xff1a;水平扩展后的WebApp的Quartz.net定时任务会多次触发&…

数据结构与算法--利用栈实现队列

利用栈实现队列 上一节中说明了栈的特点 后进先出&#xff0c;我们用数组的方式实现了栈的基本操作api&#xff0c;因此我们对栈的操作是不考虑排序的&#xff0c;每个api的操作基本都是O(1)的世界&#xff0c;因为不考虑顺序&#xff0c;所以找最大&#xff0c;最小值&#x…

ASP.NET Core 配置源:实时生效

在之前的文章 ASP.NET Core 自定义配置源 和 ASP.NET Core etcd 配置源 中主要是介绍如何实现自定义的配置源&#xff0c;但不论内置的和自定义的配置源&#xff0c;都会面临如何使配置修改后实时生效的问题&#xff08;修改配置后在不重启服务的情况下能马上生效&#xff09;。…

分布式事务理论模型

分布式事务 事务的概念&#xff0c;我们第一想到的应该是数据库的事务。所谓数据库事务就是只作为单个逻辑工作单元执行多个数据库操作的时候&#xff0c;数据库需要保证要么都成功&#xff0c;要么都失败&#xff0c;它必须满足ACID特性&#xff0c;即&#xff1a; 原子性&…

[MySQL基础]数据库的相关概念

DB: 数据库(database):存储数据的“仓库”&#xff0c;它保存了一系列有组织的数据。 DBMS: 数据库管理系统(Database Management System):数据库是通过DBMS创建和操作的容器。 SQL: 结构化查询语言(Structure Query Language):专门用来与数据库通信的语言。 SQL的优点: 1.几…

Linq下有一个非常实用的SelectMany方法,很多人却不会用

在平时开发中经常会看到有些朋友或者同事在写代码时会充斥着各种for&#xff0c;foreach&#xff0c;这种程式代码太多的话阅读性特别差&#xff0c;而且还显得特别累赘&#xff0c;其实在FCL中有很多帮助我们提高阅读感的方法&#xff0c;而现实中很多人不会用或者说不知道&am…

.NET Core前后端分离快速开发框架(Core.3.1+AntdVue)

引言时间真快&#xff0c;转眼今年又要过去了。回想今年&#xff0c;依次开源发布了Colder.Fx.Net.AdminLTE(254Star)、Colder.Fx.Core.AdminLTE(335Star)、DotNettySocket(82Star)、IdHelper(47Star)&#xff0c;这些框架及组件都是本着以实际出发&#xff0c;实事求是的态度&…

数据结构与算法--查找与排序另类用法-旋转数组中的最小数字

查找与排序 查找 查找与排序都在程序设计中常被用到的算法。查找相对而言简单&#xff0c;一般都是顺序查找&#xff0c;二分查找&#xff0c;哈希表查找&#xff0c;和二叉排序树查找。其中二分查找是我必须熟悉的一种。哈希表和二叉排序树主要点在于他的数据结构而不是算法…

[MySQL基础]MySQL常见命令介绍

show databases; use 库名; show tables; show tables from 库名 select database(); create table 名字( id int, name varchar(20)); desc 表名; select * from 表名; insert into 表名 (a,b,…,f) values(1,2,3,…,7); update 库名 set name‘lilei’ where id1; delete f…

如何选择好公司

点击蓝字关注&#xff0c;回复“职场进阶”获取职场进阶精品资料一份前几天写了一篇文章&#xff1a;怎么判断自己在不在一家好公司。附带了一个投票调查&#xff0c;结果如下图&#xff1a;调研结果有点点扎心&#xff0c;有点点出乎我的意料。61%的小伙伴&#xff0c;都认为自…

数据结构与算法--再谈递归与循环(斐波那契数列)

再谈递归与循环 在某些算法中&#xff0c;可能需要重复计算相同的问题&#xff0c;通常我们可以选择用递归或者循环两种方法。递归是一个函数内部的调用这个函数自身。循环则是通过设置计算的初始值以及终止条件&#xff0c;在一个范围内重复运算。比如&#xff0c;我们求累加…

同步异步多线程这三者关系,你能给面试官一个满意的回答吗?

前几天一位朋友去面试&#xff0c;面试官问了他同步&#xff0c;异步&#xff0c;多线程之间是什么关系&#xff0c;异步比同步高效在哪&#xff1f;多线程比单线程高效在哪&#xff1f;由于回答的不好&#xff0c;让我帮他捋一下&#xff0c;其实回答这个问题不难&#xff0c;…

分布式事务框架seata

seata 前两篇文中总结了一下分布式事务已经现阶段常用的解决方案&#xff0c;现在来讨论一下现有的分布式事务框架seata&#xff0c;点击此处是seata的官网seata致力于微服务框架下提供高性能和简单易用的分布式事务服务。它提供了AT&#xff0c;TCC&#xff0c;Saga &#xf…

[一起读源码]走进C#并发队列ConcurrentQueue的内部世界 — .NET Core篇

在上一篇《走进C#并发队列ConcurrentQueue的内部世界》中解析了Framework下的ConcurrentQueue实现原理&#xff0c;经过抛砖引玉&#xff0c;得到了一众大佬的指点&#xff0c;找到了.NET Core版本下的ConcurrentQueue源码&#xff0c;位于以下地址&#xff1a;https://github.…

Java语法基础50题训练(上)

题目1: 有两只老虎&#xff0c;一只体重为180kg&#xff0c;一只体重为200kg&#xff0c;请用程序实现判断两只老虎的体重是否相同。 代码如下: public class OperatorTest {public static void main (String[] args) {int w1 180;int w2 200;boolean ans w1 w2?true:f…

EFCore.Sharding(EFCore开源分表框架)

简介本框架旨在为EF Core提供Sharding(即读写分离分库分表)支持,不仅提供了一套强大的普通数据操作接口,并且降低了分表难度,支持按时间自动分表扩容,提供的操作接口简洁统一.源码地址:EFCore.SHarding引言读写分离分库分表一直是数据库领域中的重难点,当数据规模达到单库极限的…

分布式事务 -- seata框架AT模式实现原理

Seata AT 模式 上一节中我们提到AT模式是基于XA事务模型演变过来的&#xff0c;所以他的整体机制也是一个改进版本的两阶段提交协议。 第一阶段&#xff1a;业务数据和回滚日志记录在同一个本地事务中提交&#xff0c;释放本地锁和链接资源第二阶段&#xff1a;提交异步化&…

[Java基础]数据输入

Scanner使用的基本步骤: 1.导包: import java.util.Scanner;2.创建对象: Scanner sc new Scanner(System.in);3.接收数据: int i sc.nextInt();代码如下: import java.util.Scanner;public class OperatorTest {public static void main (String[] args) {//创建对象Scan…

k8s中流量分离以及资源隔离实战

源宝导读&#xff1a;明源云客的终端用户越来越多&#xff0c;也涌现出线上流量活动的场景&#xff0c;大量的访问和接口请求导致服务器出现较高负载。本文将介绍云客团队为了缓解服务器压力&#xff0c;通过K8S进行分流与资源隔离的实践过程。一、背景PaaS和B2C的主要客户云客…