您的位置:首页 > Web前端 > JavaScript

javascript 链表(lineked lists)算法与说明

2017-09-13 00:28 393 查看

链表的介绍

链表存储有序的元素集合,但不同于数组,链表中的元素在内存中并不是连续放置的。每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(也称为指针或链接)组成。下图讲解:



相对于传统的数组,链表的一个好处在于,添加或移除元素的时候不需要移动其他元素。然而,链表需要使用指针,因此实现链表的时候需要注意。数组的另一个细节是可以直接访问任何位置元素,而要想访问链表中间的一个元素,需要从起点(表头)开始送达列表直到找到所需要元素。

现实实例就是火车,火车有多节车厢相互衔接,通过接轨来链接火车。车厢就是链表的元素,而接轨就是指针。



链表算法实例

1.链表增删改查。

let list = new LinkedList();
list.append(15);
list.append(10);
list.append(13);//15,10,13
list.indexOf(10);//1
list.removeAt(1)//15,13
list.insert(1,17);//15,17,13
list.remove(11);//17,13


ES6链表实现代码:

let LinkedList = (function () {

class Node {//一个节点
constructor(element){
this.element = element;
this.next = null;
}
}

const length = new WeakMap();
const head = new WeakMap();

class LinkedList {
constructor () {
length.set(this, 0);
head.set(this, null);
}

append(element) {//向列表尾部添加一个新的项
let node = new Node(element),
current;

if (this.getHead() === null) {
head.set(this, node);
} else {

current = this.getHead();

while (current.next) {
current = current.next;
}

current.next = node;
}

let l = this.size();
l++;
length.set(this, l);
}

insert(position, element) {//向列表的特定位置插入一个新的项

if (position >= 0 && position <= this.size()) {

let node = new Node(element),
current = this.getHead(),
previous,
index = 0;

if (position === 0) {
node.next = current;
head.set(this, node);
} else {
while (index++ < position) {
previous = current;
current = current.next;
}
node.next = current;
previous.next = node;
}
let l = this.size();
l++;
length.set(this, l);

return true;

} else {
return false;
}
}

removeAt(position) {//从列表的特定位置移除一项
if (position > -1 && position < this.size()) {

let current = this.getHead(),
previous,
index = 0;

if (position === 0) {
head.set(this, current.next);
} else {

while (index++ < position) {

previous = current;
current = current.next;
}

previous.next = current.next;
}

let l = this.size();
l--;
length.set(this, l);

return curren
4000
t.element;

} else {
return null;
}
}

remove(element) {//移除一项

let index = this.indexOf(element);
return this.removeAt(index);
}

indexOf(element) {//返回元素在列表中的索引,没有返回-1

let current = this.getHead(),
index = 0;

while (current) {
if (element === current.element) {
return index;
}
index++;
current = current.next;
}

return -1;
}

isEmpty() {//链表不包含任何元素,返回true,如果长度大于0返回false
return this.size() === 0;
}

size() {//返回链表包含的元素个数
return length.get(this);
}

getHead() {//获取链头项
return head.get(this);
}

toString() {//已string输出链表
let current = this.getHead(),
string = '';

while (current) {
string += current.element + (current.next ? ', ' : '');
current = current.next;
}
return string;

}

print() {//string格式打印到控制台上。
console.log(this.toString());
}
}

return LinkedList;
})();


双向链表

双向链表和普通链表的区别在于。在链表中,一个节点只有链向下一个节点的链接,而在双向链表中,链接是双向的:一个链向下一个元素,另一个链向前一个元素。如下图:



双向链表提供了两种送代列表的方法:从头到尾,或者反过来。我们也可以访问一个特定节点的下一个或前一个元素。在单向链表中,如果送代列表时错过了要找的元素,就需要回到列表起点,重新开始送代。这是双向链表的一个优点。

事例:

let list = new DoublyLinkedList();
list.append(15);
list.append(16);//15,16
list.print();//15,16
list.printInverse();//16, 15
list.insert(0,13);//13,15,16
list.removeAt(0);//15,16


ES6代码实例:

