首頁 web前端 js教程 js實作資料結構: 樹和二元樹,二元樹的遍歷和基本操作方法

js實作資料結構: 樹和二元樹,二元樹的遍歷和基本操作方法

Sep 22, 2017 am 09:55 AM
javascript 基本操作 資料結構

樹型結構是一類非常重要的非線性結構。直觀地,樹型結構是以分支關係定義的層次結構。

樹在電腦領域中也有廣泛的應用,例如在編譯程式中,用樹來表示​​原始程式的語法結構;在資料庫系統中,可用樹來組織資訊;在分析演算法的行為時,可用樹來描述其執行過程等等

#首先看看樹的一些概念:1.樹(Tree)是n(n>=0)個結點的有限集合。在任一非空樹中:

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

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

例如,(a)是只有一個根結點的樹;(b)是有13個結點的樹,其中A是根,其餘結點分成3個互不相交的子集: T1={B,E,F,K,L},t2={D,H,I,J,M};T1,T2和T3都是根A的子樹,本身也是一棵樹。
js實作資料結構: 樹和二元樹,二元樹的遍歷和基本操作方法

2.樹的結點包含一個資料元素及若干指向其子樹的分支。結點擁有的子樹數稱為結點的度數(Degree)。例如,(b)中A的度數為3,C的度數為1,F的度數為0.度為0的結點稱為葉子(Leaf)或終端結點。度不為0的結點稱為非終端結點或分支結點。樹的度數是樹內各結點的度數的最大值。 (b)的樹的度為3.結點的子樹的根稱為該結點的孩子(Child)。相應的,該結點稱為孩子的雙親(Parent)。同一個雙親的孩子之間互稱兄弟(Sibling)。結點的祖先是從根到該結點所經分支上的所有結點。反之,以某結點為根的子樹中的任一結點稱為該結點的子孫。

3.結點的層次(Level)從根開始定義起,根為第一層,跟的孩子為第二層。若某結點在第l層,則其子樹的根就在第l+1層。其雙親在同一層的結點互為堂兄弟。例如,結點G與E,F,H,I,J互為堂兄弟。樹中結點的最大層次稱為樹的深度(Depth)或高度。 (b)的樹的深度為4。

4.如果將樹中結點的各子樹看成從左到右是有次序的(即不能交換),則稱該樹為有序樹,否則稱為無序樹。在有序樹中最左邊的子樹的根稱為第一個孩子,最右邊的稱為最後一個孩子。

5.森林(Forest)是m(m>=0)棵互不相交的樹的集合。對樹中每個結點而言,其子樹的集合即為森林。

接下來看看二元樹相關的概念:

二元樹(Binary Tree)是另一個樹型結構,它的特徵是每個結點至多只有兩棵子樹(即二元樹中不存在度大於2的結點),並且,二叉樹的子樹有左右之分(其次序不能任意顛倒。)

二叉樹的性質:

  1.在二叉樹的第i層上至多有2的i-1次方個結點(i>=1)。

  2.深度為k的二元樹至多有2的k次方-1個結點,(k>=1)。

  3.對任何一棵二元樹T,若其終端結點數為n0,度為2的結點數為n2,則n0 = n2 + 1;

#    一棵深度為k且有2的k次方-1個結點的二元樹稱為滿叉樹。

    深度為k的,有n個結點的二元樹,當且僅當其每一個結點都與深度為k的滿二叉樹中編號從1至n的結點一一對應時,稱為完全二元樹。

下面是完全二元樹的兩個特性:

  4.具有n個結點的完全二元樹的深度為Math.floor(log 2 n) + 1

5.如果將一棵有n個結點的完全二元樹(其深度為Math.floor(log 2 n) + 1)的結點按層序編號(從第1層到第Math.floor(2 n) + 1,每層由左至右),則對任一結點(1

    (1)如果i=1,則結點i、是二叉樹的根,無雙親;如果i>1,則其雙親parent(i)是結點Math.floor(i/2)。

    (2)如果2i > n,則結點i無左孩子(結點i為葉子結點);否則其左孩子LChild(i)是結點2i.

#    (3)如果2i + 1 > n,則結點i無右孩子;否則其右孩子RChild(i)是結點2i + 1;
js實作資料結構: 樹和二元樹,二元樹的遍歷和基本操作方法

js實作資料結構: 樹和二元樹,二元樹的遍歷和基本操作方法

############################################################ ######二元樹的儲存結構###

1.順序儲存結構
用一組連續的儲存單元依序自上而下,自左至右儲存完全二元樹上的結點元素,即將二叉樹上編號為i的結點元素儲存在加上定義的一維數組中下標為i-1的分量中。 「0」表示不存在此結點。這種順序存儲結構僅適用於完全二元樹。
因為,在最壞情況下,一個深度為k且只有k個結點的單支樹(樹中不存在度為2的結點)卻需要長度為2的n次方-1的一維數組。

2.鍊式儲存結構
二元樹的結點由一個資料元素和分別指向其左右子樹的兩個分支構成,則表示二元樹的鍊錶中的結點至少包含三個域:資料域和左右指標域。有時,為了便於找到結點的雙親,則還可在結點結構中增加一個指向其雙親結點的指標域。利用這兩種結構所得的二元樹的儲存結構分別稱之為二元鍊錶和三叉鍊錶。
在含有n個結點的二叉鍊錶中有n+1個空鏈域,我們可以利用這些空鏈域存儲其他有用信息,從而得到另一種鍊式存儲結構—線索鍊錶。

二元樹的遍歷主要分為三種:

先(根)序遍歷:根左右
中(根)序遍歷:左根右
後(根)序遍歷:左右根

二元樹的順序儲存結構:

js實作資料結構: 樹和二元樹,二元樹的遍歷和基本操作方法

#二元樹的鍊式儲存形式:

js實作資料結構: 樹和二元樹,二元樹的遍歷和基本操作方法



// 顺序存储结构
    var tree = [1, 2, 3, 4, 5, , 6, , , 7];
    // 链式存储结构
    function BinaryTree(data, leftChild, rightChild) {
        this.data = data || null;
        // 左右孩子结点
        this.leftChild = leftChild || null;
        this.rightChild = rightChild || null;
    }
登入後複製

遍歷二元樹(Traversing Binary Tree):是指依照指定的規律對二元樹中的每個結點存取一次且僅造訪一次。

1.先序遍歷二元樹

1)演算法的遞歸定義是:

  若二叉樹為空,則遍歷結束;否則

  ⑴ 訪問根結點;

  ⑵ 先序遍歷左子樹(遞歸呼叫本演算法);<br/>

  ⑶ 先序遍歷右子樹(遞歸呼叫本演算法)。

