数据结构与算法之链表
链表
JS中数组被实现成了对象,对比其他语言的数组效率很低。因此可以考虑使用链表来替代。除了对数据的随机访问,链表几乎可以用在任何使用以为数组的情况中。如果需要随机访问,数组仍然是更好的选择。
单向链表
链表是由一组节点组成的集合。 每个节点都使用一个对象的引用指向它的后继。 指向另一
个节点的引用叫做链。
遍历链表, 就是跟着链接, 从链表的首元素一直走到尾元素,下面最前面的节点叫做头节点,在最后面的尾元素指向一个null节点。
// 节点类
function Node(element){
this.element = element;
this.next = null;
}
// LinkedList类,建立一个新的链表
function LList(){
this.head = new Node("head");
}
LList.prototype = {
constructor: LList,
find(element){
var node = this.head;
while(node.element != element){
if(node.next){
node = node.next;
}else{
return null;
}
}
return node;
},
insert(newElement, item){ //在item后面插入新元素
var current = this.find(item);
var newNode = new Node(newElement);
newNode.next = current.next;
current.next = newNode;
},
remove(item){
var preNode = this.findPrevious(item);
if(preNode.next !== null){
preNode.next = preNode.next.next;
}
},
display(){ //不显示头节点
var current = this.head;
while(current.next !== null){
console.log(current.next.element);
current = current.next;
}
},
findPrevious(item){ //用来查找该值节点的前一个节点
var current = this.head;
while(current.next.element !== item){
if(current.next !== null){
current = current.next;
}else{
return null;
}
}
return current;
}
}
双向链表
// 节点类
function Node(element){
this.element = element;
this.next = null;
this.previous = null;
}
// LinkedList类,建立一个新的链表
function LList(){
this.head = new Node("head");
}
LList.prototype = {
constructor: LList,
find(element){
var node = this.head;
while(node.element != element){
if(node.next){
node = node.next;
}else{
return null;
}
}
return node;
},
insert(newElement, item){ //在item后面插入新元素
var current = this.find(item);
var newNode = new Node(newElement);
newNode.next = current.next;
newNode.previous = current;
current.next = newNode;
},
remove(item){ //双向链表删除节点效率更高了,因为不需要再查找前驱节点了
var removeNode = this.find(item);
if(removeNode){
removeNode.previous.next = removeNode.next;
removeNode.next.previous = removeNode.previous;
removeNode.next = null;
removeNode.previous = null;
}
},
display(){ //不显示头节点
var current = this.head;
while(current.next !== null){
console.log(current.next.element);
current = current.next;
}
},
findLast(){ //查找链表的最后一项
var current = this.head;
while(current.next !== null){
current = current.next;
}
return current;
},
dispReverse(){
var current = this.findLast();
while(current.previous !== null){
console.log(current.element);
current = current.previous;
}
},
}
循环链表
循环链表和单向链表相似, 节点类型都是一样的。 唯一的区别是, 在创建循环链表时, 让其头节点的 next 属性指向它本身,这样插入节点都会使得链表的尾节点都指向头节点,形成一个循环链表。
如果你希望可以从后向前遍历链表, 但是又不想付出额外代价来创建一个双向链表, 那么就需要使用循环链表。 从循环链表的尾节点向后移动, 就等于从后向前遍历链表。
避免无限循环,需要对方法进行修改:
// 节点类
function Node(element){
this.element = element;
this.next = null;
}
// LinkedList类,建立一个新的链表
function LList(){
this.head = new Node("head");
this.head.next = this.head;
}
LList.prototype = {
constructor: LList,
find(element){
var node = this.head;
while(node.element != element){
if(node.next === this.head){
return null;
}
node = node.next;
}
return node;
},
insert(newElement, item){ //在item后面插入新元素
var current = this.find(item);
var newNode = new Node(newElement);
newNode.next = current.next;
current.next = newNode;
},
remove(item){
var preNode = this.findPrevious(item);
preNode.next = preNode.next.next;
},
display(){ //不显示头节点
var current = this.head;
while(current.next !== this.head){
console.log(current.next.element);
current = current.next;
}
},
findPrevious(item){ //用来查找该值节点的前一个节点
var current = this.head;
while(current.next.element !== item){
if(current.next === this.head){
return null;
}
current = current.next;
}
return current;
}
}