Java 查找树高时堆栈溢出

Java 查找树高时堆栈溢出,java,algorithm,recursion,data-structures,tree,Java,Algorithm,Recursion,Data Structures,Tree,我正在编写一个简单的函数来查找一个节点的高度,当该树有大约50个节点时(一个AVL树,平衡),该函数可以正常工作,但是一旦树增长到一定的大小,我在线程“main”java.lang.StackOverflowerError中得到一个异常,它跟踪到行rSHeight=this.right.height()+1&lSHeight=this.left.height()+1,我想这是因为我在递归上的一个破旧的实现 public int height() { int lSHeight,rSHeig

我正在编写一个简单的函数来查找一个节点的高度,当该树有大约50个节点时(一个AVL树,平衡),该函数可以正常工作,但是一旦树增长到一定的大小,我在线程“main”java.lang.StackOverflowerError中得到一个
异常,它跟踪到行
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多个节点之前,如何避免
stackoverflowerrror
?(不假设高度变量,因为此函数用于实际
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(""); 
    }
}

}
这是
AVLnode
类:

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;
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();
}

}

另外,正如您可能已经注意到的,我花了大量的代码来检查某些东西是否为null,我想知道是否有更好的方法来这样做?我读过的一个解决方案是为所有节点的
null
子节点提供一个通用的“nil”节点。

Java堆栈随平台(和VM标志)的不同而变化,但通常为1MiB。即使假设每帧1KB,也就是>1000帧,除非您的VM具有由标志设置的异常小的堆栈

正如评论者已经指出的,此代码在堆栈上保留的最大帧数是
height(tree)
。这意味着这棵树可能有问题

对这个假设有一个简单的检验:向方法中添加一个
int-level
参数(显然是将
level+1
传递给嵌套调用),并在每次调用中打印它,看看它有多高。如果达到~1000,则检查树。如果保持在~10左右,则检查堆栈大小


无关:没有必要对
(lSHeight==0&&rSHeight==0)
进行单独的测试,因为
Math.max()
已经为这种情况做了正确的事情。

我将回答我自己的问题,因为我刚刚调试了它,我觉得其他人研究它将是浪费时间。 错误在于我没有正确执行
fost()
方法,特别是,我忘记将
tempD
连接到它的新父级(而
parent
连接到
tempD
),假设父级不是
null

我在这里学到的真正教训是,在移动到下一个父级之前,
始终完全测试当前组件,如果我正确地进行了旋转,就不会花这么多钱


谢谢你们的帮助

你的代码看起来不错。你确定在你的树中没有循环吗?你可以把它分成几部分。有关更多帮助,请使用一些测试数据发布您的树实现。树的大小应该是所需的所有递归深度限制。递归深度不能超过树深度,1000个节点的平衡二叉树只有大约10个级别…@KevinAnderson需要考虑的另一个限制是堆栈大小,以避免
stackoverflow
错误。我演示的简单技术可以用于在任何深度运行递归。。。
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;
}

}