這篇文章主要介紹了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 = ''; //存放非递归遍历之后的字母顺序 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 = '', 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 = ''; 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 = '', 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); }
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
在webpack中有关于jquery插件的环境配置(详细教程)
以上是使用JavaScript如何實作二元樹遍歷的詳細內容。更多資訊請關注PHP中文網其他相關文章!