本系列全部文章:
第一篇文章:学习数据结构与算法之栈与队列
第二篇文章:学习数据结构与算法之链表
第三篇文章:学习数据结构与算法之集合
第四篇文章:学习数据结构与算法之字典和散列表
第五篇文章:学习数据结构与算法之二叉搜索树javascript
链表一种常见的数据结构,能够存储有序的元素集合。不一样于数组,链表中元素在内存中不是连续放置,同时每个链表元素中除了自己的节点还保存了指向下一个元素的引用,这些特色使得链表元素在动态增长和删除时没必要移动其余元素,可是访问链表元素时必须从起点开始迭代列表直到找到目标元素。java
下面是两种链表的JavaScript实现:node
下图就是一个典型的单向链表,这里注意:通常链表中的第一个元素会有一个head指针指向它,这也是咱们操做链表必不可少的一环。git
(图片来源谷歌,侵删)github
与以前实现栈和队列同样,这里先声明一个构造函数:算法
function LinkedList () { var Node = function (element) { this.element = element // 保存指向下个元素的引用,默认为null this.next = null } // 链表长度 var length = 0 // head保存指向第一个元素的引用 var head = null }
链表须要实现如下方法:segmentfault
相似数组的push方法,可是只能添加一个元素。实现方法的时候分两种状况考虑:1. 链表为空时添加第一个元素;2. 链表不为空时在尾部添加元素数组
this.append = function (element) { var node = new Node(element), current if (head === null) { // 当链表为空时 // 将head指向新增的元素 head = node } else { // 链表不为空 // 使用一个current变量从head开始迭代链表 current = head // 迭代链表,直到找到最后一项 while (current.next) { current = current.next } // 找到最后一项,将其next赋为node,创建连接 current.next = node } // 更新列表长度 length++ }
在链表中特定位置移除元素,实现时也须要考虑两种状况:1. 移除第一个元素;2. 移除其余元素(包括最后一个)数据结构
this.removeAt = function (position) { // 判断位置是否越界 if (position > -1 && position < length) { var current = head, previous, index = 0 // 若是删除了第一个元素,把head指向下一个元素就好了 if (position === 0) { head = current.next } else { // 根据输入的位置查找要删除的元素 while (index++ < position) { previous = current current = current.next } // 将上一个元素的next指向current的下一项,跳过current,实现移除current previous.next = current.next } // 更新列表长度 length-- // 返回删除的元素 return current.element } else { return null } }
与removeAt相似的实现,你们能够先不看源码,本身按着removeAt的思路实现一遍app
this.insert = function (position, element) { // 检查位置是否越界 if (position >= 0 && position <= length) { var node = new Node(element), index = 0, previous, current = head // 在第一个位置添加 if (position === 0) { node.next = current head = node } else { while (index++ < position) { previous = current current = current.next } node.next = current previous.next = node } // 更新列表长度 length++ return true } else { return false } }
根据元素查找在链表中的位置,没找到就返回-1
this.indexOf = function (element) { var current = head, index = 0 while (current) { if (element === current.element) { return index } index++ current = current.next } return -1 }
// 返回全部元素的值转成字符串 this.toString = function () { var current = head, string = '' while (current) { string += current.element current = current.next } return string } // 移除特定元素 this.remove = function (element) { var index = this.indexOf(element) return this.removeAt(index) } // 判断链表是否为空 this.isEmpty = function () { return length === 0 } // 返回链表长度 this.size = function () { return length } // 返回第一个元素 this.getHead = function () { return head }
以上是单向链表的实现,有兴趣的能够下载源码来看:
双向链表和单向链表的区别就是每个元素是双向的,一个元素中包含两个引用:一个指向前一个元素;一个指向下一个元素。除此以外,双向链表还有一个指向最后一个元素的tail指针,这使得双向链表能够从头尾两个方向迭代链表,所以更加灵活。以下图:
(图片来源谷歌搜索,侵删)
同单向链表同样,先声明一个构造函数
function DoubleLinkedList () { var Node = function (element) { this.element = element this.prev = null // 新增了一个指向前一个元素的引用 this.next = null } var length = 0 var head = null var tail = null //新增了tail指向最后一个元素 }
双向链表须要有如下方法:
同单向链表相似,只不过状况更复杂了,你不只须要额外考虑在第一个元素的位置插入新元素,还要考虑在最后一个元素以后插入新元素的状况。此外若是在第一个元素插入时,链表为空的状况也须要考虑。
this.insert = function (position, element) { // 检查是否越界 if (position >= 0 && position <= length) { var node = new Node(element), current = head, previous, index = 0 if (position === 0) { // 第一个元素的位置插入 // 若是链表为空 if (!head) { head = node tail = node } else { node.next = current current.prev = node head = node } } else if (position === length) { // 在最后一个元素以后插入 current = tail node.prev = current current.next = node tail = node } else { // 在中间插入 while (index++ < position) { previous = current current = current.next } node.next = current previous.next = node current.prev = node node.prev = previous } length++ return true } else { return false } }
与insert相似,这里很少解释直接贴代码
this.removeAt = function (position) { // 检查是否越界 if (position > -1 && position < length) { var current = head, previous, index = 0 if (position === 0) { // 第一个元素 head = current.next // 若是只有一个元素 if (length === 1) { tail = null } else { head.prev = null } } else if (position === length - 1) { // 最后一个元素 current = tail tail = current.prev tail.next = null } else { while (index++ < position) { previous = current current = current.next } previous.next = current.next current.next.prev = previous } length-- return current.element } else { return null } }
和单向链表的同样,只不过多了tail有一些不一样
this.append = function (element) { var node = new Node(element), current = tail if (head === null) { head = node tail = node } else { node.prev = current current.next = node tail = node } length++ }
其余的代码都很简单,我就放上源代码地址,你们自行查阅吧~
一开始写的时候有点不习惯,可是实现了一两个方法以后发现不少思路是相似的,因而触类旁通地写出来,而后跟书上对比以后,发现仍是有点差距的。
你们有兴趣也动手去实践一下再对比源码,能够认识到本身有哪些地方不足。
链表暂时就这样了,明天继续攻克集合!