二叉樹學習筆記

1、樹(Tree)

每個元素我們叫作“節點”;用來連線相鄰節點之間的關係,叫作“父子關係” 。

  • A 節點就是 B 節點的父節點,B 節點是 A 節點的子節點

  • B、C、D 這三個節點的父節點是同一個節點,互稱爲兄弟節點

  • 沒有父節點的節點叫作根節點(E);

  • 沒有子節點的節點叫作葉子節點或者葉節點(G…L)

2、二叉樹

二叉樹,顧名思義,每個節點最多有兩個“叉”,也就是兩個子節點,分別是左子節點右子節點

  • 除了葉子節點之外,每個節點都有左右兩個子節點,這種二叉樹就叫作滿二叉樹
  • 最後一層的葉子節點都靠排列,其他層都是滿二叉樹,這種二叉樹叫作完全二叉樹

2.1 存儲二叉樹

2.1.1 基於引用的二叉鏈式存儲法

每個節點有三個字段,其中一個存儲數據,另外兩個是指向左右子節點的指針 。

2.1.2 基於數組的順序存儲法

如果節點 X 存儲在數組中下標爲 i 的位置,下標爲 2 * i 的位置存儲的就是左子節點,下標爲 2 * i + 1 的位置存儲的就是右子節點 ,同理 下標爲 i/2 。

非完全二叉樹:

## 2.2 二叉樹的遍歷
  • 前序遍歷:對於樹中的任意節點來說,先打印這個節點,然後再打印它的左子樹,最後打印它的右子樹。

  • 中序遍歷:對於樹中的任意節點來說,先打印它的左子樹,然後再打印它本身,最後打印它的右子樹。

  • 後序遍歷:對於樹中的任意節點來說,先打印它的左子樹,然後再打印它的右子樹,最後打印這個節點本身。

  • 二叉樹遍歷的時間複雜度是 O(n)。

//前序遍歷
void preOrder(Node root) {
        if (root == null) return;
        System.out.println(root.data);  // 打印 root 節點
        preOrder(root.left);
        preOrder(root.right);
    }
//中序遍歷
    void inOrder(Node root) {
        if (root == null) return;
        inOrder(root.left);
        System.out.println(root.data);  // 打印 root 節點
        inOrder(root.right);
    }
//後序遍歷
    void postOrder(Node root) {
        if (root == null) return;
        postOrder(root.left);
        postOrder(root.right);
        System.out.println(root.data); // 打印 root 節點
    }
    class Node {
        public String data;  //節點數據
        public Node left;  //左孩子
        public Node right;  //右孩子
         public Node(String data) {
                this.data = data;
            }
    }

2.3 二叉查找樹

二叉查找樹要求,在樹中的任意一個節點,其左子樹中的每個節點的值,都要小於這個節點的值,而右子樹節點的值都大於這個節點的值 。

2.3.1 查找操作

先取根節點,如果它等於我們要查找的數據,那就返回。如果要查找的數據比根節點的值小,那就在左子樹中遞歸查找;如果要查找的數據比根節點的值大,那就在右子樹中遞歸查找 。

   public Node find(Node node,int data) {
        Node p = node;
        while (p != null) {
            if (data < p.data) p = p.left;
            else if (data > p.data) p = p.right;
            else return p;
        }
        return null;
    }

2.3.2 插入操作

如果要插入的數據比節點的數據大,並且節點的右子樹爲空,就將新數據直接插到右子節點的位置;如果不爲空,就再遞歸遍歷右子樹,查找插入位置。

public void insert(int data) {
  if (tree == null) {
    tree = new Node(data);
    return;
  }
 
  Node p = tree;
  while (p != null) {
    if (data > p.data) {
      if (p.right == null) {
        p.right = new Node(data);
        return;
      }
      p = p.right;
    } else { // data < p.data
      if (p.left == null) {
        p.left = new Node(data);
        return;
      }
      p = p.left;
    }
  }
}

2.3.3 刪除操作

  • 第一種情況是,如果要刪除的節點沒有子節點;

  • 第二種情況是,如果要刪除的節點只有一個子節點(只有左子節點或者右子節點);

  • 第三種情況是,如果要刪除的節點有兩個子節點。

public void delete(int data) {
  Node p = tree; // p 指向要刪除的節點,初始化指向根節點
  Node pp = null; // pp 記錄的是 p 的父節點
  while (p != null && p.data != data) {
    pp = p;
    if (data > p.data) p = p.right;
    else p = p.left;
  }
  if (p == null) return; // 沒有找到
 
  // 要刪除的節點有兩個子節點
  if (p.left != null && p.right != null) { // 查找右子樹中最小節點
    Node minP = p.right;
    Node minPP = p; // minPP 表示 minP 的父節點
    while (minP.left != null) {
      minPP = minP;
      minP = minP.left;
    }
    p.data = minP.data; // 將 minP 的數據替換到 p 中
    p = minP; // 下面就變成了刪除 minP 了
    pp = minPP;
  }
 
  // 刪除節點是葉子節點或者僅有一個子節點
  Node child; // p 的子節點
  if (p.left != null) child = p.left;
  else if (p.right != null) child = p.right;
  else child = null;
 
  if (pp == null) tree = child; // 刪除的是根節點
  else if (pp.left == p) pp.left = child;
  else pp.right = child;
}

3、紅黑樹

  • 根節點是黑色的;
  • 每個葉子節點都是黑色的空節點(NIL),也就是說,葉子節點不存儲數據;
  • 任何相鄰的節點都不能同時爲紅色,也就是說,紅色節點是被黑色節點隔開的;
  • 每個節點,從該節點到達其可達葉子節點的所有路徑,都包含相同數目的黑色節點;

具體實現參考紅黑樹

4、遞歸樹

遞歸的思想就是,將大問題分解爲小問題來求解,然後再將小問題分解爲小小問題。這樣一層一層地分解,直到問題的數據規模被分解得足夠小,不用繼續遞歸分解爲止。

如果我們把這個一層一層的分解過程畫成圖,它其實就是一棵樹。我們給這棵樹起一個名字,叫作遞歸樹

發佈了138 篇原創文章 · 獲贊 146 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章