樹結構


樹結構示意圖

 

樹的術語

樹(tree):n個節點構成的有限集合,當n=0時稱為空樹,對於任一非空樹,具有一個根節點用r表示,其余節點可分為m(m>0)個互不相交的有限集T1,T2,T3等,其中每一個集合本身又是一棵樹,稱為原來樹的子樹

 

節點的度(Degree):節點的子樹個數

樹的度:樹的所有節點中最大的度數

葉節點(Leaf):度為0的節點(也稱為葉子節點)

父節點(Parent):有子樹的節點是其子樹的根節點的父節點

子節點(Child):若A節點是B節點的父節點,則稱B節點是A節點的子節點;子節點也稱為孩子節點

兄弟節點(Sibling):具有同一父節點的各節點彼此是兄弟節點

路徑和路徑長度:從節點n1到nk的路徑為一個節點序列n1,n2......nk,ni是ni+1的父節點,路徑包含邊的個數為路徑的長度

節點的層次(Level):規定根節點在1層,其他任一節點的層數是其父節點的層數加一

樹的深度(Depth):樹中所有節點中的最大層次是這棵樹的深度

樹的表示方法

 

 

二叉樹 

如果樹中每個節點最多只能有兩個子節點,這樣的樹就稱為二叉樹

二叉樹的定義:

  1、二叉樹可以為空,也就是沒有節點

  2、若不為空,則它是由根節點和稱為其左子樹TL和右子樹TR的兩個互不相交的二叉樹組成

二叉樹有五種形態:

 

 二叉樹的特性:

一個二叉樹第i層的最大節點樹為:2^(i-1),i>=1

深度為k的二叉樹有最大節點總數為:2^k-1,k>=1

對任何非空二叉樹T,若n0表示葉節點的個數,n2是度為2的非葉節點個數,那么兩者滿足關系n0=n2+1

完美二叉樹

在二叉樹中,除了最下一層的葉節點外,每層節點都有2個子節點,就構成了滿二叉樹

 

 完全二叉樹

除二叉樹最后一層外,其他各層的節點數都達到最大個數,且最后一層從左向右的葉節點連續存在,只缺右側若干節點,完美二叉樹是特殊的完全二叉樹

 

 

二叉樹的存儲

二叉樹常見的存儲方式是數組和鏈表

使用數組

完全二叉樹:按從上至下、從左至右順序存儲

 

 非完全二叉樹:非完全二叉樹要轉成完全二叉樹才可以按照上面的方案存儲,但是會造成很大的空間浪費

 

 二叉樹最常見的方式還是使用鏈表存儲

每個節點封裝成一個Node,Node中包含存儲的數據,左節點的引用,右節點的引用

 

二叉搜索樹

二叉搜索樹(BST,Binary Search Tree)也稱二叉排序樹或者二叉查找樹

二叉搜索樹是一顆二叉樹,可以空

如果不為空,滿足一下性質:

  • 非空左子樹的所有鍵值小於其根節點的鍵值
  • 非空右子樹的所有鍵值大於其根節點的鍵值
  • 左、右子樹本身也都是二叉搜索樹

二叉搜索樹的特點:

二叉搜索樹的特點就是相對較小的值總是保存在左節點上,相對較大的值總是保存在右節點上,二叉搜索樹的查找效率特別高

二叉搜索樹常見的操作方法

<1>  insert(key):向樹中插入一個新的鍵

<2>  search(key):在樹中查找一個鍵,如果節點存在,則返回true,如果不存在,則返回false

<3>  inOrderTraverse:通過中序遍歷方式遍歷所有節點

<4>  preOrderTraverse:通過先序遍歷方式遍歷所有節點

<5>  postOrderTraverse:通過后序遍歷方式遍歷所有節點

<6>  min:返回樹中最小的值/鍵

<7>  max:返回樹中最大的值/鍵

<8>  remove(key):從樹中移除某個鍵

封裝二叉搜索樹

  //封裝二叉搜索樹
    function BinarySearchTree() {
        function Node(key) {
            this.key = key
            this.left = null
            this.right = null
        }
        this.root = null
    }

 

insert方法

