數據結構分類中有一種很常見的結構,那就是樹,樹的分類不少種,包括二叉樹、二叉搜索樹、紅黑樹、B+樹等等,但大多數都是基於二叉樹的衍生結構,因此今天來學習下二叉樹。java
本文的目錄結構以下:node
什麼是二叉樹
定義:二叉樹是每一個結點最多有兩個子樹的樹結構。一般子樹被稱做 「左子樹」(left subtree)和 「右子樹」(right subtree),最頂層的節點叫作 「根」 。web
邏輯上,二叉樹能夠分紅五種形態,分別是:算法
1) 空二叉樹 數據結構
2) 只有一個根節點的二叉樹svg
3)只有跟和左子樹 學習
4)只有跟和右子樹 spa
5)根和左右子樹 (徹底二叉樹)
.net
注:二叉樹是遞歸定義的,也就是理論上每一個節點均可以無限延伸二叉樹的結構,因此每一個節點都有左右子樹子分,上面的形態只是一種簡單的展現。code
二叉樹的性質
性質1:二叉樹中全部結點的度數均不大於2
性質2:二叉樹第i層上的結點數目最多爲2i-1(i>=1)
性質3:深度爲k的二叉樹至多有2k-1個結點(k>=1)
性質4:包含n個結點的二叉樹的高度至少爲(log2n)+1
性質5:在任意一棵二叉樹中,若終端結點的個數爲n0,度爲2的結點數爲n2,則n0=n2+1
性質4的證實:
(1) 基於性質1,咱們知道二叉樹的節點度數最多不大於2,因此這裏假設n0表示度爲0的結點個數, n1表示度爲1的結點個數,n2表示度爲2的結點個數。三類結點加起來爲總結點個數,因而即可獲得:n=n0+n1+n2
(2) 基於性質1,咱們又能夠得出,樹的總度數爲度數0,1,2的度數加上根節點,也就是 n=n0 * 0 + n1 * 1 + n2* 1 + 1
結合上面的(1) 和 (2)的證實就能夠得出 n0=n2+1 。
二叉樹的遍歷
在將二叉樹的遍歷前,咱們須要先實現二叉樹,根據二叉樹的特色,其包含了節點自己的數據,左子樹和右子樹,用Java代碼能夠這樣表示
public class Node {
/* * 一個二叉樹包括 數據、左右孩子 三部分 */
private int mData;
private Node mLeftChild;
private Node mRightChild;
public Node(int data, Node leftChild, Node rightChild) {
mData = data;
mLeftChild = leftChild;
mRightChild = rightChild;
}
public int getData() {
return mData;
}
public void setData(int data) {
mData = data;
}
public Node getLeftChild() {
return mLeftChild;
}
public void setLeftChild(Node leftChild) {
mLeftChild = leftChild;
}
public Node getRightChild() {
return mRightChild;
}
public void setRightChild(Node rightChild) {
mRightChild = rightChild;
}
}
每個二叉樹的節點均可以用 Node 類表示,下面開始講述其遍歷方式。
前面說了,二叉樹是一種遞歸的結構,每一個節點均可以有左右子樹的結構,而二叉樹的遍歷也是個遞歸遍歷的過程,使得每一個節點有且只有被訪問一次。
按照遍歷結構的順序,通常將二叉樹的遍歷分爲三種:
- 先序遍歷
- 中序遍歷
- 後序遍歷
先序遍歷
遍歷方式:
- 先訪問根節點
- 再先序遍歷左子樹
- 再先序遍歷右子樹
代碼實現:
public void firstOrder(Node node){
if (node == null){
return;
}
showData(node);
firstOrder(node.getLeftChild());
firstOrder(node.getRightChild());
}
//輸出節點數據
public void showData(Node node){
if (node == null){
return;
}
System.out.println(node.getData());
}
中序遍歷
遍歷方式:
- 先中序遍歷左子樹
- 再訪問根節點
- 再中序遍歷右子樹
代碼實現:
public void MediumOrder(Node node){
if (node == null){
return;
}
MediumOrder(node.getLeftChild());
showData(node);
MediumOrder(node.getRightChild());
}
後序遍歷
遍歷方式:
- 前後序遍歷左子樹
- 再後序遍歷右子樹
- 最後訪問根節點
代碼實現:
public void LastOrder(Node node){
if (node == null){
return;
}
LastOrder(node.getLeftChild());
LastOrder(node.getRightChild());
showData(node);
}
如下面的二叉樹爲例,三種不一樣的遍歷結果爲
先序遍歷: 1 2 4 5 7 3 6
中序遍歷: 4 2 7 5 1 3 6
後序遍歷: 4 7 5 2 6 3 1
說完二叉樹的基本知識後,下面介紹下二叉樹的幾種延伸結構。
特殊的二叉樹
下面介紹兩種特殊的二叉樹,分別是滿二叉樹、徹底二叉樹。
滿二叉樹
定義:一顆高度爲k,擁有2^k-1 個節點。就是說除了葉子節點,每一個節點都有左右子樹。
示意圖以下:
徹底二叉樹
徹底二叉樹跟滿二叉樹很相似,但有些許不一樣,須要知足如下的條件:
- 全部葉子節點都出如今 k 或者 k-1 層,並且從 1 到 k-1 層必須達到最大節點數;
- 第 k 層能夠不是滿的,可是第 k 層的全部節點必須集中在最左邊。
給張示意圖看下二者的區別:
最後
瞭解兩種特殊的二叉樹後,咱們來作一道算法題鞏固一下。
在筆試過程當中常常會見到的,就是問你怎麼判斷一顆二叉樹爲徹底二叉樹?
思路:根據徹底二叉樹的特色,咱們知道最後一層的全部節點都在最左邊,所以能夠按照這樣的思路來解題,那就是:在層序遍歷的過程當中,找到第一個非滿節點。滿節點指的是同時擁有左右孩子的節點。在找到第一個非滿節點以後,剩下的節點不該該有孩子節點;若是有,那麼該二叉樹就不是徹底二叉樹。
根據這樣的思路,咱們能夠藉助隊列,對徹底二叉樹作廣度遍歷的方式,下面展現一下代碼實現:
public class CompleteTreeChecker {
//輔助空間,隊列
private LinkedList<Node> queue = new LinkedList<Node>();
//第n層最右節點的標誌
private boolean leftMost = false;
public boolean processChild(Node child) {
if(child != null) {
if(!leftMost) {
queue.addLast(child);
} else {
return false;
}
} else {
leftMost = true;
}
return true;
}
public boolean isCompleteTree(Node root) {
//空樹也是徹底二叉樹
if(root == null) return true;
//首先根節點入隊列
queue.addLast(root);
while(!queue.isEmpty()) {
Node node = queue.removeFirst();
//處理左子結點
if(!processChild(node.getLeftChild()))
return false;
//處理右子結點
if(!processChild(node.getRightChild()))
return false;
}
//廣度優先遍歷完畢,此樹是徹底二叉樹
return true;
}
本文分享 CSDN - 鄙人薛某。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。