二叉樹基礎及應用

二叉樹基礎:

剛看到堆排序,順便記錄一下關於樹的一些基本概念:

前言

前面介紹的棧、隊列都是線性結構(linear structure)。而樹是非線性結構(non-linear structure)。因此,樹中的元素之間一般不存在類似於線性結構的一對一的關係,更多地表現爲多對多的關係。直觀地看,它是數據元素(在樹中稱爲節點)按分支關係組織起來的結構。顯然,樹形結構是比線性結構更復雜的一種數據結構類型。

一、樹

樹的定義:樹是含有n個節點的有窮集合,其中有一個節點比較特殊稱爲根節點。在圖示樹時,用一條邊連接兩個有邏輯關係的節點,這個關係被稱爲父子關係。

二、二叉樹

二叉樹(Binary Tree)由節點的有限集合構成。這個集合或者爲空集,或者爲由一個根節點(root)和兩棵互不相交,分別稱爲這個根的左子樹(left subtree)和右子樹(right subtree)的二叉樹組成的集合。

一棵二叉樹的示意圖:

三、樹和二叉樹的主要區別

  1. 樹中節點的最大度數沒有限制,而二叉樹節點的度不超過2。
  2. 樹中節點的孩子節點,無左右之分,而二叉樹中是有區分的,即孩子是有區別的:左孩子、右孩子,且次序不可顛倒。
  3. 樹的結點個數至少爲1,而二叉樹的結點個數可以爲0。

四、常見概念

  1. 節點的度:某節點的度定義爲該節點孩子節點的個數。
  2. 葉子節點:度爲0的節點。
  3. 樹的度:一棵樹中,最大的節點的度稱爲樹的度。
  4. 節點的高度:從該節點起到葉子節點的最長簡單路徑的邊數。(簡單路徑:無重複邊的路徑)
  5. 樹的高度:根節點的高度。
  6. 節點的層數:從根開始定義起,根爲第1層,根的子節點爲第2層,以此類推。
  7. 數的層數:根節點的層數。
  8. 節點的深度:即該節點的層數。
  9. 樹的深度:根節點的深度。
  10. 外節點:葉子節點。
  11. 內節點:除葉子節點之外的節點。
  12. 滿二叉樹:二叉樹中節點的度只能是0或2。
  13. 完全二叉樹:除最後一層,每一層的節點數都達到最大。最後一層若是沒滿,則節點集中在左邊,空的只能是右邊。
  14. 擴充二叉樹:對二叉樹中度爲1的節點和葉子節點添加空節點,使之成爲滿二叉樹。
注:關於樹深度、層數、高度的定義會有不同的說法:有從0開始計數的,也有從1開始計數的。從哪兒開始計數不是關鍵,關鍵在於一致性,量的寫法要一致。
 

五、幾個量的關係

  1. 對於二叉樹,根節點是第一層,則第i層至多有2^{i-1}個結點。若共有k層,則最多有節點2^k-1個。
  2. 按層次順序對一棵有n個節點的完全二叉樹的所有節點從0到n-1編號。若父節點的編號是i,則左孩子的編號是2*i+1,右孩子的編號是2*i+2。(當然,這是在存在左右孩子的情況下)。同樣的,若孩子(無論左右孩子)節點是i,則父節點是(i-1)/2。
  3. 對於一棵滿二叉樹,外部節點或者說是葉子節點數是n,則內部節點數是n-1。
  4. 對於一棵二叉樹,用ni表示度爲i的節點個數,則n0=n2+1。證明如下:總節點數n=n0+n1+n2。用e表示邊數,則n=e+1,這是因爲除根節點外,每一個節點都和一條邊對應。同時,e=2n2+n1,推出n0+n1+n2=2n2+n1+1,化簡即得n0=n2+1。這個結論用語言表述:二叉樹中,葉子節點比度爲2的節點多一個。
  5. 有n個節點的完全二叉樹,樹高度,即logn向下取整,高度從0計數。
  6. 在二叉樹中,第i層的第一個節點(最左邊的節點)的編號是,層數從0計數。這個量在選擇排序:樹形選擇中用到了。

六、示意圖




二叉樹遍歷:

計算機科學中,二叉樹是每個節點最多有兩個子樹的樹結構。通常子樹被稱作“左子樹”(left subtree)和“右子樹”(right subtree)。

