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

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

今天要講的是二叉查詢樹(Binary Search Tree),是一種最常用的二叉搜尋樹,支援快速查詢,刪除,插入資料。

它是如何實現的呢?,其實它依靠的它的資料結構,在樹中的任意一個節點,其左子樹的每個節點的值都小於這個節點的值,右子樹都大於這個節點的值。

接下來我們來看一下二叉樹是怎麼實現快速查詢,刪除,新增

首先我們來看一下查詢是怎麼實現的,快速查詢是依賴二叉樹的這種的特殊的資料結構,我們首先取的是根節點的資料,

如果等於我們要找的資料就返回,小於的話我們就在左子樹遞迴查詢,大於的話我們就在右子樹遞迴查詢

public class BinarySearchTree {
  private Node tree;
 
  public Node find(int data) {
    Node p = tree;
    while (p != null) {
      if (data < p.data) p = p.left;
      else if (data > p.data) p = p.right;
      else return p;
    }
    return null;
  }
 
  public static class Node {
    private int data;
    private Node left;
    private Node right;
 
    public Node(int data) {
      this.data = data;
    }
  }
}

接下來要講的是插入操作,其實插入操作跟查詢也是類似的,將要插入的資料跟根節點進行比較,如果要插入的資料比

節點的資料大,並且節點的右子樹為空我們就插入到右子樹的位置裡,如果不為空,我們就遞迴下去,同理。。。。。。

反之也是同理。。。。。。。

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;
    }
  }
}

二叉查詢樹的查詢、插入操作都比較簡單易懂,但是它的刪除操作就比較複雜了 。針對要刪除節點的子節點個數的不同,我們需要分三種情況來處理。

第一種情況是,如果要刪除的節點沒有子節點,我們只需要直接將父節點中,指向要刪除節點的指標置為 null。比如圖中的刪除節點 55。

第二種情況是,如果要刪除的節點只有一個子節點(只有左子節點或者右子節點),我們只需要更新父節點中,指向要刪除節點的指標,讓它指向要刪除節點的子節點就可以了。比如圖中的刪除節點 13。

第三種情況是,如果要刪除的節點有兩個子節點,這就比較複雜了。我們需要找到這個節點的右子樹中的最小節點,把它替換到要刪除的節點上。然後再刪除掉這個最小節點,因為最小節點肯定沒有左子節點(如果有左子結點,那就不是最小節點了),所以,我們可以應用上面兩條規則來刪除這個最小節點。比如圖中的刪除節點 18。

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;
}