1. 程式人生 > >資料結構與演算法篇 二叉樹(Binary Tree)(一)

資料結構與演算法篇 二叉樹(Binary Tree)(一)

好多天沒有寫過資料結構和演算法了,好了今天抽出點時間二叉樹,前面講到的都是線性表,棧,佇列等等。

今天講到的是非線性表結構--樹,首先說一下什麼是樹的概念

樹的這種資料結果挺像我們現實中的樹,這裡的每一個元素我們叫做節點,用線把相鄰的節點連線起來,然後它們就成了父子關係。

A節點是B節點的父節點,B節點是A節點的子節點,BCD三個節點的父節點是同一個節點,所以它們之間稱為兄弟節點,我們把

沒有父節點的節點稱為根節點,也就是E;沒有子節點的節點稱為葉子節點,其中GHIJKL都是

其中樹還有三個相似的概念:高度,深度,層

換一種表達方式就是,高度相當於你從一樓開始往上數,這層樓有多高;深度就相當於你從水面測水底的深度,不同位置的深度都不一樣;

接下來講一下二叉樹,二叉樹顧名思義就是每一個節點都有兩個叉,也就是有兩個子節點,當然不強制要求有兩個節點

其中還有兩個關於二叉樹的概念就是滿二叉樹和完全二叉樹

滿二叉樹很好的理解,就是圖中第二個,就是所有滿了。。。

然而完全二叉樹呢?是圖中第三個,完全二叉樹的概念是,葉子節點都在最底下兩層,最後一層的·子節點靠左排列,並且除了最後一層其他層的節點數都要達到最大。

為什麼要定義這麼一顆樹?我們慢慢來。。。。。。。

下面我們來看一下怎麼儲存一顆二叉樹,有兩種方法,第一種是基於指標或者引用的二叉鏈式儲存,另外一種是基於陣列的順序儲存法。

首先我們先來看一下鏈式儲存,我們可以看出一個節點有三個部分組成,第一個是儲存的資料,第二三個是左右節點的指標,然後就把整棵樹串聯起來了

接下來我們再看一下陣列的順序儲存方式,如果節點x儲存在陣列中下標為i的位置,下標為2*i的儲存的左子節點,下標為2*i+1的位置儲存的是右子節點,i/2位置就是根節點,為了方便計算我們一般把根節點取為1。

講到這裡我們可以回想一下為什麼有完全二叉樹的出現了,一個完全二叉樹,它只會浪費1個位置的儲存位,如果是一顆非完全二叉樹我們就浪費了大量的位置了,所以如果某課樹是完全二叉樹那麼陣列就是最節省儲存的方式了

接下來講一個我經常在面試的題目講到的一個概念了,就是二叉樹的遍歷,經典的遍歷方法有三種分別是前序遍歷,中序遍歷,後續遍歷,一開始我經常忘記了這個死鬼概念,老是錯了。背了又忘記了,真是日了哈士奇。

下面我們來認真看一下這三個概念

前序遍歷:對於樹中任意節點來說,先列印這個節點,再列印它的左字樹,然後再到右子樹

中序遍歷:對於樹中任意節點來說,先列印它的左字樹,然後列印它本身,最後列印右字數

後序遍歷:對於樹中任意節點來說,先列印它的左字數,再列印右子樹,最後列印本身

實際上,前中後三種遍歷都是一個遞迴的過程,那麼遞迴程式碼應該怎麼去寫,如果要解決問題A,那麼子問題B,C必須解決了

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 節點
}