數據結構中一直對二叉樹不是很了解,今天趁着這個時間整理一下
許多實際問題抽象出來的數據結構往往是二叉樹的形式,即使是一般的樹也能簡單地轉換為二叉樹,而且二叉樹的存儲結構及其算法都較為簡單,因此二叉樹顯得特別重要。
二叉樹(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); } }
以上包含了全部的遍歷方式,其中層級遍歷使用了隊列,簡明扼要,關於隊列的知識,如果需要了解的很詳細可以參考下一篇博文