繁体   English   中英

查找树高时的stackoverflow

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM