1. 程式人生 > >java資料結構與演算法之樹基本概念及二叉樹(BinaryTree)的設計與實現

java資料結構與演算法之樹基本概念及二叉樹(BinaryTree)的設計與實現

關聯文章:

  樹博文總算趕上這周釋出了,上篇我們聊完了遞迴,到現在相隔算挺久了,因為樹的內容確實不少,博主寫起來也比較費時費腦,一篇也無法涵蓋樹所有內容,所以後續還會用2篇左右的博文來分析其他內容大家就持續關注吧,而本篇主要了解的知識點如下(還是蠻多的!):

樹的基本概念與術語

  (該定義源於java資料結構書)樹是資料元素之間具有次層關係的非線性的結構,樹是由n(n≥0)個結點組成的有限集合,n=0的樹是空樹,n大於0的樹T由以下兩個條件約定構成:

  • ⑴.有一個特殊的結點,稱為根結點(root),它沒有前驅結點只有後繼結點。
  • ⑵.除了根結點之外的其他結點分為m(0≤m≤n)個互不相交的集合T

    0,T1,T2,,Tm1,其中吧每個集合Ti也是一個樹型結構,稱之為子樹(Subtree)。
    以下是樹的圖形都是樹的結構:

  這裡我們需要明白樹是遞迴定義,這也是博主在開頭強調的在遞迴的基礎上學習樹的原因,如果對於遞迴還不明白的,建議先看看博主的上一篇文章,畢竟在本篇內容中,遞迴是隨處可見的。嗯,接下來我們先來認識一下樹的一些常用術語,這些術語並不要求我們去死記硬背,但在看到這些術語時,我們必須有所瞭解或者明白其主要含義(以上圖為例介紹以下的術語)。
(1)根結點: 根結點是沒有雙親的結點,一棵樹中最多有一個根結點(如上圖的A結點)。
(2)孩子結點:一棵樹中,一個結點的子樹的根結點稱為其孩子結點,如上圖的A的孩子結點右B、C、D。
(3)父母結點:

相對於孩子結點而已其前驅結點即為父母結點,如上圖的B、C、D 三個結點的父母結點都為A,當然E、F結點的父母結點則是B。
(4)兄弟結點:擁有相同的父母結點的所有孩子結點叫作兄弟結點,如上圖B、C、D 三個結點共同父結點為A,因此它們是兄弟結點,E、F也是兄弟結點,但是F、G就肯定不是兄弟結點了。
(5)祖先結點:如果存在一條從根結點到結點Q的路徑,而且結點P出現在這條路徑上,那麼P就是Q的祖先結點,而結點Q也稱為P的子孫結點或者後代。如上圖的E的祖先結點有A和B,而E則是A和B的子孫結點。
(6)葉子結點:沒有孩子結點的結點叫作葉子結點,如E、F、G、H等。
(7)結點的度:指的是結點所擁有子樹的棵數。如A的度為3,F的度為0,即葉子結點的度為0,而樹的度則是樹中各個結點度的最大值,如圖(d)樹的度為3(A結點)
(8)樹的層:
又稱結點的層,該屬性反映結點處於樹中的層次位置,我們約定根結點的層為1,如上圖所示,A層為1,B層為2,E的層為3。
(9)樹的高度(深度):是指樹中結點的最大層數,圖(d)的高度為3。
(10)邊:邊表示從父母結點到孩子結點的連結線,如上圖(d)中A到B間的連線則稱為邊。

  ok~,關於樹的術語,我們就先了解到這裡,接下來主要聊聊二叉樹。

二叉樹的定義及其基本性質

  在樹的資料結構中,二叉樹可謂是重中之重,因此我們必須好好學習它!以下是二叉樹的定義(源於java資料結構原文)

關於二叉樹的定義:二叉樹(Binary Tree)是n(n≥0)個結點組成的有限集合,n=0時稱為空二叉樹;n>0的二叉樹由一個根結點和兩棵互不相交、分別稱為左子樹和右子樹的子二叉樹構成,二叉樹也是遞迴定義的,在樹種定義的度、層次等術語,同樣適用於二叉樹。

二叉樹主要有以下5種基本形態:

  二叉樹的5種形態還是比較容易理解的,比較晦澀的應該是二叉樹的性質,畢竟關聯到了數學層面的知識點,當然對於學習程式設計的同學,邏輯思維都不會太差,因此也不必擔心這點,接著就來了解一下二叉樹的主要特性(這裡列出的博主認為需要知道和理解的特性,但不限以下特性)。

性質⑴:若根結點的層次為1,則二叉樹第i層最多有2i1(i1)個結點,使用數學歸納法證明過程如下:
步驟① 假設根為i=1層上唯一結點,則有2i1=20=1成立。
步驟② 設第i-1層最多有2i2,由於二叉樹中每個結點的度最多為2,因此第i層最多有2i1個結點也成立。

性質⑵:在高度為h的二叉樹中,最多有2h1個結點(h≥0)。
證明:由性質⑴可知,第i層最多有2i1(i1)個結點,因此高度為h的二叉樹結點數則有如下計算

i=1h2i1=2h1

性質⑶:滿二叉樹和完全二叉樹
  一棵高度為h的滿二叉樹(Full Binary Tree)是具有2h1(h0)個結點的二叉樹。滿二叉樹的最大特點是每一層次的結點數都達到最大值,我們可以對滿二叉樹的結點進行連續編號並約定根結點的序號為0,從根結點開始,自上而下,每層自左向右編號。如下圖所示(a):

  對於完全二叉樹,假設二叉樹的深度為h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,第 h 層所有的結點都連續集中在最左邊,這就是完全二叉樹。如同上圖(b)所示,後面我們會通過層序遍歷的演算法來構造完全二叉樹。

性質⑷:一棵具有n個結點的完全二叉樹,對於序號為i(0≤i<n)的結點,則有如下規則
①若i=0,則i為根結點,無父母結點;若i>0,則i的父母結點序號為i12(向下取整)。
②若2i+1<n,則i的左孩子結點序號為2i+1,否則i無左孩子。
③若2i+2>n,則i的右孩子結點序號為2i+2,否則i無右孩子。
如上圖(b)中i=0時為根結點A,其左孩子B序號為2i+1,右孩子結點C的序號則為2i+2。注意這僅使用於完全二叉樹。

  嗯,關於二叉樹的性質暫時瞭解這麼多,接著看二叉樹抽象資料型別及其儲存結構。

二叉樹抽象資料型別及其儲存結構

二叉樹抽象資料型別

  與連結串列、棧、佇列等抽象資料型別相似,二叉樹抽象資料型別也有插入、刪除、查詢等操作,同時二叉樹還有4種遍歷演算法,這個我們後面會詳細分析。現在我們宣告二叉樹的抽象資料型別頂級介面Tree如下:T表示結點元素的型別,該型別必須實現了Comparable介面,方便比較資料。而 BinaryNode是二叉樹的結點類。Tree介面宣告如下:

package com.zejian.structures.Tree.BinaryTree;
/**
 * Created by zejian on 2016/12/14.
 * Blog : http://blog.csdn.net/javazejian [原文地址,請尊重原創]
 */
public interface Tree<T extends Comparable> {

    /**
     * 判空
     * @return
     */
    boolean isEmpty();

    /**
     * 二叉樹的結點個數
     * @return
     */
    int size();

    /**
     * 返回二叉樹的高度或者深度,即結點的最大層次
     * @return
     */
    int height();

    /**
     * 先根次序遍歷
     */
    String preOrder();

    /**
     * 中根次序遍歷
     */
    String inOrder();

    /**
     * 後根次序遍歷
     */
    String postOrder();

    /**
     * 層次遍歷
     */
    String levelOrder();

    /**
     * 將data 插入
     * @return
     */
    void insert(T data);

    /**
     * 刪除
     */
    void remove(T data);

    /**
     * 查詢最大值
     * @return
     */
    T findMin();

    /**
     * 查詢最小值
     * @return
     */
    T findMax();

    /**
     * 根據值找到結點
     * @param data
     * @return
     */
    BinaryNode findNode(T data);

    /**
     * 是否包含某個值
     * @param data
     * @return
     */
    boolean contains(T data) throws Exception;

    /**
     * 清空
     */
    void clear();
}

二叉樹儲存結構

  關於二叉樹的儲存結構主要採用的是鏈式儲存結構,至於順序儲存結構僅適用於完全二叉樹或滿二叉樹,這個我們後面再介紹,這裡我們主要還是分析二叉樹的鏈式儲存結構。二叉樹的鏈式儲存結構主要有二叉連結串列和三叉連結串列兩種,下面分別說明。

二叉樹的二叉連結串列儲存結構

  二叉連結串列結構主要由一個數據域和兩個分別指向左、右孩子的結點組成,其結構如下:

BinaryNode(T data , BinaryNode<T> left , BinaryNode<T> right )

從圖中可以看出,採用二叉連結串列儲存結構,每個結點只儲存了到其孩子結點的單向關係,而沒有儲存到父結點的關係,這樣的話,每次要獲取父結點時將消耗較多的時間,因為需要從root根結點開始查詢,花費的時間是遍歷部分二叉樹的時間,而且與該結點的位置有關。為了更高效的獲取父結點,三叉連結串列儲存結構孕育而生了。

二叉樹的三叉連結串列儲存結構

  三叉連結串列主要是在二叉連結串列的基礎上多添加了一個指向父結點的域,這樣我們就儲存了父結點與孩子結點的雙向關係,當然這樣也增加了一定的空開銷其結點結構如下:

ThreeNode(T data ,ThreeNode<T> parent,ThreeNode<T> left,ThreeNode<T> right)

二叉樹的靜態二/三叉連結串列儲存結構(瞭解即可)

  除了以上兩種結構,其實我們也可採用一個結點陣列儲存所有二叉樹的所有結點,這種結構稱為靜態二/三叉連結串列,在這樣的結構中,每個結點儲存其(父結點)左、右孩子下標,通過下標表示結點間的關係,-1表示無此結點。結構如下:

ok~,關於二叉樹儲存結構就分析到這,下面我們以二叉連結串列儲存結構為例實現二叉樹。

二叉樹的設計與實現

  在開始之前,博主想再聊聊二叉樹的遞迴,畢竟下面的內容遞迴幾乎隨處可見,理解遞迴是掌握下面內容的先行課程。在上篇文章中,我們一再強調遞迴是一種化複雜問題為簡單同類問題的思維,而這種思維在程式中的體現則是遞迴演算法,那麼樹為什麼可以用遞迴定義呢?我們先來看看一個圖:

  從圖中我們可以看到,無論的那種情況下的樹都具備遞迴的結構,它們都擁有著一致的原子結構,這也就是為什麼樹可以使用遞迴定義的原因,遞迴結構與遞迴思維都體現得淋漓盡致,即使是一個十分複雜的樹,我們也可以簡化為原子的結構的求解過程,畢竟它們本質上是同類問題。這樣說,相信大家對樹是遞迴定義的說法有所理解了吧?如果還是不理解,請移步上一篇文章(java資料結構與演算法之遞迴思維),再回憶一遍,思考思考,理解遞迴後再繼續本篇博文吧。ok~,下面開始是二叉樹的設計與實現內容。
  為了使二叉樹的實現變得更有具體意義,我們將實現一種叫二叉查詢樹的資料結構,二叉查詢樹的特性是,對於樹種的每個結點T(T可能是父結點),它的左子樹中所有項的值小T中的值,而它的右子樹中所有項的值都大於T中的值。這意味著該樹所有的元素可以用某種規則進行排序(取決於Comparable介面的實現)。二叉查詢樹使用二叉連結串列儲存結構實現,其結點BinaryNode<T>宣告如下:

package com.zejian.structures.Tree.BinaryTree;

import java.io.Serializable;
/**
 * Created by zejian on 2016/12/14.
 * Blog : http://blog.csdn.net/javazejian [原文地址,請尊重原創]
 * 二叉樹結點
 */
public class BinaryNode<T extends Comparable> implements Serializable{
    private static final long serialVersionUID = -6477238039299912313L;

    public BinaryNode<T> left;//左結點

    public BinaryNode<T> right;//右結點

    public T data;

    public BinaryNode(T data,BinaryNode left,BinaryNode right){
        this.data=data;
        this.left=left;
        this.right=right;
    }

    public BinaryNode(T data){
        this(data,null,null);

    }

    /**
     * 判斷是否為葉子結點
     * @return
     */
    public boolean isLeaf(){
        return this.left==null&&this.right==null;
    }
}

二叉查詢樹BinarySearchTree類架構定義如下:

package com.zejian.structures.Tree.BinaryTree;

/**
 * Created by zejian on 2016/12/19.
 * Blog : http://blog.csdn.net/javazejian [原文地址,請尊重原創]
 */
public class BinarySearchTree<T extends Comparable> implements Tree<T> {
    //根結點
    protected BinaryNode<T> root;

    public BinarySearchTree(){
        root =null;
    }
    @Override
    public boolean isEmpty() {
        return false;
    }

    @Override
    public int size() {
        return 0;
    }

    @Override
    public int height() {
        return 0;
    }

    @Override
    public String preOrder() {
        return null;
    }

    @Override
    public String inOrder() {
        return null;
    }

    @Override
    public String postOrder() {
        return null;
    }

    @Override
    public String levelOrder() {
        return null;
    }

    @Override
    public void insert(T data) {

    }

    @Override
    public void remove(T data) {

    }

    @Override
    public T findMin() {
        return null;
    }

    @Override
    public T findMax() {
        return null;
    }

    @Override
    public BinaryNode findNode(T data) {
        return null;
    }

    @Override
    public boolean contains(T data) throws Exception {
        return false;
    }

    @Override
    public void clear() {

    }
}

大概瞭解BinarySearchTree類的基本架構後,我們接著看看如何實現這些基本方法。

二叉查詢樹基本操作的設計與實現

二叉查詢樹的插入演算法的設計與實現(遞迴)

  事實上對於二叉查詢樹的插入操作的設計是比較簡單,我們只要利用二叉查詢樹的特性(即對每個父結點,它的左子樹中所有項的值小T中的值,而它的右子樹中所有項的值都大於T中的值),找到只對應的插入位置即可,假如現在我們要插入data=4的結點,那麼可以這樣操作,沿著樹查詢(比較結點的資料與data的大小從而決定往左/右子樹繼續前行),如果找到data(4),則什麼也不做,否則將data插入到遍歷的路徑上的最後一個點,如下圖所示:

插入演算法程式設計如下:

@Override
public void insert(T data) {
    if (data==null)
        throw new RuntimeException("data can\'Comparable be null !");
    //插入操作
    root=insert(data,root);
}

/**
 * 插入操作,遞迴實現
 * @param data
 * @param p
 * @return
 */
private BinaryNode<T> insert(T data,BinaryNode<T> p){
    if(p==null){
        p=new BinaryNode<>(data,null,null);
    }

    //比較插入結點的值,決定向左子樹還是右子樹搜尋
    int compareResult=data.compareTo(p.data);

    if (compareResult<0){//左
        p.left=insert(data,p.left);
    }else if(compareResult>0){//右
        p.right=insert(data,p.right);
    }else {
        ;//已有元素就沒必要重複插入了
    }
    return p;
}

二叉查詢樹的刪除演算法的設計與實現(遞迴與非遞迴)

  對於二叉樹來說,刪除是一種比較麻煩的操作,因為涉及到了多種情況(設要刪除的結點為q,其父母結點為p):

  • ① 如果要刪除的結點q恰好是葉子結點,那麼它可以立即被刪除

  • ② 如果要刪除的結點q擁有一個孩子結點,則應該調整要被刪除的父結點(p.left 或 p.right)指向被刪除結點的孩子結點(q.left 或 q.right)

  • ③如果要刪除的結點q擁有兩個孩子結點,則刪除策略是用q的右子樹的最小的資料替代要被刪除結點的資料,並遞迴刪除用於替換的結點(此時該結點已為空),此時二叉查詢樹的結構並不會被打亂,其特性仍舊生效。採用這樣策略的主要原因是右子樹的最小結點的資料替換要被刪除的結點後可以滿足維持二叉查詢樹的結構和特性,又因為右子樹最小結點不可能有左孩子,刪除起來也相對簡單些。

為了更新清晰描述這個過程,我們可以藉助下圖來理解:

下面是刪除操作的程式實現,也是遞迴實現,其中的findMin方法是查詢二叉查詢樹中的最小值,後面我們會分析這個方法,其程式碼註釋也比較清晰:

@Override
public void remove(T data) {
  if(data==null)
      throw new RuntimeException("data can\'Comparable be null !");
  //刪除結點
  root=remove(data,root);
}

/**
* 分3種情況
* 1.刪除葉子結點(也就是沒有孩子結點)
* 2.刪除擁有一個孩子結點的結點(可能是左孩子也可能是右孩子)
* 3.刪除擁有兩個孩子結點的結點
* @param data
* @param p
* @return
*/
private BinaryNode<T> remove(T data,BinaryNode<T> p){
  //沒有找到要刪除的元素,遞迴結束
  if (p==null){
      return p;
  }
  int compareResult=data.compareTo(p.data);
  if (compareResult<0){//左邊查詢刪除結點
      p.left=remove(data,p.left);
  }else if (compareResult>0) {
      p.right=remove(data,p.right);
  }else if (p.left!=null&&p.right!=null){//已找到結點並判斷是否有兩個子結點(情況3)
      //中繼替換,找到右子樹中最小的元素並替換需要刪除的元素值
      p.data = findMin( p.right ).data;
      //移除用於替換的結點
      p.right = remove( p.data, p.right );
  }else {
      //擁有一個孩子結點的結點和葉子結點的情況
      p=(p.left!=null)? p.left : p.right;
  }

  return p;//返回該結點
}

除了遞迴實現刪除操作,我們也可以使用非遞迴方式來實現刪除操作,程式碼如下:

/**
  * 非遞迴刪除
  * @param data
  */
 public T removeUnrecure(T data){
     if (data==null){
         throw new RuntimeException("data can\'Comparable be null !");
     }
     //從根結點開始查詢
     BinaryNode<T> current =this.root;
     //記錄父結點
     BinaryNode<T> parent=this.root;
     //判斷左右孩子的flag
     boolean isLeft=true;


     //找到要刪除的結點
     while (data.compareTo(current.data)!=0){
         //更新父結點記錄
         parent=current;
         int result=data.compareTo(current.data);

         if(result<0){//從左子樹查詢
             isLeft=true;
             current=current.left;
         }else if(result>0){//從右子樹查詢
             isLeft=false;
             current=current.right;
         }
         //如果沒有找到,返回null
         if (current==null){
             return null;
         }
     }

     //----------到這裡說明已找到要刪除的結點

     //刪除的是葉子結點
     if (current.left==null&&current.right==null){
         if (current==this.root){
             this.root=null;
         } else if(isLeft){
             parent.left=null;
         }else {
             parent.right=null;
         }
     }
     //刪除帶有一個孩子結點的結點,當current的right不為null
     else if (current.left==null){
         if (current==this.root){
             this.root=current.right;
         }else if(isLeft){//current為parent的左孩子
             parent.left=current.right;
         }else {//current為parent的右孩子
             parent.right=current.right;
         }
     }
     //刪除帶有一個孩子結點的結點,當current的left不為null
     else if(current.right==null){
         if (current==this.root){
             this.root=current.left;
         }else if (isLeft){//current為parent的左孩子
             parent.left=current.left;
         }else {//current為parent的右孩子
             parent.right=current.left;
         }
     }
     //刪除帶有兩個孩子結點的結點
     else {
         //找到當前要刪除結點current的右子樹中的最小值元素
         BinaryNode<T> successor= findSuccessor(current);

         if(current == root) {
             this.root = successor;
         } else if(isLeft) {
             parent.left = successor;
         } else{
             parent.right = successor;
         }
         //把當前要刪除的結點的左孩子賦值給successor
         successor.left = current.left;
     }
     return current.data;
 }

 /**
  * 查詢中繼結點--右子樹最小值結點
  * @param delNode 要刪除的結點
  * @return
  */
 public BinaryNode<T> findSuccessor(BinaryNode<T> delNode) {
     BinaryNode<T> successor = delNode;
     BinaryNode<T> successorParent = delNode;
     BinaryNode<T> current = delNode.right;

     //不斷查詢左結點,直到為空,則successor為最小值結點
     while(current != null) {
         successorParent = successor;
         successor = current;
         current = current.left;
     }
     //如果要刪除結點的右孩子與successor不相等,則執行如下操作(如果相當,則說明刪除結點)
     if(successor != delNode.right) {
         successorParent.left = successor.right;
         //把中繼結點的右孩子指向當前要刪除結點的右孩子
         successor.right = delNode.right;
     }
     return successor;
 }

這裡主要說明一下刪除時有兩個結點的情況,這時我們需要藉助findSuccessor方法找到要被刪除結點右子樹的最小值,並用於替換要被刪除結點。對於if(successor != delNode.right)程式碼被執行的情況如下圖

對於if(successor != delNode.right)程式碼不成立的情況如下圖:

嗯,以上兩幅圖輔助大家理解程式碼,其他就不多分析了,註釋寫很明白了。

二叉查詢樹的最大和最小值的查詢演算法與實現(遞迴)

  二叉查詢樹中的findMin和findMax方法分別返回的是樹種的最小值和最大值,對於findMin(),則需要從根結點開始並且只要有左孩子就向左進行即可,其終止點即為最小值的元素;而對於findMax(),也需要從根結點開始並且只要有右孩子就向右進行即可,終止點即為值最大的元素。同樣的我們使用遞迴實現它們,程式碼如下:

@Override
public T findMin() {
   if(isEmpty())
       throw new EmptyTreeException("BinarySearchTree is empty!");

   return findMin(root).data;
}

@Override
public T findMax() {
   if(isEmpty())
       throw new EmptyTreeException("BinarySearchTree is empty!");

   return findMax(root).data;
}

/**
* 查詢最小值結點
* @param p
* @return
*/
private BinaryNode<T> findMin(BinaryNode<T> p){

   if (p==null)//結束條件
       return null;
   else if (p.left==null)//如果沒有左結點,那麼t就是最小的
       return p;
   return findMin(p.left);
}

/**
* 查詢最大值結點
* @param p
* @return
*/
private BinaryNode<T> findMax(BinaryNode<T> p){
   if (p==null)//結束條件
       return null;
   else if (p.right==null)
       return p;
   return findMax(p.right);
}

