1、前端为何要学算法和数据结构?
其实,在前端,了解一些经常使用的Jvascript Array和Object并可以灵活运用就足以解决常见的初级和中级的算法问题,若是不够用还有不少的ECMAscript的API能够依赖。一个排序功能,用冒泡排序、快速排序仍是并归排序?who care?一个原生的sort函数就直接搞定。何况前端的代码运行环境原本就不善于处理大量的数据计算。并且前端也有不少的东西比算法重要(好比安全性、用户体验、模块化和可扩展性等),在大多数状况下,前端并不在意你写的程序有多快,更加在意你能不能很好的实现功能。那咱们为何要学习算法与数据结构呢?javascript
前端工程师首先得是工程师,既然是计算机的工程师那么掌握必定的数据结构和算法并可以理解和灵活运用它们是一个最起码的要求。只有这样才能在日益复杂的前端领域站稳脚跟,越走越远。前端
当一个全新的功能摆在你面前又找不到现有的函数或api去实现的时候,良好的数据结构和算法功底就显的尤其重要。java
当你在学习数据结构和算法的时候,你的不少能力也会跟着提高。化繁为简,直达问题本质的能力、分析判断的能力、触类旁通的能力 。面试
前端的面试离不开算法与数据结构,有用良好的该方面的知识储备量可以大大加分。算法
2、数据结构
数据结构:数据结构是数据元素相互之间存在的一种和多种特定的关系集合 包括二个部分组成逻辑结构,存储结构。
逻辑结构:简单的来讲 逻辑结构就是数据之间的关系,逻辑结构大概统一的能够分红两种 一种是线性结构,非线性结构 。
线性结构:是一个有序数据元素的集合。 其中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素以外,其它数据元素都是首尾相接的。经常使用的线性结构有: 列表,栈,队列,链表,线性表,集合。
非线性结构:各个数据元素再也不保持在一个线性序列中,每一个数据元素可能与零个或者多个其余数据元素发生联系。常见的线性结构有 二维数组,多维数组,广义表,树(二叉树等),图(网)等。
存储结构:逻辑结构指的是数据间的关系,而存储结构是逻辑结构用计算机语言的实现。 常见的存储结构有顺序存储、链式存储、索引存储以及散列存储(哈希表)。
大体结构以下:api
3、线性结构--栈
栈,是一种后入先出(LIFO)的线性结构。栈的头部,称为栈底;栈的尾部,称为栈顶。元素,从栈顶压入;从栈顶弹出。
如何实现一个栈:(1)肯定存储结构:是离散的,仍是连续的。或者说,是链表,仍是连续表。在Javasript中能够选用数组,做为存储结构。
(2)肯定操做方法:入栈方法(push),出栈方法(pop),预览栈顶方法(peek),清栈方法(clear),获取栈高方法(length)。具体实现以下:数组
function Stack() { this.dataStore = []; this.top = 0; this.push = function ( element ) { this.dataStore[this.top++] = element; }; this.pop = function () { return this.dataStore.splice(--this.top,1)[0]; }; this.peek = function () { return this.dataStore[this.top-1]; }; this.length = function () { return this.top; }; this.clear = function () { delete this.dataStore; this.dataStore = []; this.top = 0; } };
(1)进制转化:将十进制整数转化为任意正整数进制。
原理:取余法。
实现以下:安全
function newBase( num, base ) { var stack = new Stack(); // 除n取余 do { stack.push(num % base); num = Math.floor(num/base); } while (num > 0); // 倒写余数 var str = ''; for (var i = 0, length = stack.length(); i < length; i++) { str += stack.pop(); }; return str; };
(2)阶乘的迭代实现
原理:将1到n依次压入栈中,再将栈顶弹出的数,一一相乘。
实现以下:前端工程师
function newFactorial( n ) { var stack = new Stack(); while (n > 1) { stack.push(n--) }; var result = 1; while (stack.length() > 0) { result *= stack.pop(); }; return result; }
(3)判断单词是否回文
原理:倒写单词。将原单词从首字母到尾字母,依次压入栈中,再依次弹出,写入字符串,完成倒写,生成新单词。判断倒写以后的新单词和原单词是否相等。
实现以下:数据结构
function isPalindrome( words ) { var stack = new Stack(); for (var i = 0; i < words.length; i++) { stack.push(words[i]); }; var rwords = ''; for (var i = 0, length = stack.length(); i < length; i++) { rwords += s.pop(); }; if ( words === rwords ) { return true; } else { return false; }; };
(4)中缀表达式转化为后缀表达式(逆波兰表达式)
原理:自左向右扫描中缀表达式,若是当前是运算数,则压入栈中;若是当前是运算符(左括号,加减乘除),则压入栈;若是当前是运算符(右括号),则从栈中弹出,输出到后缀表达式中。直到栈顶不是(加减乘除)。再将左括号弹出。重复第一步和第二步,直到算术表达式扫描完毕。
实现以下:
function reversePolishExpression( arithmetic ) { var operater = new Stack(); var reverseExp = ''; for (var i = 0; i < arithmetic.length; i++) { if ( '0123456789'.indexOf(arithmetic[i]) !== -1 ) { reverseExp += arithmetic[i]; } else if ( '(+-*/'.indexOf(arithmetic[i]) !== -1 ) { operater.push(arithmetic[i]); } else if ( arithmetic[i] === ')' ) { while ( '+-*/'.indexOf(operater.peek()) !== -1 ) { reverseExp += operater.pop(); }; operater.pop(); }; }; return reverseExp; };
4、线性结构--队列
队列是只容许在一端进行插入操做,另外一个进行删除操做的线性表,队列是一种先进先出(FIFO)的数据结构
队列在程序程序设计中用的很是的频繁,由于javascript单线程,因此致使了任何一个时间段只能执行一个任务,并且还参杂了异步的机制。
队列原本也是一种特殊的线性表,在JavaScript咱们能够直接使用数组实现这样的一个设计,数组的push()方法能够在数组末尾加入元素,shift()方法则可删除数组的第一个元素。
实现以下:
function Queue() { this.dataStore = []; this.enqueue = enqueue; this.dequeue = dequeue; this.first = first; this.end = end; this.toString = toString; this.empty = empty; this.enqueue = function(element) { //向队尾添加一个元素 this.dataStore.push(element); } this.dequeue = function() { //删除队首的元素 return this.dataStore.shift(); } this.getFirst = function() { //读取队首元素 return this.dataStore[0]; } this.getEnd = function() { //读取队尾元素 return this.dataStore[this.dataStore.length - 1]; } this.showAll = function() { //显示队列中的全部元素 var retStr = ""; for (var i = 0; i < this.dataStore.length; ++i) { retStr += this.dataStore[i] + "\n"; } return retStr; } this.isEmpty = function() { //判断队列是否为空 if (this.dataStore.length == 0) { return true; } else { return false; } } }
(1)优先队列(可实现javascript桥接模式)
原理:数据成员入队的时候要携带数据和优先级,优先级在这里的定义是数字越大在数组中的位置靠后。当插入一个数组元素的时候,先要根据它的优先级来判断,最小是直接插入到第一位,紧邻一个比他大的优先级是,就插入到这个邻居的前面。若是是最大的数字就插到数组的最后。这样,一维数组变成了二位数组。
实现以下:
function PriorityQueue() { let items = []; function QueueElement (element, priority){ this.element = element;//成员自己 this.priority = priority; //优先级 } this.enqueue = function(element, priority){ let queueElement = new QueueElement(element, priority); let added = false; //添加标记 for (let i=0; i<items.length; i++){ if (queueElement.priority < items[i].priority){ //找到优先级数字比他大的那个元素 items.splice(i,0,queueElement); // 插到该元素的前面 added = true; break; } } if (!added){ //若是是优先级数字是最大的,直接插入到数组的末尾 items.push(queueElement); //{5} } }; //其余操做和普通队列是同样的 this.dequeue = function(){ return items.shift(); }; this.front = function(){ return items[0]; }; this.isEmpty = function(){ return items.length == 0; }; this.size = function(){ return items.length; }; this.print = function(){ for (let i=0; i<items.length; i++){ console.log(`${items[i].element} - ${items[i].priority}`); } }; } let priorityQueue = new PriorityQueue(); priorityQueue.enqueue("zhao", 2); priorityQueue.enqueue("wang", 1); priorityQueue.enqueue("zhang", 1); priorityQueue.enqueue("li", 2); priorityQueue.enqueue("liu", 3); priorityQueue.print(); //=> //打印出的结果(按优先级排序) wang - 1 zhang - 1 zhao - 2 li - 2 liu - 3
由浅入深,后续会有更多数据结构和算法的思考和整理。。