java 树的同构_【JAVA】复习数据结构——树

本文介绍了二叉树的基本概念,包括树的定义、特点和相关术语。重点讨论了二叉树,强调其重要性,并讲解了完全二叉树的性质。文章通过Java代码展示了二叉树的创建、遍历(前序、中序、后序)、深度计算、节点计数等操作,还包括了判断两棵二叉树是否同构的方法。
摘要由CSDN通过智能技术生成

我的复习一般是按照自己需求而定,所以不一定是按照顺序来的,在此也是为了怕自己忘记今天所学,特地记下来。

首先什么是树结构?

树是一种描述非线性层次关系的数据结构,树是n个数据结点的集合,这些集结点包含一个根节点,根节点下有着互相不交叉的子集合,这些子集合便是根节点的子树。

树的特点

在一个树结构中,有且仅有一个结点没有直接前驱,它就是根节点。

除了根节点,其他结点有且只有一个直接前驱

每个结点可以有任意多个直接后继

树的名词解释

结点的度:一个结点包含子树的数量。

树的度:该树所有结点中最大的度。

兄弟结点:具有同一父结点的结点称为兄弟结点。

树的深度(高度):叶子结点的深度(高度)为1,根节点深度(高度)最高;

层数:从树根开始算,树根是第一层,以此类推。

森林:由多个树组成

只说干货,现在直接复习最重要的——二叉树

二叉树

二叉树是一种超级超级超级重要的数据结构!也是树表家族最为基础的结构

先看看定义:二叉树嘛,每个结点最多只能有二棵子树,二叉树的子树有左右之分,次序不能颠倒

再看看完全二叉树的性质:

第i层至多有 2的i次方减一 个结点

深度为k的二叉树至多有2k-1个结点

任意二叉树,度为0的节点数=度为2的节点数+1;

如果i为父亲的编号,则孩子的编号为2i和2i+1;

如果孩子编号为n,父亲结点编号为k/2,向下取整

关于树的度:

二叉树中连接节点和节点的线就是度

上面说过——度为0的节点数为度为2的节点数加1,即n0=n2+1

这个公式的推理方法如下:

设:

k:总度数

k+1:总节点数

n0:度为0的节点

n1:度为1的节点

n2:度为二的节点

根据二叉树中度和节点的守衡原理,可列出以下一组方程:

k=n2*2+n1;

k+1=n2+n1+n0;

根据方程可以求出n0=n2+1

基本概念不说,研究一下二叉树的遍历

二叉树的遍历

首先,我们看看前序、中序、后序遍历的特性:

前序遍历: (根——左——右)

1.访问根节点

2.前序遍历左子树

3.前序遍历右子树

中序遍历: (左——根——右)

1.中序遍历左子树

2.访问根节点

3.中序遍历右子树

后序遍历: (左——右——根)

1.后序遍历左子树

2.后序遍历右子树

3.访问根节点

要知道:

中序遍历是很重要的一个判断参照!如果只给我们前序遍历和后序遍历的结果,我们将无法推导出唯一的树。

给我们前序遍历和中序遍历,我们可以推出中序

给我们后序遍历和中序遍历,我们可以推出中序

好了,已经写了很多但是并没有拿出来什么真干货,接下来用代码写一下关于树的操作,这里先讲好——对于“树”这个运行结构,最重要的概念是“递归”,要想把树的概念理解好,必须先培养递归的思想。我向这篇文章:写递归函数的正确思想学习了一下,真的写的很棒,条理清晰而且有详有略。在这之后我就希望通过自己亲手敲代码的方式去学习,但是研究了两天,依然进展比较一般,最后通过研究各方博客的C代码,从而找到了思路。不再说废话了,这东西,必须通过自己动手敲才能有收获,好的接下来就要拿代码出来了。这里感谢这片文章二叉树题目实现

以下则是用java代码去写的,注解已经很详细,没有分开写,时不时有新体会也会去添加或修改:

二叉树类:

package learnTree;

import java.util.LinkedList;

import java.util.List;

import java.util.Queue;

import java.util.Scanner;

/**

* Created by AceCream on 2017/3/14.

* 二叉树

*/

class TreeNode {

int key = 0; //key 为层序编码

String data = null; //data 为数据域

boolean isVisited = false;

/*树的每一个节点的数据结构都是TreeNode类型,

createBinTree里定义的root为TreeNode类型,所以左右孩子也为TreeNode类型,

加上二叉树的递归思想,所以所有节点都是TreeNode类型

*/

TreeNode leftChild = null;

TreeNode rightChild = null;

public TreeNode(int key,String data){

this.key = key;

this.data = data;

this.isVisited = false;

this.leftChild = null;

this.rightChild = null;

}

}

