首頁 web前端 js教程 使用JavaScript如何實作二元樹遍歷

使用JavaScript如何實作二元樹遍歷

Jun 19, 2018 pm 04:02 PM
javascript 二元樹 定義 尋找 遍歷

這篇文章主要介紹了JavaScript實現二元樹定義、遍歷及查找的方法,結合實例形式較為詳細的分析了二叉樹的相關概念及javascript構建二叉樹、遍歷、查找二叉樹的常用操作技巧,需要的朋友可以參考下方

本文實例講述了JavaScript實作二元樹定義、遍歷及尋找的方法。分享給大家供大家參考,具體如下:

二叉樹(binary tree)

在寫這篇文章之前說一下資料結構和演算法這個系列,這個系列包含了很多東西,比如啥子排序,線性表,廣義表,樹,圖這些大家都是知道的,但是這些東西我們學了之後工作中能用到的又有多少呢,據我所知絕大部分公司,一線碼農,屌絲,程序猿是用不到這些東西,既然這樣為啥子我還要強調這個系列呢,本人覺得算法和數據結構是程序的基本功,前提想脫離一線碼農,普通程序猿行列,說得通俗一點就是讓自己變的更屌。其次語言都是想通的,只要是掌握了一門語言學習其他語言就如同順水推舟,不費一點力氣。另外還有一點就是我會一直把這個系列寫下去, 雖然網上一搜一大筐,已經寫爛了,但是我寫作的目的有兩個,第一和大家分享, 第二可以讓自己更深入的理解。好了,其他的不多說了,最近複習了一下二叉樹, 就先寫這個,後面會依次的加上排序, 線性表,廣義表。 。 。 。等等

二元樹

一說到二元樹我們一定會問,什麼是二元樹,二叉樹是個啥子東東,拿來有啥子用嘛,我們為啥子要學習它嘛?如果當初你在學習二元樹的時候你沒有問過自己這些問題,那麼你對它的了解也僅僅也只是了解。那我們現在來說說什麼是二元樹,二元樹就是一種資料結構, 它的組織關係就像是自然界中的樹一樣。官方語言的定義是:是一個有限元素的集合,該集合或者為空、或者由一個稱為根的元素及兩個不相交的、被分別稱為左子樹和右子樹的二叉樹組成。至於為啥子要學習它,媽媽總是說,孩子,等你長大了就明白了。

二元樹的性質

性質1:二元樹第i層上的節點數目最多為2i-1(i≥1);
性質2:深度為k的二元樹至多有2k-1個結點(k≥1)。
性質3: 在任一-棵二元樹中,若葉子結點(即度為0的結點)的個數為n0,度為1的結點數為n1,度為2的結點數為n2,則no=n2 1。

二元樹的儲存結構與建構

二元樹的儲存方式有兩種,一種順序存儲,例如:
var binaryTree = [' a', 'b', 'c', 'd', 'e', 'f', 'h', 'i']; 這就是一顆二元樹,假設binaryTree[i]是二元樹的一個節點,那麼它的左孩子節點leftChild = binaryTree[i*2 1]那麼相應的右孩子節點rightChild = binaryTree[i*2 2]; 一般情況下順序存儲的這種結構用的較少,另外一種存儲方式就是鍊式存儲,下面我會用程式碼來詳細描述二元樹式結構的構建與存儲方式,構建二叉樹也有兩種方式一種是遞歸方式構建,這種很簡單,另一種是非遞歸方法構建,這種呢相對於前一種複雜一點點,不過也不用擔心,我在程式碼中加上詳細的註釋,一步一步的走下去。我們現在就以26個英文字母來建構二元樹

複製程式碼 程式碼如下:

var charecters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P ', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];

#在建構二元樹之前我們會用到一個節點對象,節點對像如下:(注意:關於javascript的面向對象,原型,語法特點我會放在javascript語言知識點這個系列)

/*
 *二叉树的节点对象
 */
function Node() {
  this.text = '';      //节点的文本
  this.leftChild = null;  //节点的左孩子引用
  this.rightChild = null;  //节点右孩子引用
}
登入後複製

#遞歸建構二元樹

在建構好二元樹節點之後我們緊接著用遞歸來建構二元樹

var charecters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
function buildTree(node, i) {
    var leftIndex = 2*i+1,             //左孩子节点的索引
      rightIndex = 2*i+2;             //右孩子节点的索引
    if(leftIndex < charecters.length) {       //判断索引的长度是否超过了charecters数组的大小
      var childNode = new Node();         //创建一个新的节点对象
      childNode.text = charecters[leftIndex];   //给节点赋值
      node.leftChild = childNode;         //给当前节点node加入左孩子节点
      buildTree(childNode, leftIndex);      //递归创建左孩子
    }
    if(rightIndex < charecters.length) {      //下面注释参照上面的构建左孩子的节点
      var childNode = new Node();
      childNode.text = charecters[rightIndex];
      node.rightChild = childNode;
      buildTree(childNode, rightIndex);
    }
}
//下面构造二叉树
var node = new Node();
node.text = charecters[0];
buildTree(node, 0);  //索引i是从0开始构建
登入後複製

非遞歸來建構二元樹

以下是以非遞歸方式建構二元樹:

var root;
function createBinaryTree() {
    var len = charecters.length,        //数组的长度
      index = 0,               //索引从0开始
      nodes = new Array();          //创建一个临时数组,用于存放二叉树节点
    //循环创建二叉树节点存放到数组中
    for (var i = 0 ; i < charecters.length ; i++) {
      var node = new Node();
      node.text = charecters[i];
      nodes.push(node);
    }
    //循环建立二叉树子节点的引用
    while(index < len) {
      var leftIndex = 2*index+1,       //当前节点左孩子索引
        rightIndex = 2*index+2;       //当前节点右孩子索引
      //给当前节点添加左孩子
      nodes[index].leftChild = nodes[leftIndex];
      //给当前节点添加右孩子
      nodes[index].rightChild = nodes[rightIndex];
      index++;
    }
    root = nodes[0];
}
登入後複製

二元樹的三種遍歷##

好了,现在我们已经成功构建了二叉树的链式结构,在构建了二叉树的链式结构后我们进入二叉树的最基本的遍历了,遍历有三种最基本的遍历,我不说想必大家都知道,先序遍历,中序遍历和后续遍历。虽然这三种遍历递归方式都比较简单,但非递归方式就不是那么容易了,当时我在实现的时候都卡了半天,真的是说起来容易做起来难啊,在实现遍历前我们首先要来实现的是栈,因为在非递归遍历的时候会用到栈,那到底什么是栈呢,这里我就简单介绍下吧,有兴趣的朋友可以去维基百科有权威的定义,栈和队列也是一种数据结构,栈存放数据的时候是先进先出,而队列是先进后出。

实现栈的对象

下面用javascript来实现栈的对象

function Stack() {
    var stack = new Array();        //存放栈的数组
    //压栈
    this.push = function(o) {
      stack.push(o);
    };
    //出栈
    this.pop = function() {
      var o = stack[stack.length-1];
      stack.splice(stack.length-1, 1);
      return o;
    };
    //检查栈是否为空
    this.isEmpty = function() {
      if(stack.length <= 0) {
        return true;
      }
      else {
        return false;
      }
    };
}
//使用方式如下
var stack = new Stack();
stack.push(1);    //现在栈中有一个元素
stack.isEmpty();   //false , 栈不为空
alert(stack.pop()); //出栈, 打印1
stack.isEmpty();   //true, 此时栈为空,因为在调用了stack.pop()之后元素出栈了,所以为空
登入後複製

1. 先序遍历

在实现了栈对象以后我们首先来进行先序遍历的递归方式

function firstIteration(node) {
    if(node.leftChild) {          //判断当前节点是否有左孩子
      firstIteration(node.leftChild);  //递归左孩子
    }
    if(node.rightChild) {         //判断当前节点是否有右孩子
      firstIteration(node.rightChild);  //递归右孩子
    }
}
//递归遍历二叉树
firstIteration(root);
登入後複製

先序遍历的非递归方式

上面的代码大家可以在firstIteration()方法中加个alert()函数来验证是否正确。那么下面就要说说先序遍历的非递归方式,遍历思想是这样的:先访问根节点在访问左节点, 最后访问右节点。从根节点一直往下访问找左孩子节点,直到最后一个左孩子节点(将这条路径保存到栈中),然后再访问最后一个左孩子的兄弟节点(右孩子节点),之后回溯到上一层(将栈中的元素取出 就是出栈),又开始从该节点(回溯到上一层的节点)一直往下访问找左孩子节点... 直到栈中的元素为空,循环结束。

function notFirstIteration(node) {
    var stack = new Stack(),         //开辟一个新的栈对象
      resultText = &#39;&#39;;           //存放非递归遍历之后的字母顺序
    stack.push(root);            //这个root在上面非递归方式构建二叉树的时候已经构建好的
    var node = root;
    resultText += node.text;
    while(!stack.isEmpty()) {
      while(node.leftChild) {       //判断当前节点是否有左孩子节点
        node = node.leftChild;      //取当前节点的左孩子节点
        resultText += node.text;     //访问当前节点
        stack.push(node);        //将当前节点压入栈中
      }
      stack.pop();             //出栈
      node = stack.pop().rightChild;    //访问当前节点的兄弟节点(右孩子节点)
      if(node) {              //当前节点的兄弟节点不为空
        resultText += node.text;     //访问当前节点
        stack.push(node);        //将当前节点压入栈中
      }
      else {                //当前节点的兄弟节点为空
        node = stack.pop();       //在回溯到上一层
      }
    }
}
//非递归先序遍历
notFirstIteration(root);
登入後複製

2. 中序遍历

只要把思路理清楚了现实起来其实还是挺容易的,只要我们熟悉了一种二叉树的非递归遍历方式,其他几种非递归方式就容易多了,照着葫芦画瓢,下面是中序遍历的递归方式,中序遍历的思想是:先访问左孩子节点,在访问根节点,最后访问右节点

var strText = "";
function secondIteration(node) {
    //访问左节点
    if(node.leftChild) {
      if(node.leftChild.leftChild) {
        secondIteration(node.leftChild);
      }
      else {
        strText += node.leftChild.text;
      }
    }
    //访问根节点
    strText += node.text;
    //访问右节点
    if(node.rightChild) {
      if(node.rightChild.leftChild) {
        secondIteration(node.rightChild);
      }
      else {
        strText += node.rightChild.text;
      }
    }
}
secondIteration(root);
alert(strText);
登入後複製

中序遍历的非递归方式

思想是:1. 从根节点一直往下找左孩子节点,直到找到最后一个左孩子节点(用栈将此路径保存,但不访问)2.访问最后一个左孩子节点,然后再访问根节点(要先弹出栈,就是在栈中取上一层节点)3.在访问当前节点(最后一个左孩子节点)的兄弟节点(右孩子节点),这里要注意如果兄弟节点是一个叶节点就直接访问,否则是兄弟节点是一颗子树的话不能马上访问,要先来重复 1, 2,3步骤, 直到栈为空,循环结束

function notSecondIteration() {
    var resultText = &#39;&#39;,
      stack = new Stack(),
      node = root;
    stack.push(node);
    while(!stack.isEmpty()) {
      //从根节点一直往下找左孩子节点直到最后一个左孩子节点,然后保存在栈中
      while(node.leftChild) {
        node = node.leftChild;
        stack.push(node);
      }
      //弹出栈
      var tempNode = stack.pop();
      //访问临时节点
      resultText += tempNode.text;
      if(tempNode.rightChild) {
        node = tempNode.rightChild;
        stack.push(node);
      }
    }
    alert(resultText);
}
登入後複製

3. 后续遍历

最后就还剩下一种遍历方式,二叉树的后续遍历,后续遍历的思想是:先访问左孩子节点,然后在访问右孩子节点,最后访问根节点

后续遍历的递归方式

var strText = &#39;&#39;;
function lastIteration(node) {
    //首先访问左孩子节点
    if(node.leftChild) {
      if(node.leftChild.leftChild) {
        lastIteration(node.leftChild);
      }
      else {
        strText += node.leftChild.text;
      }
    }
    //然后再访问右孩子节点
    if(node.rightChild) {
      if(node.rightChild.rightChild) {
        lastIteration(node.rightChild);
      }
      else {
        strText += node.rightChild.text;
      }
    }
    //最后访问根节点
    strText += node.text;
}
//中序递归遍历
lastIteration(root);
alert(strText);
登入後複製

后续非递归遍历

后续非递归遍历的思想是:1.从根节点一直往下找左孩子节点,直到最后一个左孩子节点(将路径保存到栈中,但不访问)2.弹出栈访问最后一个左孩子节点 3.进入最后一个左孩子节点的兄弟节点,如果兄弟节点是叶节点就访问它,否则将该节点重复 1, 2步骤, 直到栈中的元素为空,循环结束。3.访问根节点

function notLastIteration() {
    var strText = &#39;&#39;,
    stack = new Stack();
    nodo = root;
    stack.push(node);
    while(!stack.isEmpty()) {
      while(node.leftChild) {
        node = node.leftChild;
        stack.push(node);
      }
      //弹出栈
      var tempNode = stack.pop();
      //访问左孩子节点
      strText += tempNode.text;
      //访问右孩子节点
      if(tempNode.rightChild) {
        if(tempNode.rightChild.leftChild || tempNode.rightChild.rightChild) { //判断最后一个左孩子节点的兄弟节点是否为页节点
          stack.push(tempNode.rightChild);
        }
        else {
          strText += tempNode.rightChild.text;
        }
      }
    }
    alert(strText);
}
登入後複製

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

如何使用vuex实现菜单管理

详细解读Angular5.1新功能

使用nodejs如何实现gulp打包

在vue2中通过keep-alive如何使用

在webpack中有关于jquery插件的环境配置(详细教程)

以上是使用JavaScript如何實作二元樹遍歷的詳細內容。更多資訊請關注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

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

熱工具

記事本++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 教程
1421
52
Laravel 教程
1315
25
PHP教程
1266
29
C# 教程
1239
24
硬碟序號和mac位址怎麼查 硬碟序號和mac位址怎麼查 Feb 18, 2024 pm 07:45 PM

硬碟序號和MAC位址是電腦硬體中重要的標識符,它們在管理和維護電腦系統時非常有用。本文將介紹如何尋找硬碟序號和MAC位址。一、尋找硬碟序號硬碟序號是硬碟製造商為了辨識和追蹤硬碟的唯一識別碼。在不同的作業系統中,尋找硬碟序號的方法略有不同。 Windows系統:開啟命令提示字元(在開始功能表中搜尋「cmd」),然後輸入以下命令並按下回車鍵:wmicdisk

在 iPhone 上關閉「尋找」的 4 種方法 在 iPhone 上關閉「尋找」的 4 種方法 Feb 02, 2024 pm 04:15 PM

Apple的「尋找」應用程式可讓您定位您的iPhone或其他設備,以防止遺失或遺忘。雖然「查找」是一個有用的工具來追蹤設備,但如果您關注隱私問題、不想耗盡電池或其他原因,您可能想要停用它。幸運的是,有幾種方法可以關閉iPhone上的“查找”,我們將在這篇文章中解釋所有這些方法。如何在iPhone上關閉「尋找」[4種方法]您可以透過四種方式關閉iPhone的「查找」。如果您使用方法1關閉“查找”,則可以從要停用它的裝置上執行此操作。若要繼續執行方法2、3和4,要關閉「尋找」的iPhone應關閉電源或

