我正在编写一个简单的函数来查找一个节点的高度,当树有大约50个节点(一个avl树,平衡)时,这个函数工作得很好,但是当树增长到一定的大小时,我得到了一个Exception in thread "main" java.lang.StackOverflowError
,它跟踪到了rSHeight = this.right.height()+1;
&lSHeight = this.left.height()+1;
行,我想这是因为递归的一个糟糕的实现,
public int height() {
int lSHeight,rSHeight;
if (this.left != null) {
lSHeight = this.left.height()+1;
} else {
lSHeight = 0;
}
if (this.right != null) {
rSHeight = this.right.height()+1;
}else {
rSHeight = 0;
}
if (lSHeight ==0 && rSHeight ==0) {
return 0;
} else {
int ret = Math.max(lSHeight, rSHeight);
return ret;
}
}
我想知道是否有人可以给我一个想法,如何避免
stackoverflowerror
之前,树有1000多个节点?(不假设高度变量,因为此函数用于实际BaseNode
派生的AVLnode
类)感谢各位阅读我的文章,如前所述,这里是我的实现,基本思想是让一个
BaseNode
实现一个树的最基本的功能,这样我就可以实现我在实践中学习到的其他树类型。AVLnode
是我当前正在处理的,以trial
开头的方法都是我为检查功能而编写的测试,public abstract class BaseNode <T extends BaseNode<T>>{
int val;
T parent;
T left;
T right;
public boolean insert(T tender) {
if ((tender.val < this.val) && (this.left != null)){
return this.left.insert(tender);
} else if ((tender.val < this.val)&& (this.left == null)){
this.left = tender;
tender.parent = (T) this;
// host instance will be the exact type, no real cast involved
return true;
} else if((tender.val>this.val)&&(this.right!=null)) {
return this.right.insert(tender);
} else if ((tender.val>this.val)&&(this.right == null)) {
this.right = tender;
tender.parent = (T) this;
return true;
} else {
return false;
}
}
public BaseNode<?> min(){
if (this.left != null) {
return this.left.min();
} else {
return this;
}
}
public BaseNode<?> successor(){
if (this.right != null) {
return this.right.min();
} else {
boolean spot = true;
BaseNode tempt = this;
while(spot) {
if (tempt.parent == null) {
return null;
}
else if (tempt == tempt.parent.left) {
spot = false;
return tempt.parent;
} else {
tempt = tempt.parent;
}
}
}
return null;
}
public BaseNode<?> search(int key){
if ((key < this.val) && (this.left != null)){
return this.left.search(key);
} else if ((key < this.val)&& (this.left == null)){
return null;
} else if((key>this.val)&&(this.right!=null)) {
return this.right.search(key);
} else if ((key>this.val)&&(this.right == null)) {
return null;
} else {
return this;
}
}
//replace the host node with jim in the Tree
//certainly not a good practice to just change the value
public void swapIn(BaseNode jim) {
//the connections on New Node side are done here
//the connections on other Nodes side are done in 'adopt'
jim.parent = this.parent;
if(this.left != jim) {
jim.left = this.left;
}
if (this.right!=jim) {
jim.right=this.right;
}
this.adopt(jim);
}
public void adopt(BaseNode stepK) {
if(this.parent!=null) {
if (this == this.parent.left) {
this.parent.left = (T) stepK;
} else {
this.parent.right = (T) stepK;
}
}
if(this.left != stepK && this.left != null) {
this.left.parent = (T) stepK;
}
if (this.right!= stepK && this.right!=null) {
this.right.parent = (T) stepK;
}
}
public boolean delete(int key) {
BaseNode sp = this.search(key);
if (sp==null) {
return false;
}else {
if ((sp.left==null)&&(sp.right==null)) {
sp.swapIn(null);
} else if((sp.left==null)^(sp.right==null)) {
if (sp.left==null) {
sp.swapIn(sp.right);
} else {
sp.swapIn(sp.left);
}
} else {
BaseNode hinch =sp.successor(); //it's not possible to have hinch== null here
if(hinch.right!=null) {
hinch.swapIn(hinch.right);
}
sp.swapIn(hinch);
//sp.findRoot().delete(hinch.val);
}
return true;
}
}
//A recursive algorithm the returns height
public int height() {
int lSHeight,rSHeight;
if (this.left != null) {
lSHeight = this.left.height()+1;
} else {
lSHeight = 0;
}
if (this.right != null) {
rSHeight = this.right.height()+1;
}else {
rSHeight = 0;
}
if (lSHeight ==0 && rSHeight ==0) {
return 0;
} else {
int ret = Math.max(lSHeight, rSHeight);
return ret;
}
}
//Recursively put tree rooted at hose instance into array 'rack' as a heap
public void stashBST(T rack[],int idx){
//rack was created as subclass array, the host is also a subclass
object, proper cast
rack[idx] = (T) this;
if(this.left!=null) {
this.left.stashBST(rack, idx*2+1);
}
if (this.right != null) {
this.right.stashBST(rack, idx*2+2);
}
}
//return val of host as a string object with 'toklen' length
public String printableNode(int tokLen) {
String signi = Integer.toString(this.val);
try {
if (signi.length()<= tokLen) {
int gap = tokLen - signi.length();
int front = gap/2;
int back = gap - front;
String pre ="";
String post= "";
for(int i =0;i< front;i++) {
pre = pre+ " ";
}
for(int i =0;i< back;i++) {
post = post+ " ";
}
String ret = pre+signi+post;
return ret;
} else {
throw new RuntimeException("the number is too big!");
}
} catch (RuntimeException e) {
return null;
}
}
public BaseNode findRoot() {
if(this.parent!=null) {
return this.parent.findRoot();
} else {
return this;
}
}
public boolean fost(T nbie) {
if (this.parent != null){
if (this == this.parent.left) {
this.parent.left = nbie;
nbie.parent = this.parent;
} else {
this.parent.right = nbie;
nbie.parent = this.parent;
}
return true;
} else {
nbie.parent = null;
return false;
}
}
public boolean leftRot() {
if(this.right == null) {
return false;
} else {
this.fost(this.right);
this.parent = this.right;
T tempD = this.right.left;
this.right.left = (T) this;
this.right = tempD;
return true;
}
}
public boolean rightRot() {
if(this.left == null) {
return false;
} else {
this.fost(this.left);
this.parent = this.left;
T tempD = this.left.right;
this.left.right = (T) this;
this.left = tempD;
return true;
}
}
//print a tree rooted at host
public void printTree() {
int height = this.height();
//Hvae Array of BaseNode doesn't hurt, it's just reference, we can cast
it back if needed
BaseNode rack[]=new BaseNode[(int) Math.pow(2, height+1)];
this.stashBST((T[]) rack, 0);
int TokCap = (int)Math.pow(2, height);
int maxWidth = TokCap*5;
for (int i=0;i<height+1;i++) {
int numLv =(int) Math.pow(2, i);
int widthLv = maxWidth/numLv;
for(int j =(int)Math.pow(2, i)-1; j<(int)Math.pow(2, i+1)-1;j++) {
if(rack[j]!= null) {
if (rack[j].val==1){
int begal = 15;
}
System.out.print(rack[j].printableNode(widthLv));
} else {
String temp = "";
for(int k=0;k<widthLv;k++) {
temp = temp+" ";
}
System.out.print(temp);
}
}
System.out.println("");
}
}
}
这是
Tree
类:public class tree <T extends BaseNode> {
T root;
public tree(T adam) {
if (adam != null) {
root = adam;
}
}
public void reCal() {
while(root.parent != null) {
root = (T) root.parent;
}
}
public void showTree() {
root.printTree();
}
public boolean insert(T nbie) {
if (this.root != null){
boolean res = this.root.insert(nbie);
//this.reCal();
if (root instanceof AVLnode) {
((AVLnode) nbie).fixProp();
}
this.reCal();
return res;
} else {
//if empty tree, assume the we having a plain
root = nbie;
return true;
}
}
public boolean delete(int key) {
if (root.val == key) {
if (root.right != null) {
T temp = (T) root.successor();
root.delete(key);
this.root = temp;
return true;
} else {
root.swapIn(root.left);
this.root = (T) root.left;
return true;
}
} else {
return root.delete(key);
}
}
public T search(int key) {
if(root == null) {
return null;
} else {
return (T) root.search(key);
}
}
}
import java.util.Arrays;
这是
AVLnode
类:public class AVLnode extends BaseNode<AVLnode>{
public int hMark;
public AVLnode(int key) {
this.val = key;
this.left = null;
this.right = null;
this.parent = null;
this.hMark = 0;
}
public boolean insert(AVLnode nbie) {
boolean result = super.insert(nbie);
if (result == true) {
if (((this.left == nbie) && (this.right ==null))||((this.right == nbie)&&(this.left==null))) {
this.hMark = this.hMark + 1;
}
} else {
return result;
}
if (this.left == null) {
this.hMark = this.right.hMark +1;
} else if (this.right == null) {
this.hMark = this.left.hMark + 1;
} else {
this.hMark = Math.max(this.left.hMark,this.right.hMark)+1;
}
return result;
}
public void fixProp() {
int lh, rh;
if(this.left == null) {
lh = -1;
} else {
lh = this.left.hMark;
}
if (this.right == null) {
rh=-1;
} else {
rh = this.right.hMark;
}
if(Math.abs(lh-rh) >1 ) {
int llh,lrh,rrh,rlh;
if (this.left!=null) {
if (this.left.left == null) {
llh = -1;
} else {
llh = this.left.left.hMark;
}
if(this.left.right == null) {
lrh = -1;
} else {
lrh = this.left.right.hMark;
}
} else {
llh = -1;
lrh = -1;
}
if(this.right !=null ) {
if(this.right.left == null) {
rlh = -1;
} else {
rlh = this.right.left.hMark;
}
if(this.right.right == null) {
rrh = -1;
} else {
rrh = this.right.right.hMark;
}
} else {
rlh = -1;
rrh = -1;
}
if((lh>rh) && (llh>lrh)){
this.rightRot();
if(this.parent.parent != null) {
this.parent.parent.fixProp();
} else {
return;
}
} else if ((rh>lh)&&(rrh>rlh)) {
this.leftRot();
if(this.parent.parent != null) {
this.parent.parent.fixProp();
} else {
return;
}
} else if ((lh>rh)&&(lrh>llh)) {
this.left.leftRot();
this.rightRot();
if(this.parent.parent != null) {
this.parent.parent.fixProp();
} else {
return;
}
} else if((rh>lh)&&(rlh>rrh)) {
this.right.rightRot();
this.leftRot();
if(this.parent.parent != null) {
this.parent.parent.fixProp();
} else {
return;
}
}
} else {
if(this.parent != null) {
this.parent.fixProp();
} else {
return;
}
}
}
public boolean heightFeatureCheck() {
if (this.hMark == this.height()) {
boolean lOut = true;
boolean rOut = true;
if (this.left!=null) {
lOut = this.left.heightFeatureCheck();
}
if (this.right != null) {
rOut = this.right.heightFeatureCheck();
}
return (lOut && rOut);
} else {
return false;
}
}
public static void trialInsertionAVL() {
// for testing convenience, not gonna have a tree
int statRack [] = new int [] {45,48,35,40,30,8};
AVLnode adam = new AVLnode(statRack[0]);
for(int i =1;i<statRack.length;i++) {
AVLnode bitco = new AVLnode(statRack[i]);
adam.insert(bitco);
}
adam.printTree();
System.out.println("====================================================");
//System.out.print(adam.heightFeatureCheck());
AVLnode chris = (AVLnode) adam.search(8);
AVLnode futKing = (AVLnode) adam.search(35);
chris.fixProp();
futKing.printTree();
}
public static void trialAVLTree() {
int pool [] = new int [] {15, 42, 12, 29, 29, 44, 38, 29, 29, 33, 0,};
AVLnode adam = new AVLnode(pool[0]);
tree oak = new tree(adam);
for(int i=1;i<pool.length;i++) {
AVLnode son = new AVLnode(pool[i]);
oak.insert(son);
oak.showTree();
System.out.println("++++++++++++++++++++++++++++++++++++++");
}
oak.showTree();
}
public static void trialDynamicAVL() {
int pool [] = Node.rawGene();
//System.out.println(Arrays.toString(pool));
AVLnode adam = new AVLnode(pool[0]);
tree oak = new tree(adam);
for(int i=1;i<pool.length;i++) {
AVLnode son = new AVLnode(pool[i]);
oak.insert(son);
oak.showTree();
System.out.println("++++++++++++++++"+Integer.valueOf(i)+"++++++++++++++++++++++");
}
oak.showTree();
System.out.println("this is it!!!!!!");
}
public static void main(String args[]) {
trialDynamicAVL();
}
}
这是
Node
课import java.util.Arrays;
import java.util.Random;
import java.math.*;
public class Node extends BaseNode<Node>{
public Node(int key) {
this.val = key;
this.parent = null;
this.left = null;
this.right =null;
}
public static int[] rawGene() {
int drizzy [] = new int [100];
Random magic = new Random();
for (int i=0;i<100;i++) {
drizzy[i] = magic.nextInt(50);
}
System.out.println(Arrays.toString(drizzy));
return drizzy;
}
public int bfsTrial(int counted) {
counted++;
System.out.println(this.val);
if(this.left != null) {
counted = this.left.bfsTrial(counted);
}
if (this.right != null) {
counted = this.right.bfsTrial(counted);
}
if ((this.left==null) && (this.right == null)) {
return counted;
}
return counted;
}
public void bstInArray(Node yard [], int r_idx) {
//the adam is the node we just discover, r_idx is the idx of the adam
yard[r_idx] = this;
if (this.left != null){
this.left.bstInArray( yard, r_idx*2);
}
if(this.right != null) {
this.right.bstInArray(yard, (r_idx*2+1));
}
if((this.left == null)&&(this.right==null)) {
return;
}
}
public static Node makeTree(int pool []) {
Node root = new Node(pool[0]);
for(int i =1;i<pool.length;i++) {
Node bitco = new Node(pool[i]);
root.insert(bitco);
}
return root;
}
public static Node makeTree() {
int statRack [] = new int [] {45, 14, 5, 47, 20, 9, 4, 37, 30, 1};
return makeTree(statRack);
}
//make an shuffled array of integer [1:size]
public static int[ ] shuffleArrGen(int size) {
int rack [] = new int[size];
Random r = new Random();
for(int i=0;i<size;i++) {
rack[i] = i+1;
}
for(int j=size-1;j>0;j--) {
int idx = r.nextInt(j+1);
int buff = rack[j];
rack[j] = rack[idx];
rack[idx] = buff;
}
return rack;
}
}
另外,正如您可能已经注意到的,我使用扩展的代码行来检查某个东西是否为空,我想知道是否有更好的方法这样做?我读过的一个解决方案是,对于所有节点的
null
子节点都有一个通用的“nil”节点。 最佳答案
Java堆栈随平台(和虚拟机标志)的不同而不同,但通常是1MiB即使假设每帧1kib,也就是大于1000帧,除非您的vm有由标志设置的异常小的堆栈。
正如评论家已经指出的,这个代码保存在堆栈上的最大帧数是height(tree)
。这意味着这棵树可能有问题。
这个假设有一个简单的测试:在方法中添加一个int level
参数(显然将level+1
传递给嵌套的调用),并在每次调用时打印它,以查看它有多高。如果是1000,那就检查一下树。如果保持在~10左右,则检查堆栈大小。
无关:不需要对(lSHeight ==0 && rSHeight ==0)
进行单独的测试,因为Math.max()
已经为这个案例做了正确的事情。