public class BinaryTree {

//二叉树通常用树结点结构存储,有时也包含指向唯一父节点的指针

TreeNode root = null;

//BinaryTree()该方法与类名字相同,所以是构造方法,被默认强制为void

//初始化根

public BinaryTree(){

root = new TreeNode(1,"A");

}

//创建二叉树bt,树由结点构成

public void createBinTree(TreeNode root){

TreeNode newNodeB = new TreeNode(2,"B");

TreeNode newNodeC = new TreeNode(3,"C");

TreeNode newNodeD = new TreeNode(4,"D");

TreeNode newNodeE = new TreeNode(5,"E");

TreeNode newNodeF = new TreeNode(6,"F");

//填充它

root.leftChild = newNodeB;

root.rightChild = newNodeC;

root.leftChild.leftChild = newNodeD;

root.leftChild.rightChild = newNodeE;

root.rightChild.rightChild = newNodeF;

}

//创建二叉树bt2,树由结点构成

public void createBinTree2(TreeNode root){

TreeNode newNodeB = new TreeNode(2,"B");

TreeNode newNodeC = new TreeNode(3,"C");

TreeNode newNodeD = new TreeNode(4,"D");

TreeNode newNodeE = new TreeNode(5,"E");

//填充它

root.leftChild = newNodeB;

root.rightChild = newNodeC;

root.leftChild.leftChild = newNodeD;

root.leftChild.rightChild = newNodeE;

}

//访问结点,输出结点,便于我们查看效果

public void visitNode(TreeNode node){

System.out.print(node.data+" ");

}

//1.获取结点个数

//递归解法:

//(1)如果二叉树为空,节点个数为0

//(2)如果二叉树不为空,二叉树节点个数 = 左子树节点个数 + 右子树节点个数 + 1

public int size(){

return size(root);

}

//通过递归求size

public int size(TreeNode subtree){

if (subtree==null){

return 0;

}else {

return 1+size(subtree.leftChild)+size(subtree.rightChild);

}

}

//2.求深度

//递归解法:

//(1)如果二叉树为空,二叉树的深度为0

//(2)如果二叉树不为空,二叉树的深度 = max(左子树深度, 右子树深度) + 1

public int getDepth(TreeNode root){

if (root==null){

return 0;

}else {

int depthLeft = getDepth(root.leftChild);

int depthRight = getDepth(root.rightChild);

return depthLeft > depthRight ? (depthLeft+1) : (depthRight+1);

}

}

//3. 前序遍历,中序遍历,后序遍历

//a.前序遍历

//前序遍历递归解法:

//(1)如果二叉树为空,空操作

//(2)如果二叉树不为空,访问根节点,前序遍历左子树,前序遍历右子树

public void preOrderTraverse(TreeNode root){

if (root!=null){

visitNode(root);//此处访问根节点

preOrderTraverse(root.leftChild);

preOrderTraverse(root.rightChild);

}

}

//b.中序遍历

//中序遍历递归解法

//(1)如果二叉树为空,空操作。

//(2)如果二叉树不为空,中序遍历左子树,访问根节点,中序遍历右子树

public void inOrderTraverse(TreeNode root){

if (root!=null){

inOrderTraverse(root.leftChild);

visitNode(root);//此处访问根节点

inOrderTraverse(root.rightChild);

}

}

//c.后序遍历

//后序遍历递归解法

//(1)如果二叉树为空,空操作。

//(2)如果二叉树不p为空,后序遍历左子树,后序遍历右子树,访问根节点,

public void postOrderTraverse(TreeNode root){

if (root!=null){

postOrderTraverse(root.leftChild);

postOrderTraverse(root.rightChild);

visitNode(root);//此处访问根节点

}

}

//4.分层遍历二叉树(按层次从上往下,从左往右)

//相当于广度优先搜索,使用队列实现。队列初始化,将根节点压入队列。

//当队列不为空,进行如下操作:弹出一个节点,访问,若左子节点或右子节点不为空,将其压入队列。

public void levelTraverse(TreeNode root){

if (root==null){

return;

}

Queue queue = new LinkedList<>();

//让根节点入队(队列:先进先出)

queue.offer(root);

while (!queue.isEmpty()){

//让元素出队

TreeNode node = queue.poll();

//访问它 这里就是用visit方法输出看效果~~

visitNode(node);

if (node.leftChild!=null){

queue.offer(node.leftChild);

}

if (node.rightChild!=null){

queue.offer(node.rightChild);

}

}

return;

}

//5.求二叉树第K层的节点个数

//递归解法:

//(1)如果二叉树为空或者k<1返回0

//(2)如果二叉树不为空并且k==1,返回1

//(3)如果二叉树不为空且k>1,返回左子树中k-1层的节点个数与右子树k-1层节点个数之和

public int getNodeNumKthLevel(TreeNode root,int k){

if (root==null||k<1){

return 0;

}else if (k==1){

return 1;

}else {

int leftNum = getNodeNumKthLevel(root.leftChild,k-1);

int rightNum = getNodeNumKthLevel(root.rightChild,k-1);

return leftNum+rightNum;

}

}

//6.求二叉树中叶子节点的个数

// 递归解法:

//(1)如果二叉树为空,返回0

//(2)如果二叉树不为空且左右子树为空,返回1

//(3)如果二叉树不为空,且左右子树不同时为空,返回左子树中叶子节点个数加上右子树中叶子节点个数

public int getLeafNodeNum(TreeNode root){

if (root==null){

return 0;

}else if (root.leftChild==null&&root.rightChild==null){

return 1;

}else {

int leftNum = getLeafNodeNum(root.leftChild);

int rightNum = getLeafNodeNum(root.rightChild);

return leftNum+rightNum;

}

}

//8. 判断两棵二叉树是否结构相同

//不考虑数据内容。结构相同意味着对应的左子树和对应的右子树都结构相同。

//递归解法:

//(1)如果两棵二叉树都为空,返回真

//(2)如果两棵二叉树一棵为空,另一棵不为空,返回假

//(3)如果两棵二叉树都不为空,如果对应的左子树和右子树都同构返回真,其他返回假

public boolean StructureCmp(TreeNode root1,TreeNode root2){

if (root1==null&&root2==null){

//都为空,返回真

return true;

}else if (root1==null||root2==null){

//一个为空,一个不为空,返回假

return false;

}else {

//都不为空

boolean leftResult = StructureCmp(root1.leftChild,root2.leftChild);

boolean rightResult = StructureCmp(root1.rightChild,root2.rightChild);

//都同构则为真,否则为假

return leftResult&&rightResult;

}

}

}

