二叉樹基礎上

1. 樹(Tree)

首先咱們來看幾個樹的例子。算法

在一個樹結構裏,每一個元素咱們稱之爲節點,從上到下相鄰節點連線的關係,咱們稱之爲父子關係數組

在上面的圖中,A 節點就是 B 節點的父節點,B 節點就是 A 節點的子節點。B、C、D 這三個節點的父節點是同一個節點,因此它們之間互稱爲兄弟節點。另外,若是一個節點沒有父節點,咱們稱之爲根節點,好比上圖中的 E 節點。沒有子節點的節點稱爲葉子節點或者葉節點,好比上圖中的 G、H、I、J、K、L 節點。數據結構

還有幾個關於樹的比較重要的概念:高度(Height)、深度(Depth)、層(Level),它們的定義以下。post

高度其實就是從下往上計量,最下面的葉節點高度爲 0,每向上一個節點,高度加 1。深度則是從上往下計量,根節點深度爲 0,每向下一個節點,深度加 1。層則和深度相似,只不過根節點的層爲 1,計數從 1 開始罷了。spa

2. 二叉樹(Binary Tree)

樹的結構多種多樣,但咱們最經常使用的仍是二叉樹。3d

二叉樹,顧名思義,就是說每一個節點最多有兩個分叉,也就是兩個子節點,分別爲左子節點和右子節點。不過,二叉樹並不要求每一個結點都有兩個子節點,而是最多隻能有兩個子節點。以此類推,四叉樹、八叉樹也和二叉樹同理。指針

上圖中,第 2 個二叉樹中的葉子節點全都在最底層,並且,除了葉子節點外,每一個節點都有左右兩個子節點,這種二叉樹就叫做滿二叉樹code

第 3 個二叉樹中的葉子節點都在最底下兩層,最後一層的葉子節點都靠左排列,而且除了最後一層,其它層的節點個數都要達到最大,這種二叉樹就叫做徹底二叉樹cdn

徹底二叉樹爲何最後一層的葉子節點要靠左排列呢?這個定義有什麼由來或者說目的在哪裏呢?咱們要從二叉樹的存儲方式來了解。想要存儲一棵二叉樹,咱們有兩種方法,一種是基於指針或者引用的二叉鏈式存儲法,一種是基於數組的順序存儲法。blog

咱們先來看比較簡單、直觀的鏈式存儲法。其中,每一個節點有三個字段,其中一個存儲數據,另外兩個分別是指向左右子節點的指針。

再來看基於數組的順序存儲法。咱們把根節點存儲在下標爲 i = 1 的位置,左子節點就存儲在下標爲 2 * i = 2 的位置,右子節點就存儲在下標爲 2 * i + 1 = 3 的位置。以此類推,B 節點的左子節點 就存儲在 2 * 2 = 4 的位置,右子節點就存儲在 2 * 2 + 1 = 5 的位置。

也就是說,若是節點 X 存儲在下標爲 i 的位置,那下標爲 2 * i 的位置存儲的就是左子節點,下標爲 2 * i + 1 的位置存儲的就是右子節點,並且下標爲 i / 2 的位置存儲的就是其父節點。以這種方式存儲,咱們就能夠經過根節點的位置很容易地把整棵樹都串起來。

不過,剛纔的狀況是一棵徹底二叉樹,咱們就浪費了數組中一個下標爲 0 的位置。若是是一個非徹底二叉樹,那就會浪費比較多的數組存儲空間。

因此,若是某棵樹是徹底二叉樹,那用數組來存儲無疑是最節省內存的方式,由於數組不須要額外的指針來指向左右子節點。所以,這也就是爲何徹底二叉樹會要求最後一層的子節點都靠左的緣由。

3. 二叉樹的遍歷

二叉樹的遍歷有三種經典方法,前序遍歷、中序遍歷和後序遍歷,其中,前、中、後序指的是節點和它的左右子樹節點打印的前後順序。

  • 前序遍歷,就是對於樹中的任意節點來講,先打印這個節點,而後再打印它的左子樹,最後打印它的右子樹。

  • 中序遍歷,就是對於樹中的任意節點來講,先打印它的左子樹,而後再打印這個節點,最後打印它的右子樹。

  • 後序遍歷,就是對於樹中的任意節點來講,先打印它的左子樹,而後再打印它的右子樹,最後打印這個節點。

能夠看到,二叉樹的遍歷其實就是一個遞歸的過程。好比前序遍歷,其實就是先打印根節點,而後再遞歸地打印左子樹,最後遞歸地打印右子樹。

void preOrder(Node* root) {
  if (root == null) return;
  print root // 此處爲僞代碼,表示打印 root 節點
  preOrder(root->left);
  preOrder(root->right);
}

void inOrder(Node* root) {
  if (root == null) return;
  inOrder(root->left);
  print root // 此處爲僞代碼,表示打印 root 節點
  inOrder(root->right);
}

void postOrder(Node* root) {
  if (root == null) return;
  postOrder(root->left);
  postOrder(root->right);
  print root // 此處爲僞代碼,表示打印 root 節點
}
複製代碼

在遍歷的過程當中,每一個節點最多會被訪問兩次,因此遍歷的時間複雜度與節點的個數成正比,爲 O(n)。

參考資料-極客時間專欄《數據結構與算法之美》

獲取更多精彩,請關注「seniusen」!

相關文章
相關標籤/搜索