Skip to main content
 首页 » 编程设计

JS实现数据结构与算法系列:功能完整的线性链表

2022年07月16日27duanxz

由于链表在空间的合理利用上和插入,删除时不需要移动等的有点,因此在很多场合下,它是线性表的首选存储结构。然而,它也存在着实现某些基本操作,如求线性表长度时不如顺序存储结构的缺点;另一方面,由于在链表中,结点之间的关系使用指针来表示,则数据元素在线性表中的“位序”的概念已淡化,而被数据元素在线性链表中的“位置”所代替。为此,从实际出发重新定义线性链表及其基本操作

结构图:

  1 (function(module){ 
  2   function List() { 
  3     this.head = null; 
  4     this.tail = null; 
  5   } 
  6   module.exports = List; 
  7  
  8   List.mergeList = function (a, b, compare) { 
  9     var ha = a.head; 
 10     var hb = b.head; 
 11     var pa = ha; 
 12     var pb = hb; 
 13     var c = new List(); 
 14     var q; 
 15     compare = compare || function (data1, data2) { 
 16       return data1 <= data2; 
 17     }; 
 18  
 19     while (pa && pb) { 
 20       var data1 = pa.data; 
 21       var data2 = pb.data; 
 22  
 23       if (!compare(data1, data2)) { 
 24         // delete head node 
 25         q = a.delFirst(); 
 26         // append the node to c linkedList 
 27         c.append(q); 
 28         pa = a.head; 
 29       } else { 
 30         q = b.delFirst(); 
 31         c.append(q); 
 32         pb = b.head; 
 33       } 
 34     } 
 35  
 36     if (pa) { 
 37       c.append(pa); 
 38     } else { 
 39       c.append(pb); 
 40     } 
 41  
 42     return c; 
 43   }; 
 44  
 45   List.prototype = { 
 46     makeNode: function(data, next){ 
 47       return { 
 48         data: data != null ?  data : null, 
 49         next: next || null 
 50       }; 
 51     }, 
 52     delFirst: function () { 
 53       var head = this.head; 
 54       this.head = this.head.next; 
 55       head.next = null; 
 56  
 57       if(this.head === null) this.tail = null; 
 58       return head; 
 59     }, 
 60     append: function (node) { 
 61       if (this.head !== null) { 
 62         this.tail.next = node; 
 63         this.tail = this.tail.next; 
 64       } else { 
 65         this.head = node; 
 66         this.tail = node; 
 67       } 
 68     }, 
 69     add: function (data) { 
 70       if (this.head === null) { 
 71         this.head = this.makeNode(data); 
 72         this.tail = this.head; 
 73       } else { 
 74         this.tail.next = this.makeNode(data); 
 75         this.tail = this.tail.next; 
 76       } 
 77  
 78       this.tail.data = data; 
 79     }, 
 80     'delete': function (data) { 
 81       var current = this.head; 
 82       var previous = this.head; 
 83       var elem; 
 84  
 85       while (current !== null) { 
 86         if (data === current.data) { 
 87           if (current === this.head) { 
 88             this.head = current.next; 
 89             elem =  current.data; 
 90             break; 
 91           } 
 92  
 93           if (current === this.tail) this.tail = previous; 
 94  
 95           previous.next = current.next; 
 96           elem =  current.data; 
 97           break; 
 98         } 
 99  
100         previous = current; 
101         current = current.next; 
102       } 
103  
104       if(this.head === null) this.tail = null; 
105  
106       return elem ? elem : false; 
107     }, 
108     insertAsFirst: function (data) { 
109       var temp = this.makeNode(data); 
110       temp.next = this.head; 
111       this.head = temp; 
112     }, 
113     insertAfter: function (target, data) { 
114       var current = this.head; 
115       while (current !== null) { 
116         if (current.data === target) { 
117           var temp = this.makeNode(data); 
118           temp.next = current.next; 
119  
120           if (current === this.tail) this.tail = temp; 
121  
122           current.next = temp; 
123           return; 
124         } 
125  
126         current = current.next; 
127       } 
128     }, 
129     item: function (index) { 
130       var current = this.head; 
131  
132       while (current !== null) { 
133         if (--index === 0) return current; 
134  
135         current = current.next; 
136       } 
137  
138       return null; 
139     }, 
140     each: function (callback) { 
141       var current = this.head; 
142  
143       while (current !== null) { 
144         callback(current); 
145         current = current.next; 
146       } 
147     }, 
148     orderInsert: function(data, cmp){ 
149       cmp = typeof cmp === 'function' ? cmp : function (a, b){ 
150         if(a > b) 
151           return 1; 
152         else if(a === b) 
153           return 0; 
154         else 
155           return -1; 
156       }; 
157       var previous = this.head; 
158       var current = this.head; 
159  
160       if(current === null){ 
161         this.head = this.tail = this.makeNode(data); 
162         return; 
163       } 
164  
165       var me = this; 
166       while(current){ 
167         var ret = cmp(data, current.data); 
168         // 如果插入元素大于当前元素,准备下次遍历 
169         if(ret > 0){ 
170           previous = current; 
171           current = current.next; 
172  
173           // 如果等于,直接插入到后面 
174         } else if(ret === 0){ 
175           return insertBetween(data, previous, current); 
176  
177           // 如果小于则插入到前节点和当前节点中 
178           // 因为已经是排序了,所以不需要多余判断了 
179         } else { 
180           if(this.head === previous && previous === current){ 
181             return this.insertAsFirst(data); 
182           } else { 
183             return insertBetween(data, previous, current); 
184           } 
185         } 
186       } 
187  
188       // 插入到最后一个结点 
189       previous.next = this.makeNode(data); 
190       this.tail = previous.next; 
191  
192       function insertBetween(data, a, b){ 
193         var temp = me.makeNode(data); 
194         temp.next = b; 
195         a.next = temp; 
196         return true; 
197       } 
198     } 
199   }; 
200   /* 
201   var list = new List(); 
202   list.add('b'); 
203   list.insertAsFirst('a'); 
204   list.insertAfter('b', 'c'); 
205   console.log(list.item(2)); 
206   console.log(JSON.stringify(list)); 
207   list.each(function (node) { 
208     if (node.data === 'b') { 
209       console.log('get b in each'); 
210     } 
211   }); 
212   list['delete']('c'); 
213   list['delete']('a'); 
214   console.log(list); 
215  
216   var list2 = new List(); 
217   list2.add('c'); 
218   list2.insertAsFirst('d'); 
219   list2.insertAfter('d', 'b'); 
220   console.log(JSON.stringify(list2)); 
221  
222   var list3 = List.mergeList(list, list2); 
223   console.log(list3); 
224   */ 
225   /* 
226   var list = new List(); 
227  
228   list.orderInsert('e'); 
229   list.orderInsert('b'); 
230   list.orderInsert('c'); 
231   list.orderInsert('a'); 
232   list.orderInsert('d'); 
233   list.orderInsert('f'); 
234   */ 
235 }(this.module || this));

以下是对应的单元测试代码:

 1 describe('linkedList tests', function(){ 
 2  
 3   var list = new List(); 
 4  
 5   it('should find the second item', function(){ 
 6     list.add('b'); 
 7     expect(list.head.data).toBe('b'); 
 8     expect(list.tail.next).toBe(null); 
 9  
10     list.insertAsFirst('a'); 
11     expect(list.head.data).toBe('a'); 
12     expect(list.head.next.data).toBe('b'); 
13  
14     list.insertAfter('b', 'c'); 
15     expect(list.item(2).data).toBe('b'); 
16     expect(list.tail.data).toBe('c'); 
17   }); 
18  
19   it('should remove one item', function(){ 
20     expect(list['delete']('c')).toBe(true); 
21     list['delete']('a'); 
22     expect(list.head.data).toBe('b'); 
23   }); 
24  
25   var list2 = new List(); 
26  
27   it('should match the json', function(){ 
28     list2.add('c'); 
29     list2.insertAsFirst('d'); 
30     list2.insertAfter('d', 'b'); 
31     expect(JSON.stringify(list2)).toBe('{"head":{"data":"d","next":{"data":"b","next":{"data":"c","next":null}}},"tail":{"data":"c","next":null}}'); 
32   }); 
33  
34   it('should merge the lists', function(){ 
35     var list3 = List.mergeList(list, list2); 
36     expect(list3.head.data).toBe('d'); 
37     expect(list3.head.next.data).toBe('b'); 
38     expect(list3.head.next.next.data).toBe('c'); 
39     expect(list3.tail.data).toBe('b'); 
40   }); 
41 });

本文参考链接:https://www.cnblogs.com/webFrontDev/p/3668187.html