let DoublyLinkedList = (function () {

class Node {
constructor(element) {
this.element = element;
this.next = null;
this.prev = null; //NEW
}
}

const length = new WeakMap();
const head = new WeakMap();
const tail = new WeakMap(); //NEW

class DoublyLinkedList {

constructor () {
length.set(this, 0);
head.set(this, null);
tail.set(this, null);
}

append(element) {

let node = new Node(element),
current, _tail;

if (this.getHead() === null) { //first node on list
head.set(this, node);
tail.set(this, node); //NEW
} else {
//attach to the tail node //NEW
_tail = this.getTail();
_tail.next = node;
node.prev = _tail;
tail.set(this, node);
}

//update size of list
let l = this.size();
l++;
length.set(this, l);
}

insert(position, element) {

//check for out-of-bounds values
if (position >= 0 && position <= this.size()) {

let node = new Node(element),
current = this.getHead(),
previous,
index = 0;

if (position === 0) { //add on first position

if (!this.getHead()) {       //NEW
head.set(this, node);
tail.set(this, node);
} else {
node.next = current;
current.prev = node; //NEW {1}
head.set(this, node);
}

} else if (position === this.size()) { //last item //NEW

current = tail;     // {2}
current.next = node;
node.prev = current;
tail.set(this, node);

} else {
while (index++ < position) { //{3}
previous = current;
current = current.next;
}
node.next = current;
previous.next = node;

current.prev = node; //NEW
node.prev = previous; //NEW
}

//update size of list
let l = this.size();
l++;
length.set(this, l);

return true;

} else {
return false;
}
}

removeAt(position) {

//check for out-of-bounds values
if (position > -1 && position < this.size()) {

let _head = this.getHead(),
_tail = this.getTail(),
current = _head,
previous,
index = 0;

//removing first item
if (position === 0) {

_head = current.next; // {1}

//if there is only one item, then we update tail as well //NEW
if (this.size() === 1) { // {2}
_tail = null;
} else {
_head.prev = null; // {3}
}

} else if (position === this.size() - 1) { //last item //NEW

current = _tail; // {4}
_tail = current.prev;
_tail.next = null;

} else {

while (index++ < position) { // {5}

previous = current;
current = current.next;
}

//link previous with current's next - skip it to remove
previous.next = current.next; // {6}
current.next.prev = previous; //NEW
}

head.set(this,_head);
tail.set(this,_tail);

//update size of list
let l = this.size();
l--;
length.set(this, l);

return current.element;

} else {
return null;
}
}

remove(element) {

let index = this.indexOf(element);
return this.removeAt(index);
}

indexOf(element) {

let current = this.getHead(),
index = -1;

//check first item
if (element == current.element) {
return 0;
}

index++;

//check in the middle of the list
while (current.next) {

if (element == current.element) {
return index;
}

current = current.next;
index++;
}

//check last item
if (element == current.element) {
return index;
}

return -1;
}

isEmpty() {
return this.size() === 0;
}

size() {
return length.get(this);
}

toString() {

let current = this.getHead(),
s = current ? current.element : '';

while (current && current.next) {
current = current.next;
s += ', ' + current.element;
}

return s;
}

inverseToString() {

let current = this.getTail(),
s = current ? current.element : '';

while (current && current.prev) {
current = current.prev;
s += ', ' + current.element;
}

return s;
}

print() {
console.log(this.toString());
}

printInverse() {
console.log(this.inverseToString());
}

getHead() {
return head.get(this);
}

getTail() {
return tail.get(this);
}
}
return DoublyLinkedList;
})();


循环链表

循环链表。就是最后一个元素指向不是null,而是指向第一个元素(head)。如下图。



实例用法:

let circularLinkedList = new CircularLinkedList();
circularLinkedList.append(15);
circularLinkedList.append(16);//15,16
circularLinkedList.insert(0,14);//14,15,16
circularLinkedList.removeAt(0);//15,16
circularLinkedList.indexOf(16)//1


ES6循环链表实例:

let CircularLinkedList = (function () {

class Node {
constructor(element) {
this.element = element;
this.next = null;
}
}

const length = new WeakMap();
const head = new WeakMap();

class CircularLinkedList {

constructor () {
length.set(this, 0);
head.set(this, null);
}

append(element) {

let node = new Node(element),
current;

if (this.getHead() === null) { //first node on list
head.set(this, node);
} else {

current = this.getHead();

//loop the list until find last item
while (current.next !== this.getHead()) { //last element will be head instead of NULL
current = current.next;
}

//get last item and assign next to added item to make the link
current.next = node;
}

//set node.next to head - to have circular list
node.next = this.getHead();

//update size of list
let l = this.size();
l++;
length.set(this, l);
}

insert(position, element) {

//check for out-of-bounds values
if (position >= 0 && position <= this.size()) {

let node = new Node(element),
current = this.getHead(),
previous,
index = 0;

if (position === 0) { //add on first position

node.next = current;

//update last element
while (current.next !== this.getHead()) { //last element will be head instead of NULL
current = current.next;
}

head.set(this, node);
current.next = this.getHead();

} else {
while (index++ < position) {
previous = current;
current = current.next;
}
node.next = current;
previous.next = node;
}

//update size of list
let l = this.size();
l++;
length.set(this, l);

return true;

} else {
return false;
}
}

removeAt(position) {

//check for out-of-bounds values
if (position > -1 && position < this.size()) {

let current = this.getHead(),
previous,
index = 0;

//removing first item
if (position === 0) {

while (current.next !== this.getHead()) { //needs to update last element first
current = current.next;
}

head.set(this, this.getHead().next);
current.next = this.getHead();

} else { //no need to update last element for circular list

while (index++ < position) {

previous = current;
current = current.next;
}

//link previous with current's next - skip it to remove
previous.next = current.next;
}

let l = this.size();
l--;
length.set(this, l);

return current.element;

} else {
return null;
}
}

remove(element) {

let index = indexOf(element);
return removeAt(index);
}

indexOf(element) {

let current = this.getHead(),
index = -1;

//check first item
if (element == current.element) {
return 0;
}

index++;

//check in the middle of the list
while (current.next !== this.getHead()) {

if (element == current.element) {
return index;
}

current = current.next;
index++;
}

//check last item
if (element == current.element) {
return index;
}

return -1;
}

isEmpty() {
return this.size() === 0;
}

size() {
return length.get(this);
}

getHead() {
return head.get(this);
}

toString() {

let current = this.getHead(),
s = current.element;

while (current.next !== this.getHead()) {
current = current.next;
s += ', ' + current.element;
}

return s.toString();
}

print() {
console.log(this.toString());
}
}
return CircularLinkedList;
})();
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息