[英]stackoverflow while finding tree height
我正在編寫一個簡單的函數來查找節點的高度,當樹有大約50個節點(AVL樹,平衡)時,該節點的工作情況很好,但是一旦樹長到一定大小, Exception in thread "main" java.lang.StackOverflowError
出現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;
}
}
我想知道是否有人可以給一個想法,在樹具有超過1000個節點之前如何避免stackoverflowerror
? (不假設高度變量,因為此函數用於實際的AVLnode
派生的BaseNode
類)謝謝大家閱讀我的建議,這是我的實現,基本思想是讓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堆棧隨平台(和VM標志)的不同而不同,但通常為1MiB。 即使假設每幀1KiB,也就是> 1000幀,除非您的VM的標志設置了異常小的堆棧。
正如評論者已經指出的那樣,此代碼在堆棧上保留的最大幀數為height(tree)
。 這意味着樹可能有問題。
對於此假設有一個簡單的檢驗:向該方法添加一個int level
參數(顯然將level+1
傳遞給嵌套的調用),並在每次調用時打印該參數,以查看其level+1
。 如果達到〜1000,則檢查樹。 如果它保持在10左右,則檢查堆棧大小。
無關:不需要對(lSHeight ==0 && rSHeight ==0)
進行單獨的測試,因為Math.max()
已經針對這種情況做了正確的事情。
因為我剛剛調試了這個問題,所以我將回答我自己的問題,我認為這會讓其他人浪費時間研究這個問題。 錯誤是我沒有正確地執行fost()
方法,特別是,我忘記將tempD
連接到它的新父級(而parent
確實連接到tempD
),假設父級不為null
我在這里學到的真正教訓是, always fully test out the current component before moving to the next parent
,請always fully test out the current component before moving to the next parent
,如果我進行了輪換校正,則不會花費那么多錢。
謝謝你們的幫助!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.