資料結構 其五 樹與二叉樹學習總結

sumandavg發表於2020-11-28

資料結構是計算機儲存、組織資料的方式。資料結構是指相互之間存在一種或多種特定關係的資料元素的集合。通常情況下,精心選擇的資料結構可以帶來更高的執行或者儲存效率。

前言

樹是一種非線性儲存結構,通過一些方法可以用二叉樹表示任意樹。因此,本文重點介紹二叉樹的基本操作。

1. 樹與二叉樹介紹

1.1 什麼是樹

對於資料結構的學習只需要關注兩點就夠了:1. 資料 2. 結構。

樹(tree)是一種非線性儲存結構,例如下述示意圖:

樹示意圖

  1. 每一個方框代表一個節點(資料),連線節點的線稱之為邊或枝(結構)。
  2. 通過邊相連的節點之間存在父子關係,位於上層的節點稱之為父節點,位於下層的節點稱之為子節點,具有相同父節點的節點稱之為兄弟節點。
  3. 整棵樹的最上層的節點(即沒有父節點)稱之為根節點。
  4. 從根節點到某個節點之間的邊數,稱之為這個節點的深度
  5. 以一個節點的一個子節點為根節點的樹,稱之為這個節點的子樹。例如:上圖中的root節點有三顆子樹。
  6. 一個節點的子節點個數稱之為該節點的度。例如:上圖中root節點的度為3.

1.1.1 樹的性質

  • 一個節點可以有多個子節點,但只能有一個父節點

  • 一顆樹的節點個數 = 邊數 + 1

1.2 什麼是二叉樹

  • 每個節點最多隻有兩個子節點,分別稱之為左子節點、右子節點。
  • 當假設二叉樹的根節點為第0層時,二叉樹的第n層最多有2^n個節點。

1.2.1 二叉樹的分類

  • 滿二叉樹 (中國版):假設一顆樹的深度為d,則當這顆樹在深度為d的節點個數為2^(d + 1) - 1時,稱之為滿二叉樹
  • 完全二叉樹(complete binary tree):除了最下層外,其餘層的節點數都達到了該層的上限,並且最下層的節點滿足從左到右依次排列(可以沒有右節點,但不能在有右節點的情況下沒有左節點)。
  • 全二叉樹 (full binary tree): 每個節點要麼沒有子節點,要麼同時有兩個子節點。

1.3 二叉樹的框架程式碼(C++版)

#include <iostream>

using namespace std;
// 節點
class Node {
public:
    int data;				// 資料
    Node *lchild;			// 左節點
    Node *rchild;			// 右節點
    Node (int new_data);	// 初始化節點
    ~Node ();				// 回收節點空間
};
// 二叉樹
class BinaryTree {
private:
    Node *root;				// 根節點
public:
    BinaryTree();			// 初始化二叉樹
    ~BinaryTree();			// 回收二叉樹的空間
    void build(int n);		// 構造一棵二叉樹
    void pre_order(Node *);	// 先序遍歷
    void in_order(Node *);	// 中序遍歷
    void post_order(Node *);// 後序遍歷
};

下述的所有程式碼都是基於這個框架

2. 二叉樹的操作

2.1 初始化節點

初始化節點是將資料封裝為節點

2.1.1 步驟

  1. 將資料儲存到資料域中
  2. 將左右子節點置為空

2.1.2 程式碼展示

Node(int new_data) {
    data   = new_data;
    lchild = NULL;
    rchild = NULL;
}
// 回收節點空間只需要依次回收左右子節點即可
~Node() {
    if (lchild != NULL) {
        delete lchild;
    }
    if (rchild != NULL) {
        delete rchild;
    }
}

2.2 初始化二叉樹

初始二叉樹中不存在節點

2.2.1 步驟

  1. 將根節點置空

2.2.2 程式碼展示

BinaryTree() {
    root = NULL;
}
// 二叉樹中的節點已經通過Node類回收完畢
~BinaryTree() {
    delete root;
}

2.3 構造二叉樹

void build(int n);

2.3.1 步驟

  • 首先將資料封裝成節點
  • 之後根據需要將節點插入到二叉樹中(本文以左子<=當前節點<右子 的規則插入)

2.3.2 程式碼展示

void build(Node *node) {
    root = insert(root, node);
    return;
}
Node* insert(Node *root, Node *node) {
    if (root == NULL) return node;
    if (node->data <= root->data) {
        root->lchild = insert(root->lchild, node);
    }
    else {
        root->rchild = insert(root->rchild, node);
    }
    return root;
}

2.4 先序遍歷

void pre_order(Node *node)

樹的訪問都遵循著 先左後右的順序,但根據節點的列印順序分為先序遍歷、中序遍歷和後序遍歷。

2.4.1 步驟

  • 列印當前節點
  • 列印左子節點
  • 列印右子節點

2.4.2 程式碼展示

void pre_order(Node *node) {
    if (node == NULL) return;
    cout << node->data << " ";
    pre_order(node->lchild);
    pre_order(node->rchild);
}

2.5 中序遍歷

void in_order(Node *)

2.5.1 步驟

  • 列印左子節點
  • 列印當前節點
  • 列印右子節點

2.5.2 程式碼展示

void in_order(Node *node) {
    if (node == NULL) return;
    in_order(node->lchild);
    cout << node->data << " ";
    in_order(node->rchild);
}

2.6 後序遍歷

void post_order(Node *)

2.5.1 步驟

  • 列印左子節點
  • 列印右子節點
  • 列印當前節點

2.5.2 程式碼展示

void post_order(Node *node) {
    if (node == NULL) return;
    post_order(node->lchild);
    post_order(node->rchild);
    cout << node->data << " ";
}

3. 結果展示

3.1 完整程式碼

1
2
3

執行結果

執行結果

總結

本文主要介紹了基礎的樹型結構以及二叉樹的遍歷。如果給二叉樹的構造附加上其他的規則,那麼便能衍生出更為高階的樹型資料結構。

本文僅為個人的學習總結,歡迎各位大佬勘誤。

相關文章