MySQL 複合主鍵的定義與作用 MySQL 複合主鍵的定義與作用 Mar 15, 2024 pm 05:18 PM

MySQL中的複合主鍵是指表中由多個欄位組合而成的主鍵,用來唯一標識每筆記錄。與單一主鍵不同的是,複合主鍵由多個欄位的值組合在一起形成。在建立表格的時候,可以透過指定多個欄位為主鍵來定義複合主鍵。為了示範複合主鍵的定義與作用,我們先建立一個名為users的表,其中包含了id、username和email這三個字段,其中id是自增主鍵,user

什麼是Discuz? Discuz的定義與功能介紹 什麼是Discuz? Discuz的定義與功能介紹 Mar 03, 2024 am 10:33 AM

《探索Discuz:定義、功能及程式碼範例》隨著網路的快速發展,社群論壇已成為人們獲取資訊、交流觀點的重要平台。在眾多的社群論壇系統中,Discuz作為國內較知名的一種開源論壇軟體,備受廣大網站開發者和管理員的青睞。那麼,什麼是Discuz?它又有哪些功能,能為我們的網站提供怎樣的幫助呢?本文將對Discuz進行詳細介紹,並附上具體的程式碼範例,幫助讀者更

Java如何遍歷資料夾並取得所有檔案名 Java如何遍歷資料夾並取得所有檔案名 Mar 29, 2024 pm 01:24 PM

Java是一種流行的程式語言,具有強大的檔案處理功能。在Java中,遍歷資料夾並取得所有檔案名稱是一種常見的操作,可以幫助我們快速定位和處理特定目錄下的檔案。本文將介紹如何在Java中實作遍歷資料夾並取得所有檔案名稱的方法,並提供具體的程式碼範例。 1.使用遞歸方法遍歷資料夾我們可以使用遞歸方法遍歷資料夾,遞歸方法是一種自身呼叫自身的方式,可以有效地遍歷資料夾中

簡易JavaScript教學:取得HTTP狀態碼的方法 簡易JavaScript教學:取得HTTP狀態碼的方法 Jan 05, 2024 pm 06:08 PM

JavaScript教學:如何取得HTTP狀態碼,需要具體程式碼範例前言:在Web開發中,經常會涉及到與伺服器進行資料互動的場景。在與伺服器進行通訊時,我們經常需要取得傳回的HTTP狀態碼來判斷操作是否成功,並根據不同的狀態碼來進行對應的處理。本篇文章將教你如何使用JavaScript來取得HTTP狀態碼,並提供一些實用的程式碼範例。使用XMLHttpRequest

如何找到電腦硬碟的序號 如何找到電腦硬碟的序號 Feb 20, 2024 am 10:33 AM

電腦硬碟序號怎麼查隨著電腦科技的發展,電腦硬碟已經成為我們生活中不可或缺的一部分。無論是儲存重要的文件,還是安裝作業系統和軟體,都需要依靠硬碟來完成。而了解電腦硬碟的一些基本訊息,例如硬碟的序號,可以幫助我們更好地管理和維護電腦系統。那麼,電腦硬碟序號怎麼查呢?本文將介紹幾種常見的方法。方法一:使用Windows系統自帶的命令列工具Windows系統

PHP介面簡介及其定義方式 PHP介面簡介及其定義方式 Mar 23, 2024 am 09:00 AM

PHP介面簡介及其定義方式PHP是一種廣泛應用於Web開發的開源腳本語言,具有靈活、簡單、強大等特性。在PHP中,介面(interface)是一種定義多個類別之間公共方法的工具,實現了多態性,讓程式碼更加靈活和可重複使用。本文將介紹PHP介面的概念及其定義方式,同時提供具體的程式碼範例展示其用法。 1.PHP介面概念介面在物件導向程式設計中扮演著重要的角色,定義了類別應

See all articles