演算法實作:###
// 顺序存储结构的递归先序遍历
    var tree = [1, 2, 3, 4, 5, , 6, , , 7];    console.log(&#39;preOrder:&#39;);
    void function preOrderTraverse(x, visit) {
        visit(tree[x]);
        if (tree[2 * x + 1]) preOrderTraverse(2 * x + 1, visit);
        if (tree[2 * x + 2]) preOrderTraverse(2 * x + 2, visit);
    }(0, function (value) {
        console.log(value);
    });
    // 链式存储结构的递归先序遍历
    BinaryTree.prototype.preOrderTraverse = function preOrderTraverse(visit) {
        visit(this.data);
        if (this.leftChild) preOrderTraverse.call(this.leftChild, visit);
        if (this.rightChild) preOrderTraverse.call(this.rightChild, visit);
    };
登入後複製
###2)非遞迴演算法:######設T是指向二元樹根結點的變量,非遞歸演算法是: 若二元樹為空,則返回;否則,令p=T;######  (1) p為根結點;######  (2) 若p不為空或堆疊不為空;###### (3) 若p不為空,訪問p所指向的結點, p進棧, p = p.leftChild,訪問左子樹;######    (4) 否則;退棧到p,然後p = p.rightChild, 訪問右子樹######  (5) 轉(2),直到棧空為止。 ######程式碼實作:###
// 链式存储的非递归先序遍历

    // 方法1
    BinaryTree.prototype.preOrder_stack = function (visit) {        
    var stack = new Stack();        
    stack.push(this);        
    while (stack.top) {            
    var p;            // 向左走到尽头
            while ((p = stack.peek())) {
                p.data && visit(p.data);                
                stack.push(p.leftChild);
            }            
            stack.pop();            
            if (stack.top) {
                p = stack.pop();                
                stack.push(p.rightChild);
            }
        }
    };    // 方法2
     BinaryTree.prototype.preOrder_stack2 = function (visit) {        
     var stack = new Stack();        
     var p = this;        
     while (p || stack.top) {            
     if (p) {                
     stack.push(p);
                p.data && visit(p.data);
                p = p.leftChild;
            } else {
                p = stack.pop();
                p = p.rightChild;
            }
        }
    };
登入後複製
###2.中序遍歷二元樹:######1)演算法的遞歸定義為:######  若二元樹為空,則遍歷結束;否則######  ⑴ 中序遍歷左子樹(遞歸調用本演算法);######  ⑵ 存取根結點;######  ⑶ 中序歸來右子樹(遞歸調用本演算法)。 ###
// 顺序存储结构的递归中序遍历
    var tree = [1, 2, 3, 4, 5, , 6, , , 7];    
    console.log(&#39;inOrder:&#39;);
    void function inOrderTraverse(x, visit) {        
    if (tree[2 * x + 1]) inOrderTraverse(2 * x + 1, visit);
        visit(tree[x]);
        if (tree[2 * x + 2]) inOrderTraverse(2 * x + 2, visit);
    }(0, function (value) {
        console.log(value);
    });

    // 链式存储的递归中序遍历
    BinaryTree.prototype.inPrderTraverse = function inPrderTraverse(visit) {        
    if (this.leftChild) inPrderTraverse.call(this.leftChild, visit);
        visit(this.data);
        if (this.rightChild) inPrderTraverse.call(this.rightChild, visit);
    };
登入後複製
###2) 非遞歸演算法######  T是指向二元樹根結點的變量,非遞歸演算法是: 若二元樹為空,則傳回;否則,令p=T### ###  ⑴ 若p不為空,p進棧, p=p.leftChild ;###
<br/>
登入後複製
###  ⑵ 否則(即p為空),退棧到p,訪問p所指向的結點,p =p.rightChild ;######  ⑶ 轉(1);######  直到棧空為止。 ###  ###
// 方法1
    inOrder_stack1: function (visit) {        
    var stack = new Stack();        
    stack.push(this);        
    while (stack.top) {            
    var p;            // 向左走到尽头
            while ((p = stack.peek())) {                
            stack.push(p.leftChild);
            }            
            stack.pop();            
            if (stack.top) {
                p = stack.pop();
                p.data && visit(p.data);                
                stack.push(p.rightChild);
            }
        }
    },    // 方法2
    inOrder_stack2: function (visit) {        
    var stack = new Stack();        
    var p = this;        
    while (p || stack.top) {            
    if (p) {                
    stack.push(p);
                p = p.leftChild;
            } else {
                p = stack.pop();
                p.data && visit(p.data);
                p = p.rightChild;
            }
        }
    },
登入後複製
###3.後序遍歷二叉樹:######1)遞歸演算法######  若二叉樹為空,則遍歷結束;否則######  ⑴後序遍歷左子樹(遞歸呼叫本演算法);######  ⑵ 後序遍歷右子樹(遞歸呼叫本演算法) ;######  ⑶ 存取根結點。 ######
// 顺序存储结构的递归后序遍历
    var tree = [1, 2, 3, 4, 5, , 6, , , 7];    
    console.log(&#39;postOrder:&#39;);
    void function postOrderTraverse(x, visit) {        
    if (tree[2 * x + 1]) postOrderTraverse(2 * x + 1, visit);
        if (tree[2 * x + 2]) postOrderTraverse(2 * x + 2, visit);
        visit(tree[x]);
    }(0, function (value) {
        console.log(value);
    });
    // 链式存储的递归后序遍历
    BinaryTree.prototype.postOrderTraverse = function postOrderTraverse(visit) {        
    if (this.leftChild) postOrderTraverse.call(this.leftChild, visit);
        if (this.rightChild) postOrderTraverse.call(this.rightChild, visit);
        visit(this.data);
    };
登入後複製
###2) 非遞迴演算法######在後序遍歷中,根結點是最後被存取的。因此,在遍歷過程中,當搜尋指標指向某根結點時,不能立即訪問,而要先遍歷其左子樹,此時根結點進棧。當其左子樹遍歷完後再搜尋到該根結點時,還是不能訪問,還需遍歷其右子樹。所以,此根結點還需再次進棧,當其右子樹遍歷完後再退棧到到該根結點時,才能被存取。 因此,設立一個狀態標誌變數mark:######   mark=0表示剛剛訪問此結點,mark=1表示左子樹處理結束返回,######  mark=2表示右子樹處理結束返回。每次根據棧頂的mark域決定要做何動作######演算法實作想法:######  (1) 根結點入棧,且mark = 0;######  (2 ) 若堆疊不為空,出棧到node;######    (3) 若node的mark = 0,修改當前node的mark為1,左子樹入堆疊;######    (4 ) 若node的mark = 1,修改目前的nodemark為2,右子樹入堆疊;######    (5) 若node的mark = 2,存取目前node結點的值;#### ##  (6) 直到堆疊為空結束。 ###  ###
postOrder_stack: function (visit) {
        var stack = new Stack();        
        stack.push([this, 0]);        
        while (stack.top) {
            var a = stack.pop();
            var node = a[0];            
            switch (a[1]) {                
            case 0:                    
            stack.push([node, 1]);  // 修改mark域
                    if (node.leftChild) stack.push([node.leftChild, 0]);  // 访问左子树
                    break;                
                    case 1:                    
                    stack.push([node, 2]);                    
                    if (node.rightChild) stack.push([node.rightChild, 0]);                    
                    break;                
                    case 2:
                    node.data && visit(node.data);                    
                    break;                
                    default:                    
                    break;
            }
        }
    }
登入後複製
###具體的一個例子###
<html>
  <head>
    <meta charset="utf-8">
    <title>文档标题</title>
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <meta name="renderer" content="webkit">
    <meta name="keywords" content="your keywords">
    <meta name="description" content="your description">
    <link rel="shortcut icon" type="image/ico" href="/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0"></head><body>
    <p class="container"></p>
    <script>
   // 顺序存储结构
   (function () {
       // 顺序存储结构的遍历
       var tree = ["a","b","c","d","e","f","g"];

       console.log(&#39;preOrder:&#39;);
       +function preOrderTraverse(x, visit) {
           visit(tree[x]);           
           if (tree[2 * x + 1]) preOrderTraverse(2 * x + 1, visit);           
           if (tree[2 * x + 2]) preOrderTraverse(2 * x + 2, visit);
       }(0, function (value) {
           console.log(value);
       });

       console.log(&#39;inOrder:&#39;);       
       void function inOrderTraverse(x, visit) {
           if (tree[2 * x + 1]) inOrderTraverse(2 * x + 1, visit);
           visit(tree[x]);          
           if (tree[2 * x + 2]) inOrderTraverse(2 * x + 2, visit);
       }(0, function (value) {
           console.log(value);
       });

       console.log(&#39;postOrder:&#39;);       
       void function postOrderTraverse(x, visit) {
           if (tree[2 * x + 1]) postOrderTraverse(2 * x + 1, visit);           
           if (tree[2 * x + 2]) postOrderTraverse(2 * x + 2, visit);
           visit(tree[x]);
       }(0, function (value) {
           console.log(value);
       });
   }());   // 求从tree到node结点路径的递归算法
   function findPath(tree, node, path, i) {
       var found = false;       
       void function recurse(tree, i) {
           if (tree == node) {
               found = true;              
               return;
           }

           path[i] = tree;           
           if (tree.leftChild) recurse(tree.leftChild, i + 1);           
           if (tree.rightChild && !found) recurse(tree.rightChild, i + 1);           
           if (!found) path[i] = null;
       }(tree, i);
   }   var global = Function(&#39;return this;&#39;)();    
   </script>
 </body>
</html>
登入後複製

以上是js實作資料結構: 樹和二元樹,二元樹的遍歷和基本操作方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

<🎜>:泡泡膠模擬器無窮大 - 如何獲取和使用皇家鑰匙
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系統,解釋
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1664
14
CakePHP 教程
1423
52
Laravel 教程
1318
25
PHP教程
1269
29
C# 教程
1248
24
使用Java函數比較進行複雜資料結構比較 使用Java函數比較進行複雜資料結構比較 Apr 19, 2024 pm 10:24 PM

Java中比較複雜資料結構時,使用Comparator提供靈活的比較機制。具體步驟包括:定義比較器類,重寫compare方法定義比較邏輯。建立比較器實例。使用Collections.sort方法,傳入集合和比較器實例。

Java資料結構與演算法:深入詳解 Java資料結構與演算法:深入詳解 May 08, 2024 pm 10:12 PM

資料結構與演算法是Java開發的基礎,本文深入探討Java中的關鍵資料結構(如陣列、鍊錶、樹等)和演算法(如排序、搜尋、圖演算法等)。這些結構透過實戰案例進行說明,包括使用陣列儲存分數、使用鍊錶管理購物清單、使用堆疊實現遞歸、使用佇列同步執行緒以及使用樹和雜湊表進行快速搜尋和身份驗證等。理解這些概念可以編寫高效且可維護的Java程式碼。

深入了解Go語言中的引用類型 深入了解Go語言中的引用類型 Feb 21, 2024 pm 11:36 PM

引用類型在Go語言中是一種特殊的資料類型,它們的值並非直接儲存資料本身,而是儲存資料的位址。在Go語言中,引用型別包括slices、maps、channels和指標。深入了解引用類型對於理解Go語言的記憶體管理和資料傳遞方式至關重要。本文將結合具體的程式碼範例,介紹Go語言中引用類型的特點和使用方法。 1.切片(Slices)切片是Go語言中最常用的引用類型之一

PHP資料結構:AVL樹的平衡之道,維持高效有序的資料結構 PHP資料結構:AVL樹的平衡之道,維持高效有序的資料結構 Jun 03, 2024 am 09:58 AM

AVL樹是一種平衡二元搜尋樹,確保快速且有效率的資料操作。為了實現平衡,它執行左旋和右旋操作,調整違反平衡的子樹。 AVL樹利用高度平衡,確保樹的高度相對於節點數始終較小,從而實現對數時間複雜度(O(logn))的查找操作,即使在大型資料集上也能保持資料結構的效率。

Java集合框架全解析:解剖資料結構,揭秘高效率儲存之道 Java集合框架全解析:解剖資料結構,揭秘高效率儲存之道 Feb 23, 2024 am 10:49 AM

Java集合框架概述Java集合框架是Java程式語言的重要組成部分,它提供了一系列可以儲存和管理資料的容器類別庫。這些容器類別庫具有不同的資料結構,可以滿足不同場景下的資料儲存和處理需求。集合框架的優點在於它提供了統一的接口,使得開發人員可以使用相同的方式來操作不同的容器類別庫,從而降低了開發難度。 Java集合框架的資料結構Java集合框架中包含多種資料結構,每種資料結構都有其獨特的特性和適用場景。以下是幾種常見的Java集合框架資料結構:1.List:List是一個有序的集合,它允許元素重複。 Li

基於哈希表的資料結構優化PHP數組交集和並集的計算 基於哈希表的資料結構優化PHP數組交集和並集的計算 May 02, 2024 pm 12:06 PM

利用雜湊表可最佳化PHP數組交集和並集計算,將時間複雜度從O(n*m)降低到O(n+m),具體步驟如下:使用雜湊表將第一個數組的元素映射到布林值,以快速找出第二個陣列中元素是否存在,提高交集計算效率。使用雜湊表將第一個陣列的元素標記為存在,然後逐一新增第二個陣列的元素,忽略已存在的元素,提高並集計算效率。

PHP SPL 資料結構:為你的專案注入速度與彈性 PHP SPL 資料結構:為你的專案注入速度與彈性 Feb 19, 2024 pm 11:00 PM

PHPSPL資料結構庫概述PHPSPL(標準php庫)資料結構庫包含一組類別和接口,用於儲存和操作各種資料結構。這些資料結構包括數組、鍊錶、堆疊、佇列和集合,每個資料結構都提供了一組特定的方法和屬性,用於操縱資料。數組在PHP中,數組是儲存一系列元素的有序集合。 SPL數組類別提供了對原生的PHP數組進行加強的功能,包括排序、過濾和映射。以下是使用SPL陣列類別的範例:useSplArrayObject;$array=newArrayObject(["foo","bar","baz"]);$array

深入學習Go語言資料結構的奧秘 深入學習Go語言資料結構的奧秘 Mar 29, 2024 pm 12:42 PM

深入學習Go語言資料結構的奧秘,需要具體程式碼範例Go語言作為一門簡潔、高效的程式語言,在處理資料結構方面也展現了其獨特的魅力。數據結構是電腦科學中的基礎概念,它旨在組織和管理數據,使得數據能夠更有效地被存取和操作。透過深入學習Go語言資料結構的奧秘,我們可以更好地理解資料的儲存方式和操作方法,從而提高程式效率和程式碼品質。一、數組數組是最簡單的資料結構之一

See all articles