java如何创建普通二叉树

java创建二叉树

这段时间一直在复习数据结构的知识。

从最基础的开始,实现一个普通的二叉树。但发现也不那么简单。因为之前学数据结构时是用C语言写的。

指针用来对结构体的值操作比较好理解。但java没有指针。

而Node节点在方法中传递的是地址。

如果直接对形参进行new操作是错误的。无法改变实参的值的。这一点坑了我很久,然后一顿查资料。

时隔很久,终于填上这个坑了

下面是以递归创建的二叉树.还有一些常见的遍历和树的高度与树的最大宽度.

  • 一个方法不能修改一个基本数据类型的参数
  • 一个方法可以修改一个对象参数的状态
  • 一个方法不能实现让对象参数引用一个新对象(这句话在这里尤为适用)

代码中的二叉树如下图

下面是非常简单的实现

这里为了,后面的输出格式,使用了JDK的动态代理。并写了一个接口

package test.tree;
public interface AbstractBinaryTree {
 void printPostOder();
 void printPostOderByRecursion();
 void printPreOder();
 void printPreOderByRecursion();
 void printInOderByRecursion();
 void printInOder();
 void printHeight();
 void printMaxWidth();
 void printLevelOrder();
}

主要的代码

package test.tree;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;

/**
 * 为了方便展示,并没有将Node属性私有
 */

class Node {
	public String data;
	public Node left = null;
	public Node right = null;
	public boolean flag;

	Node(String data) {
		this.data = data;
	}

	Node() {
	}
	@Override
	public String toString() {
		return this.data;
	}
}

public class BinaryTree implements AbstractBinaryTree{
	private Node root = new Node();
	public Node getRoot() {
		return root;
	}

	public void printNode(Node node) {

		if (node.data == null) {
			System.out.print("");
		} else {
			System.out.print(node.data);
		}
	}

	public BinaryTree(String tree) {
		String[] treeNodes = tree.split(",");
		createTreeByRecursion(treeNodes);
	}

	private int createTreeByRecursion(Node node, String[] treeNodes, int n) {
		if ("#".equals(treeNodes[n]))
			return n + 1;
		node.data = treeNodes[n];
		node.left = new Node();
		int left = createTreeByRecursion(node.left, treeNodes, n + 1);
		node.right = new Node();
		int right = createTreeByRecursion(node.right, treeNodes, left);
		return right;
	}

	public void createTreeByRecursion(String[] treeNodes) {
		createTreeByRecursion(root, treeNodes, 0);
	}

	/**
	 * 先序非递归创建
	 */
	public void createTree(String[] treeNodes) {
		Stack<Node> stack = new Stack<>();
		int index = 0;
		Node node = root;
		while (index < treeNodes.length) {
			while (true) {

				if ("#".equals(treeNodes[index])) {

					node = stack.pop();

					if (node.flag == false) {
						node.left = null;
						node.flag = true;
						stack.push(node);
					} else {
						node.right = null;
					}

					// 记得加1
					index++;
					break;
				}

				if (node.flag == true) {
					node.right = new Node();
					node = node.right;
				}

				node.data = treeNodes[index];
				stack.push(node);
				node.left = new Node();
				node = node.left;
				index++;
			}

			if (node.flag == false) {
				stack.push(node);
				node.flag = true;
				node = node.right;
			} else {
				node = stack.peek();
				node.flag = true;
			}
		}
	}

	// 递归调用的方法,需要将root传递进去
	private void printPreOderByRecursion(Node node) {
		if (node == null)
			return;
		printNode(node);
		printPreOderByRecursion(node.left);
		printPreOderByRecursion(node.right);
	}

	public void printPreOderByRecursion() {
		printPreOderByRecursion(root);
	}

	private void printInOderByRecursion(Node node) {

		if (node == null)
			return;

		printInOderByRecursion(node.left);
		printNode(node);
		printInOderByRecursion(node.right);
	}

	public void printInOderByRecursion() {
		printInOderByRecursion(root);
	}

	private void printPostOderByRecursion(Node node) {

		if (node == null)
			return;
		printPostOderByRecursion(node.left);
		printPostOderByRecursion(node.right);
		printNode(node);
	}

	public void printPostOderByRecursion() {
		printPostOderByRecursion(root);
	}

	// 非递归遍历二叉树

	// 先序遍历
	public void printPreOder() {
		Stack<Node> stack = new Stack<>();
		Node tempNode = root;
		while (true) {
			while (tempNode != null) {
				printNode(tempNode);
				stack.push(tempNode);
				tempNode = tempNode.left;
			}

			if (stack.isEmpty()) {
				break;
			}
			tempNode = stack.pop();
			tempNode = tempNode.right;
		}
	}

	// 中序遍历
	public void printInOder() {
		Stack<Node> stack = new Stack<>();
		Node tempNode = root;
 		while (true) {
			while (tempNode != null) {
				stack.push(tempNode);
				tempNode = tempNode.left;
			}

			if (stack.isEmpty()) {
				break;
			}
			tempNode = stack.pop();
			printNode(tempNode);
			tempNode = tempNode.right;
		}
	}

	// 后序遍历
	public void printPostOder() {
		Stack<Node> stack = new Stack<>();
		Node tempNode = root;
		while (true) {

			while (tempNode != null) {
				if (tempNode.flag == true) {
					tempNode = tempNode.right;
				} else {
					stack.push(tempNode);
					tempNode = tempNode.left;
				}
			}

			tempNode = stack.pop();
			if (tempNode.flag == false) {
				stack.push(tempNode);
				tempNode.flag = true;
				tempNode = tempNode.right;
			} else {
				printNode(tempNode);
				if (stack.isEmpty()) {
					break;
				}
				tempNode = stack.peek();
				tempNode.flag = true;
			}
		}
	}

	// 层序遍历 利用队列
	public void printLevelOrder() {
		Queue<Node> queue = new LinkedList<>();
		Node tempNode = root;
		queue.offer(tempNode);
		while (!queue.isEmpty()) {
			Node topNode = queue.poll();
			if (topNode == null)
				continue;
			printNode(topNode);
			queue.offer(topNode.left);
			queue.offer(topNode.right);
		}
	}

	// 树高 递归,分别求出左子树的深度、右子树的深度,两个深度的较大值+1
	public int getHeightByRecursion(Node node) {
		if (node == null) {
			return 0;
		}
		int left = getHeightByRecursion(node.left);
		int right = getHeightByRecursion(node.right);
		return 1 + Math.max(left, right);
	}

	/**
	 * 为什么不直接写成调用 root,而是另写一个方法去调用呢 因为,这样可以不再为root,单独设置一个临时变量去存贮
	 * 而且也固定外部调用的方法,而不用关心内部的实现
	 */

	public void printHeight() {
		int height = getHeightByRecursion(root);
		System.out.print(height);
	}

	// 利用层序遍历,得到树的最大宽度
	public void printMaxWidth() {
		Queue<Node> queue = new LinkedList<>();
		Queue<Node> queueTemp = new LinkedList<>();

		int maxWidth = 1;
		Node tempNode = root;
		queue.offer(tempNode);
		while (!queue.isEmpty()) {
			while (!queue.isEmpty()) {
				Node topNode = queue.poll();
				if (topNode == null)
					continue;
				if (topNode.left.data != null) {
					queueTemp.offer(topNode.left);
				}

				if (topNode.right.data != null) {
					queueTemp.offer(topNode.right);
				}
			}

			maxWidth = Math.max(maxWidth, queueTemp.size());
			queue = queueTemp;
			queueTemp = new LinkedList<>();
		}
		System.out.print(maxWidth);
	}
}

下面是写的测试类

package test.tree;
import java.lang.reflect.Proxy;
public class BinaryTreeTest {

	public static void main(String[] args) {
		String treeStr = "A,B,D,#,#,#,C,#,E,#,#";
		// String treeStr = "A,#,#";
		AbstractBinaryTree binaryTree =  BinaryTreeTest.proxyBinaryTree(treeStr);
		binaryTree.printPostOder();
		binaryTree.printPostOderByRecursion();
		binaryTree.printPreOder();
		binaryTree.printPreOderByRecursion();
		binaryTree.printInOderByRecursion();
		binaryTree.printInOder();
		binaryTree.printLevelOrder();
		binaryTree.printHeight();
		binaryTree.printMaxWidth();
	}

	public static AbstractBinaryTree proxyBinaryTree(String treeStr) {
		AbstractBinaryTree binaryTree = new BinaryTree(treeStr);
		Object newProxyInstance = Proxy.newProxyInstance(binaryTree.getClass().getClassLoader(),
				binaryTree.getClass().getInterfaces(), (proxy, method, args) -> {
					System.out.println(method.getName());
					Object invoke = method.invoke(binaryTree, args);
					System.out.println();
					return invoke;
				});

		return (AbstractBinaryTree) newProxyInstance;
	}
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • JAVA二叉树的几种遍历(递归,非递归)实现

