二叉樹 Java 實現 前序遍歷 中序遍歷 后序遍歷 層級遍歷 獲取葉節點 寬度 ,高度,隊列實現二叉樹遍歷 求二叉樹的最大距離


數據結構中一直對二叉樹不是很了解,今天趁着這個時間整理一下

 

許多實際問題抽象出來的數據結構往往是二叉樹的形式,即使是一般的樹也能簡單地轉換為二叉樹,而且二叉樹的存儲結構及其算法都較為簡單,因此二叉樹顯得特別重要。
    二叉樹(BinaryTree)是n(n≥0)個結點的有限集,它或者是空集(n=0),或者由一個根結點及兩棵互不相交的、分別稱作這個根的左子樹和右子樹的二叉樹組成。
    這個定義是遞歸的。由於左、右子樹也是二叉樹, 因此子樹也可為空樹。下圖中展現了五種不同基本形態的二叉樹。

   其中 (a) 為空樹, (b) 為僅有一個結點的二叉樹, (c) 是僅有左子樹而右子樹為空的二叉樹, (d) 是僅有右子樹而左子樹為空的二叉樹, (e) 是左、右子樹均非空的二叉樹。這里應特別注意的是,二叉樹的左子樹和右子樹是嚴格區分並且不能隨意顛倒的,圖 (c) 與圖 (d) 就是兩棵不同的二叉樹。

 

二叉樹的遍歷

對於二叉樹來講最主要、最基本的運算是遍歷。
    遍歷二叉樹 是指以一定的次序訪問二叉樹中的每個結點。所謂 訪問結點 是指對結點進行各種操作的簡稱。例如,查詢結點數據域的內容,或輸出它的值,或找出結點位置,或是執行對結點的其他操作。遍歷二叉樹的過程實質是把二叉樹的結點進行線性排列的過程。假設遍歷二叉樹時訪問結點的操作就是輸出結點數據域的值,那么遍歷的結果得到一個線性序列。

從二叉樹的遞歸定義可知,一棵非空的二叉樹由根結點及左、右子樹這三個基本部分組成。因此,在任一給定結點上,可以按某種次序執行三個操作:
     (1)訪問結點本身(N),
     (2)遍歷該結點的左子樹(L),
     (3)遍歷該結點的右子樹(R)。
以上三種操作有六種執行次序:
     NLR、LNR、LRN、NRL、RNL、RLN。

注意:
    前三種次序與后三種次序對稱,故只討論先左后右的前三種次序。

 由於被訪問的結點必是某子樹的根,所以N(Node)、L(Left subtlee)和R(Right subtree)又可解釋為根、根的左子樹和根的右子樹。NLR、LNR和LRN分別又稱為先根遍歷、中根遍歷和后根遍歷。

 

二叉樹的java實現

首先創建一棵二叉樹如下圖,然后對這顆二叉樹進行遍歷操作(遍歷操作的實現分為遞歸實現和非遞歸實現),同時還提供一些方法如獲取雙親結點、獲取左孩子、右孩子等。

 

 

特點:

     (1) 每個結點最多有兩棵子樹,沒有子樹或者只有一棵子樹也是可以的;

      (2)左子樹和右子樹是有順序的,次序不能任意顛倒;

      (3)即使樹中只有一棵子樹,也要區分它是左子樹還是右子樹;

(2)滿二叉樹:在一棵二叉樹中,如果所有分支結點都存在左子樹和右子樹,並且所有葉子都在同一層上,這樣的二叉樹稱為滿二叉樹;如圖:

 

 

         (3)完全二叉樹:對一棵具有n個結點的二叉樹按層序編號,如果編號為i的結點與同樣深度的滿二叉樹中編號為i的結點在二叉樹中位置完全相同,那么這棵二叉樹稱為完全二叉樹;或者這樣理解:在一棵二叉樹中,除最后一層外,若其余層都是滿的,並且最后一層或者是滿的,或者是右邊缺少連續若干個結點,則稱此樹為完全二叉樹;

 

 

所以我們可以這樣判斷完全二叉樹:那就是看着樹的示意圖,心中默默給每個結點按照滿二叉樹的結構逐層順序編號,如果編號出現空檔,就說明不是完全二叉樹,否則就是;

 

還有個經典問題:

問題一、求二叉樹的最大距離;

寫一個程序求一棵二叉樹相距最遠的兩個節點之間的距離

如下圖:

 

2.分析與解法

對於任意一個節點,以該節點為根,假設這個根有k個孩子節點,那么距離最遠的兩個節點U與V之間的路徑與這個根節點的關系有兩種。

1).若路徑經過Root,則U和V屬於不同子樹的,且它們都是該子樹中到根節點最遠的節點,否則跟它們的距離最遠相矛盾

2).如果路徑不經過Root,那么它們一定屬於根的k個子樹之一,並且它們也是該子樹中相距最遠的兩個頂點

因此,問題就可以轉化為在字數上的解,從而能夠利用動態規划來解決。

設第K棵子樹中相距最遠的兩個節點:Uk和Vk,其距離定義為d(Uk,Vk),那么節點Uk或Vk即為子樹K到根節點Rk距離最長的節點。不失一般性,我們設Uk為子樹K中道根節點Rk距離最長的節點其到根節點的距離定義為d(Uk,R)。取d(Ui,R)(1<=i<=k)中最大的兩個值max1和max2,那么經過根節點R的最長路徑為max1+max2+2,所以樹R中相距最遠的兩個點的距離為:max{d(U1,V1),…, d(Uk,Vk),max1+max2+2}。

問題二、二叉樹寬度

  使用隊列,層次遍歷二叉樹。在上一層遍歷完成后,下一層的所有節點已經放到隊列中,此時隊列中的元素個數就是下一層的寬度。以此類推,依次遍歷下一層即可求出二叉樹的最大寬度。

關於二叉樹的介紹如上所示,現在需要自己寫一下二叉樹的實現,包含了 前序遍歷,中序遍歷,后序遍歷,層級遍歷,

以及求二叉樹的最大距離,獲取二叉樹的寬度

 

 

 

package com.binary_tree;

import java.util.LinkedList;
import java.util.Queue;

public class BinTreeTest1 {
    //地址:
    //https://zhidao.baidu.com/question/441846714.html
    
    public  final static int MAX=40;
    BinTreeTest1[] elements =new BinTreeTest1[MAX];//層次遍歷時,保存各個節點;
    int front;//層次遍歷時,對首
    int rear;//層次遍歷時,對尾
    private Object data;//數據元數
    
    private BinTreeTest1 left,right;
    
    private int nMaxLeft;//左子樹的最長距離
    
    private int nMaxRight;//右子數的最長距離;
    private int nMaxLen; //最長距離
    
    private int treeWidth;//二叉樹寬度
    
    
    
    public BinTreeTest1(){}
    public BinTreeTest1(Object data){
        this.data=data;
        left=null;
        right=null;
    }
    public BinTreeTest1(Object data,BinTreeTest1 left,BinTreeTest1 right){
        this.data=data;
        this.left=left;
        this.right=right;
    }
    public String toString(){
        return data.toString();
                
    }
    
    //層次遍歷二叉樹 ,並獲取二叉樹的寬度
    //參考:http://www.cnblogs.com/xudong-bupt/p/4036190.html
    public void LayerOrder(BinTreeTest1 root){
        //http://www.cnblogs.com/beihoushan/p/6401416.html
       if(root==null) return;
       
       Queue<BinTreeTest1> queue=new LinkedList<BinTreeTest1>();
       queue.add(root);
       while(queue.size()!=0){
           int len = queue.size();
           treeWidth=Math.max(treeWidth,len);
           System.out.println("層級遍歷="+len);
           for(int i=0;i<len;i++){
               BinTreeTest1 temp=queue.poll();
               System.out.println("層級遍歷111="+queue.size());
               System.out.println(temp.data);
               if(temp.left!=null) queue.add(temp.left);
               if(temp.right!=null) queue.add(temp.right);
               System.out.println("層級遍歷111="+queue.size());
           }
       }
       
    }
    
     
    
    
    //前序遍歷
    
    public static void preOrder(BinTreeTest1 parent){
        
        if(parent==null) return;
        System.out.println(parent.data+" ");
        preOrder(parent.left);
        preOrder(parent.right);
        
    }
    
    //中序遍歷
    
    public static void inOrder(BinTreeTest1 parent){
        if(parent==null) return;
        inOrder(parent.left);
        System.out.println(parent.data+" ");
        inOrder(parent.right);
    }
    
    //后序遍歷
    
    public static void postOrder(BinTreeTest1 parent){
        if(parent==null) return;
        postOrder(parent.left);
        postOrder(parent.right);
        System.out.println(parent.data+" ");
    }
    
    //返回樹的葉子節點數
    
    public int leaves(){
        if(this==null) return 0;
        if(left==null&&right==null) {
            
            System.out.print(this.data);
            return 1;
        
        }
        //左邊節點數
        int leftCount=(left==null?0:left.leaves());
        int rightCount=(right==null?0:right.leaves());
        return leftCount+rightCount;
    }
    
    //返回樹的葉子節點數
    
    public int Allleaves(){
        if(this==null) return 0;
        System.out.print(this.data);
        if(left==null&&right==null) { 
            return 1; 
        }
     
        //左邊節點數
        int leftCount=(left==null?0:left.Allleaves());
        int rightCount=(right==null?0:right.Allleaves()); 
        return 1+leftCount+rightCount;
    }
    
     // 獲取高度
    
    public int height(){
        int heightOfTree; 
        if(this==null) return 0;
        
        int leftHeight=(left==null?0:left.height());
        int rightHeight=(right==null?0:right.height());        
heightOfTree=Math.max(leftHeight,rightHeight); System.out.println(
"value=="+this.data+" left right=="+leftHeight+" right height=="+rightHeight); return 1+heightOfTree; } public void getMaxDistance(BinTreeTest1 parent) { // TODO Auto-generated method stub if(parent==null) return; //葉子節點 返回 if(parent.left==null) parent.nMaxLeft=0; if(parent.right==null) parent.nMaxRight=0; if(parent.left!=null) getMaxDistance(parent.left); //如果左子樹不為空,遞歸尋找左子樹最長距離 if(parent.right!=null) getMaxDistance(parent.right);//如果右子樹不為空,遞歸尋找右子樹最長距離 if(parent.left!=null){  parent.nMaxLeft=Math.max(parent.left.nMaxLeft, parent.left.nMaxRight) +1; } if(parent.right!=null){  parent.nMaxRight=Math.max(parent.right.nMaxLeft, parent.right.nMaxRight) +1; }  nMaxLen=Math.max(parent.nMaxLeft+parent.nMaxRight,nMaxLen); } public static void main(String[] args) { // TODO Auto-generated method stub BinTreeTest1 e=new BinTreeTest1("E"); BinTreeTest1 g=new BinTreeTest1("G"); BinTreeTest1 h=new BinTreeTest1("H"); BinTreeTest1 i=new BinTreeTest1("I"); BinTreeTest1 d=new BinTreeTest1("D",null,g); BinTreeTest1 f=new BinTreeTest1("F",h,i); BinTreeTest1 b=new BinTreeTest1("B",d,e); BinTreeTest1 c=new BinTreeTest1("C",f,null); BinTreeTest1 tree=new BinTreeTest1("A",b,c); System.out.println("前序遍歷二叉樹結果:\n"); tree.preOrder(tree); System.out.println("中序遍歷二叉樹結果:\n"); tree.inOrder(tree); System.out.println("后序遍歷二叉樹結果:\n"); tree.postOrder(tree); System.out.println("層級遍歷二叉樹:\n"); tree.LayerOrder(tree); System.out.println("二叉樹的寬度:"+tree.treeWidth); System.out.println("二叉樹的高度:"+tree.height()); System.out.println(); System.out.println("二叉樹的節點數為;"+tree.leaves()); System.out.println("二叉樹的所有的節點數為:"+tree.Allleaves()); System.out.println("獲取二叉樹的最大距離:\n"); tree.getMaxDistance(tree); System.out.println("MaxLen="+tree.nMaxLen); } }

 

以上包含了全部的遍歷方式,其中層級遍歷使用了隊列,簡明扼要,關於隊列的知識,如果需要了解的很詳細可以參考下一篇博文

參考:Java Queue 專題

參考:[編程之美]求二叉樹中節點的最大距離

參考:求二叉樹的深度和寬度[Java]

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM