链表
定义
链表和数组很相似,不同的是,链表中的元素在内存中不是连续放置的,链表中的元素实际上是由一组节点构成的,其中每一个节点都是由数据元素和指向下一个数据元素的引用(指针)构成的。要想访问链表中间的一个元素,需要从头(表头)开始迭代链表,直到找到所需的元素。
In computer science, a linked list is a data structure consisting of a group of nodes which together represent a sequence. Under the simplest form, each node is composed of data and a reference (in other words, a link) to the next node in the sequence; more complex variants add additional links.
链表是一种动态的数据结构,可以从中任意的添加或者移除项,它会按需进行扩容。数组的大小是固定的,从数组的起点或者中间插入或者移除项的成本很高,因为需要移动元素。
类别现实生活中的实例就是火车了,每节车厢彼此链接,很容易分离某一节车皮,改变它的位置,添加或者移除一节车皮。每节车皮就是链表的元素,车皮间的连接就是指针。
代码实现
js是这样实现普通链表的:
function LinkedList() {
var Node = function(element){
this.element = element;
this.next = null;//每一个元素的默认下一个元素的索引是null
};
var length = 0;
var head = null;//初始化,当链表为空的时候,head指向null。
this.append = function(element){
var node = new Node(element),
current;
if (head === null){ //first node on list
head = node;
} else {
current = head;
//loop the list until find last item
while(current.next){
current = current.next;
}
//get last item and assign next to added item to make the link
current.next = node;
}
length++; //update size of list,此时已经在链表中增加了一个元素
};
this.insert = function(position, element){
//check for out-of-bounds values
if (position >= 0 && position <= length){
var node = new Node(element),
current = head,
previous,
index = 0;
if (position === 0){ //add on first position
node.next = current;
head = node;
} else {
while (index++ < position){
previous = current;
current = current.next;
}
node.next = current;
previous.next = node;
}
length++; //update size of list
return true;
} else {
return false;
}
};
this.removeAt = function(position){
//check for out-of-bounds values
if (position > -1 && position < length){
var current = head,
previous,
index = 0;
//removing first item
if (position === 0){
head = current.next;//移除第一个元素很容易,直接将指针指向current.next即可。
} else {
while (index++ < position){
previous = current;
current = current.next;
}
//link previous with current's next - skip it to remove
previous.next = current.next;
}
length--;
return current.element;
} else {
return null;
}
};
this.remove = function(element){
var index = this.indexOf(element);
return this.removeAt(index);
};
this.indexOf = function(element){
var current = head,
index = 0;
while (current) {
if (element === current.element) {
return index;
}
index++;
current = current.next;
}
return -1;
};
this.isEmpty = function() {
return length === 0;
};
this.size = function() {
return length;
};
this.getHead = function(){
return head;
};
this.toString = function(){
var current = head,
string = '';
while (current) {
string = current.element;
current = current.next;
}
return string;
};
this.print = function(){
console.log(this.toString());
};
}
其中尤其需要注意从链表移除元素的removeAt函数。
previous.next = current.next;
current变量是对要移除元素的引用,previous是对要移除元素的前一个元素的引用,那么要移除current元素所需要做的就是将previous.next和current.next链接起来。
插入一个元素时,需要注意跳出循环时,current是对想要插入新元素的位置之后的一个元素的引用。previous则是对想要插入的新元素前一个元素的引用。这时,在previous和current之间添加新项,需要作出如下的变换。
node.next = current;
previous.next = node;
head变量是LinkedList类的私有变量,如果我们要在类的外部实现循环访问链表,则需要提供一种获取类的第一个元素的方法~getHead方法。
双向链表
定义
链表由许多变体,其中一种是双向链表。在双向链表中,链接是双向的,一个链接链向下一个元素,另一个链接链向前一个元素。
In computer science, a doubly linked list is a linked data structure that consists of a set of sequentially linked records called nodes. Each node contains two fields, called links, that are references to the previous and to the next node in the sequence of nodes.
在双向链表中可以有两种迭代链表的方向。
代码实现
js是这样实现双向链表的:
function DoublyLinkedList() {
var Node = function(element){
this.element = element;
this.next = null;
this.prev = null; //NEW
};
var length = 0;
var head = null;
var tail = null; //NEW
this.append = function(element){
var node = new Node(element),
current;
if (head === null){ //first node on list
head = node;
tail = node; //NEW
} else {
//attach to the tail node //NEW
tail.next = node;
node.prev = tail;
tail = node;
}
length++; //update size of list
};
this.insert = function(position, element){
//check for out-of-bounds values
if (position >= 0 && position <= length){
var node = new Node(element),
current = head,
previous,
index = 0;
if (position === 0){ //add on first position
if (!head){ //NEW
head = node;
tail = node;
} else {
node.next = current;
current.prev = node; //NEW {1}
head = node;
}
} else if (position === length) { //last item //NEW
current = tail; // {2}
current.next = node;
node.prev = current;
tail = 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
}
length++; //update size of list
return true;
} else {
return false;
}
};
this.removeAt = function(position){
//check for out-of-bounds values
if (position > -1 && position < length){
var 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 (length === 1){ // {2}
tail = null;
} else {
head.prev = null; // {3}
}
} else if (position === length-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
}
length--;
return current.element;
} else {
return null;
}
};
this.remove = function(element){
var index = this.indexOf(element);
return this.removeAt(index);
};
this.indexOf = function(element){
var current = head,
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;
};
this.isEmpty = function() {
return length === 0;
};
this. size = function() {
return length;
};
this.toString = function(){
var current = head,
s = current ? current.element : '';
while(current && current.next){
current = current.next;
s += ', ' + current.element;
}
return s;
};
this.inverseToString = function() {
var current = tail,
s = current ? current.element : '';
while(current && current.prev){
current = current.prev;
s += ', ' + current.element;
}
return s;
};
this.print = function(){
console.log(this.toString());
};
this.printInverse = function(){
console.log(this.inverseToString());
};
this.getHead = function(){
return head;
};
this.getTail = function(){
return tail;
}
}
在任意位置插入新的元素,或者移除元素时,可以对position进行判断,如果position>length/2,就最好从尾部开始迭代,从而减少迭代次数,优化算法。
循环链表
定义
循环链表只是在普通链表的基础上做了一点点的变动,即最后一个元素指向下一个元素的指针(tail.next)不是null,而是指向第一个元素(head)。
A circular linked list is a linked list in which the head element's previous pointer points to the tail element and the tail element's next pointer points to the head element. In the special case of a circular list with only one element, the element's previous and next pointers point to itself, and it is both the head and tail of the list.
Programming a circular list presents some challenges, but the code is actually simpler and more concise than for a conventional linked list, because the lack of NULL pointers means that we never need to check for them.
代码实现
js是这样实现循环链表的:
function CircularLinkedList() {
var Node = function(element){
this.element = element;
this.next = null;
};
var length = 0;
var head = null;
this.append = function(element){
var node = new Node(element),
current;
if (head === null){ //first node on list
head = node;
} else {
current = head;
//loop the list until find last item
while(current.next !== head){ //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 = head;
length++; //update size of list
};
this.insert = function(position, element){
//check for out-of-bounds values
if (position >= 0 && position <= length){
var node = new Node(element),
current = head,
previous,
index = 0;
if (position === 0){ //add on first position
node.next = current;
//update last element
while(current.next !== head){ //last element will be head instead of NULL
current = current.next;
}
head = node;
current.next = head;
} else {
while (index++ < position){
previous = current;
current = current.next;
}
node.next = current;
previous.next = node;
if (node.next === null){ //update in case last element
node.next = head;
}
}
length++; //update size of list
return true;
} else {
return false;
}
};
this.removeAt = function(position){
//check for out-of-bounds values
if (position > -1 && position < length){
var current = head,
previous,
index = 0;
//removing first item
if (position === 0){
while(current.next !== head){ //needs to update last element first
current = current.next;
}
head = head.next;
current.next = head;
} 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;
}
length--;
return current.element;
} else {
return null;
}
};
this.remove = function(element){
var index = this.indexOf(element);
return this.removeAt(index);
};
this.indexOf = function(element){
var current = head,
index = -1;
//check first item
if (element == current.element){
return 0;
}
index++;
//check in the middle of the list
while(current.next !== head){
if (element == current.element){
return index;
}
current = current.next;
index++;
}
//check last item
if (element == current.element){
return index;
}
return -1;
};
this.isEmpty = function() {
return length === 0;
};
this.size = function() {
return length;
};
this.getHead = function(){
return head;
};
this.toString = function(){
var current = head,
s = current.element;
while(current.next !== head){
current = current.next;
s += ', ' + current.element;
}
return s.toString();
};
this.print = function(){
console.log(this.toString());
};
}
双向循环链表
双向循环链表有指向head元素的tail.next和指向tail元素的head.prev从而构成一个环路。
来源:oschina
链接:https://my.oschina.net/u/2392809/blog/539738