二叉查詢樹的深度(height)和大小(size)計算的設計與實現(遞迴)

  根據前面的術語,可知樹的深度即為最大層的結點所在層次,而大小就是樹的結點數,關於深度,我們只需要從根結點開始尋找,然後計算出左子樹的深度和右子樹的深度,接著比較左子樹與右子樹的深度,最後返回深度大的即可。深度求解過程圖示以及程式碼實現如下:

/**
 * 計算深度
 * @return
 */
@Override
public int height() {
    return height(root);
}

/**
 * 遞迴實現
 * @param subtree
 * @return
 */
private int height(BinaryNode<T> subtree){
    if (subtree==null){
        return 0;
    }else {
        int l=height(subtree.left);
        int r=height(subtree.right);
        return (l>r) ? (l+1):(r+1);//返回並加上當前層
    }
}

  接著在看看求解二叉樹大小(size)的演算法該如何實現,實際上,size的求解跟上篇文章分析遞迴時,漢諾塔問題求解過程十分相似(其實不止是大小求解過程,二叉查詢樹的所有使用遞迴的操作都是這樣的思想),我們先看看下圖:

相關推薦

java資料結構演算法基本概念BinaryTree設計實現

關聯文章:   樹博文總算趕上這周釋出了,上篇我們聊完了遞迴,到現在相隔算挺久了,因為樹的內容確實不少,博主寫起來也比較費時費腦,一篇也無法涵蓋樹所有內容,所以後續還會用2篇左右的博文來分析其他內容大家就持續關注吧,而本篇主要了解的知識點如下(還是蠻多

資料結構演算法美專欄學習筆記-基礎(上

樹 節點的定義 樹中的元素稱之為節點 高度的定義 節點的高度:節點到葉子節點的最長路徑 樹的高度:跟節點的高度 深度的定義 根節點到這個節點所經歷的邊的個數 層的定義 節點的深度+1   二叉樹 滿二叉樹 除了葉子結點外每個節點都有左右兩個子節點 完全二叉樹 葉子結

資料結構演算法美專欄學習筆記-基礎(下

二叉查詢樹 Binary Search Tree  二叉查詢樹的定義 二叉查詢樹又稱二叉搜尋樹。其要求在二叉樹中的任意一個節點,其左子樹中的每個節點的值,都要小於這個節點的值,而右子樹的節點的值都大於這個節點的值。 二叉查詢樹的查詢操作 二叉樹類、節點類以及查詢方法的程式碼實現

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

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

[PTA] 資料結構演算法題目集 6-8 求高度

6.8 二叉樹高度 int GetHeight(BinTree BT) { if (BT == NULL) return 0; int leftH = GetHeight(BT->Left); int rightH = GetHeight(BT->Rig

資料結構演算法題目集7-23——還原

我的資料結構與演算法題目集程式碼倉:https://github.com/617076674/Data-structure-and-algorithm-topic-set 原題連結:https://pintia.cn/problem-sets/15/problems/838 題目描述:

浙大版《資料結構》習題4.5 順序儲存的的最近的公共祖先問題 25 分

設順序儲存的二叉樹中有編號為i和j的兩個結點,請設計演算法求出它們最近的公共祖先結點的編號和值。 輸入格式: 輸入第1行給出正整數n(≤1000),即順序儲存的最大容量;第2行給出n個非負整數,其間以空格分隔。其中0代表二叉樹中的空結點(如果第1個結點為0,則

資料結構 《4》---- 一個漂亮的列印的程式

寫二叉樹的程式時經常會遇到希望漂亮地把二叉樹給輸出,本文給出了一個小程式。 以下時列印的效果: // copyright @ L.J.SHOU Jan.16, 2014 // a fancy binary tree printer #ifndef BINARY_TREE_

資料結構學習筆記】——根據中綴表示式構建並輸出

要求 輸入一箇中綴表示式,構造表示式樹,以文字方式輸出樹結構。 輸入:例如,輸入a+b+c*(d+e) 輸出:以縮排表示二叉樹的層次,左(根),右(葉),上(右子樹),下(左子樹) 分析 我們有兩個核心的問題需要解決,一是如何按照中綴表示式來

python序列元組概念相關函式總結

元組是序列的一種,元組是不可變序列(不能修改,替換),但可進行查詢,增添;元組的建立語法很簡單:用逗號分隔一些值,用圓括號括起來,元組就建立了。 1.先來一個簡單的元組:(圓括號也可以不帶) tou

Java資料結構演算法基本操作

Java資料結構和演算法(二)樹的基本操作 一、樹的遍歷 二叉樹遍歷分為:前序遍歷、中序遍歷、後序遍歷。即父結點的訪問順序 1.1 前序遍歷 基本思想:先訪問根結點,再先序遍歷左子樹,最後再先序遍歷右子樹即根—左—右。圖中前序遍歷結果是:1,2,4,5,7,8,3,6。 // 遞迴實現前序遍歷

java資料結構演算法Stack設計實現

關聯文章:   本篇是java資料結構與演算法的第4篇,從本篇開始我們將來了解棧的設計與實現,以下是本篇的相關知識點: 棧的抽象資料型別   棧是一種用於儲存資料的簡單資料結構,有點類似連結串列或者順序表(統稱線性表),棧與線性表的最大區別是

js資料結構演算法——陣列基本用法

陣列建立方式: var arr=new Array();                              var arr=new Array(10);                              var  arr=new Array(1,2,3,4,

Java資料結構演算法

Java資料結構和演算法(一)樹 前面講到的連結串列、棧和佇列都是一對一的線性結構,這節講一對多的線性結構 - 樹。「一對多」就是指一個元素只能有一個前驅,但可以有多個後繼。 一、樹 度(Degree) :節點擁有的子樹數。樹的度是樹中各個節點度的最大值。 節點 :度為 0 的節點稱為葉節

Java資料結構演算法順序儲存的結構

Java資料結構和演算法(三)順序儲存的樹結構 二叉樹也可以用陣列儲存,可以和完全二叉樹的節點一一對應。 一、樹的遍歷 // 二叉樹儲存在陣列中 int[] data; public void preOrder() { preOrder(0); } // 前序遍歷指定的節點 public

Java資料結構演算法赫夫曼

Java資料結構和演算法(四)赫夫曼樹 哈夫曼樹又稱為最優二叉樹,赫夫曼樹的一個最主要的應用就是哈夫曼編碼。 一、赫夫曼樹 can you can a can as a can canner can a can. 1.1 定長編碼 99 97 110 32 121 111 117 32 99 97

Java資料結構演算法線性結構單鏈表

Java資料結構和演算法(一)線性結構之單鏈表 prev current next -------------- -------------- -------------- | value | next | ->

Java資料結構演算法--

在資料結構中,對於有序陣列來說查詢很快,但是插入和刪除慢,因為插入和刪除需要先找到指定的位置,後面所有的元素都要移動一個位置,為插入騰出一個位置或填入刪除的那個位置; 而對於連結串列來說,插入和刪除快,但是查詢很慢,插入和刪除只要更改一下元素的引用值即可,而查詢每次都要從頭開始遍歷直到

資料結構演算法美-

學習筆記 “樹”這種資料結構的形態特徵 包括有哪些命名節點和它們的概念,這些節點是根節點,葉子節點,父節點,子節點,兄弟節點等;以及相關節點關係的建立,這些關係是父子關係和兄弟關係 “樹"這種資

Java資料結構演算法:哈夫曼

本章介紹哈夫曼樹。和以往一樣,本文會先對哈夫曼樹的理論知識進行簡單介紹,然後給出C語言的實現。後續再分別給出C++和Java版本的實現;實現的語言雖不同,但是原理如出一轍,選擇其中之一進行了解即可。若文章有錯誤或不足的地方,請幫忙指出! 哈夫曼樹的介紹