本系列全部文章:
第一篇文章:学习数据结构与算法之栈与队列
第二篇文章:学习数据结构与算法之链表
第三篇文章:学习数据结构与算法之集合
第四篇文章:学习数据结构与算法之字典和散列表
第五篇文章:学习数据结构与算法之二叉搜索树javascript
最近要准备校招,打开某网站准备开始刷题,发现算法题根本没法动手,因而以为这块须要恶补。(⊙v⊙)嗯,至少得先知道概念吧。因而翻出了机房里的这本《学习JavaScript数据结构与算法》开始学习程序员的基础知识。这本书用了我最熟悉的JS来实现各类数据结构和算法,并且书很薄,能够说是一本不错的入门教程。虽然我是个前端,可是计算机基础不能丢下。前端
栈能够理解为一种特殊的数组。遵循后进先出(LIFO)的原则,元素在栈顶添加和删除。生活中经常使用来比做栈的例子主要是一叠盘子或一堆书,可是我以为不够形象,由于盘子或书能够从中间被抽走。因此我通常把栈当作弹匣,想象一会儿弹被一个个推动弹匣中,好比下图:java
(图片来自谷歌搜索,侵删)git
声明一个构造函数:程序员
function Stack () { // 使用数组来保存栈元素 var items = [] }
为栈声明一些方法:github
用数组的push方法向数组末尾添加新元素,实现元素入栈算法
// 栈顶添加 this.push = function (element) { items.push(element) }
用数组的pop方法在数组末尾删除一个元素,并返回删除元素,实现元素出栈segmentfault
// 栈顶删除并返回删除元素 this.pop = function () { return items.pop() }
栈顶就是数组最后一个元素,使用Array[Array.length - 1]得到数组
// 返回栈顶元素 this.peek = function () { return items[items.length - 1] }
队列里面也用了这些方法,为避免重复,就先单独拿出来了。数据结构
// 栈是否为空 this.isEmpty = function () { return items.length === 0 } // 返回栈里的元素个数 this.size = function () { return items.length } // 清空栈 this.clear = function () { items = [] } // 打印栈 this.print = function () { console.log(items.toString()) }
书上的例子有将十进制转二进制,这里我把后面那个十进制转任意进制的代码贴出来。
十进制转二进制原理很简单:把十进制数不断除以2直到为0,而后把每次的余数拼接到一块儿就是二进制数。
转其余进制也是相似的方法,只不过是把除以2换成其余数而已。代码以下:
// 把十进制转成任何进制 function BaseConverter (decNumber, base) { var remStack = new Stack(), rem, binaryString = '', digits = '0123456789ABCDEF' // 判断十进制数是否为0,把余数推入栈中 while (decNumber > 0) { rem = Math.floor(decNumber % base) remStack.push(rem) decNumber = Math.floor(decNumber / base) } // 把栈中的元素拼接打印出来 while (!remStack.isEmpty()) { binaryString += digits[remStack.pop()] } // 返回转换的二进制数 return binaryString }
这里的decNumber是要转换的十进制数,base是要转换的进制,remStack是上面Stack的实例,在remStack中操做栈的方法。这里的digits是对打印出来的数作一个处理,好比十六进制的数余数会大于9,那么就要用A、B、C、D、E、sF来表示10~15。
栈的学习暂时就这样了,这里贴上代码地址,有兴趣的能够看看:
和栈很相似,只是原则不一样,队列是先进先出(FIFO),又称先来先服务。队列在头部删除元素,尾部添加元素。生活中队列的例子就是排队了,这也很容易理解。
一样声明构造函数:
function Queue () { var items = [] }
队列的方法:
其余的和栈同样。
用push方法推入元素
// 向队列尾部添加元素 this.enqueue = function (element) { items.push(element) }
用shift方法删除第一个数组元素,并返回删除的元素
// 删除队列头部的元素并返回删除元素 this.dequeue = function () { return items.shift() }
直接返回第一个数组元素
// 返回队列头部的元素 this.front = function () { return items[0] }
书上有讲优先队列和循环队列的应用,这里就简单讲一下优先队列的原理:
要实现优先队列有两种思路:一是将元素按正确的位置添加到队列中,而后元素正常在队列头部被删除;二是元素在队列尾部不按优先级正常入列,而后按优先级删除对应元素。
书上是按第一种思路实现的优先队列,这里限于篇幅就贴上代码地址:
接下来谈谈循环队列的应用——击鼓传花游戏
function hotPotato (nameList, num) { var queue = new Queue() // 参与者的名字入列 for (var i = 0; i < nameList.length; i++) { queue.enqueue(nameList[i]) } var eliminated = '' // 队列中的最后一我的为胜者 while (queue.size() > 1) { // 按设定的击鼓次数,每一个人都从队列头部出列转到队列尾部(模拟传花) for (var i = 0; i < num; i++) { queue.enqueue(queue.dequeue()) } // 到了规定次数后,在队列头部的人(至关于拿到花)被淘汰 eliminated = queue.dequeue() console.log(eliminated + '在击鼓传花游戏中被淘汰') } // 胜者出列并被返回 return queue.dequeue() }
其中nameList是参与游戏的名字列表,num是击鼓次数。
队列学习就暂时到这里,明天继续学习链表。