java实现树的双亲表示法
C++版
package com.treetest.test01;
import java.util.Arrays;
import java.util.Scanner;
//孩子双亲数组表示法
public class TreeOrder01 {
public static void main(String[] args) {
System.out.println("输入树的结点个数:");
int num=new Scanner(System.in).nextInt();
nodetree t=new nodetree(num);
t.creatTree();
System.out.println(t);
}
}
class nodedata{//结点内容 + 双亲位置
int parent;
Object element;
public nodedata() {}
@Override
public String toString() {
/*return "nodedata{" +
"parent=" + parent +
", element=" + element +
'}';*///"parent"+"\t"+"\t"+"element"+"\t"
return parent+"\t"+element+"\n";
}
}
class nodetree extends nodedata{//树的结点
nodedata[] data;
//int root;
int numchild;
public nodetree(int num) {
this.numchild=num;
data=new nodedata[numchild];//这里是建立N个nodedata类型的数组 并没有new对象
//this.root=-1;
}
public void creatTree(){
System.out.println("根结点的父结点为-1,请输入后续结点内容和双亲位置:");
System.out.println("结点个数:"+this.numchild);
Scanner scanner=new Scanner(System.in);//下面输入的时候发现一个scanner,无法负责不同类型的输入
Scanner scanner2=new Scanner(System.in);
for (int i=0;i<this.numchild;i++){
nodedata d=new nodedata();
data[i]=d;
if(i==0){
System.out.println("这是根结点,其双亲结点下标为-1");
data[i].parent=-1;
System.out.println("输入根结点内容");
data[i].element=scanner.nextLine();
}else{
System.out.println("输入第"+(i+1)+"个结点内容");
data[i].element=scanner.nextLine();
System.out.println("输入第"+(i+1)+"个结点parent位置");
data[i].parent=scanner2.nextInt();
while (data[i].parent>=i){
System.out.println("parent下标错误,请再输入:");
data[i].parent=scanner2.nextInt();
}
}
}
}
public static String toStringshuzu(Object[] a){
if (a == null)
return "null";
int iMax = a.length - 1;
if (iMax == -1)
return "[]";
StringBuilder b = new StringBuilder();
//b.append(' ');
for (int i = 0; ; i++) {
b.append(String.valueOf(a[i]));
if (i == iMax)
return b.toString();
//b.append("\t");
}
}
@Override
public String toString() {
System.out.println("parent"+"\t"+"element");
return toStringshuzu(data);
}
}
C++版本如下
数据结构——树|N叉树之孩子双亲表示法——顺序存储结构+链表
数据结构Java实现——树|N叉树之孩子双亲表示法——顺序存储结构+链表(细节部分就不一一实现)
package com.treetest.test01;
import java.util.Arrays;
import java.util.Scanner;
/*
* 孩子表示法
* 数组+链表表示
* 数组保存所有的结点模式,链表保存其相关的孩子结点 构成单链表
*一个结点元素: 内容+孩子指针个数n+n个孩子指针
* 那么涉及到三个结点
* 01第一个孩子的兄弟指针
* 02结点结构:元素+双亲下标+第一个孩子指针
* 03 树的整体结构 结点个数+根结点下标+各个结点的结构(指向上一级结点)
* */
public class TreeLink01 {
public static void main(String[] args) {
TreeNode t=new TreeNode(9);
t.creatrootnode();
t.Addnode();
System.out.println(t);
}
}
/*孩子结点
* 孩子可以指向同双亲的其他孩子指针
* */
class ChildNode{
int child;//用来存储 某个孩子结点 在其父结点下的下标
ChildNode next;//用来存储同一结点的下一孩子结点
}
/*表头结构
* 每个结点有三部分
* 元素
* 保存双亲信息
* 第一个孩子指针
* */
class BoxNode{
Object data;
int parent;
ChildNode firstchild;
@Override
public String toString() {
return parent+"\t"+" "+data+"\n";
}
}
/*树结构
* 结点个数 --->数组结构
* 根结点双亲位置 设定 -1
* */
class TreeNode{
BoxNode[] nodes;
int roon,numnode;
public TreeNode(int numnode) {
this.numnode = numnode;
nodes=new BoxNode[this.numnode];
System.out.println("根结点的双亲位置指向-1");
this.roon=-1;
}
/*创建一个根结点*/
public void creatrootnode(){
BoxNode b=new BoxNode();
nodes[this.roon+1]=b;
System.out.println("请根结点赋值:");
nodes[this.roon+1].data= new Scanner(System.in).nextLine();
//根结点赋值 之后 要构造其与双亲的关系
nodes[this.roon+1].parent=this.roon;//根结点 指向-1 双亲
//该方法只构造一个根结点,所以孩子皆为空
nodes[this.roon+1].firstchild=null;
/*其他结点先设定为null*/
/* for (int i = this.roon+2; i
}
/*向已经建立根结点的树添加其他结点内容*/
public void Addnode(){
System.out.println("向已经建立根结点的树添加其他结点内容");
for (int i = this.roon+2; i < this.numnode; i++) {
BoxNode b2=new BoxNode();
nodes[i]=b2;
System.out.println("输入第"+(i+1)+"结点元素");
nodes[i].data=new Scanner(System.in).nextLine();
System.out.println("输入第"+(i+1)+"结点双亲下标");
Scanner scanner0=new Scanner(System.in);
nodes[i].parent=scanner0.nextInt();
while (nodes[i].parent>=i){
System.out.println("parent下标错误,请再输入:");
nodes[i].parent=scanner0.nextInt();
}
//当建立后续结点之后 就要考虑其兄弟信息
//建立一个新的孩子结点
ChildNode p=new ChildNode();
p.child=i;
p.next=null;
int tem_p=nodes[i].parent;//保存双亲信息
if(nodes[tem_p].firstchild==null){//如果此时构造的结点 的双亲的第一个孩子不存在,那么将孩子结点p赋予它
//这个时候孩子结点p 保存了新生成结点的信息
nodes[tem_p].firstchild=p;
}else{//如果其本身有第一个孩子 ,那么就将新生成的结点 保存在其第一个孩子的后面,构成一个单链表
ChildNode tem_c=nodes[tem_p].firstchild;
while(tem_c.next!=null){
tem_c=tem_c.next;
}
tem_c.next=p;
}
}
}
//
public static String toStringshuzu(Object[] a){
if (a == null)
return "null";
int iMax = a.length - 1;
if (iMax == -1)
return "[]";
StringBuilder b = new StringBuilder();
//b.append(' ');
for (int i = 0; ; i++) {
b.append(String.valueOf(a[i]));
if (i == iMax)
return b.toString();
//b.append("\t");
}
}
@Override
public String toString() {
System.out.println("parent"+"\t"+"element");
return toStringshuzu(nodes);
}
}
数据结构——二叉树|二叉树的链式表示|前序输出|后续输出|中序输出|销毁等操作
单个单个加入
package com.treetest.test01;
public class BTreeTest01 {
public static void main(String[] args) {
TreeBinary t=new TreeBinary();
t.add(5);
t.add(2);;
t.add(6);
t.add(3);
}
}
class Node{
int data;
Node Left;
Node Right;
public Node(int data) {
this.data = data;
Left = null;
Right = null;
}
}
class TreeBinary{
Node root;//定义一个根结点,以此为开始
public TreeBinary() {
}
//我的想法是 循环生成二叉树,而不是单个单个加
//既然之前都行不通,那先尝试单个单个加的
private Node addNode(Node currentnode,int value){
if(currentnode==null)
return new Node(value);
if (value < currentnode.data) {
currentnode.Left = addNode(currentnode.Left, value);
} else if (value > currentnode.data) {
currentnode.Right = addNode(currentnode.Right, value);
} else {
return currentnode;
}
return currentnode;
}
public void add(int v){
root=addNode(root,v);
}
}
循环遍历加入
import java.util.Scanner;
public class BTreeTest {
public static void main(String[] args) {
Btree tree=new Btree();
tree.creatTree(tree.root);
System.out.println("****前序输出树****");//这三种区分是按照根结点来的,先输出根 就是前序,先输左 根 右 就是中序输出
tree.printbefore(tree.root);
System.out.println();
System.out.println("****中序输出树****");
tree.printmid(tree.root);
System.out.println();
System.out.println("****后序输出树****");
tree.printafter(tree.root);
}
}
class BNode<E>{
E data;
BNode Lchild;
BNode Rchild;
public BNode(E data) {
this.data = data;
this.Lchild=null;
this.Rchild=null;
}
public BNode() {
}
}
class Btree{
BNode root;
public Btree() {
}
public BNode creatTree(BNode node){
System.out.println("input a element:");
Scanner scanner=new Scanner(System.in);
String value=scanner.nextLine();
if(value.equals("#")){//遇到# 结束此分支插入。
return null;
}
if(root==null){
node=new BNode<String>();
node.data=value;
root=node;
//return root;
System.out.println("插入根结点的左子树:");
root.Lchild=creatTree(root.Lchild);//一开始总是连接不起来,最后原因是因为结点之间没有相连接,这和C++不一样,
// C++有指针,所以实际上是指向的,而这里是引用,所以要我们自己加
//creatTree(root.Lchild);
System.out.println("插入根结点的柚子树:");
root.Rchild=creatTree(root.Rchild);
//creatTree(root.Rchild);
return root;
}else{
node=new BNode();
node.data=value;
System.out.println("插入"+node.data+"结点的左子树:");
node.Lchild=creatTree(node.Lchild);
//creatTree(node.Lchild);
System.out.println("插入"+node.data+"结点的柚子树:");
node.Rchild=creatTree(node.Rchild);
//creatTree(node.Rchild);
return node;
}
}
@Override
public String toString() {
return "Btree{" +
"root=" + root +
'}';
}
public int printbefore(BNode node){
if(node==null)
return 0;
else
System.out.print(node.data);
if(node.Lchild!=null)
printbefore(node.Lchild);
if(node.Rchild!=null)
printbefore(node.Rchild);
return 0;
}
public int printmid(BNode node){
if(node==null)
return 0;
if (node.Lchild!=null)
printmid(node.Lchild);
System.out.print(node.data);
if(node.Rchild!=null)
printmid(node.Rchild);
return 0;
}
public int printafter(BNode node){
if(node==null)
return 0;
if(node.Lchild!=null)
printafter(node.Lchild);
if(node.Rchild!=null){
printafter(node.Rchild);
}
System.out.print(node.data);
return 0;
}
}
前序线索化和后续线索化都相对简单
和遍历二叉树类似
把握好
if(tree.lchild==null){
tree.child=tree;
tree.ltag=1;
}
if(pre!=null&&pre.rchild==null){
pre.child=tree;
pre.Rtag=1;
}
pre=p;
在进入左右子树的前后位置就好
稍难处在于遍历输出已经线索化的二叉树
遍历输出前序线索二叉树
思路:对于相对中间结点来说,先中间 后左右
所以第一个是根结点,后面是左边的相对根结点
所以在找到最左边的子树的时候,每次都对其相对根结点做处理
因为线索化,其左右孩子之间已经建立了联系
所以这时候我们根据线索标记 来寻找相对右结点的结点
而在最右边一个结点记得做null跳出处理。
public void Beoutputtree(CBNode tree){
if (tree==null)
return;
else {
while (tree.Ltag==0){//找到最边上一个结点
System.out.println(tree.data);
tree=tree.lchild;
}
//tree=H
while (tree!=null){
System.out.println(tree.data);
if(tree.rchild==null)//最后一个结点右孩子为空时跳出
return;
if (tree.Rtag==1){
tree=tree.rchild;
}else {
tree=tree.lchild;
}
}
}
}
实现
package com.treetest.test01;
import java.util.Scanner;
public class clueBTree {
public static void main(String[] args) {
CBTree tree=new CBTree();
tree.CreatBTree(tree);
//前序线索
tree.BeThread(tree.root);
tree.Beoutputtree(tree.root);
//中序线索
tree.MidThread(tree.root);
tree.Mioutputtree(tree.root);
//后序线索
tree.aftThread(tree.root);
}
}
/*线索化二叉树 在进行遍历二叉树的时候,所得是一个单向链表,如果我们在遍历过程 按照遍历顺序将每个结点前后加上线索,即可以形成一个双向链表*/
class CBNode<E>{
E data;
CBNode rchild;
CBNode lchild;
int Ltag;
int Rtag;
public CBNode(E data) {
this.data = data;
this.rchild = null;
this.lchild = null;
this.Ltag=0;
this.Rtag=0;
}
public CBNode(){
}
}
class CBTree extends CBNode{
CBNode root;
public CBTree() {
}
public CBNode CreatBTree(CBNode node){
System.out.println("输入一个结点信息:");
Scanner scanner=new Scanner(System.in);
String ele=scanner.nextLine();
if(ele.equals("#")){
return null;
}
if(root==null){
node=new CBNode(ele);
root=node;
System.out.println("插入根结点的左子树:");
root.lchild=CreatBTree(root.lchild);
System.out.println("插入根结点的柚子树:");
root.rchild=CreatBTree(root.rchild);
return root;
}else{
node=new CBNode();
node.rchild=null;
node.lchild=null;
node.Rtag=node.Ltag=0;//添加时都没有前驱后继信息
node.data=ele;
System.out.println("插入"+node.data+"结点的左子树:");
node.lchild=CreatBTree(node.lchild);
System.out.println("插入"+node.data+"结点的柚子树:");
node.rchild=CreatBTree(node.rchild);
return node;
}
}
//前序线索化 将线索化 放在前面 进入子节点放后面,pre标记 一直跟在线索化后面 要不断更新pre
private CBNode pre;
public void BeThread(CBNode p){
if(p==null)
return;
else {
if(p.lchild==null){
p.Ltag=1;
p.lchild=pre;
}
if(pre!=null&&pre.rchild==null){
pre.rchild=p;
pre.Rtag=1;
}
pre=p;
if(p.Ltag==0){
BeThread(p.lchild);
}
if(p.Rtag==0){
BeThread(p.rchild);
}
}
}
/*遍历输出前序线索二叉树*/
public void Beoutputtree(CBNode tree){
if (tree==null)
return;
else {
while (tree.Ltag==0){//找到最边上一个结点
System.out.println(tree.data);
tree=tree.lchild;
}
//tree=H
while (tree!=null){
System.out.println(tree.data);
if(tree.rchild==null)//最后一个结点右孩子为空时跳出
return;
if (tree.Rtag==1){
tree=tree.rchild;
}else {
tree=tree.lchild;
}
}
}
}
// 中序线索化 将进入左右孩子分支 放在首位 线索化放中间
public void MidThread(CBNode p){
if (p==null)
return;
else {
MidThread(p.lchild);
if (p.lchild==null){
p.Ltag=1;
p.lchild=pre;
}
if(pre!=null&&pre.rchild==null){
pre.Rtag=1;
pre.rchild=p;
}
pre=p;
MidThread(p.rchild);
}
}
public void Mioutputtree(CBNode tree){
if (tree!=null){//先直接遍历到最左边的左子树
while (tree.Ltag==0){
tree=tree.lchild;
}
while (tree!=null){
System.out.println(tree.data);
if(tree.Rtag==1){//如果右子树 是线索,回到那
tree=tree.rchild;
}else{//如果不是线索 那么我们没有必要再走一次 不然进入两个结点的死循环
tree=tree.rchild;
if(tree==null)
return;
while (tree.Ltag==0&&tree!=null){
tree=tree.lchild;
}
}
}
}
}
public void aftThread(CBNode p){
if(p==null){
return;
}else {
if (p.Ltag==0){
aftThread(p.lchild);
}
if(p.Rtag==0){
aftThread(p.rchild);
}
if(p.lchild==null){
p.lchild=pre;
p.Ltag=1;
}
if(pre!=null&&pre.rchild==null){
pre.rchild=p;
pre.Rtag=1;
}
pre=p;
}
}
//后续线索化输出 比较难 单独讲
}
遍历后续线索二叉树,可以看出,只有每条支路的最左边才和左结点有联系
其他的都是和其相对根节点建立起了联系
因此要建立双亲指针,来寻找左右兄弟
class CBNode<E>{
E data;
CBNode rchild;
CBNode lchild;
int Ltag;
int Rtag;
CBNode parent;
public CBNode(E data) {
this.data = data;
this.rchild = null;
this.lchild = null;
this.parent=null;
this.Ltag=0;
this.Rtag=0;
}
public CBNode(){
}
}
建立二叉树时建立双亲指向
class CBTree extends CBNode{
CBNode root;
public CBTree() {
}
public CBNode CreatBTree(CBNode node){
System.out.println("输入一个结点信息:");
Scanner scanner=new Scanner(System.in);
String ele=scanner.nextLine();
if(ele.equals("#")){
return null;
}
if(root==null){
node=new CBNode(ele);
root=node;
System.out.println("插入根结点的左子树:");
root.lchild=CreatBTree(root.lchild);
if(root.lchild!=null)
root.lchild.parent=root;
System.out.println("插入根结点的柚子树:");
root.rchild=CreatBTree(root.rchild);
if (root.rchild!=null)
root.rchild.parent=root;
return root;
}else{
node=new CBNode();
node.data=ele;
System.out.println("插入"+node.data+"结点的左子树:");
node.lchild=CreatBTree(node.lchild);
System.out.println("插入"+node.data+"结点的柚子树:");
node.rchild=CreatBTree(node.rchild);
if (node.lchild!=null)
node.lchild.parent=node;
if (node.rchild!=null)
node.rchild.parent=node;
return node;
}
}
后序线索化+遍历后续二叉树
public void aftThread(CBNode p){
if(p==null){
return;
}else {
if (p.Ltag==0){
aftThread(p.lchild);
}
if(p.Rtag==0){
aftThread(p.rchild);
}
if(p.lchild==null){
p.lchild=pre;
p.Ltag=1;
}
if(pre!=null&&pre.rchild==null){
pre.rchild=p;
pre.Rtag=1;
}
pre=p;
}
}
//后续线索化输出
//后续线索二叉树中,存在 相邻的左右结点 没有直接联系
//通过回溯父结点 来处理
public void aftOutputree(CBNode tree){
if (tree==null){
return;
}
while (tree!=null&&tree.Ltag==0){//先到最左端
tree=tree.lchild;
}
CBNode p=null;//保留遍历过程中上一个结点信息
while (tree!=null){
if(tree.Rtag==1){
System.out.println(tree.data);
p=tree;
tree=tree.rchild;
}else {
if (tree.rchild==p){//说明往右走会重复循环——需要往双亲走
System.out.println(tree.data);
//可能遇到根结点 后续到根节点就返回
if (tree==root)
return;
p=tree;
tree=tree.parent;
}else {//进入双亲结点 且进入右分支 寻找最左结点
tree=tree.rchild;
while (tree!=null&&tree.Ltag==0){
tree=tree.lchild;
}
}
}
}
}