//封裝二叉搜索樹
    function BinarySearchTree() {
        function Node(key) {
            this.key = key
            this.left = null
            this.right = null
        }
        this.root = null
        BinarySearchTree.prototype.insert = function (key) {
            //!、根據key創建節點
            var newNode = new Node(key)
            //2、判斷是否存在根節點
            if (this.root == null) {
                this.root = newNode
            } else {
                this.insertNode(this.root, newNode)
            }
        }
        //判斷節點大小的遞歸方法
        BinarySearchTree.prototype.insertNode = function (node, newNode) {
            if (newNode.key < node.key) {
                //如果新節點key小於根節點的key就向左查找
                if (node.left == null) {
                    //沒有左節點,就放在這里當做左節點
                    node.left = newNode
                } else {
                    //如果有左節點,遞歸調用
                    this.insertNode(node.left, newNode)
                }
            } else {
                //如果新節點key大於根節點的key就向右查找
                if (node.right == null) {
                    //沒有右節點,就放在這里當做右節點
                    node.right = newNode
                } else {
                    //如果有右節點,遞歸調用
                    this.insertNode(node.right, newNode)
                }
            }
        }
    }

測試:

    var tree = new BinarySearchTree()
    tree.insert(11)
    tree.insert(7)
    tree.insert(15)
    tree.insert(5)
    tree.insert(3)
    tree.insert(9)
    tree.insert(8)
    tree.insert(10)
    tree.insert(13)
    tree.insert(12)
    tree.insert(14)
    tree.insert(20)
    tree.insert(18)
    tree.insert(25)

 

二叉樹的遍歷

二叉樹遍歷常見的有三種方式:先序遍歷、中序遍歷、后序遍歷、層序遍歷(使用較少)

 先序遍歷

先訪問根節點,然后再訪問左子節點,最后訪問右子節點

       //遍歷
        BinarySearchTree.prototype.preOrderTraversal = function (handler) {
            this.preOrderTraversalNode(this.root, handler)
        }
        BinarySearchTree.prototype.preOrderTraversalNode = function (node, handler) {
            if(node != null) {
                //處理經過的節點
                handler(node.key)
                //處理經過節點的左子節點
                this.preOrderTraversalNode(node.left,handler)
                //處理經過節點的右子節點
                this.preOrderTraversalNode(node.right,handler)
            }
        }

測試:

 var tree = new BinarySearchTree();
    tree.insert(11)
    tree.insert(7)
    tree.insert(15)
    tree.insert(5)
    tree.insert(3)
    tree.insert(9)
    tree.insert(8)
    tree.insert(10)
    tree.insert(13)
    tree.insert(12)
    tree.insert(14)
    tree.insert(20)
    tree.insert(18)
    tree.insert(25)
    var result = ''
    tree.preOrderTraversal(function (key) {
        result += key + ' '
    })
    alert(result)

中序遍歷

先訪問左子節點,然后再訪問根節點,最后訪問右子節點

BinarySearchTree.prototype.midOrderTraversal = function (handler) {
            this.midOrderTraversalNode(this.root, handler)
        }
        BinarySearchTree.prototype.midOrderTraversalNode = function (node,handler) {
            if(node != null) {
                //處理經過的節點的左子節點
                this.midOrderTraversalNode(node.left,handler)
                //處理經過的節點
                handler(node.key)
                //處理經過節點的右子節點
                this.midOrderTraversalNode(node.right,handler)
            }
        }

測試:

 var tree = new BinarySearchTree();
    tree.insert(11)
    tree.insert(7)
    tree.insert(15)
    tree.insert(5)
    tree.insert(3)
    tree.insert(9)
    tree.insert(8)
    tree.insert(10)
    tree.insert(13)
    tree.insert(12)
    tree.insert(14)
    tree.insert(20)
    tree.insert(18)
    tree.insert(25)
    var result = ''
    tree.midOrderTraversal(function (key) {
        result += key + ' '
    })
    alert(result)

 

 后序遍歷

先訪問左子節點,然后再訪問右子節點,最后訪問根節點

  BinarySearchTree.prototype.postOrderTraversal = function (handler) {
            this.postOrderTraversalNode(this.root, handler)
        }
        BinarySearchTree.prototype.postOrderTraversalNode = function (node,handler) {
            if(node != null) {
                //處理經過的節點的左子節點
                this.postOrderTraversalNode(node.left,handler)
                //處理經過節點的右子節點
                this.postOrderTraversalNode(node.right,handler)
                //處理經過的節點
                handler(node.key)
            }
        }

測試:

 var tree = new BinarySearchTree();
    tree.insert(11)
    tree.insert(7)
    tree.insert(15)
    tree.insert(5)
    tree.insert(3)
    tree.insert(9)
    tree.insert(8)
    tree.insert(10)
    tree.insert(13)
    tree.insert(12)
    tree.insert(14)
    tree.insert(20)
    tree.insert(18)
    tree.insert(25)
    var result = ''
    tree.postOrderTraversal(function (key) {
        result += key + ' '
    })
    alert(result)

 

 

