时隔一个多月,终于又开始写关于js的文章啦,本次我写的是如何使用JavaScript实现链表。
在这我就不再介绍链表的定义了,直接开始内容部分。
老规矩,先看看链表的方法。
定义链表的方法
在尾部添加新的项
方法:append()
语法:LinkedList.append(element1,element2…elementX)
参数:必需。element不能为空。
描述:添加一个或几个新项在链表尾部,返回添加的项的内容
指定位置插入新的项
方法:insert()
语法:LinkedList.insert(index,element)
参数:必需。index为整数,element内容不能为空。
描述:向链表的特定位置插入一个新的项,返回添加的项的内容
返回元素在链表中的索引
方法:indexOf()
语法:LinkedList.indexOf(element)
参数:必需。element不能为空。
描述:返回元素在链表中首次出现的索引。如果不存在索引的元素,则返回-1
移除指定位置的项
方法:removeAt()
语法:LinkedList.removeAt(index)
参数:必需。index为整数。
描述:删除指定项并返回项的内容。
移除包含指定元素的项
方法:remove()
语法:LinkedList.remove(element)
参数:必需。element不能为空。
描述:删除第一个包含指定元素的项并返回项的内容。如果不存在包含指定元素的项,则返回错误提示。
链表是否为空
方法:isEmpty()
语法:LinkedList.isEmpty()
参数:无
描述:如果链表中没有任何项则返回true,否则返回false
返回链表的长度
方法:size()
语法:LinkedList.size()
参数:无
描述:返回链表中项的个数
清空链表
方法:clear()
语法:LinkedList.clear()
参数:无
描述:移除链表中的所有项
输出链表
方法:show()
语法:LinkedList.show()
参数:无
描述:返回链表中的所有项
单链表代码实现
function LinkedList() {
var Node = function (element) {
this.element = element;
this.next = null;
}, //节点
length = 0, //长度
head = null; //头部
//动态原型模式创建原型方法
if (typeof LinkedList.prototype.append != 'function') {
/*
在尾部添加新的项。
*/
LinkedList.prototype.append = function () {
var arr = arguments,
node,
current;
//添加内容不能为空
if (!arr) {
return false;
} else {
node = new Node(arr[0]);
//如果是首节点,直接建立连接
if (head == null) {
head = node;
} else {
//否则从首节点开始
current = head;
//循环链表,直到找到最后一个节点
while (current.next) {
current = current.next;
}
//尾节点指向新的节点
current.next = node;
}
length++; //更新长度
//循环添加节点
for (var i = 1; i < arr.length; i++) {
LinkedList.prototype.append(arr[i]);
}
}
}
/*
在指定位置插入新的项
*/
LinkedList.prototype.insert = function (index, element) {
if (!/^[0-9]*[1-9][0-9]*$/.test(index)) {
return new TypeError("插入的位置必须为正整数")
}
if (!element) {
return new TypeError("插入内容不能为空")
}
//检查index越界情况
if (index >= 0 && index <= length) {
var node = new Node(element),
current = head, //当前节点
previous, //前一个节点
position = 0; //当前位置
if (index === 0) {
//在第一个位置插入
node.next = current; //新的节点指向原首节点
head = node; //切断head与原首节点的联系,新节点成为首节点
} else {
//找到要插入位置
while (position++ < index) {
previous = current; //前一个节点改为当前节点
current = current.next; //当前节点更改为下个节点
}
//完成插入
node.next = current;
previous.next = node;
}
//更新长度
length++;
//插入成功返回element
return element;
} else {
return new Error("index越界,不能正常插入!index必须为正整数,且不能超出链表长度");
}
}
/*
返回元素在链表中的索引
*/
LinkedList.prototype.indexOf = function (element) {
var current = head, //当前节点
index = 0; //记录当前位置
//循环比较项的元素
while (current.element !== element && current.next !== null) {
current = current.next;
index++;
}
//如果找到元素,返回索引,否则返回-1
if (current.element === element) {
return index;
} else {
return -1;
}
}
/*
移除指定位置的项
*/
LinkedList.prototype.removeAt = function (index) {
if (!/^[0-9]*[0-9][0-9]*$/.test(index) || index > length - 1) {
return new Error("删除的位置必须为零或正整数且不能超过链表长度")
}
if(head==null){
return new Error("链表长度为空!无法删除");
}
var current = head, //当前的节点
position = 0, //当前的位置
previous, //前一个节点
content; //节点内容
//如果是首节点,则把下一个节点当成首节点
if (index === 0) {
content=current.element;
current = current.next;
head = current;
} else {
//跳转到需要删除的位置
while (position++ < index) {
previous = current; //当前节点变成上个节点
current = current.next; //下个节点变成当前节点
}
previous.next = current.next; //跳过当前节点
content = current.element; //获取节点内容
current = null; //删除当前节点
}
length--; //更新长度
return content; //返回删除的节点内容
}
/*
移除包含指定元素的项
*/
LinkedList.prototype.remove = function (element) {
var index = LinkedList.prototype.indexOf(element);
if (index === -1) {
return new Error("链表中不包含指定元素的项");
}
return LinkedList.prototype.removeAt(index)
}
/*
链表是否为空
*/
LinkedList.prototype.isEmpty = function () {
return length === 0;
}
/*
链表的长度
*/
LinkedList.prototype.size = function () {
return length;
}
/*
清空链表
*/
LinkedList.prototype.clear = function () {
head=null;//删除首节点与后续节点的联系。后续节点会被JavaScript垃圾回收机制回收
length=0;//更新长度
}
/*
输出链表
*/
LinkedList.prototype.show = function () {
var current = head, //首节点
arr = []; //用于存储项的内容的数组
while (current) {
arr.push(current.element);
current = current.next;
}
return head===null?"链表为空":arr;
}
}
}
测试代码
var lis = new LinkedList();
lis.append(1, 2, 3, 4, 5, 6);
console.log(lis.show());//[1, 2, 3, 4, 5, 6]
lis.insert(3, 3.5);
console.log(lis.show());//[1, 2, 3, 3.5, 4, 5, 6]
console.log(lis.indexOf(3.5));//3
console.log(lis.remove(3.5));//3.5
console.log(lis.show());//[1, 2, 3, 4, 5, 6]
console.log(lis.removeAt(5));//6
console.log(lis.isEmpty());//false
console.log(lis.size());//5
console.log(lis.clear());//undefined
console.log(lis.show());//链表为空
双链表代码实现
双链表在原来的单链表基础上增加了一个前节点(previous)的指向,同时多出了一个尾节点(tail)的概念。由于增加了尾节点和前节点指向,append方法可以直接在尾部增加新的项、insert方法可以选择向前搜索或向后搜索来查找要插入的位置,显著的提升运行的效率。
function DoublyLinkedList() {
var Node = function (element) {
this.element = element;
this.previous = null;
this.next = null;
}, //节点
length = 0, //长度
head = null, //头部
tail = null; //尾部
//动态原型模式创建原型方法
if (typeof DoublyLinkedList.prototype.append != 'function') {
/*
在尾部添加新的项
*/
DoublyLinkedList.prototype.append = function () {
var arr = arguments,
node, //新的节点
current; //当前节点
//添加内容不能为空
if (!arr) {
return false;
} else {
node = new Node(arr[0]);
//如果是首节点,直接建立连接
if (head == null) {
//首节点和尾节点都是一个节点
head = node;
tail = node;
} else {
//否则从尾节点开始
current = tail;
current.next = node; //尾节点的下个节点指向新的节点
node.previous = current; //新节点的前节点指向原来的尾节点
tail = node; //尾节点更改新的节点
}
length++; //更新长度
//循环添加节点
for (var i = 1; i < arr.length; i++) {
DoublyLinkedList.prototype.append(arr[i]);
}
}
}
/*
在指定位置插入新的项
*/
DoublyLinkedList.prototype.insert = function (index, element) {
if (!/^[0-9]*[1-9][0-9]*$/.test(index)) {
return new TypeError("插入的位置必须为正整数");
}
if (!element) {
return new TypeError("插入内容不能为空")
}
var current = head, //当前节点
previous, //上个节点
node = new Node(element); //新的节点
//如果链表为空
if (!head) {
//首节点和尾节点都是一个节点
head = node;
tail = node;
} else {
//链表不为空时,插入位置分成三种情况,一是首位插入,二是尾部插入,三是中间插入
if (index === 0) {
node.previous = null; //新节点的前节点为空
current.previous = node; //原首节点的前节点设为新节点
node.next = current; //新节点的下个节点为原首节点
head = node; //新节点成为首节点
} else if (index === length - 1) {
current = tail; //当前节点换成尾节点
previous = current.previous; //获尾二节点(倒数第二个节点)
previous.next = node; //原尾二节点的下个节点指向新节点
node.next = current; //新节点的下个节点指向尾节点
node.previous = previous; //新节点的前节点指向原尾二节点
current.previous = node; //尾节点的前节点指向新的节点
} else if (index > 0 && index < length - 1) {
var direction = (index - length / 2) <= 0 ? "front" : "later", //判断方向
position, //当前位置
previous, //前一个节点
current; //当前节点
//从前到后查询节点
if (direction === "front") {
position = 0,
current = head;
while (position++ < index) {
current = current.next;
}
} else {
//从后到前查询节点
position = length - 1,
current = tail;
while (position > index) {
current = current.previous;
position--;
}
}
//完成节点插入
previous = current.previous;
previous.next = node;
node.previous = previous;
node.next = current;
current.previous = node;
}
}
length++; //更新长度
return node.element;
}
/*
返回元素在链表中的索引
*/
DoublyLinkedList.prototype.indexOf = function (element) {
var current = head, //当前节点
index = 0; //记录当前位置
if (!element) {
return new TypeError("插入内容不能为空")
}
//循环比较项的元素
while (current.element !== element && current.next !== null) {
current = current.next;
index++;
}
//如果找到元素,返回索引,否则返回-1
if (current.element === element) {
return index;
} else {
return -1;
}
}
/*
移除指定位置的项
*/
DoublyLinkedList.prototype.removeAt = function (index) {
if (!/^[0-9]*[0-9][0-9]*$/.test(index) || index > length - 1) {
return new Error("删除的位置必须为零或正整数且不能超过链表长度")
}
if (head == null) {
return new Error("链表长度为空!无法删除");
}
var current, //当前的节点
position = 0, //当前的位置
previous, //前一个节点
content, //节点内容
direction = (index - length / 2) <= 0 ? "front" : "later"; //判断方向
//位置的移除分成三种情况,一是首节点,二是尾节点,三是中间
if (index === 0) {
content = head.element; //获取首节点内容
current = head.next; //当前节点为首节点的下个节点
current.previous = null; //当前节点的上个几点为空
head = current; //当前节点成为新的首节点
} else if (index === length - 1) {
content = tail.element; //获取尾节点内容
current = tail; //当前节点为尾节点
previous = current.previous; //获取尾节点的前一个节点
current = null; //清空尾节点内容
tail = previous; //设置新的尾节点
tail.previous = null; //尾节点的下个节点为空
} else if (index > 0 && index < length - 1) {
//从前到后查询节点
if (direction === "front") {
position = 0,
current = head;
while (position++ < index) {
current = current.next;
}
} else {
//从后到前查询节点
position = length - 1,
current = tail;
while (position > index) {
current = current.previous;
position--;
}
}
//完成删除
previous = current.previous;
previous.next = current.next;
current.next.previous = previous;
current = null;
}
length--; //更新长度
return content; //返回删除的节点内容
}
/*
移除包含指定元素的项
*/
DoublyLinkedList.prototype.remove = function (element) {
//移除第一个包含指定元素的项,说明智能从前往后查询
var current = head;//当前节点
if (!element) {
return new TypeError("插入内容不能为空")
}
if (current != null) {
//循环比较项的元素
while (current.element !== element && current.next !== null) {
current = current.next;
}
//找到指定元素之后要分成三种情况,一是头部删除,二是尾部删除,三是中间删除
if(current.previous==null){
current.next.previous=null;
content=current.element;
current=null;
}else if(current.next==null){
current.previous.next=null;
current=null;
}else{
current.previous.next=current.next;
current.next.previous=current.previous;
content=current.element;
current=null;
}
return content;
length--;
} else {
return new Error("链表为空,无法删除")
}
}
/*
链表是否为空
*/
DoublyLinkedList.prototype.isEmpty = function () {
return length === 0;
}
/*
链表的长度
*/
DoublyLinkedList.prototype.size = function () {
return length;
}
/*
清空链表
手动释放和自动释放内存并无太大差异。并且在测试中手动清空内存会显著增加运行时间。
*/
DoublyLinkedList.prototype.clear = function () {
//手动释放所有内存
// var current=tail,//最后一个节点
// previous;//上个节点
// while(length-->0){
// previous=current.previous;
// current=null;
// current=previous;
// }
// head=null;
// tail=null;
// length = 0; //更新长度
//自动释放所有内存
head = null;
tail = null;
length = 0;
}
/*
输出链表
*/
DoublyLinkedList.prototype.show = function () {
var current = head, //首节点
arr = []; //用于存储项的内容的数组
while (current) {
arr.push(current.element);
current = current.next;
}
return head === null ? "链表为空" : arr;
}
}
}
测试代码
var lis = new DoublyLinkedList();
lis.append(1, 2, 3, 4, 5, 6);
console.log(lis.show());//[1, 2, 3, 4, 5, 6]
lis.insert(3, 3.5);
console.log(lis.show());//[1, 2, 3, 3.5, 4, 5, 6]
console.log(lis.indexOf(3.5));//3
console.log(lis.remove(3.5));//3.5
console.log(lis.show());//[1, 2, 3, 4, 5, 6]
console.log(lis.removeAt(5));//6
console.log(lis.isEmpty());//false
console.log(lis.size());//5
console.log(lis.clear());//undefined
console.log(lis.show());//链表为空
参考资料:
《学习JavaScript数据结构与算法》