一顆簡單的二叉樹

二叉樹的遍歷分爲三種:前(先)序、中序、後序遍歷。

設L、D、R分別表示二叉樹的左子樹、根結點和遍歷右子樹,則先(根)序遍歷二叉樹的順序是DLR,中(根)序遍歷二叉樹的順序是LDR,後(根)序遍歷二叉樹的順序是LRD。還有按層遍歷二叉樹。這些方法的時間複雜度都是O(n),n爲結點個數。

假設我們有一個包含值的value和指向兩個子結點的left和right的樹結點結構。我們可以寫出這樣的過程

先序遍歷(遞歸實現):
visit(node)
    print node.value
    if node.left  != null then visit(node.left)
    if node.right != null then visit(node.right)

 中序遍歷(遞歸實現):
visit(node)
    if node.left  != null then visit(node.left)
    print node.value
    if node.right != null then visit(node.right)

 後序遍歷(遞歸實現):
visit(node)
    if node.left  != null then visit(node.left)
    if node.right != null then visit(node.right)
    print node.value


二叉樹創建和遍歷:

public class BinaryTree {  
  
    private Node root;  
      
    /** 
     *  
     * 內部節點類 
     * @author yhh 
     */  
    private class Node{  
        private Node left;  
        private Node right;  
        private int data;  
        public Node(int data){  
            this.left = null;  
            this.right = null;  
            this.data = data;  
        }  
    }  
      
    public BinaryTree(){  
        root = null;  
    }  
      
    /** 
     * 遞歸創建二叉樹 
     * @param node 
     * @param data 
     */  
    public void buildTree(Node node,int data){  
        if(root == null){  
            root = new Node(data);  
        }else{  
            if(data < node.data){  
                if(node.left == null){  
                    node.left = new Node(data);  
                }else{  
                    buildTree(node.left,data);  
                }  
            }else{  
                if(node.right == null){  
                    node.right = new Node(data);  
                }else{  
                    buildTree(node.right,data);  
                }  
            }  
        }  
    }  
      
    /** 
     * 前序遍歷 
     * @param node 
     */  
    public void preOrder(Node node){  
        if(node != null){  
            System.out.println(node.data);  
            preOrder(node.left);  
            preOrder(node.right);  
        }  
    }  
      
    /** 
     * 中序遍歷 
     * @param node 
     */  
    public void inOrder(Node node){  
        if(node != null){  
            inOrder(node.left);  
            System.out.println(node.data);  
            inOrder(node.right);  
        }  
    }  
      
    /** 
     * 後序遍歷 
     * @param node 
     */  
    public void postOrder(Node node){  
        if(node != null){  
            postOrder(node.left);  
            postOrder(node.right);  
            System.out.println(node.data);  
        }  
    }  
      
    public static void main(String[] args) {  
        int[] a = {2,4,12,45,21,6,111};  
        BinaryTree bTree = new BinaryTree();  
        for (int i = 0; i < a.length; i++) {  
            bTree.buildTree(bTree.root, a[i]);  
        }  
        bTree.preOrder(bTree.root);  
        bTree.inOrder(bTree.root);  
        bTree.postOrder(bTree.root);  
    }  
  
}  

 插入操作


二叉樹查找樹b插入操作x的過程如下:


1、若b是空樹,則直接將插入的結點作爲根結點插入。

2、x等於b的根結點的數據的值,則直接返回,否則。

3、若x小於b的根結點的數據的值,則將x要插入的結點的位置改變爲b的左子樹,否則。

 4、將x要出入的結點的位置改變爲b的右子樹。


具體代碼:

 public void insert(T t)  
   {  
       rootTree = insert(t, rootTree);  
   }  
  
   public BinaryNode insert(T t,BinaryNode node)  
   {  
       if(node==null)  
       {  
           //新構造一個二叉查找樹  
           return new BinaryNode(t, null, null);  
       }  
       int result = t.compareTo(node.data);  
       if(result<</SPAN>0)  
          node.left= insert(t,node.left);  
       else if(result>0)  
          node.right= insert(t,node.right);  
       else  
           ;//doNothing  
       return node;  
   }  

刪除操作:

對於二叉查找樹的刪除操作(這裏根據值刪除,而非結點)分三種情況:

不過在此之前,我們應該確保根據給定的值找到了要刪除的結點,如若沒找到該結點

不會執行刪除操作!

下面三種情況假設已經找到了要刪除的結點。

1、如果結點爲葉子結點(沒有左、右子樹),此時刪除該結點不會玻化樹的結構

  直接刪除即可,並修改其父結點指向它的引用爲null.如下圖:


2、如果其結點只包含左子樹,或者右子樹的話,此時直接刪除該結點,並將其左子樹

或者右子樹設置爲其父結點的左子樹或者右子樹即可,此操作不會破壞樹結構。
                 
                           

3、 當結點的左右子樹都不空的時候,一般的刪除策略是用其右子樹的最小數據 (容易找到)代替要刪除的結點數據並遞歸刪除該結點(此時爲null),因爲 右子樹的最小結點不可能有左孩子,所以第二次刪除較爲容易。 z的左子樹和右子樹均不空。找到z的後繼y,因爲y一定沒有左子樹,所以可以刪除y,並讓y的父親節點成爲y的右子樹的父親節點,並用y的值代替z的值.如圖:

                    

具體代碼:

 public void remove(T t)  
   {  
       rootTree = remove(t,rootTree);  
   }   
   public BinaryNode remove(T t,BinaryNode node)  
   {  
       if(node == null)  
           return node;//沒有找到,doNothing  
       int result = t.compareTo(node.data);  
       if(result>0)  
           node.right = remove(t,node.right);  
       else if(result<</SPAN>0)  
           node.left = remove(t,node.left);  
       else if(node.left!=null&&node.right!=null)  
       {  
           node.data = findMin(node.right).data;  
           node.right = remove(node.data,node.right);  
       }  
       else  
           node = (node.left!=null)?node.left:node.right;  
       return node;  
             
   }  


查找二叉樹節點:

在二叉查找樹中查找x的過程如下:
1、若二叉樹是空樹,則查找失敗。
2、若x等於根結點的數據,則查找成功,否則。
3、若x小於根結點的數據,則遞歸查找其左子樹,否則。
4、遞歸查找其右子樹。

具體代碼:

public TreeNode search(int Key) {  
        TreeNode node = root;  
        // 首先定義一個節點讓其指向根,在下面的循環中  
        // 只要節點值不等於要查找的節點值就進入循環如果沒有找到則返回null  
        while (node.keyValue != Key) {  
            if (Key < node.keyValue) { // 如果要查找的值小於節點值則指向左節點  
                node = node.leftNode;  
            } else { // 否則指向右節點  
                node = node.rightNode;  
            }  
            if (node == null) { // 如果節點爲空了則返回null  
                return null;  
            }  
        }  
        return node;  
    }  


最小值和最大值:


思路:最大值一直往右走,最小值一直往左走。 

具體代碼:

public int max() {  
    TreeNode node = root;  
    TreeNode parent = null;  
    while (node != null) {  
        parent = node;  
        node = node.rightNode;  
    }  
    return parent.keyValue;  
}  
  
  
public int min() {  
    TreeNode node = root;  
    TreeNode parent = null;  
    while (node != null) {  
        parent = node;  
        node = node.leftNode;  
    }  
    return parent.keyValue;  
}  

樹的深度:

具體代碼:

int length(Node root){
     int depth1;
     int depth2;
     if(root == null) return 0;
     //左子樹的深度
     depth1 = length(root.right);
     //右子樹的深度
     depth2 = length(root.left);
     if(depth1>depth2)
	    return depth1+1;
     else
	    return depth2+1;
}



參考鏈接:

http://blog.sina.com.cn/s/blog_401823210101f8t6.html

http://blog.sina.com.cn/s/blog_937cbcc10101dmqm.html

http://www.cnblogs.com/wuchanming/p/4067951.html

http://blog.csdn.net/fansongy/article/details/6798278

http://www.cricode.com/3489.html

http://www.cricode.com/3212.html

http://blog.csdn.net/sjf0115/article/details/8645991

http://www.cnblogs.com/vamei/archive/2013/03/17/2962290.html

http://blog.csdn.net/sysu_arui/article/details/7865876

http://blog.163.com/xiaopengyan_109/blog/static/14983217320108168618624/

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