獲取最大值最小值

 BinarySearchTree.prototype.max = function () {
            //獲取根節點
            var node = this.root
            //依次向右查找,直到節點為null
            var key = null
            while (node != null) {
                key = node.key
                node = node.right
            }
            return key
        }
        BinarySearchTree.prototype.min = function () {
            //獲取根節點
            var node = this.root
            var key = null
            //依次向左查找,直到節點為null
            while (node != null) {
                key = node.key
                node = node.left
            }
            return key
        }

測試

var tree = new BinarySearchTree();
    tree.insert(11)
    tree.insert(7)
    tree.insert(15)
    tree.insert(5)
    tree.insert(3)
    tree.insert(9)
    tree.insert(8)
    tree.insert(10)
    tree.insert(13)
    tree.insert(12)
    tree.insert(14)
    tree.insert(20)
    tree.insert(18)
    tree.insert(25)
    alert(tree.max())
    alert(tree.min())

 

 搜索特定的值

 BinarySearchTree.prototype.search = function (key) {
            //獲取根節點
            var node = this.root
            //循環搜索key
            while (node != null) {
                if (key < node.key) {
                    node = node.left
                } else if (key > node.key) {
                    node = node.right
                } else {
                    return true
                }
            }
            return false
        }

測試:

   var tree = new BinarySearchTree();
    tree.insert(11)
    tree.insert(7)
    tree.insert(15)
    tree.insert(5)
    tree.insert(3)
    tree.insert(9)
    tree.insert(8)
    tree.insert(10)
    tree.insert(13)
    tree.insert(12)
    tree.insert(14)
    tree.insert(20)
    tree.insert(18)
    tree.insert(25)
    alert(tree.search(25))
    alert(tree.search(2))

 

 

刪除操作

 思路:

1、找到要刪除的節點,如果沒有找到,不需要刪除

2、找到要刪除的節點

  1)刪除葉子節點

  2)刪除只有一個子節點的節點

  3)刪除有兩個子節點的節點

  BinarySearchTree.prototype.remove = function (key) {
            //1、尋找刪除的節點
            //定義變量,保存一些信息
            var current = this.root
            var parent = null
            var isLeftChild = true
            //尋找刪除的節點
            while (current.key != key) {
                parent = current
                if (key < current.key) {
                    isLeftChild = true
                    current = current.left
                } else {
                    isLeftChild = false
                    current = current.right
                }
                //沒有找到,已經找到了最后的節點仍然沒有找到
                if (current == null) return false
            }
            //2、根據情況刪除節點
            //刪除的節點是葉子節點
            if (current.left == null && current.right == null) {
                if (current == this.root) {
                    this.root = null
                } else if (isLeftChild) {
                    parent.left = null
                } else {
                    parent.right = null
                }
            }
            // 刪除的節點有一個子節點
            else if (current.right == null) {
                if (current == this.root) {
                    this.root = current.left
                } else if (isLeftChild) {
                    parent.left = current.left
                } else {
                    parent.right = current.left
                }
            } else if (current.left == null) {
                if (current == this.root) {
                    this.root = current.right
                } else if (isLeftChild) {
                    parent.left = current.right
                } else {
                    parent.right = current.right
                }
            }
            // 刪除的節點有兩個子節點
            else {
                //獲取后繼節點
                var successor = this.getSuccessor(current)
                //判斷是否根節點
                if(current == this.root) {
                    this.root = successor
                }else if(isLeftChild) {
                    parent.left = successor
                } else {
                    parent.right = successor
                }
            successor.left = current.left
            }
        }
        //找后繼節點的方法
        BinarySearchTree.prototype.getSuccessor = function (delNode) {
            //定義變量
            var successor = delNode
            var current = delNode.right
            var successorParent = delNode
            //循環查找
            while(current!=null){
                successorParent = successor
                successor = current
                current = current.left
            }
            //判斷尋找的后繼節點是否直接就是delNode的right節點
            if(successor!=delNode.right){
                successorParent.left = successor.right
                successor.right = delNode.right
            }
            return successor
        }

測試:

 var tree = new BinarySearchTree();
    tree.insert(11)
    tree.insert(7)
    tree.insert(15)
    tree.insert(5)
    tree.insert(3)
    tree.insert(9)
    tree.insert(8)
    tree.insert(10)
    tree.insert(13)
    tree.insert(12)
    tree.insert(14)
    tree.insert(20)
    tree.insert(18)
    tree.insert(25)
    tree.remove(9)
    tree.remove(7)
    tree.remove(15)
    var result = ''
    tree.postOrderTraversal(function (key) {
        result += key + ' '
    })
    alert(result)

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM