鏈表和數組很類似,不一樣的是,鏈表中的元素在內存中不是連續放置的,鏈表中的元素其實是由一組節點構成的,其中每個節點都是由數據元素和指向下一個數據元素的引用(指針)構成的。要想訪問鏈表中間的一個元素,須要從頭(表頭)開始迭代鏈表,直到找到所需的元素。node
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是這樣實現普通鏈表的:app
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連接起來。oop
插入一個元素時,須要注意跳出循環時,current是對想要插入新元素的位置以後的一個元素的引用。previous則是對想要插入的新元素前一個元素的引用。這時,在previous和current之間添加新項,須要做出以下的變換。優化
node.next = current; previous.next = node;
head變量是LinkedList類的私有變量,若是咱們要在類的外部實現循環訪問鏈表,則須要提供一種獲取類的第一個元素的方法~getHead方法。this
鏈表由許多變體,其中一種是雙向鏈表。在雙向鏈表中,連接是雙向的,一個連接鏈向下一個元素,另外一個連接鏈向前一個元素。
spa
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從而構成一個環路。