JavaScript数据结构(双向链表)
双向链表&单向链表
单向链表
:只能从一端向另一端遍历
双向链表
:既可以从头部向尾部开始遍历,也可以熊尾部向头部开始遍历。
一个head指针
指向第一个节点,一个tail指针
指向最后一个节点。
当没有节点时,head指针和tail指针都指向NULL。
双向链表常见的操作
append(element)
向链表尾部追加一个新元素。insert(position, element)
向链表的指定位置插入一个新元素。getElement(position)
获取指定位置的元素。indexOf(element)
返回元素在链表中的索引。如果链表中没有该元素就返回 -1。update(position, element)
修改指定位置上的元素。removeAt(position)
从链表中的删除指定位置的元素。remove(element)
从链表删除指定的元素。isEmpty()
如果链表中不包含任何元素,返回trun
,如果链表长度大于 0 则返回false
。size()
返回链表包含的元素个数,与数组的length
属性类似。toString()
由于链表项使用了 Node 类,就需要重写继承自 JavaScript 对象默认的toString
方法,让其只输出元素的值。forwardString()
返回正向遍历节点字符串形式。backwordString()
返回反向遍历的节点的字符串形式。getHeadNode()
获取第一个节点getTailNode()
获取最后一个节点
JavaScript代码双向链表的封装
class Node {
constructor(data) {
this.data = data;
this.prev = null;
this.next = null;
}
}
class DoublyNodeList {
constructor() {
this.head = null;
this.length = 0;
this.tail = null;
}
append(element) {
const node = new Node(element);
if (this.length == 0) {
this.head = node;
this.tail = node;
} else {
node.prev = this.tail;
node.prev.next = node;
this.tail = node;
}
this.length += 1;
}
insert(position, element) {
const node = new Node(element);
// 越界判断
if (position < 0 || position > this.length) {
return false;
}
if (position == 0) {
node.next = this.head;
node.next.prev = node;
this.head = node;
} else if (position == this.length) {
this.tail.next = node;
node.prev = this.tail;
this.tail = node;
} else {
//插入的时候判断position的位置离第一个节点近还是最后一个节点近
//离第一个节点近从前往后开始遍历查找
//离最后一个节点近从后往前开始遍历查找
//同下方代码,未作抽离
const flag = Math.ceil((this.length - 1) / 2);
if (flag > position) {
let current = this.head;
let index = 0;
while (index < position) {
index += 1;
current = current.next;
}
node.prev = current.prev;
node.prev.next = node;
node.next = current;
current.prev = node;
} else {
let index = this.length - 1;
let current = this.tail;
while (index > position) {
index -= 1;
current = current.prev;
}
node.prev = current.prev;
node.prev.next = node;
node.next = current;
current.prev = node;
}
}
this.length += 1;
}
getData(position) {
if (position < 0 | position >= this.length) {
return -1;
}
const flag = Math.ceil((this.length - 1) / 2);
if (flag > position) {
let index = 0;
let current = this.head;
while (index < position) {
index += 1;
current = current.next;
}
return current.data;
} else {
let index = this.length - 1;
let current = this.tail;
while (index > position) {
index -= 1;
current = current.prev;
}
return current.data;
}
}
indexOf(data) {
let current = this.head;
let index = 0;
while (current) {
if (current.data == data) {
return index;
}
index += 1;
current = current.next;
}
return -1;
}
update(position, data) {
if (position < 0 || position >= this.length) {
return false;
}
const flag = Math.ceil((this.length - 1) / 2);
if (flag > position) {
let index = 0;
let current = this.head;
while (index < position) {
index += 1;
current = current.next;
}
current.data = data;
return current.data;
} else {
let index = this.length - 1;
let current = this.tail;
while (index > position) {
index -= 1;
current = current.prev;
}
current.data = data;
return current.data;
}
}
removeAt(position) {
if (position < 0 || position >= this.length) {
return -1;
}
let current = null;
if (this.length == 1) {
current = this.head;
this.head = null;
this.tail = null;
this.length -= 1;
return current.data;
} else {
if (position == 0) {
current = this.head;
this.head = this.head.next;
this.head.prev = null;
this.length -= 1;
return current.data;
} else if (position == this.length - 1) {
current = this.tail;
this.tail = this.tail.prev;
this.tail.next = null;
this.length -= 1;
return current.data;
} else {
const flag = Math.ceil((this.length - 1) / 2);
if (flag > position) {
let index = 0;
current = this.head;
while (index < position) {
index += 1;
current = current.next;
}
} else {
let index = this.length - 1;
current = this.tail;
while (index > position) {
index -= 1;
current = current.prev;
}
}
current.next.prev = current.prev;
current.prev.next = current.next;
this.length -= 1;
return current.data;
}
}
}
remove(element) {
const index = this.indexOf(element);
return this.removeAt(index);
}
isEmpty() {
return this.length === 0;
}
size() {
return this.length;
}
toString() {
let current = this.head;
let result = '';
while (current) {
result += current.data + ' ';
current = current.next;
}
return result;
}
forwardString() {
return this.toString()
}
backwordString() {
let current = this.tail;
let result = '';
while (current) {
result += current.data + ' ';
current = current.prev;
}
return result;
}
getHeadNode() {
return this.head;
}
getTailNode() {
return this.tail;
}
}
代码测试
const doublyNodeList = new DoublyNodeList();
doublyNodeList.append('111');
doublyNodeList.append('222');
doublyNodeList.append('333');
console.log(doublyNodeList.toString()); //111 222 333
doublyNodeList.insert(0, '444');
doublyNodeList.insert(2, '555');
console.log(doublyNodeList.toString()); // 444 111 555 222 333
console.log(doublyNodeList.getData(1)); // 111
console.log(doublyNodeList.indexOf('444')); // 0
doublyNodeList.removeAt(0);
doublyNodeList.removeAt(1);
console.log(doublyNodeList.toString()); //111 222 333
doublyNodeList.update(0, '666');
console.log(doublyNodeList.toString()); // 666 222 333
console.log(doublyNodeList.forwardString()); // 666 222 333
console.log(doublyNodeList.backwordString()); // 333 222 666
console.log(doublyNodeList.getHeadNode().data); // 666
console.log(doublyNodeList.getTailNode().data); // 333
doublyNodeList.toString()); // 666 222 333
console.log(doublyNodeList.forwardString()); // 666 222 333
console.log(doublyNodeList.backwordString()); // 333 222 666
console.log(doublyNodeList.getHeadNode().data); // 666
console.log(doublyNodeList.getTailNode().data); // 333