1. 程式人生 > >資料結構-二叉樹和二叉查詢樹

資料結構-二叉樹和二叉查詢樹

先按樹-二叉樹-二叉查詢樹的順序解釋會比較清楚。

一,樹

樹(Tree)是n(n≥0)個結點的有限集。在任意一棵非空樹中:

(1)有且僅有一個特定的被稱為根(Root)的結點;

(2)當n>1時,其餘結點可分為m(m>0)個互不相交的有限集T1,T2,…,Tm,其中每一個集合本身又是一棵樹,並且稱為根的子樹(SubTree)。

結點的度(Degree)結點擁有的子樹數稱為結點的度(Degree)。度為0的結點稱為葉子(Leaf)或終端結點。度不為0的結點稱為非終端結點或分支結點。
樹的度:是樹內各結點的度的最大值。
孩子和雙親:結點的子樹的根稱為該結點的孩子(Child),相應地,該結點稱為孩子的雙親(Parent)。
結點的層次(Level):
是從根結點開始計算起,根為第一層,根的孩子為第二層,依次類推。樹中結點的最大層次稱為樹的深度(Depth)或高度。

如果將樹中結點的各子樹看成從左至右是有次序的(即不能互換),則稱該樹為有序樹,否則稱為無序樹。

二,二叉樹

二叉樹(Binary Tree)的特點是每個結點至多具有兩棵子樹(即在二叉樹中不存在度大於2的結點),並且子樹之間有左右之分

二叉樹的性質:
(1)、在二叉樹的第i層上至多有2i-1個結點(i≥1)。
(2)、深度為k的二叉樹至多有2k-1個結點(k≥1)。
(3)、對任何一棵二叉樹,如果其終端結點數為n0,度為2的結點數為n2,則n0=n2+1。


三,二叉查詢樹(左<中<右)

我們從一種特殊的、使用很廣泛的二叉樹入手:二叉查詢樹。

二叉查詢樹的性質:

(1)、若它的左子樹不為空,則左子樹上所有結點的值均小於它的根結點的值;
(2)、若它的右子樹不為空,則右子樹上所有結點的值均大於它的根結點的值;
(3)、它的左、右子樹也分別為二叉查詢樹。
用一句話概括,二叉查詢樹的特點是,一個節點的左子節點的關鍵字值小於這個節點,右子節點的關鍵字值大於或等於這個父節點。

二叉查詢樹的基本操作是查詢,插入,刪除,遍歷,下面一一介紹:

1,查詢(search)

我們已經知道,二叉搜尋樹的特點是左子節點小於父節點,右子節點大於或等於父節點。查詢某個節點時,先從根節點入手,如果該元素值小於根節點,則轉向左子節點,否則轉向右子節點,以此類推,直到找到該節點,或者到最後一個葉子節點依然沒有找到,則證明樹中沒有該節點



程式碼是:

  /** 查詢元素,返回true */
  public boolean search(E e) {
    TreeNode<E> current = root; // 從根元素開始

    while (current != null) {
      if (e.compareTo(current.element) < 0) {//如果比當前元素值小,就指向當前元素的左子樹
        current = current.left;
      }
      else if (e.compareTo(current.element) > 0) {//如果比當前元素值大,就指向當前元素的右子樹
        current = current.right;
      }
      else // element等於 current.element
        return true; //發現元素,返回true
    }

    return false;
  }

2,插入(insert)

插入一個新節點首先要確定插入的位置,關鍵思路是確定新節點父節點所在的位置


程式碼:

/** 插入元素,成功返回true */
  public boolean insert(E e) {
    if (root == null)
      root = createNewNode(e); // 如果是樹空則創造一個跟節點
    else {
      // 標記當前父節點位置
      TreeNode<E> parent = null;
      TreeNode<E> current = root;
      while (current != null)
        if (e.compareTo(current.element) < 0) {
          parent = current;
          current = current.left;
        }
        else if (e.compareTo(current.element) > 0) {
          parent = current;
          current = current.right;
        }
        else
          return false; // 有重複節點,不能被插入

      // 建立一個新節點掛在父節點下
       if (e.compareTo(parent.element) < 0)
        parent.left = createNewNode(e);
       else
        parent.right = createNewNode(e);
    }

    size++;
    return true; // 插入成功
  }
