上一節中, 線性表的順序存儲結構的特色是邏輯關係上相鄰的兩個元素在物理位置上也相鄰,所以能夠隨機存取表中任一元素,它的存儲位置可用一個簡單,直觀的公式來表示。而後,另外一方面來看,這個特色也形成這種存儲結構的弱點,在作插入或刪除操做時,需移動大量元素。node
而鏈式存儲結構,因爲它不須要邏輯上相鄰的元素在物理位置上也相鄰,所以它沒有順序存儲結構所具備的弱點,但同時也失去了順序表可隨機存取的優勢。編程
線性鏈表數組
wiki中的定義:promise
鏈表(Linked list)是一種常見的基礎數據結構,是一種線性表,可是並不會按線性的順序存儲數據,而是在每個節點裏存到下一個節點的指針(Pointer)。因爲沒必要須按順序存儲,鏈表在插入的時候能夠達到O(1)的複雜度,比另外一種線性表順序表快得多,可是查找一個節點或者訪問特定編號的節點則須要O(n)的時間,而順序表相應的時間複雜度分別是O(logn)和O(1)。數據結構
使用鏈表結構能夠克服數組鏈表須要預先知道數據大小的缺點,鏈表結構能夠充分利用計算機內存空間,實現靈活的內存動態管理。可是鏈表失去了數組隨機讀取的優勢,同時鏈表因爲增長告終點的指針域,空間開銷比較大。編程語言
在計算機科學中,鏈表做爲一種基礎的數據結構能夠用來生成其它類型的數據結構。鏈表一般由一連串節點組成,每一個節點包含任意的實例數據(data fields)和一或兩個用來指向明上一個/或下一個節點的位置的連接("links")。鏈表最明顯的好處就是,常規數組排列關聯項目的方式可能不一樣於這些數據項目在記憶體或磁盤上順序,數據的訪問每每要在不一樣的排列順序中轉換。而鏈表是一種自我指示數據類型,由於它包含指向另外一個相同類型的數據的指針(連接)。鏈表容許插入和移除表上任意位置上的節點,可是不容許隨機存取。鏈表有不少種不一樣的類型:單向鏈表,雙向鏈表以及循環鏈表。ide
鏈表能夠在多種編程語言中實現。像Lisp和Scheme這樣的語言的內建數據類型中就包含了鏈表的訪問和操做。程序語言或面嚮對象語言,如C/C++和Java依靠易變工具來生成鏈表。svg
線性表的鏈式存儲結構的特色是用一組任意的存儲單元儲存線性表的數據元素(這組存儲單元能夠是連續的,也能夠是不連續的)。所以,爲了表示每一個數據元素a(i)與其直接後繼數據元素a(i+1)之間的邏輯關係,對數據元素a(i)來講,除了存儲其自己的信息以外,還需存儲一個指示其直接後繼的信息(即直接後繼的存儲位置)。這兩部分信息組成數據元素a(i)的存儲映像,稱爲結點(node)。它包括兩個域:其中存儲數據元素信息的域稱爲數據域;存儲直接後繼存儲位置的域稱爲指針域,指針域中存儲的信息稱作指針或鏈。工具
又因爲此鏈表的每一個結點中只包含一個指針域,故又稱線性鏈表或單鏈表。ui
單鏈表的整個鏈表的存取必須從頭指針開始進行,頭指針指示鏈表中第一個結點(即第一個數據元素的存儲映像)的存儲位置。
同時,因爲最後一個數據元素沒有直接後繼,則線性鏈表中最後一個結點的指針爲空null。
鏈表中最簡單的一種是單向鏈表,它包含兩個域,一個信息域和一個指針域。這個連接指向列表中的下一個節點,而最後一個節點則指向一個空值。
一個單向鏈表的節點被分紅兩個部分。第一個部分保存或者顯示關於節點的信息,第二個部分存儲下一個節點的地址。單向鏈表只可向一個方向遍歷。
1 // 線性表的單鏈表存儲結構 2 function LNode(data, node) { 3 this.data = data; 4 this.next = node || null; 5 }
假設p是指向線性表中第i個數據元素(結點a(i))的指針,則p->next是指向第i+1個數據元素(結點a(i+1))的指針。
下面咱們來看GetElem在單鏈表中的實現:
1 function getElem(i) { 2 // 初始化,p指向第一個節點,j爲計數器 3 var p = this.next; 4 var j = 1; 5 // 順指針向後查找,知道p指向第i個元素或p爲空 6 while (p && j < i) { 7 p = p.next; 8 ++j; 9 } 10 // 第i個元素不存在 11 // 或者取第i個元素 12 return (!p || j > i) ? null : p.data; 13 }
單鏈表的基本操做:
假設咱們在線性表的兩個數據元素a和b之間插入一個數據元素x,已知p爲其單鏈表存儲結構中指向結點a的指針。
插入:
假設s爲指向結點x的指針,則可用語句描述:s->next = p->next; p->next = s;
刪除:
假設p爲指向結點a的指針,則修改指針的語句爲: p->next = p->next->next;
實現:
1 function listInsert(i, data) { 2 var j = 0; 3 var p = this; 4 // 尋找第i-1個節點 5 while (p && j < i - 1) { 6 p = p.next; 7 ++j; 8 } 9 // i < 1或者大於表長+1 10 if (!p || j > i - 1) return false; 11 // 生成新節點,插入p節點後面 12 p.next = new LNode(data, p.next); 13 return true; 14 } 15 16 function listDelete(i) { 17 var j = 0; 18 var p = this; 19 20 while (p.next && j < i - 1) { 21 p = p.next; 22 ++j; 23 } 24 25 if (!p.next || j > i - 1) return false; 26 var q = p.next; 27 p.next = q.next; 28 return q.data; 29 }
單鏈表的其餘操做
逆位序輸入n個元素的值,創建帶表頭結點的單鏈線性表L。
function createList_L(n) { var deferred = require('rsvp').defer(); var l = new LNode(); var count = n; process.stdin.setEncoding('utf8'); process.stdin.on('data', function handler(data) { console.log(123); data = data.replace('\n', ''); l.next = new LNode(data, l.next); if (!(--count)) { console.log('pausing'); process.stdin.pause(); deferred.resolve(l); } }); return deferred.promise; }
假設頭指針爲La和Lb的單鏈表分別爲線性表LA和LB的存儲結構,先要歸併La和Lb獲得單鏈表Lc:
1 function mergeList(a, b) { 2 var pa = a.next; 3 var pb = b.next; 4 // 用a的頭結點做爲c的頭結點 5 var c = a; 6 var pc = a; 7 8 while (pa && pb) { 9 if (pa.data <= pb.data) { 10 pc.next = pa; 11 pc = pa; 12 pa = pa.next; 13 } else { 14 pc.next = pb; 15 pc = pb; 16 pb = pb.next; 17 } 18 } 19 20 // 插入剩餘段 21 pc.next = pa ? pa : pb; 22 23 return c; 24 }
結構圖:
完整代碼:
1 // 單鏈表 2 /* 3 線性鏈表存儲結構 4 整個鏈表的存取必須從頭指針開始進行,頭指針指示鏈表中第一個結點(即第一個數據元素的存儲映像)的存儲位置。 5 同時,因爲最後一個數據元素沒有直接後繼,則線性鏈表中最後一個結點的指針爲空null。 6 */ 7 8 function LNode(data, node) { 9 this.data = data; 10 this.next = node || null; 11 } 12 LNode.prototype = { 13 // 時間複雜度O(n) 14 getElem: function getElem(i) { 15 // 初始化,p指向第一個節點,j爲計數器 16 var p = this.next; 17 var j = 1; 18 // 順指針向後查找,知道p指向第i個元素或p爲空 19 while (p && j < i) { 20 p = p.next; 21 ++j; 22 } 23 // 第i個元素不存在 24 // 或者取第i個元素 25 return (!p || j > i) ? null : p.data; 26 }, 27 // 時間複雜度O(n) 28 listInsert: function listInsert(i, data) { 29 var j = 0; 30 var p = this; 31 // 尋找第i-1個節點 32 while (p && j < i - 1) { 33 p = p.next; 34 ++j; 35 } 36 // i < 1或者大於表長+1 37 if (!p || j > i - 1) return false; 38 // 生成新節點,插入p節點後面 39 p.next = new LNode(data, p.next); 40 return true; 41 }, 42 listDelete: function listDelete(i) { 43 var j = 0; 44 var p = this; 45 46 while (p.next && j < i - 1) { 47 p = p.next; 48 ++j; 49 } 50 51 if (!p.next || j > i - 1) return false; 52 var q = p.next; 53 p.next = q.next; 54 return q.data; 55 } 56 }; 57 58 LNode.createList_L = function createList_L(n) { 59 var deferred = require('D:\\node\\node_modules\\rsvp').defer(); 60 var l = new LNode(); 61 var count = n; 62 process.stdin.setEncoding('utf8'); 63 64 process.stdin.on('data', function handler(data) { 65 console.log(123); 66 data = data.replace('\n', ''); 67 l.next = new LNode(data, l.next); 68 if (!(--count)) { 69 console.log('pausing'); 70 process.stdin.pause(); 71 deferred.resolve(l); 72 } 73 }); 74 75 return deferred.promise; 76 }; 77 78 function deepCopy(obj) { 79 var newObj = {}; 80 81 for (var i in obj) { 82 if (typeof obj[i] === 'object') { 83 newObj[i] = deepCopy(obj[i]); 84 } else { 85 newObj[i] = obj[i]; 86 } 87 } 88 89 return newObj; 90 } 91 92 // TODO 93 /* 94 已知單鏈線性表a和b的元素按值非遞減排列。 95 歸併a和b獲得新的單鏈線性表c,c的元素也按值非遞減排列。 96 */ 97 LNode.mergeList = function mergeList(a, b) { 98 var pa = a.next; 99 var pb = b.next; 100 // 用a的頭結點做爲c的頭結點 101 var c = a; 102 var pc = a; 103 104 while (pa && pb) { 105 if (pa.data <= pb.data) { 106 pc.next = pa; 107 pc = pa; 108 pa = pa.next; 109 } else { 110 pc.next = pb; 111 pc = pb; 112 pb = pb.next; 113 } 114 } 115 116 // 插入剩餘段 117 pc.next = pa ? pa : pb; 118 119 return c; 120 }; 121 122 function log(list) { 123 var arr = []; 124 125 do { 126 arr.push(list.data); 127 list = list.next; 128 } while (list); 129 130 console.log(arr.join(',')); 131 } 132 133 void function test() { 134 var a1 = new LNode(1); 135 a1.listInsert(1, 2); 136 a1.listInsert(2, 3); 137 a1.listInsert(1, 4); 138 console.log(a1.getElem(1)); 139 console.log(a1); 140 log(a1); 141 a1.listDelete(1); 142 console.log('a1 linkList:'); 143 console.log(a1); 144 log(a1); 145 /* 146 LNode.createList_L(5) 147 .then(function(list){ 148 console.log(list); 149 }); 150 */ 151 var a2 = new LNode(3); 152 a2.listInsert(1, 3); 153 a2.listInsert(2, 8); 154 a2.listInsert(1, 4); 155 a2.listDelete(2); 156 console.log('a2 linkList'); 157 log(a2); 158 159 var a3 = LNode.mergeList(a2, a1); 160 console.log('merging linkLists'); 161 console.log(a3); 162 log(a3); 163 }();