二叉樹是最常用的數據結構之一,筆者過去一直將關注點放在復雜的樹結構(例如紅黑樹,自平衡樹),認為那些才是樹的重要應用,但當重新由基本看起,才發現樹的基本定中就隱藏着樹這一結構的精髓。盡管是些淺薄蠢笨的理解和推演,但筆者還是滿懷興奮的想要將它記錄下來。
一、二叉樹的定義
二叉樹的定義不用多說,很多書本上都有明確的定義,但有些細節是筆者過去所沒有注意的,先給出殷人昆教授對於二叉樹的基本定義——
二叉樹是結點的一個有限集合,該集合或者為空,或者是由一個根結點加上兩棵分別稱為左子樹和右子樹的、互不相交的二叉樹組成。
可以看出二叉樹的定義是遞歸的,根結點的子樹仍然是二叉樹,到達空子樹時遞歸定義結束。
一般來說,關於樹的術語對於二叉樹都是適用的,但應該明確的是二叉樹不是樹,理由如下:
- 樹在圖論中被視為用n-1條邊連接n個結點的的特殊的圖。圖的頂點結合非空,故樹的頂點非空。圖論中另外定義了N叉樹,它可以是空樹,二叉樹屬於N叉樹。
- 非空二叉樹有根,根結點的子樹有左右之分,次序不能顛倒;若其中一棵子樹為空,則另一棵子樹也必須保持左右之分。樹可以沒有根(自由樹);即使有根,其子樹也沒有這種區分。
那么,這就出現一個問題——二叉樹的葉結點無子女,是否可稱它為無子樹?
根據定義,應該是不能的,因為可認為葉子點的左右子樹為空子樹。
雖然認識這一區別的目的並非應試,但筆者還是提一句:個人認為在一般的測試中未必會考察到這么細致,所以遇到忽略這一細致區別的出題人時請不要過於驚訝。
二、二叉樹的性質
接下來的描述中有必要用到一些數學符號,在Markdown中不好畫出,因此我們再次規定一些符號——
- a^b—— a的b的次方 (計算機常用,無需多言)
- int_UP()—— 向上取整(即去掉浮點數的小數部分,然后將整數部分加1)
- int_DOWN()—— 向下取整(即去掉浮點數的小數部分,只留整數部分)
- log(a,b) —— 表示以a為底取b的對數
性質的內容
二叉樹具有以下五個性質:
1. 在二叉樹的第i(i>=1)層最多有2^(i - 1)個結點。
2. 深度為k(k>=0)的二叉樹最少有k個結點,最多有2^k-1個結點。
3. 對於任一棵非空二叉樹,若其葉結點數為n0,度為2的非葉結點數為n2,則n0 = n2 +1。
4. 具有n個結點的完全二叉樹的深度為int_UP(log(2,n+1))。
5. 如果將一棵有n個結點的完全二叉樹自頂向下,同一層自左向右連續給結點編號1,2,3,......,n,然后按此結點編號將樹中各結點順序的存放於一個一維數組,並簡稱編號為i的結點為結點i( i>=1 && i<=n),則有以下關系:
(1)若 i= 1,則結點i為根,無父結點;若 i> 1,則結點 i 的父結點為結點int_DOWN(i / 2);
(2)若 2*i <= n,則結點 i 的左子女為結點 2*i;
(3)若2*i<=n,則結點i的右子女為結點2*i+1;
(4)若結點編號i為奇數,且i!=1,它處於右兄弟位置,則它的左兄弟為結點i-1;
(5)若結點編號i為偶數,且i!=n,它處於左兄弟位置,則它的右兄弟為結點i+1;
(6)結點i所在的層次為 int_DOWN(log(2,i))+1。
部分性質的證明
- 性質1可以通過數學歸納法得到證明
- 性質2證明:
由性質1可知,k層的最大節點總數可表示為2^0+2^ 1+……+2^ (k-1) = 2^k- 1; - 性質3證明:
首先,由節點的角度看n1+n2+n0=n,設此為(1)式;
再從邊的角度看,n2下接兩條邊,n1下接一條邊,n個節點兩兩相連一共需要n-1條邊,可得2*n2+n1=n-1,此為(2)式;
由(1)式-(2)式,可得
n0-n2=1。
三、一些拓展和說明
1. 完全二叉樹
可以看出性質4和5是針對重要的特殊二叉樹——完全二叉樹的,在此先給出特殊二叉樹的定義。
(1)滿二叉樹
深度k的滿二叉樹是有2^k-1個結點的二叉樹,在滿二叉樹中,每一層結點都達到了最大個數,除最底層結點的度為0外,其他各層結點的度都為2。
(2)完全二叉樹
如果一棵具有n個結點的深度為k的二叉樹,它的每一個結點都與高度為k的滿二叉樹中編號為1 ~ n-1的結點一一對應,則稱這棵二叉樹為完全二叉樹。
其特點是:上面從第1層到第k-1層的所有各層的結點數都是滿的,僅最下面的第k層是滿的,或從右往左連續缺少若干結點。
(3)完全二叉樹的結論
- 若完全二叉樹有n個結點,當n為奇數時,n1 = 0,n2 = int_DOWN(n/2),n0 = n2 + 1;
- 當n為偶數時,n1 = 1, n0 = n/2;n2 = n0 - 1。
證明 ——
由之前的結論可知,k層結點數為n-(2^(k-1)-1);
由於僅最下面的第k層是滿的,或從右往左連續缺少若干結點;
k層結點依次從左到右位於k-1層結點的下方,且中間不留空子樹;
故表達式(n-(2^(k-1)-1))/2的商為k-1層中度為2的結點,余數為度為1的結點。
故當n為奇數時,n1=0;
當n為偶數時,n1=1;
由於k-1層以及以上的結點都是滿的,即一共2^(k-2)-1個結點;
n2= int_DOWN((n-(2^(k-1)-1))/2)+2^(k-2)-1
= int_DOWN((n+1)/2)-1
故當n為奇數時,n2= int_DOWN(n/2)+1-1= int_DOWN(n/2),n0=n2+1;
當n為偶數時,n2= n/2-1,n0=n/2-1+1=n/2。
證畢。
2. 性質5的拓展
如果結點編號從0開始,則有以下結論:
(1)結點i(1<=i<=n-1)的父結點為結點int_DOWN((i-1)/2),結點0無父結點;
(2)分支結點中編號最大的是結點int_DOWN((n-2)/2)或結點int_DOWN(n/2)-1;
(3)若i<=int_DOWN((n-2)/2),則結點i的左子女為2*i+1;若i<=int_DOWN((n-3)/2),則結點i的右子女為2*i+2;
(4)若i為偶數且大於0,則結點i有左兄弟結點i-1;若i為奇數且i<=n-2,則結點i有右兄弟結點i+1;
(5)結點i(0<=i<=n-1)在第int_DOWN(log(2,i+1))+1。
3. 二叉樹存儲和遍歷的小結論
- 含有 n 個結點的二叉鏈表中有 n+1 個空指針域,這是因為在所有結點的2n個鏈指針域中只有n-1個存有邊信息的緣故。三叉鏈表則有n+2個空指針域。
- 前序遍歷算法中,第一個被訪問的元素一定是二叉樹的根,如果根的左子樹非空,則緊跟在根后的一定是根的左子女;如果左子樹為空,則其后緊跟的是其右子樹的根。
- 后序遍歷算法中,最后一個被訪問的一定是二叉樹的根,如果根的右子樹非空,則在根之前的一定是根的右子女;如果右子樹為空,則其前的元素是其左子樹的根。