    首先二叉树是树形结构的一种特殊类型,它符合树形结构的所有特点.本篇博客会针对二叉树来介绍一些树的基本概念,二叉树的基本操作(存储,返回树的深度,节点个数,每一层的节点个数),二叉树的四种遍历(层次,先序,中序,后序) 一.基本概念 二叉树有5种基本形态: 注:二叉树有序树,就是说一个节点的左右节点是有大小之分的,我们通常设定为左孩子一定大于右孩子,下面的实现都是基于这个规则的.二叉树分为三种:满二叉树,完全二叉树,不完全二叉树 二叉树的四种遍历:层次,先序,中序,后序首先是非递归实现上图的满二叉

  • Java二叉树的四种遍历(递归和非递归)

    二叉树的遍历可以分为前序.中序.后序.层次遍历. 前中后是指何时访问中间节点,即前序遍历,遍历节点的顺序为:中->左->右: 中序遍历,遍历节点的顺序为:左->中->右: 后序遍历,遍历节点的顺序为:左->右->中. 前序遍历 递归实现 public void preorder_Traversal(TreeNode root) { if(root==null)return; //访问节点的逻辑代码块 System.out.print(root.val+" &q

  • Java源码解析之平衡二叉树

    一.平衡二叉树的定义 平衡二叉树是一种二叉排序树,其中每一个节点的左子树和右子树的高度差至多等于1 .它是一种高度平衡的二叉排序树.意思是说,要么它是一棵空树,要么它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1 .我们将二叉树上结点的左子树深度减去右子树深度的值称为平衡因子BF (Balance Factor),那么平衡二叉树上所有结点的平衡因子只可能是-1 .0 和1. 这里举个栗子: 仔细看图中值为18的节点,18的节点的深度为2 .而它的右子树的深度为0,其差

  • java二叉树的非递归遍历

    二叉树的递归遍历比较简单,这里就不聊了.今天主要聊聊二叉树的非递归遍历,主要借助于"栈"后进先出的特性来保存节点的顺序,先序遍历和中序遍历相对来说比较简单,重点理解后序遍历. 1. 先看看节点类型: //二叉树的节点类型 private class Node{ int data; //节点值 Node leftChild; //左孩子 Node rightChild; //右孩子 public Node(int data) { this.data=data; } } 2.先序遍历. 非

  • java栈实现二叉树的非递归遍历的示例代码

    一般来说遍历二叉树用到递归,但是用Stack进行遍历也是一个不错的方法. 二叉树设置 class Node{ public int val; public Node left; public Node right; public Node(int v) { val=v; left=null; right=null; } } public class Main { public static void main(String[] args) { Node head =new Node(0); No

  • java二叉树的几种遍历递归与非递归实现代码

    前序(先序)遍历 中序遍历 后续遍历 层序遍历 如图二叉树: 二叉树结点结构 public class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int x){ val=x; } @Override public String toString(){ return "val: "+val; } } 访问函数 public void visit(TreeNode node){ System.out.print(

  • Java 二叉树遍历的常用方法

    采用前序遍历.中序遍历.后续遍历实现时,即便采用不同的实现方式(递归方式.非递归),它们的算法结构是有很大的相似性.因而针对前三种的遍历我们会总结出对应通用的解决框架,便于在解决二叉树问题时进行使用. 递归方式 递归方式遍历二叉树时,无论是 前序遍历.中序遍历 还是 后续遍历 的方式,它们最大的区别就是对节点数据的访问位置不同.除此之外其结构完全一致,因而我们总结出如下的框架结构: void traverse(TreeNode root) { //终止条件 if(root == null) re

  • Java数据结构学习之二叉树

    1 背景知识:树(Tree) 在之前的笔记中,我们介绍的链表.栈.队列.数组和字符串都是以线性结构来组织数据的.本篇笔记要介绍的树采用的是树状结构,这是一种非线性的数据组织形式. 树结构由节点和边构成,且不存在环.我们曾在线性表型的数据结构中介绍过循环链表和循环队列,这两种数据结构使得存储容器中的元素形成一个闭环,具体可参看"数据结构学习笔记"系列的相关博文,链接贴在下面: 链表:https://www.jb51.net/article/215278.htm 队列:https://ww

  • Java求解二叉树的最近公共祖先实例代码

    一.题目 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先. 百度百科中最近公共祖先的定义为:"对于有根树 T 的两个结点 p.q,最近公共祖先表示为一个结点 x,满足 x 是 p.q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)." 例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4] 二.分析 本题需要找公共祖先,如果可以从下往上查找,就可以很方便的找到公共祖先 所以需要先访问叶子节点,然后在往上访问,对应着二叉树的

  • java如何创建普通二叉树

    java创建二叉树 这段时间一直在复习数据结构的知识. 从最基础的开始,实现一个普通的二叉树.但发现也不那么简单.因为之前学数据结构时是用C语言写的. 指针用来对结构体的值操作比较好理解.但java没有指针. 而Node节点在方法中传递的是地址. 如果直接对形参进行new操作是错误的.无法改变实参的值的.这一点坑了我很久,然后一顿查资料. 时隔很久,终于填上这个坑了 下面是以递归创建的二叉树.还有一些常见的遍历和树的高度与树的最大宽度. 一个方法不能修改一个基本数据类型的参数 一个方法可以修改一

  • 详解Spring Boot 使用Java代码创建Bean并注册到Spring中

    从 Spring3.0 开始,增加了一种新的途经来配置Bean Definition,这就是通过 Java Code 配置 Bean Definition. 与Xml和Annotation两种配置方式不同点在于: 前两种Xml和Annotation的配置方式为预定义方式,即开发人员通过 XML 文件或者 Annotation 预定义配置 bean 的各种属性后,启动 spring 容器,Spring 容器会首先解析这些配置属性,生成对应都?Bean Definition,装入到 DefaultL

  • java 线程创建多线程详解

    Java 线程类也是一个 object 类,它的实例都继承自 java.lang.Thread 或其子类. 可以用如下方式用 java 中创建一个线程,执行该线程可以调用该线程的 start()方法: Tread thread = new Thread(); thread.start(); 在上面的例子中,我们并没有为线程编写运行代码,因此调用该方法后线程就终止了. 编写线程运行时执行的代码有两种方式:一种是创建 Thread 子类的一个实例并重写 run 方法,第二种是创建类的时候实现 Run

  • java存储以及java对象创建的流程(详解)

    java存储: 1)寄存器:这是最快的存储区,位于处理器的内部.但是寄存器的数量有限,所以寄存器根据需求进行分配.我们不能直接进行操作. 2)堆栈:位于通用RAM中,可以通过堆栈指针从处理器那里获取直接支持.堆栈指针往下移动,则分配新的内存.网上移动,则释放内存.但是 在创建程序的时候必须知道存储在堆栈中的所有项的具体生命周期,以便上下的移动指针.一般存储基本类型和java对象引用. 3)堆:位于通用RAM中,存放所有的java对象,不需要知道具体的生命周期. 4)常量存储:常量值通常直接存放在

  • java实现创建缩略图、伸缩图片比例生成的方法

    本文实例讲述了java实现创建缩略图.伸缩图片比例生成的方法.分享给大家供大家参考.具体实现方法如下: 该实例支持将Image的宽度.高度缩放到指定width.height,并保存在指定目录 通过目标对象的大小和标准(指定)大小计算出图片缩小的比例,可以设置图片缩放质量,并且可以根据指定的宽高缩放图片. 具体代码如下所示: 复制代码 代码如下: package com.hoo.util;   import java.awt.Image; import java.awt.image.Buffere

  • Java中创建ZIP文件的方法

    java创建zip文件的代码如下如下: import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; public cla

  • Java实现创建运行时类的对象操作示例

    本文实例讲述了Java实现创建运行时类的对象操作.分享给大家供大家参考,具体如下: 获取运行时类的方法: public void test() throws ClassNotFoundException { /* * Class类是反射的源头 * 创建一个类,通过编译(javac.exe),生成对应的.class文件,之后使用java.exe加载(JVM的类加载器完成的)此.class文件. * 此.class文件加载到内存后,就是一个运行时类,存放在缓存区. * 那么这个运行时类本身就是一个C

  • 通过实例了解Java 8创建Stream流的5种方法

    这篇文章主要介绍了通过实例了解Java 8创建Stream流的5种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 不知不觉间,Java已经发展到13了,来不及感慨时间过得真的太快了,来不及学习日新月异的技术更新,目前大多数公司还是使用的JDK8版本,一方面是版本的稳定,另一方面是熟悉,所以很多公司都觉得不升级也挺好. 说到JDK8,真的是一个里程碑的版本,一出世就受到所有开发者的青睐,并主动花时间和精力去学习,也是我见过企业升级JDK最豪爽

  • JAVA spark创建DataFrame的方法

    述说正传,接下来开始说正事. 以前用Python和Scala操作Spark的时候比较多,毕竟Python和Scala代码写起来要简洁很多. 今天一起来看看Java版本怎么创建DataFrame,代码写起来其实差不多,毕竟公用同一套API.测试数据可以参考我之前的文章. 先来总结下Spark的一般流程: 1,先创建Spark基础变量,spark,sc 2,加载数据,rdd.textFile,spark.read.csv/json等 3,数据处理,mapPartition, map,filter,r

随机推荐