1、树(Tree)
树形结构是一种非线性数据结构;树是以分支关系定义的层次结构。
问题来了,什么是线性数据结构?什么是非线性数据结构?
线性结构是一组有序数据元素的集合,好比数组、栈、队列,除了第一个元素和最后一个元素之外,其余元素都是首尾相连的。
非线性结构中各个数据元素没有对应的顺序,每一个数据元素均可能和零个或多个数据元素发生关系,典型的非线性结构包括二维数组、树、图。
生活中比较常见的树形结构:企业组织层级架构、计算机文件系统、族谱等。javascript
2、二叉树(Binary Tree)
二叉树是一种特殊的树形结构,每一个节点(Node)最多只能有两棵子树(SubTree),子树有左右之分,顺序不能任意颠倒。
那么二叉树有何特色呢?
能够高效的插入、查找和删除数据。html
3、二叉查找树(Binary Search Tree)的实现
二叉查找树(BST)是一种特殊的二叉树,相对较小的值保存在左子树中,较大的值保存在右子树中。
首先定义节点,节点是组成二叉树的基本单元。(如下代码均用JavaScript实现)前端
function Node(data, left, right) { this.data = data; // 节点值 this.left = left; // 左子树(左节点) this.right = right; // 右子树(右节点) this.show = show; // 显示当前节点值 } function show() { return this.data; }
其次定义BST类,包含:
1.一个根节点,初始化为null
2.向BST对象中插入节点的insert方法java
function BST() { this.root = null; this.insert = insert; } function insert(data) { var n = new Node(data, null, null); if (this.root === null) { this.root = n; } else { var current = this.root; var parent; while (true) { parent = current; if (data < parent.data) { current = current.left; if (current === null) { parent.left = n; break; } } else { current = current.right; if (current === null) { parent.right = n; break; } } } } }
遍历二叉查找树
所谓遍历,即按照某种顺序访问树中每一个节点,使得每一个节点都被访问一次,且仅被访问一次。
二叉树由三个基本单元组成——根节点、左节点和右节点,因此遍历整个二叉树,也就能够看做依次遍历这三部分。
通常遵循先左后右的原则,因此常见的遍历方案有前序遍历(根左右)、中序遍历(左根右)和后序遍历(左右根)。
前序遍历node
function preOrder(node) { if (!(node === null)) { console.log(node.show()); preOrder(node.left); preOrder(node.right); } }
中序遍历算法
function inOrder(node) { if (!(node === null)) { inOrder(node.left); console.log(node.show()); inOrder(node.right); } }
后序遍历数组
function postOrder(node){ if (!(node ===null)) { postOrder(node.left); postOrder(node.right); console.log(node.show()); } }
以上实现基于递归算法数据结构
下面介绍非递归算法架构
非递归算法须要用到另一种数据结构——栈,本篇对栈不进行展开叙述,只贴出简单的JavaScript实现数据结构和算法
function Stack() { this.dataStore = []; this.top = 0; this.push = push; // 入栈 this.pop = pop; // 出栈 this.peek = peek; // 返回栈顶元素 this.clear = clear; // 清空栈 this.length = length; // 返回栈内元素个数 } function push(element) { this.dataStore[this.top++] = element; } function pop() { return this.dataStore[--this.top]; } function peek() { return this.dataStore[this.top - 1]; } function clear() { this.top = 0; } function length() { return this.top; }
前序遍历非递归算法
思路:
1.当前节点为根节点,根节点不为空且栈不为空
2.当前节点不为空,访问当前节点,当前节点入栈,访问当前节点左子树
3.不然,节点出栈,当前节点指向出栈节点右子树
function preOrder_Stack(root) { var result = []; var stack = new Stack(); var p = root; while (stack.length() || p !== null) { if (p !== null) { stack.push(p); result.push(p.data); // 在遍历左子树以前加入根元素 p = p.left; } else { var node = stack.pop(); p = node.right; } } return result; }
中序遍历非递归算法
思路:
1.当前节点为根节点,根节点不为空且栈不为空
2.当前节点不为空,当前节点入栈,访问当前节点左子树
3.不然,节点出栈,访问出栈节点,当前节点指向出栈节点右子树
function inOrder_Stack(root) { var result = []; var stack = new Stack(); var p = root; while (stack.length() || p !== null) { if (p !== null) { stack.push(p); p = p.left; } else { var node = stack.pop(); result.push(node.data); // 遍历完左子树以后加入根元素 p = node.right; } } return result; }
后序遍历非递归算法
思路:
后序遍历访问顺序为左右根,能够换个思路,按照根右左的顺序访问,而后利用数组unshift方法实现结果反转,这样就转化成根右左的遍历算法
1.当前节点为根节点,根节点不为空且栈不为空
2.当前节点不为空,访问当前节点,当前节点入栈,访问当前节点右子树
3.不然,节点出栈,当前节点指向出栈节点左子树
function postOrder_Stack(root) { var result = []; var stack = new Stack(); var p = root; while (stack.length() || p !== null) { if (p !== null) { stack.push(p); result.unshift(p.data); p = p.right; } else { var node = stack.pop(); p = node.left; } } return result; }
4、备注
1.第一次写文,确定有诸多不足,请多多指教,多多批评!
2.花时间写这篇文章的时候,才真的体会到其中的不易,向各位前辈致敬!
3.参考书目:
[美]Michael McMillan.数据结构与算法JavaScript描述[M]王群锋、杜欢译.北京:人民邮电出版社,2014
严蔚敏、吴伟民.数据结构(C语言版)[M].北京:清华大学出版社,1997
4.参考技术博客:
线性结构和非线性结构 - CSDN博客blog.csdn.net