Javascript 数据结构学习笔记 (三)链表
什么是链表
- 当我们储存多个数据的时候,最常用的可能是数组,每一种语言都实现了数组。它非常方便让我们访问其中的元素。但它有个缺点,数组的大小往往是固定的,如果要从中间插入数据,需要耗费的成本会有点高,虽然js很多方法在表面上看起来很容易操作,但是在底层一样的情况。
- 链表存储有序的元素集合,但和数组不一样,链表的元素在内存中不是连续的。每个元素包含一个数据和一个指向下一个元素位置的指针。
- 链表有一个好处,就是在增删元素的时候不需要移动其他元素。
- 生活中如果有人插队,没人直至这个行为的话,就和链表很像了。
创建链表
在创建链表时,需要一个node辅助类,表示要加入的项,包含一个element属性和一个next属性,next属性用来指向下一个节点项的指针。
- 代码如下:
function LinkedList() {
var Node = function(element){
this.element = element;
this.next = null;
};
var length = 0;
var head = null;
this.append = function(element){
var node = new Node(element),
current;
if (head === null){ //列表的第一个节点
head = node;
} else {
current = head;
//循环列表至最后一项
while(current.next){
current = current.next;
}
//找到最后一项,将它的next赋给node
current.next = node;
}
length++; //更新列表长度
};
this.insert = function(position, element){
//检查越界值
if (position >= 0 && position <= length){
var node = new Node(element),
current = head,
previous,
index = 0;
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;
}
};
this.removeAt = function(position){
//检查越界值
if (position > -1 && position < length){
var current = head,
previous,
index = 0;
//移除第一项
if (position === 0){
head = current.next;
} else {
while (index++ < position){
previous = current;
current = current.next;
}
//将pre与cur的下一项连接起来,跳过cur,从而移除它
previous.next = current.next;
}
length--;
return current.element;
} else {
return null;
}
};
this.remove = function(element){
var index = this.indexOf(element);
return this.removeAt(index);
};
this.indexOf = function(element){
var current = head,
index = 0;
while (current) {
if (element === current.element) {
return index;
}
index++;
current = current.next;
}
return -1;
};
this.isEmpty = function() {
return length === 0;
};
this.size = function() {
return length;
};
this.getHead = function(){
return head;
};
this.toString = function(){
var current = head,
string = '';
while (current) {
string = current.element;
current = current.next;
}
return string;
};
this.print = function(){
console.log(this.toString());
};
}
- 我们来使用一下:
var list = new LinkedList();
list.append(15);
list.print();
console.log(list.indexOf(15));
list.append(10);
list.print();
console.log(list.indexOf(10));
list.append(13);
list.print();
console.log(list.indexOf(13));
console.log(list.indexOf(10));
list.append(11);
list.append(12);
list.print();
console.log(list.removeAt(1));
list.print()
console.log(list.removeAt(3));
list.print();
list.append(14);
list.print();
list.insert(0,16);
list.print();
list.insert(1,17);
list.print();
list.insert(list.size(),18);
list.print();
list.remove(16);
list.print();
list.remove(11);
list.print();
list.remove(18);
list.print();
结果如下:
双向链表
与链表不同的是,双向链表有一个指向上一个节点的指针,双向链表既可以从头到尾迭代,也可以反过来。
- 来看代码:
function DoublyLinkedList() {
var Node = function(element){
this.element = element;
this.next = null;
this.prev = null; //NEW
};
var length = 0;
var head = null;
var tail = null; //NEW
//其他方法
}
- 可以看到,双向链表与单链表的区别在于结构上多了一个可以指向上一个节点的指针。
循环列表
循环链表与单链表唯一的区别就是最后一个元素的next所指的不再是null,而是第一个元素