二叉樹(shù)結(jié)構(gòu)

二叉樹(shù)的主要性質(zhì)

性質(zhì)1:非空二叉樹(shù)上葉子節(jié)點(diǎn)數(shù)等于雙分支節(jié)點(diǎn)數(shù)加1。(使用總分支數(shù)=總節(jié)點(diǎn)數(shù)-1證明)
性質(zhì)2:二叉樹(shù)的第i層上最多有2^(i-1)個(gè)節(jié)點(diǎn)(i>=1)。
性質(zhì)3:高度為k的二叉樹(shù)最多有2^(i-1)個(gè)節(jié)點(diǎn)。
性質(zhì)4:高度為k的二叉樹(shù)最多有2^k-1個(gè)節(jié)點(diǎn)。

二叉樹(shù)的存儲(chǔ)結(jié)構(gòu)

(1)順序存儲(chǔ)結(jié)構(gòu):就是采用數(shù)組來(lái)存儲(chǔ)一個(gè)二叉樹(shù)。
(2)鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu):就是采用鏈表來(lái)存儲(chǔ)一個(gè)二叉樹(shù)。

二叉樹(shù)的遍歷

(1)先序遍歷:根左右。
(2)中序遍歷:左根右。
(3)后序遍歷:左右根。
(4)層序遍歷:從上到下,從左到右,開(kāi)始遍歷。

二叉樹(shù)的實(shí)現(xiàn)

20170525140317402.png

二叉樹(shù)節(jié)點(diǎn)的結(jié)構(gòu)類:

class Node{  
    private Object data =;  
    private Node lchild =;  
    private Node rchild =;  
    public Node(){}  
    public Node(Object data) {  
        super();  
        this.data = data;  
    }  
    public Object getData() {  
        return data;  
    }  
    public void setData(Object data) {  
        this.data = data;  
    }  
    public Node getLchild() {  
        return lchild;  
    }  
    public void setLchild(Node lchild) {  
        this.lchild = lchild;  
    }  
    public Node getRchild() {  
        return rchild;  
    }  
    public void setRchild(Node rchild) {  
        this.rchild = rchild;  
    }  
}  

使用輸入的數(shù)組來(lái)構(gòu)造二叉樹(shù):

思想:

首先將輸入的數(shù)組數(shù)據(jù)轉(zhuǎn)換成Node節(jié)點(diǎn),并使用List存儲(chǔ)。然后構(gòu)造完了一堆節(jié)點(diǎn),需要對(duì)節(jié)點(diǎn)之間建立關(guān)系。而關(guān)系可以根據(jù)自身定義,我的定義是輸入數(shù)組中的數(shù)據(jù)下標(biāo)為奇數(shù)做為左孩子節(jié)點(diǎn),偶數(shù)為右孩子節(jié)點(diǎn)。然后對(duì)邊界的定義如2i+2,無(wú)論當(dāng)總節(jié)點(diǎn)為偶數(shù)時(shí),二叉樹(shù)本身的結(jié)構(gòu)是沒(méi)有最右邊的葉子節(jié)點(diǎn)的,也就是說(shuō)此樹(shù)非滿二叉樹(shù),那么我們使用2i+2來(lái)計(jì)算不存在的最右邊葉子節(jié)點(diǎn)的右孩子節(jié)點(diǎn)時(shí)會(huì)報(bào)下標(biāo)越界的異常。

實(shí)現(xiàn):

/*構(gòu)造樹(shù)  
     * 使用數(shù)組構(gòu)造二叉樹(shù)  
     * 定義的規(guī)則:奇數(shù)作為左孩子,偶數(shù)的時(shí)候作為右孩子  
     *   
     * */  
    public static List<Node> createBt(Object[] objArr){  
        List<Node> retList =new ArrayList<Node>();  
        for(Object obj :objArr){  
            retList.add(new Node(obj));  
        }  
        if(retList.size() !=0){  
            for(int i=0;i<retList.size()/2;i++){  
                //奇數(shù)情況下  
                if(retList.get(2*i+1) !=null){  
                    retList.get(i).setLchild(retList.get(2*i+1));  
                }  
                //偶數(shù)情況下    
                if(2*i+2 <=retList.size()-1 && retList.get(2*i+2) !=null){  
                    retList.get(i).setRchild(retList.get(2*i+2));  
                }  
            }  
        }  
        return retList;  
    }  

先序遍歷,遞歸方式遍歷二叉樹(shù)

    /* 先序遍歷  
     * 根左右  
     * 遞歸方式  
     * */  
    public static void preBtByRecursion(Node n){  
        if(n !=null){  
            System.out.println("值:"+n.getData());  
            preBtByRecursion(n.getLchild());  
            preBtByRecursion(n.getRchild());  
        }else{  
            return;  
        }  
    }  

中序遍歷,遞歸方式遍歷二叉樹(shù):

public static void orderBtByRecursion(Node n){  
        if(n !=null){  
            orderBtByRecursion(n.getLchild());  
            System.out.println("值:"+n.getData());  
            orderBtByRecursion(n.getRchild());  
        }else{  
            return;  
        }  
 }  

后序遍歷,遞歸方式遍歷二叉樹(shù):

    /* 后序遍歷  
     * 左右根  
     * 遞歸方式  
     * */  
    public static void postBtByRecursion(Node n){  
        if(n !=null){  
            postBtByRecursion(n.getLchild());  
            postBtByRecursion(n.getRchild());  
            System.out.println("值:"+n.getData());  
        }else{  
            return;  
        }  
    }  

先序遍歷,非遞歸方式遍歷二叉樹(shù):

思想:
遍歷二叉樹(shù)采用當(dāng)前遍歷路徑盡可能深的去遍歷二叉樹(shù),如果當(dāng)前路徑走到了盡頭就需要往回走,那么就需要回溯了。為了對(duì)之前走過(guò)的路徑進(jìn)行回溯,那么就需要使用一個(gè)輔助的結(jié)構(gòu)來(lái)記錄之前走過(guò)的節(jié)點(diǎn)。而我們從頭到尾的存入遍歷過(guò)的節(jié)點(diǎn),然后從尾到頭的彈出節(jié)點(diǎn)。這種結(jié)構(gòu)恰好符合棧的結(jié)構(gòu)。所以就采用輔助棧來(lái)存儲(chǔ)遍歷過(guò)的節(jié)點(diǎn)。從而達(dá)到回溯的目得。

實(shí)現(xiàn):

    /* 先序遍歷  
     * 根左右  
     * 非遞歸方式  
     * */  
    public static void preBt(List<Node> list){  
        Stack<Node> stack =new Stack<Node>();  
        Node root =list.get(0);  
        while(root !=null || !stack.isEmpty()){  
            System.out.println("值:"+root.getData());  
            stack.push(root);  
            root =root.getLchild();  
            while(root ==null && !stack.isEmpty()){  
                root =stack.pop();  
                root =root.getRchild();  
            }  
        }  
    }  

中序遍歷,非遞歸方式遍歷二叉樹(shù):

思想:
和前面的前序遍歷二叉樹(shù)的思想差不多,都是采用輔助棧來(lái)實(shí)現(xiàn)回溯。
實(shí)現(xiàn):

    /* 中序遍歷  
     * 左根右  
     * 非遞歸方式  
     * */  
    public static void orderBt(List<Node> list){  
        Stack<Node> stack =new Stack<Node>();  
        Node n =list.get(0);  
        while(n !=null || !stack.isEmpty()){  
            stack.push(n);  
            n =n.getLchild();  
            while(n ==null && !stack.isEmpty()){  
                n =stack.pop();  
                System.out.println("值:"+n.getData());  
                n =n.getRchild();  
            }  
        }  
    }  

后序遍歷,非遞歸方式遍歷二叉樹(shù):

思想:
采用雙輔助棧的方式來(lái)實(shí)現(xiàn)遍歷的。后序遍歷是左右根,為了調(diào)整遍歷出來(lái)的節(jié)點(diǎn)的順序,其實(shí)除了樹(shù)最頂上的節(jié)點(diǎn)作為根外,剩下的節(jié)點(diǎn),對(duì)于孩子節(jié)點(diǎn)父節(jié)點(diǎn),對(duì)于同層的點(diǎn)來(lái)說(shuō)就是兄弟節(jié)點(diǎn)。所以剩下的點(diǎn)都可以當(dāng)作左右節(jié)點(diǎn)來(lái)看待了。樹(shù)最頂上的點(diǎn)在進(jìn)行操作前,先將其存入棧s1中,在從棧s1中彈出存入棧s2中。(肯定有人會(huì)問(wèn),直接放入棧s2中不就完事了,這樣做,當(dāng)棧s1都沒(méi)有根節(jié)點(diǎn),那么它從哪開(kāi)始遍歷都不知道)。其他節(jié)點(diǎn)就是同樣的套路了。壓入棧s1的順序左右,彈出棧的順序是右左,在壓入到棧s2的順序是右左,那么彈出棧的順序就是左右了,加上之前特殊處理的根在s2最底下,s2的整體順序就是左右根,符合后序遍歷的規(guī)則了。
實(shí)現(xiàn):

    /* 后序遍歷  
     * 左右根  
     * 非遞歸方式  
     * */  
    public static void postBt(List<Node> list){  
        Stack<Node> stack1 =new Stack<Node>();  
        Stack<Node> stack2 =new Stack<Node>();  
        Node n =list.get(0);  
        stack1.push(n);  
        while(!stack1.isEmpty()){  
            n =stack1.pop();  
            stack2.push(n);  
            if(n.getLchild() !=null){  
                stack1.push(n.getLchild());  
            }  
            if(n.getRchild() !=null){  
                stack1.push(n.getRchild());  
            }  
        }  
        while (!stack2.isEmpty()) {   
                        System.out.println(stack2.pop().getData());   
        }   
    }  

測(cè)試

 public static void main(String[] args) {  
    String[] str ={"1","2","3","4","5","6"};  
    List<Node> list =createBt(str);  
    postBt(list);  
}  
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容