簡體   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