3,刪除(delete)
刪除BST中的一個節點是最麻煩的操作,總結一下大概下面兩種方法:

Case 1:刪除點沒有左孩子這是隻需要將該節點的父節點和當前節點的有孩子相連即可



Case2:刪除點有左孩子.這種情況下先找到當前節點的左子樹的最右節點,因為一個節點的左子樹的最右節點也比右子樹的最左節點小,把最右節點複製給刪除點,然後刪除最右節點



程式碼:

 /** 刪除節點,刪除成功返回true,不在樹中返回false*/
  public boolean delete(E e) {
    // 標記被刪除的節點和該節點的父節點位置
    TreeNode<E> parent = null; 
    TreeNode<E> current = root;
    while (current != null) {
      if (e.compareTo(current.element) < 0) {
        parent = current;
        current = current.left;
      }
      else if (e.compareTo(current.element) > 0) {
        parent = current;
        current = current.right;
      }
      else
        break; // 元素在這個樹中
    }

    if (current == null)
      return false; // 元素不在樹中

    
    if (current.left == null) { // 第一種情況:元素沒有左子樹,把當前節點的右子樹直接掛在其父節點的右子樹上
      // 把當前節點的右子樹直接掛在其父節點的右子樹上
      if (parent == null) {
        root = current.right;
      }
      else {
        if (e.compareTo(parent.element) < 0)
          parent.left = current.right;
        else
          parent.right = current.right;
      }
    }
    else {  // 第二種情況:元素有左子樹,先找到當前節點的左子樹的最右節點
     
      //標記當前節點的左子樹的父節點和最右節點
      TreeNode<E> parentOfRightMost = current;
      TreeNode<E> rightMost = current.left;
      
      //一直向右,找到最右端的節點,因為一個節點的左子樹的最右節點也比右子樹的最左節點小
      while (rightMost.right != null) {
        parentOfRightMost = rightMost;
        rightMost = rightMost.right; // 一直向右
      }
   /*
    * 以上程式碼的目的是要找到刪除節點的左子樹最右節點 ,因為一個節點的左子樹的最右節點也比右子樹的最左節點小*/
      
      
      // 找到最右節點後,放到當前要刪除的位置
      current.element = rightMost.element;

      // 消除最右節點
      if (parentOfRightMost.right == rightMost)
        parentOfRightMost.right = rightMost.left;//把最右節點的左子樹掛在其父節點的右子樹上
      else
        // 具體情況: parentOfRightMost == current
        parentOfRightMost.left = rightMost.left;     
    }

    size--;
    return true; // 刪除成功
  }

下面介紹一下二叉樹的構成:


Tree.java

package com.hust.cn;
public interface Tree<E extends Comparable<E>> {
  //查詢元素 
  public boolean search(E e);
  //插入元素
  public boolean insert(E e);
  //刪除元素
  public boolean delete(E e);

  
  //中序遍歷
  public void inorder();
  //後序遍歷
  public void postorder();
  //前序遍歷
  public void preorder();

  
  //返回大小
  public int getSize();
  //判空
  public boolean isEmpty();
  //返回樹的迭代器
  public java.util.Iterator iterator();
}
AbstractTree.java
package com.hust.cn;
public abstract class AbstractTree<E extends Comparable<E>>
    implements Tree<E> {
	//中序遍歷
  public void inorder() {
  }

  //後序遍歷
  public void postorder() {
  }

  //前序遍歷
  public void preorder() {
  }

  //判空
  public boolean isEmpty() {
    return getSize() == 0;
  }

  //返回樹的迭代器
  public java.util.Iterator iterator() {
    return null;
  }
}
BinaryTree.java
package com.hust.cn;
public class BinaryTree<E extends Comparable<E>>
    extends AbstractTree<E> {
  protected TreeNode<E> root;//節點類,是內部類
  protected int size = 0;

  /** 建構函式 */
  public BinaryTree() {
  }

  /** 物件陣列建立一個二叉查詢樹 */
  public BinaryTree(E[] objects) {
    for (int i = 0; i < objects.length; i++)
      insert(objects[i]);
  }

  /** 查詢元素,返回true */
  public boolean search(E e) {
    TreeNode<E> current = root; // 從根元素開始

    while (current != null) {
      if (e.compareTo(current.element) < 0) {//如果比當前元素值小,就指向當前元素的左子樹
        current = current.left;
      }
      else if (e.compareTo(current.element) > 0) {//如果比當前元素值大,就指向當前元素的右子樹
        current = current.right;
      }
      else // element等於 current.element
        return true; //發現元素,返回true
    }

    return false;
  }

  /** 插入元素,成功返回true */
  public boolean insert(E e) {
    if (root == null)
      root = createNewNode(e); // 如果是樹空則創造一個跟節點
    else {
      // 標記當前父節點位置
      TreeNode<E> parent = null;
      TreeNode<E> current = root;
      while (current != null)
        if (e.compareTo(current.element) < 0) {
          parent = current;
          current = current.left;
        }
        else if (e.compareTo(current.element) > 0) {
          parent = current;
          current = current.right;
        }
        else
          return false; // 有重複節點,不能被插入

      // 建立一個新節點掛在父節點下
       if (e.compareTo(parent.element) < 0)
        parent.left = createNewNode(e);
       else
        parent.right = createNewNode(e);
    }

    size++;
    return true; // 插入成功
  }
  /*建立一個新節點*/
  protected TreeNode<E> createNewNode(E e) {
    return new TreeNode<E>(e);
  }

  /** 中序遍歷*/
  public void inorder() {
    inorder(root);
  }

  /** 從根節點中序遍歷 ,遞迴方法*/
  protected void inorder(TreeNode<E> root) {
    if (root == null) return;
    inorder(root.left);
    System.out.print(root.element + " ");
    inorder(root.right);
  }

  /**後序遍歷 */
  public void postorder() {
    postorder(root);
  }

  /**從根節點後序遍歷,遞迴方法 */
  protected void postorder(TreeNode<E> root) {
    if (root == null) return;
    postorder(root.left);
    postorder(root.right);
    System.out.print(root.element + " ");
  }

  /**前序遍歷 */
  public void preorder() {
    preorder(root);
  }

  /** 從根節點前序遍歷,遞迴方法 */
  protected void preorder(TreeNode<E> root) {
    if (root == null) return;
    System.out.print(root.element + " ");
    preorder(root.left);
    preorder(root.right);
  }

  /** 返回樹的大小 */
  public int getSize() {
    return size;
  }

  /** 返回根節點 */
  public TreeNode getRoot() {
    return root;
  }

  /** 返回從根節點到一個具體元素的路徑 */
  public java.util.ArrayList<TreeNode<E>> path(E e) {
    java.util.ArrayList<TreeNode<E>> list =
      new java.util.ArrayList<TreeNode<E>>();//用陣列存放路徑上的元素
    TreeNode<E> current = root; // 從根節點開始

    while (current != null) {
      list.add(current); // 添加當前元素到數組裡
      if (e.compareTo(current.element) < 0) {
        current = current.left;
      }
      else if (e.compareTo(current.element) > 0) {
        current = current.right;
      }
      else
        break;
    }

    return list; // 返回節點陣列
  }

  /** 刪除節點,刪除成功返回true,不在樹中返回false*/
  public boolean delete(E e) {
    // 標記被刪除的節點和該節點的父節點位置
    TreeNode<E> parent = null; 
    TreeNode<E> current = root;
    while (current != null) {
      if (e.compareTo(current.element) < 0) {
        parent = current;
        current = current.left;
      }
      else if (e.compareTo(current.element) > 0) {
        parent = current;
        current = current.right;
      }
      else
        break; // 元素在這個樹中
    }

    if (current == null)
      return false; // 元素不在樹中

    
    if (current.left == null) { // 第一種情況:元素沒有左子樹,把當前節點的右子樹直接掛在其父節點的右子樹上
      // 把當前節點的右子樹直接掛在其父節點的右子樹上
      if (parent == null) {
        root = current.right;
      }
      else {
        if (e.compareTo(parent.element) < 0)
          parent.left = current.right;
        else
          parent.right = current.right;
      }
    }
    else {  // 第二種情況:元素有左子樹,先找到當前節點的左子樹的最右節點
     
      //標記當前節點的左子樹的父節點和最右節點
      TreeNode<E> parentOfRightMost = current;
      TreeNode<E> rightMost = current.left;
      
      //一直向右,找到最右端的節點,因為一個節點的左子樹的最右節點也比右子樹的最左節點小
      while (rightMost.right != null) {
        parentOfRightMost = rightMost;
        rightMost = rightMost.right; // 一直向右
      }
   /*
    * 以上程式碼的目的是要找到刪除節點的左子樹最右節點 ,因為一個節點的左子樹的最右節點也比右子樹的最左節點小*/
      
      
      // 找到最右節點後,放到當前要刪除的位置
      current.element = rightMost.element;

      // 消除最右節點
      if (parentOfRightMost.right == rightMost)
        parentOfRightMost.right = rightMost.left;//把最右節點的左子樹掛在其父節點的右子樹上
      else
        // 具體情況: parentOfRightMost == current
        parentOfRightMost.left = rightMost.left;     
    }

    size--;
    return true; // 刪除成功
  }

  /** 獲得中序迭代器 */
  public java.util.Iterator iterator() {
    return inorderIterator();
  }

  /**  建立一個迭代器類*/
  public java.util.Iterator inorderIterator() {
    return new InorderIterator();
  }

  // 中序迭代器類,內部類
  class InorderIterator implements java.util.Iterator {
    // 儲存元素的陣列
    private java.util.ArrayList<E> list =
      new java.util.ArrayList<E>();
    private int current = 0; //陣列中當前元素的位置

    public InorderIterator() {
      inorder(); // 中序遍歷二叉樹
    }

    /** 從根部中序遍歷*/
    private void inorder() {
      inorder(root);
    }

    /** 中序遍歷子樹 */
    private void inorder(TreeNode<E> root) {
      if (root == null)return;
      inorder(root.left);
      list.add(root.element);
      inorder(root.right);
    }

    /** 遍歷下一個元素*/
    public boolean hasNext() {
      if (current < list.size())
        return true;

      return false;
    }

    /** 獲得當前元素,並把指標指向另一個元素 */
    public Object next() {
      return list.get(current++);
    }

    /** 移出當前元素 */
    public void remove() {
      delete(list.get(current)); //刪除當前元素
      list.clear(); //清理陣列
      inorder(); //重新中序遍歷陣列
    }
  }

  /** 清楚樹的所有元素 */
  public void clear() {
    root = null;
    size = 0;
  }
  
  

  /** 內部類,樹的節點類 */
  public static class TreeNode<E extends Comparable<E>> {
    E element;
    TreeNode<E> left;
    TreeNode<E> right;

    public TreeNode(E e) {
      element = e;
    }
  }

}
TestBinaryTree.java
package com.hust.cn;
public class TestBinaryTree {
  public static void main(String[] args) {
    // 建立一個二叉查詢樹
    BinaryTree<String> tree = new BinaryTree<String>();
    tree.insert("George");
    tree.insert("Michael");
    tree.insert("Tom");
    tree.insert("Adam");
    tree.insert("Jones");
    tree.insert("Peter");
    tree.insert("Daniel");

    // 遍歷樹
    System.out.println("Inorder (sorted): ");
    tree.inorder();
    System.out.println("\nPostorder: ");
    tree.postorder();
    System.out.println("\nPreorder: ");
    tree.preorder();
    System.out.println("\nThe number of nodes is " + tree.getSize());

    // 查詢一個元素
    System.out.println("\nIs Peter in the tree? " + 
      tree.search("Peter"));

    // 從root到peter的一條路徑
    System.out.println("\nA path from the root to Peter is: ");
    java.util.ArrayList<BinaryTree.TreeNode<String>>  path 
      = tree.path("Peter");
    for (int i = 0; path != null && i < path.size(); i++)
      System.out.print(path.get(i).element + " ");
    
    //利用陣列構建一個二叉查詢樹,並中序遍歷
    Integer[] numbers = {2, 4, 3, 1, 8, 5, 6, 7};
    BinaryTree<Integer> intTree = new BinaryTree<Integer>(numbers);
    System.out.println("\nInorder (sorted): ");
    intTree.inorder();
  }
}

測試結果:




相關推薦

資料結構學習筆記-森林的轉化、最優

int min(HuffmanTree &HT,int i) { int k = MAX; int j,flag = 0; for(j=1;j<=i;++j) { if(HT[j].weights2) { change = s1; s1 = s2; s2 = chang

資料結構作業10—陣列廣義表以及的基本概念(選擇題)

2-1已知廣義表L=((x,y,z),a,(u,t,w)),從L表中取出原子項t的運算是()。 (2分) A.head(tail(head(tail(tail(L))))) B.head(tail(head(tail(L)))) C.tail(head(head

Java資料結構(十五)—— 多路查詢

多路查詢樹 二叉樹和B樹 二叉樹的問題分析 二叉樹操作效率高 二叉樹需要載入到記憶體,若二叉樹的節點多存在如下問題: 問題1:構建二叉樹時,需多次進行I/O操作,對與速度有影響 問題2:節點海量造成二叉樹的高度很大,會降低操作速度 多叉樹 在二叉樹中,每個節點有資料項,最多有兩個子節

資料結構——第三章:02

1.二叉樹的儲存結構: (1)二叉樹的順序儲存表示: #define MAX_TREE_SIZE 100 //二叉樹的最大結點數 typedef TElemType SqBiTree[MAX_TREE_SIZE];  SqBiTree bt; (2)二叉樹的鏈式儲存表示: ①二叉連結

資料結構——第三章:03森林

1.樹的三種儲存結構: (1)雙親表示法: #define MAX_TREE_SIZE 100 結點結構: typedef struct PTNode {   Elem data;   int parent; //雙親位置域 } PTNode; (2)孩子雙親連結串列表示法: &nbs

再談資料結構)數

1 - 引言 關於樹和二叉樹,我們需要達到的能力有: 熟悉樹和二叉樹的有關概念 熟悉二叉樹的性質 熟練掌握遍歷二叉樹的遞迴演算法,並靈活運用 遞迴遍歷二叉樹及其應用 本文著重在樹和二叉樹實際應用與程式碼實現基本操作,對概念就不再贅述 2 - 二

資料結構--03

8.平衡二叉樹 平衡二叉樹的定義:它或者是一棵空樹,或者樹的任意一結點的左右子鼠深度只差不會超過1,切記不可光比較根結點就進行斷定 平衡二叉樹提出的原因:如下圖傳入一個值,對該樹進行查詢比較,通過比較次數可知,平衡二叉樹結構更優 衡量一個排序二叉樹是否合格的指標 平衡二叉

資料結構--02

6.查詢樹(二叉排序樹)的基本定義 查詢二叉樹,又稱二叉排序樹。一棵查詢二叉樹,或為空樹,活滿足以下遞迴條件: 1.查詢樹的左右子樹各是一棵查詢樹 2.若查詢樹的左子樹非空,則左子樹上的各個結點值均小於根結點的值 3.若查詢樹的右子樹非空,則右子樹上的各個結點的值均大於根結點的值

資料結構--01

1.樹的基本概念: 樹的度:所有結點的度當中,度數最大的。 葉子結點:度為0的結點 分支結點:除了葉子節點以外,都是分支結點。 內部結點:除了葉子節點,和根節點以外所有的結點。 總結點為  N,總度數為K  ,則  N = K +1

資料結構-王道-

樹和二叉樹        樹:是\(N(N\geq0)\)個結點的有限集合,\(N=0\)時,稱為空樹,這是一種特殊情況。在任意一棵非空樹中應滿足: 有且僅有一個特定的稱為根的結點。 當\(N>1\)時,其餘結點可分為\(m(m>0)\)個互不相交的有限集合\(T_1,T_2,\ldots,T_

資料結構

樹型結構是一類重要的非線性資料結構,樹是以分支關係定義的層次結構。 樹(Tree) 樹是n(n>=0)個結點的有限集。 在任意一棵非空樹中: (1)有且僅有一個特定的根結點(Root) (2)當n>1時,其餘節點可分為m(m>0)個互不相交的有限集

Java資料結構:前序中序還原

根據二叉樹前根中根遍歷出來的陣列還原二叉樹。 前根:ABDGCEFH           中跟:DGBAECHF 上程式碼: private BinaryNode<T> create(T[] prelist, T

資料結構7--初識

  1.樹的基本概念         樹是一種重要的非線性結構,他是由n個節點組成的有限集合(n>0)                             n=0時,稱為空樹,樹的頂端節點稱為樹的根,其餘節點可分為若干個互不相交的子集,每個子集本身又是一棵樹,

資料結構——的基本概念

樹是一種非線性結構,是遞迴結構。 樹的基本術語: 樹結點:包含一個數據元素及若干指向子樹的分支; 孩子結點:結點的子樹的根稱為該結點的孩子; 雙親結點:B結點是A結點的孩子,則A結點是B結點的雙親; 兄弟結點:同一雙親的孩子結點; 堂兄結點:同一層上結點;

C語言 資料結構

樹 1、樹:是n節點的有限集。樹是n(n=>0)個節點的有限集。 n=0時成為空樹。 在任意一顆非空樹中:(1)有且僅有一個稱為根的節點;(2)當n>0時,其餘節點可分為m(m>0)個互不相交的有限集T1、T2、T3、Tm,其中每個節點又是一棵樹,並且稱

資料結構》實驗五: 實驗(實驗報告)

一.實驗目的      鞏固樹和二叉樹的相關知識,特別是二叉樹的相關內容。學會運用靈活應用。 1.回樹和二叉樹的邏輯結構和儲存方法,清楚掌握樹和二叉樹的遍歷操作。 2.學習樹的相關知識來解決實際問題。 3.進一步鞏固程式除錯方法。 4.進一步鞏固模板程式設計。

Javascript之資料結構與演算法的搜尋實現

Javascript之資料結構與演算法的二叉樹和二叉搜尋樹實現 簡介 程式碼實現 簡介 二叉樹中的節點最多隻能有兩個子節點:一個是左側子節點,另一個是右側子節點。 二叉搜尋樹( BST)是二叉樹的一種,但是它只允許你在

資料結構-查詢

先按樹-二叉樹-二叉查詢樹的順序解釋會比較清楚。 一,樹 樹(Tree)是n(n≥0)個結點的有限集。在任意一棵非空樹中: (1)有且僅有一個特定的被稱為根(Root)的結點; (2)當n>1時,其餘結點可分為m(m>0)個互不相交的有限集T1,T2,…,Tm,

淺談演算法資料結構(7):查詢

前文介紹了符號表的兩種實現,無序連結串列和有序陣列,無序連結串列在插入的時候具有較高的靈活性,而有序陣列在查詢時具有較高的效率,本文介紹的二叉查詢樹(Binary Search Tree,BST)這一資料結構綜合了以上兩種資料結構的優點。 二叉查詢樹具有很高的靈活性

資料結構:簡單算數表示式的構建求值

內容:編寫一個程式,先用二叉樹來表示一個簡單算術表示式,樹的每一個結點包括一個運算子或者運算數。在簡單算術表示式中只含+,-,*,/ 和一位正整數且格式正確(不包含括號),並且要先按照先乘除後加減的原則構造二叉樹,然後由對應的二叉樹計算該表示式的值。 解: 這裡用非遞迴演算