Java基础复习笔记07数据结构树的概述.docx
《Java基础复习笔记07数据结构树的概述.docx》由会员分享,可在线阅读,更多相关《Java基础复习笔记07数据结构树的概述.docx(20页珍藏版)》请在冰豆网上搜索。
Java基础复习笔记07数据结构树的概述
Java基础复习笔记07数据结构-树的概述
刘岩
Email:
suhuanzheng7784877@
1.树的概念
如果线性表、栈、队列是线性结构(一维结构)的话,那么树就代表着一种非线性的、复杂的二维结构,何为线性结构、何为二维结构?
就是1对1的一条直线,每个元素都是这条线上的节点、节点之间只知道1VS1的、前后关系。
而二维结构就是一个面,1对N的一个面,这个面上的每一个元素都对应着多个此面上其他的元素。
树就是指N个有父子关系的节点的有限集合。
树仅仅只能有一个根节点。
除了根节点,其他没有子节点的节点叫做叶子节点。
树的遍历是最考究程序员算法的,因为后面的算法很多用到了一些概念,那先将树的有关概念解释一下。
节点的度:
节点拥有的子树的个数
树的度:
树中所有节点的度的最大值
节点的层次:
从根节点开始算起,根的层次为1,其余节点层次为父节点层次+1。
树的深度:
树中节点的最大层次值称为树的深度。
2.树的基本操作:
树的操作有以下:
为指定节点增加子节点、判断树是否为空、返回树的根节点、返回指定节点的父节点、返回指定节点的所有子节点集合、返回指定节点的第i个子节点、返回树的深度、返回指定节点的深度、返回指定节点的索引位置。
3.树的使用场景
树形结构使用场景非常之多,抛开编程语言不说,就看身边的例子。
大家windows的资源管理器、开发web系统的树形菜单、负载均衡的内核算法可以使用哈夫曼树来辅助实现、将要排序的数据用排序二叉树存储,查找的效率很快。
4.树的记父节点实现
书上管其叫做父节点表示法,这个名称我觉得很容易让人产生不解,所以笔者管它叫做记父节点实现更好一些。
它的算法的原理就是利用一个节点对象,该节点对象不仅记录了该节点的数据,还记录了此节点的父节点的位置。
那么我们访问这个节点的时候,实际上父节点也能间接的获取到了。
其实我们在做很多数据库设计的时候,遇到一个树形菜单基本上都是采用这种记父方式来实现的。
其实很多的看似二维的结构,数据库记录,都可以提取出相应的一种图形化的模型。
就像UML的类图关系就完全可以转化成关系型结构化数据库表模型,而数据库表结构也能提取出相应的领域模型一样。
主键
数据
父节点主键
0
根菜单
-1
1
子菜单1
0
2
子菜单2
0
3
子菜单11
1
上面的二维表结构可以描绘成以下结构
算法如下:
packagedateStructer.tree;
importjava.util.ArrayList;
importjava.util.List;
publicclassTreeParent{
/**
*节点结构
*/
publicclassNode{
//真正的数据域
privateEdate;
//记录父节点的索引位置
privateintparentIndex;
publicNode(){
}
publicNode(Edate,intparentIndex){
this.date=date;
this.parentIndex=parentIndex;
}
@Override
publicbooleanequals(Objectobj){
if(((Node)obj).date==this.date
&&((Node)obj).parentIndex==this.parentIndex)
returntrue;
returnfalse;
}
@Override
publicinthashCode(){
returnsuper.hashCode();
}
@Override
publicStringtoString(){
return(String)date;
}
}
//默认数组大小
privatefinalintDefSize=150;
//记录节点个数
privateintnodeSize;
//父节点对象
privateNode[]nodes;
publicTreeParent(){
nodes=newNode[DefSize];
}
publicTreeParent(Ee){
nodes=newNode[DefSize];
nodeSize++;
nodes[0]=newNode(e,-1);
}
/**
*为指定节点增加子节点
*
*@parame
*@paramparentNode
*@return
*/
publicbooleanaddNodeByParent(Ee,NodeparentNode){
for(inti=0;iif(nodes[i]==null){
intparentNodeIndex=getNodeIndex(parentNode);
nodes[i]=newNode(e,parentNodeIndex);
nodeSize++;
returntrue;
}
}
returnfalse;
}
/**
*根据node获得索引
*
*@paramnode
*@return
*/
publicintgetNodeIndex(Nodenode){
for(inti=0;iif(nodes[i].equals(node)){
returni;
}
}
return-1;
}
/**
*判断是否是空树
*
*@return
*/
publicbooleanisEmpty(){
returnnodeSize==0;
}
/**
*返回树的根节点
*
*@return
*/
publicNodegetRootNode(){
if(nodeSize==0){
returnnull;
}
returnnodes[0];
}
/**
*根据子节点返回父节点对象
*
*@paramchileNode
*@return
*/
publicNodegetParentNodeByChildNode(NodechileNode){
for(inti=0;iif(chileNode==null){
returnnull;
}
if(i==chileNode.parentIndex){
returnnodes[i];
}
}
returnnull;
}
/**
*根据父节点返回子节点对象集合
*
*@paramchileNode
*@return
*/
publicNodegetParentNodeByChildNode(NodechileNode){
if(chileNode==null){
returnnull;
}
returnnodes[chileNode.parentIndex];
}
/**
*返回指定节点的第index个子节点,第1个代表第一个
*
*@paramparentNode
*@paramindex
*@return
*/
publicNodegetIndexChildByParentNode(NodeparentNode,intindex){
if(index==0){
thrownewRuntimeException("没有第0个子节点,从1开始");
}
intchildCount=0;
intparentIndex=getNodeIndex(parentNode);
for(inti=0;iif(nodes[i].parentIndex==parentIndex){
++childCount;
if(childCount==index){
returnnodes[i];
}
}
}
returnnull;
}
/**
*返回节点的深度
*
*@return
*/
publicintreturnNodeDeep(Nodenode){
if(node==getRootNode()){
return1;
}else{
NodeparentNode=getParentNodeByChildNode(node);
if(parentNode!
=null){
returnreturnNodeDeep(parentNode)+1;
}
return0;
}
}
/**
*返回树的深度
*
*@return
*/
publicintreturnTreeDeep(){
intmax=0;
for(inti=0;iintnodeDeep=returnNodeDeep(nodes[i]);
if(maxmax=nodeDeep;
}
}
returnmax;
}
@Override
publicStringtoString(){
StringBufferstr=newStringBuffer("[");
for(inti=0;istr.append("["+nodes[i].date+"],");
}
if(nodeSize>0){
returnstr.substring(0,str.lastIndexOf(","))+"]";
}
returnstr.append("]").toString();
}
}
测试代码
publicstaticvoidmain(String[]args){
TreeParenttree=newTreeParent("根菜单");
TreeParent.Noderoot=tree.getRootNode();
tree.addNodeByParent("子菜单1",root);
tree.addNodeByParent("子菜单2",root);
TreeParent.Nodenode1=tree.getIndexChildByParentNode(root,1);
tree.addNodeByParent("子菜单11",node1);
System.out.println("树的数据:
"+tree);
System.out.println("节点的深度:
"+tree.returnNodeDeep(node1));
System.out.println("节点的深度:
"+tree.returnNodeDeep(root));
TreeParent.Nodenode11=tree.getIndexChildByParentNode(node1,1);
System.out.println("节点的深度:
"+tree.returnNodeDeep(node11));
System.out.println("树的深度:
"+tree.returnTreeDeep());
}
测试效果如下
树的数据:
[[根菜单],[子菜单1],[子菜单2],[子菜单11]]
节点的深度:
2
节点的深度:
1
节点的深度:
3
树的深度:
3
记父节点是让树的节点记住自己节点的父节点索引位置,可以根据索引查找父节点,查找某个节点的父节点自然是手到擒来了,但是查找某个节点的子节点就略显笨拙,得遍历整个树的存储数组。
5.树的记子节点实现
与记父节点不同的是,记子节点实现是Node节点记录的是自己子节点的信息,之后利用第一个子节点记录第二个子节点信息、利用第二个子节点记录第三个字节点信息,以此类推。
也就是说子节点对象就是记录父节点的子节点信息的。
第n个节点记录了第n+1个子节点的信息。
代码如下
packagedateStructer.tree;
importjava.util.ArrayList;
importjava.util.List;
publicclassTreeChildren{
/**
*记录子节点对象
*
*@authorliuyan
*/
classSonNode{
Edata;
intindex;
SonNodesonNode;
publicSonNode(Edata,intindex,SonNodesonNode){
this.data=data;
this.index=index;
this.sonNode=sonNode;
}
@Override
publicStringtoString(){
return(String)data;
}
publicNodepaseNode(){
returnnewNode(data,index);
}
}
/**
*实际应用的Node对象
*
*@authorliuyan
*/
classNode{
Edata;
intindex;
SonNodefirstSonNode;
publicNode(Edata,intindex){
this.data=data;
this.index=index;
}
publicNode(Edata,intindex,SonNodesonNode){
this.data=data;
this.index=index;
this.firstSonNode=sonNode;
}
@Override
publicbooleanequals(Objectobj){
if(((Node)obj).data==this.data
&&((Node)obj).index==this.index)
returntrue;
returnfalse;
}
@Override
publicinthashCode(){
returnsuper.hashCode();
}
@Override
publicStringtoString(){
return(String)data;
}
}
//默认数组大小
privatefinalintDefSize=150;
//记录节点个数
privateintnodeSize;
//父节点对象
privateNode[]nodes;
publicTreeChildren(){
nodes=newNode[DefSize];
}
publicTreeChildren(Ee){
nodes=newNode[DefSize];
nodeSize++;
nodes[0]=newNode(e,0);
}
/**
*为指定节点增加子节点
*
*@parame
*@paramparentNode
*@return
*/
@SuppressWarnings("unchecked")
publicbooleanaddNodeByParent(Ee,NodeparentNode){
for(inti=0;iif(nodes[i]==null){
SonNodesonNode=newSonNode(e,i,null);
SonNodelastSonNode=getLastSonNodeByParent(parentNode);
if(lastSonNode==null){
parentNode.firstSonNode=sonNode;
}else{
lastSonNode.sonNode=sonNode;
}
nodes[i]=sonNode.paseNode();
nodeSize++;
returntrue;
}
}
returnfalse;
}
/**
*由SonNode到普通Node的转化
*@paramsonNode
*@return
*/
publicNodepaseNode(SonNodesonNode){
for(inti=0;iif(nodes[i]!
=null&&nodes[i].data==sonNode.data
&&nodes[i].index==sonNode.index){
returnnodes[i];
}
}
returnnull;
}
/**
*获得一个父节点的最后子节点对象
*
*@paramparentNode
*@return
*/
@SuppressWarnings("unchecked")
publicSonNodegetLastSonNodeByParent(NodeparentNode){
if(parentNode.firstSonNode==null){
returnnull;
}
SonNodesonNodeNow=parentNode.firstSonNode;
while(sonNodeNow.sonNode!
=null){
sonNodeNow=sonNodeNow.sonNode;
}
returnsonNodeNow;
}
/**
*根据node获得索引
*
*@paramnode
*@return
*/
publicintgetNodeIndex(Nodenode){
for(inti=0;iif(nodes[i].equals(node)){
returni;
}
}
return-1;
}
/**
*判断是否是空树
*
*@return
*/
publicbooleanisEmpty(){
returnnodeSize==0;
}
/**
*返回树的根节点
*
*@return
*/
publicNodegetRootNode(){
if(nodeSize==0){
returnnull;
}
returnnodes[0];
}
/**
*根据子节点返回父节点对象
*
*@paramchileNode
*@return
*/
@SuppressWarnings("unchecked")
publicNodegetParentNodeByChildNode(NodechileNode){
for(inti=0;iif(nodes[i]!
=null&&nodes[i].firstSonNode!
=null){
SonNodesonNode=nodes[i].firstSonNode;
while(sonNode!
=null){
if(sonNode.data==chileNode.data
&&sonNode.index==chileNode.index){
returnnodes[i];
}
sonNode=sonNode.sonNode;
}
}
}
returnnull;
}
/**
*根据父节点返回父子节点对象集合
*
*@paramchileNode
*@return
*/
@SuppressWarnings("unchecked")
publicList>getChildsNodeByParentNode(NodeparentNode){
if(parentNode==null){
returnnull;
}
SonNodesonNodeNow=parentNode.firstSonNode;
List>list=newArrayList>();
while(sonNodeNow.sonNode!
=null){
list.add(s