以上是方法。接下来是测试类:主函数

package learnTree;

/**

* Created by AceCream on 2017/3/14.

*/

public class Main {

public static void main(String[] args) {

BinaryTree bt = new BinaryTree();

BinaryTree bt2 = new BinaryTree();

/*

由初始化的时候可知:我创建了一个这样的树,供查看写的方法是否正确

这棵树起名为bt: 这棵树起名为bt2 供比较

A A

/ \ / \

B C B C

/\ / /\

D E F D E

*/

bt.createBinTree(bt.root);

bt2.createBinTree2(bt2.root);

//1.看结点数

System.out.println("1.树的结点个数为:" + bt.size(bt.root));

//2.看深度

System.out.println("2.树的深度为:"+bt.getDepth(bt.root));

//3. 前序遍历,中序遍历,后序遍历

System.out.print("3-1.先序遍历:");

bt.preOrderTraverse(bt.root);

System.out.println();

System.out.print("3-2.中序遍历:");

bt.inOrderTraverse(bt.root);

System.out.println();

System.out.print("3-3.后序遍历:");

bt.postOrderTraverse(bt.root);

System.out.println();

//4.将二叉树用层次遍历

System.out.print("4.二叉树层次遍历:");

bt.levelTraverse(bt.root);

System.out.println();

//5.二叉树K层有多少个结点:由上图绘制可知:0层没有,1层有一个根节点,第二层有俩,第三次有仨

System.out.println("5.二叉树K层结点个数:");

System.out.println(" 当K=0时有"+bt.getNodeNumKthLevel(bt.root,0)+"个结点");

System.out.println(" 当K=1时有"+bt.getNodeNumKthLevel(bt.root,1)+"个结点");

System.out.println(" 当K=2时有"+bt.getNodeNumKthLevel(bt.root,2)+"个结点");

System.out.println(" 当K=3时有"+bt.getNodeNumKthLevel(bt.root,3)+"个结点");

//6.求二叉树中叶子节点的个数

System.out.print("6.二叉树叶子节点个数:");

System.out.println(bt.getLeafNodeNum(bt.root));

//7.比较两个二叉树相不相同

System.out.println("7.测试两树结构是否相同:");

System.out.println(" b1和b1:"+bt.StructureCmp(bt.root,bt.root));

System.out.println(" b1和b2:"+bt.StructureCmp(bt.root,bt2.root));

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值