线性表(List):零个或者多个数据元素的有限序列。线性表是一个序列,具有一对一的关系,而不能具备一对多的关系。线性表要求有限性,数据类型也要相同。
本文参考的主要是大话数据结构的原理进行理解,用 Javascript 实现线性表的相关操作。
创建单向链表:
// 创建节点
class Node{constructor(element){
this.element = element;// 数据域
this.next = undefined;// 指针域
}
};
// 判断传入的对象与链表的元素是否相等,待会实现 indexOf() 要用到,在这里先写上
function(a,b){return a == b;};
// 创建链表
class LinkedList {constructor(equalsFn = defaultEquals) {
this.equalsFn = equalsFn;
this.count = 0;
this.head = undefined;
}
};
查询操作
判断单向表是否为空
isEmpty() {return this.size() === 0;
}
计算单向链表的长度
size() {return this.count;}
查询链表的某个元素的位置
indexOf(element) {
let current = this.head;
for (let i = 0; i < this.size() && current != null; i++) {if (this.equalsFn(element, current.element)) {return i;}
current = current.next;
}
return -1;
}
获取第一个元素
getHead() {return this.head;}
获得元素操作
getElementAt(index) {if (index >= 0 && index <= this.count) {
let node = this.head;
for (let i = 0; i < index && node != null; i++) {node = node.next;}
return node;
}
return undefined;
}
插入操作
尾插法
push(element) {const node = new Node(element);
let current;
if (this.head == null) {
// catches null && undefined
this.head = node;
} else {
current = this.head;
while (current.next != null) {current = current.next;}
current.next = node;
}
this.count++;
}
任意位置插入元素
insert(element, index) {if (index >= 0 && index <= this.count) {const node = new Node(element);
if (index === 0) {
const current = this.head;
node.next = current;
this.head = node;
} else {const previous = this.getElementAt(index - 1);
node.next = previous.next;
previous.next = node;
}
this.count++;
return true;
}
return false;
}
删除操作
删除特定位置的元素
removeAt(index) {if (index >= 0 && index < this.count) {
let current = this.head;
if (index === 0) {this.head = current.next;} else {const previous = this.getElementAt(index - 1);
current = previous.next;
previous.next = current.next;
}
this.count--;
return current.element;
}
return undefined;
}
直接删除链表的某个元素
remove(element) {const index = this.indexOf(element);
return this.removeAt(index);
}
修改操作
单向链表转为字符串
toString() {if (this.head == null) {return '';}
let objString = `${this.head.element}`;
let current = this.head.next;
for (let i = 1; i < this.size() && current != null; i++) {objString = `${objString},${current.element}`;
current = current.next;
}
return objString;
}
}
清空单向链表
clear() {
this.head = undefined;
this.count = 0;
}
以上就是单向链表的常见操作。
由于单向链表只能从头到尾的遍历,如果查询得是下一节点,单向表时间复杂度为 O(1),但是要查询的是上一节点的话,那么时间复杂度就是 O(n)。如果链表也可以正反遍历的话,那么查询操作的时间复杂度就都是 O(1)啦,这时我们的前辈就提出了双向链表这一神奇的链表。由于双向链表是单向链表的拓展,只是多了一个指针,对于查询操作并没有帮助,所以实现方法还是跟单向链表一样,这里就不多加阐述。
创建双向链表
// 创建节点
class DoublyNode extends Node {constructor(element, next, prev) {super(element, next);// 继承
this.prev = prev;// 添加前继指针
}
};
// 初始化双向链表
class DoublyLinkedList extends LinkedList {constructor(equalsFn = defaultEquals) {super(equalsFn);
this.tail = undefined;// 对链表最后一个元素进行引用
}
插入操作
尾插法
push(element) {const node = new DoublyNode(element);
if (this.head == null) {
this.head = node;
this.tail = node; // NEW
} else {
// attach to the tail node // NEW
this.tail.next = node;
node.prev = this.tail;
this.tail = node;
}
this.count++;
}
任意位置添加元素
insert(element, index) {if (index >= 0 && index <= this.count) {const node = new DoublyNode(element);
let current = this.head;
if (index === 0) {if (this.head == null) { // NEW
this.head = node;
this.tail = node; // NEW
} else {
node.next = this.head;
this.head.prev = node; // NEW
this.head = node;
}
} else if (index === this.count) { // last item NEW
current = this.tail;
current.next = node;
node.prev = current;
this.tail = node;
} else {const previous = this.getElementAt(index - 1);
current = previous.next;
node.next = current;
previous.next = node;
current.prev = node; // NEW
node.prev = previous; // NEW
}
this.count++;
return true;
}
return false;
}
删除操作
删除特定位置的元素
removeAt(index) {if (index >= 0 && index < this.count) {
let current = this.head;
if (index === 0) {
this.head = this.head.next;
// if there is only one item, then we update tail as well //NEW
if (this.count === 1) {// {2}
this.tail = undefined;
} else {this.head.prev = undefined;}
} else if (index === this.count - 1) {
// last item //NEW
current = this.tail;
this.tail = current.prev;
this.tail.next = undefined;
} else {current = this.getElementAt(index);
const previous = current.prev;
// link previous with current's next - skip it to remove
previous.next = current.next;
current.next.prev = previous; // NEW
}
this.count--;
return current.element;
}
return undefined;
}
查询操作
查询元素的位置
indexOf(element) {
let current = this.head;
let index = 0;
while (current != null) {if (this.equalsFn(element, current.element)) {return index;}
index++;
current = current.next;
}
return -1;
}
查询尾部元素
getTail() {return this.tail;}
修改操作
清空双向链表
clear() {super.clear();
this.tail = undefined;
}
双向链表对象转为字符串
inverseToString() {if (this.tail == null) {return '';}
let objString = `${this.tail.element}`;
let previous = this.tail.prev;
while (previous != null) {objString = `${objString},${previous.element}`;
previous = previous.prev;
}
return objString;
}
}