我们可以想象成。。。。
链表存储有序的元素集合,在内存中元素是不连续的。每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(也称指针或链接)组成。
单向链表
首先,我们需要创建一个数据结构用来作为链表的节点,节点包含value(用来存放数值)与next(用来存放下一个数据节点的地址)。
然后我们再实现一些方法。
方法
append(value):向列表尾插一个新的节点
insert(position, value):向列表的特定位置插入一个新的节点
remove(value):移除列表中元素值为value的项
indexOf(value):返回元素在列表中的索引值,如果没有该值,就返回-1
removeAt(position):从特定位置移除一项
isEmpty():判断链表是否为空
size():返回链表元素的个数
toString():输出元素的值
clear():清空链表
function linkedList(){
var Node = function(value){
this.value = value;
this.next = null;
}
var length = 0;
var head = null;
this.append = function(value){
var node = new Node(value),
temporary;
if(head){//有元素循环至尾部添加
temporary = head;
while(temporary.next){
temporary = temporary.next;
}
temporary.next = node;
}else{//没有元素直接添加
head = node;
}
length++;//长度+1
return;
}
this.insert = function(position,value){
//检查position是否越界
if(position < 0 || position > length){
console.log("position is error");
return;
}else{
var node = new Node(value),
temporary = head,
front;
if(!position){//为0时
head = node;
head.next = temporary;
}else{
while(position--){//循环到目标节点的前一个节点
front = temporary;
temporary = temporary.next;
}
front.next = node;
node.next = temporary;
}
length++;
return;
}
}
this.removeAt = function(position){
//检查position是否越界
if(position < 0 || position > length){
console.log("position is error");
return;
}else{
var temporary = head,
front;
if(!position){
head = head.next;
}else{
while(position--){//一直循环到待删结点的前一个节点
front = temporary;//当前节点给前一个节点
temporary = temporary.next;//当前节点后移
}
front.next = temporary.next;
}
length--;//记住长度要-1
return temporary.value;
}
}
this.remove = function(value){
var position = this.indexOf(value);
return this.removeAt(position);
}
this.indexOf = function(value){
var position = -1,
temporary = head;
while(temporary){
if(temporary.value == value){
return position;
}
temporary = temporary.next;
position++;
}
return -1;
}
this.isEmpty = function(){
return !length;
}
this.size = function(){
return length;
}
this.toString = function(){
var str = '',
temporary = head;
while(temporary){
str += temporary.value + ' ';
temporary = temporary.next;
}
return str;
}
this.print = function(){
console.log(this.toString());
}
this.getHead = function(){
return head;
}
this.clear = function(){
head = null;
length = 0;
}
}
/*使用链表*/
var list = new linkedList();
list.append(5);
list.append(4);
list.insert(0,3);
list.append(2);
list.append(1);
list.print();
list.remove(0);
list.print();
list.removeAt(0);
list.print();
console.log(list.size());
list.clear();
console.log(list.isEmpty());
控制台输出:
双向链表
双向链表与单向链表的区别在于其节点存储紧前节点,紧后节点,数值。
如图:
双向链表删除(三种情况)
头删
尾删
中间删
双向链表插入(三种情况)
头插
尾插
中间插
function DoulyLinkedList(){
//定义节点并初始化
var Node = function(value){
this.value = value;
this.prev = null;
this.next = null;
}
var head = null,
tail = null,
length = 0;
//定义方法
this.append = function(value){
var node = new Node(value),
temporary;
if(head){//有元素循环至尾部添加
tail.next = node;
node.prev = tail;
tail = node;
}else{//没有元素直接添加
head = node;
tail = node;
}
length++;//长度+1
return;
}
this.insert = function(position,value){//在任意节点插入元素
//判断position是否正确
if(position < 0 || position > length){
return false;
}else{
var node = new Node(value),
front,
temporary = head;
if(!position){//头插
if(!head){
head = node;
tail = node;
}else{
node.next = temporary;
temporary.prev = node;
head = node;
}
}else if(position-length){//中间插
while(position--){
front = temporary;
temporary = temporary.next;
}
front.next = node;
node.prev = front;
node.next = temporary;
temporary.prev = node;
}else{//尾插
tail.next = node;
node.prev = tail;
tail = node;
}
length++;
return true;
}
}
this.removeAt = function(position){
//检查是否越界
if(position < 0 || position > length){
console.log("postion is crossing!");
return false;
}else{
var front,
temporary = head;
if(!position){//删除第一个
head = head.next;
//注意,这里需要判断链表中元素是否只有一个
if(length === 1){
tail = null;
}else{
head.prev = null;
}
}else if(position-length){//删除中间元素
while(position--){//找出待删结点temporary以及紧前节点front
front = temporary;
temporary = temporary.next;
}
//连接两个节点
front.next = temporary.next;
temporary.next.prev = front;
}else{//删除最后一个
tail = tail.prev;
tail.next = null;
}
length--;//长度-1
return temporary.value;
}
}
this.remove = function(value){
var position = this.indexOf(value);
return this.removeAt(position);
}
this.indexOf = function(value){
var position = -1,
temporary = head;
while(temporary){
if(temporary.value == value){
return position;
}
temporary = temporary.next;
position++;
}
return -1;
}
this.isEmpty = function(){
return !length;
}
this.size = function(){
return length;
}
this.toString = function(){
var str = '',
temporary = head;
while(temporary){
str += temporary.value + ' ';
temporary = temporary.next;
}
return str;
}
this.print = function(){
console.log(this.toString());
}
this.getHead = function(){
return head;
}
this.clear = function(){
head = null;
length = 0;
}
}
//使用双向链表,此处代码与单向链表使用过程相同,所以结果显示一项
var list = new DoulyLinkedList();
list.append(5);
list.append(4);
list.insert(0,3);
list.append(2);
list.append(1);
list.print();
list.remove(0);
list.print();
list.removeAt(0);
list.print();
console.log(list.size());
list.clear();
console.log(list.isEmpty());
循环链表
最后一个元素指向下一个元素的指针(tail.next)不是引用null, 而是指向第一个元素(head),第一个元素指向前一个元素的指针不是引用null,而是最后一个元素。
循环链表与双向链表的创建代码非常相似,自行发挥。