链表
数组不总是组织数据的最佳数据结构,原因如下。在很多编程语言中,数组的长度是固定
的,所以当数组已被数据填满时,再要加入新的元素就会非常困难。在数组中,添加和删
除元素也很麻烦,因为需要将数组中的其他元素向前或向后平移,以反映数组刚刚进行了
添加或删除操作。然而,JavaScript 的数组并不存在上述问题,因为使用 split() 方法不需
要再访问数组中的其他元素了- 。
JavaScript 中数组的主要问题是,它们被实现成了对象,与其他语言(比如 C++ 和 Java)
的数组相比,效率很低
链表是由一组节点组成的集合,每个节点都使用一个对象的引用指向它的后继,指向另一个节点的引用叫做链。
Node类:用来表示节点
linkedList:提供插入、删除、显示元素的方法,以及一些其他方法
Node类:包含两个属性:
element:当前数据
next:用来指向下个节点的链接
单链表的实现
var Node = function(element){
this.element = element;
this.next = null;
}
var linkedList = function(){
this.head = new Node('head');
}
var linkedList.prototype = {
//查找一个元素
find:function(item){
//遍历整个链表,获取头节点
var currentNode = this.head;
while(currentNode.element != item && currentNode.next != null){
currentNode = currentNode.next;
}
return currentNode;
}
//查找一个元素的前一个元素
findPrevious:function(item){
var currentNode = this.head;
while(currentNode.next.element != item && currentNode.next!=null){
currentNode = current.next;
}
return curretnNode;
}
//添加一个元素
//element要插入的元素
//item 要插入在什么元素的后面
//需要找到前一个元素,并且判断前一个元素是否为空
//需要一个辅助函数find
insert:function(element,item){
var nowNode = new Node(element);
var currentNode = this.find(item);
nowNode.next = currentNode.next;
currentNode.next = nowNode;
}
//删除一个元素
remove:function(item){
var preNode =this.findPrevious(item);
if(preNode!=null){
preNode.next = preNode.next.next;
}
}
//显示链表的元素
display:function(){
var str = '';
var currentNode = this.head;
while(currentNode.next != null){
str+=currentNode.next.element;
currentNode = currentNode.next;
}
return str;
}
}
双向链表的实现
通过给 Node 对象增加一个属性,该属性存储指向前驱节点的链接。此时向链表插入一个节点需要更多的工作,我们需要指出该节点正确的前驱和后继。但是在从链表中删除节点时,效率提高了,不需要再查找待删除节点的前驱节点了。
删除节点: 首先需要在链表中找出存储待删除数据的节点,然后设置该节点前驱的 next 属性,使其指向待删除节点的后继;设置该节点后继的 previous 属性,使其指向待删除节点的前驱。
var Node = function (element) {
this.next = null;
this.element = element;
this.previous = null;
}
var linkedList = function () {
this.head = new Node('head');
}
linkedList.prototype = {
//查找当前节点的父节点
find: function (item) {
var currentNode = this.head;
while (currentNode.element != item) {
currentNode = currentNode.next;
}
return currentNode;
},
//返回最后一个节点
findLast: function () {
var currentNode = this.head;
while (currentNode.next != null) {
currentNode = currentNode.next;
}
return currentNode;
},
//添加一个元素
insert: function (element, item) {
var nowNode = new Node(element);
var currentNode = this.find(item);
nowNode.next = currentNode.next;
nowNode.previous = currentNode;
currentNode.next = nowNode;
},
//删除一个元素
remove: function (item) {
var currentNode = this.find(item);
if (currentNode.next != null) {
currentNode.next.previous = currentNode.previous;
currentNode.previous.next = currentNode.next
currentNode.next = null;
currentNode.previous = null;
}
},
//展示整个列表
display: function () {
var curNode = this.head;
var curStr = ''
while (curNode.next != null) {
curStr += curNode.next.element;
curNode = curNode.next;
}
return curStr;
},
//反向展示
displayReverse: function () {
var endNode = this.findLast();
var endStr = '';
while (endNode.previous != null) {
endStr += endNode.element;
endNode = endNode.previous;
}
return endStr;
}
}
循环链表
如果你希望可以从后向前遍历链表,但是又不想付出额外代价来创建一个双向链表,那么
就需要使用循环链表。从循环链表的尾节点向后移动,就等于从后向前遍历链表。
创建循环链表,只需要修改 linkedList 类的原型函数: