首页 > 技术文章 > JavaScript数据结构与算法(六) 链表的实现

menu 2017-06-09 11:20 原文

 1 // 链表存储有序的元素集合,但不同于数组,链表中的元素在内存中并不是连续放置的。每个
 2 // 元素由一个存储元素本身的节点和一个指向下一个元素的引用(也称指针或链接)组成。下图展
 3 // 示了一个链表的结构:
 4 
 5 
 6 // 相对于传统的数组,链表的一个好处在于,添加或移除元素的时候不需要移动其他元素。然
 7 // 而,链表需要使用指针,因此实现链表时需要额外注意。数组的另一个细节是可以直接访问任何
 8 // 位置的任何元素,而要想访问链表中间的一个元素,需要从起点(表头)开始迭代列表直到找到
 9 // 所需的元素。
10 
11 // 链表应用场景 火车车厢、寻宝游戏

TypeScript方式实现源码

  1 class Node {
  2     element;
  3     next;
  4     constructor(element) {
  5         this.element = element;
  6         this.next = null;
  7     }
  8 }
  9 class LinkedList {
 10     private length = 0;
 11     head = null;
 12 
 13     constructor() {
 14     }
 15     /**
 16      * 向列表尾部添加一个新的项
 17      * @param element 
 18      */
 19     public append(element) {
 20         let node = new Node(element), current;
 21 
 22         if (this.head === null) {
 23             this.head = node;
 24         } else {
 25             current = this.head;
 26 
 27             // 循环列表,知道找到最后一项
 28             while (current.next) {
 29                 current = current.next;
 30             }
 31 
 32             // 找到最后一项,将其next赋为node,建立链接
 33             current.next = node;
 34         }
 35         this.length++; // 更新列表长度
 36     }
 37     /**
 38      * 向列表的特定位置插入一个新的项
 39      * @param position 
 40      * @param element 
 41      */
 42     public insert(position, element) {
 43         // 检查越界值
 44         if (position >= 0 && position <= length) {
 45             let node = new Node(element),
 46                 current = this.head,
 47                 previous,
 48                 index = 0;
 49 
 50             if (position === 0) { // 在第一个位置添加
 51                 node.next = current;
 52                 this.head = node;
 53             } else {
 54                 while (index++ < position) {
 55                     previous = current;
 56                     current = current.next;
 57                 }
 58                 node.next = current;
 59                 previous.next = node;
 60             }
 61             this.length++; // 更新列表长度
 62             return true;
 63         } else {
 64             return false;
 65         }
 66     }
 67     /**
 68      * 从列表的特定位置移除一项
 69      * @param position 
 70      */
 71     public removeAt(position) {
 72         // 检查越界值
 73         if (position > -1 && position < this.length) {
 74             let current = this.head,
 75                 previous,
 76                 index = 0;
 77 
 78             // 移除第一项
 79             if (position === 0) {
 80                 this.head = current.next;
 81             } else {
 82                 while (index++ < position) {
 83                     previous = current;
 84                     current = current.next;
 85                 }
 86                 // 将previous与current的下一项链接起来:跳过current,从而移除它
 87                 previous.next = current.next;
 88             }
 89             this.length--;
 90             return current.element;
 91         } else {
 92             return null;
 93         }
 94     }
 95     /**
 96      * 从列表中移除一项
 97      * @param element 
 98      */
 99     public remove(element) {
100         let index = this.indexOf(element);
101         return this.removeAt(index);
102     }
103     /**
104      * :返回元素在列表中的索引。如果列表中没有该元素则返回-1
105      * @param element 
106      */
107     public indexOf(element) {
108         let current = this.head,
109             index = -1;
110 
111         while (current) {
112             if (element === current.element) {
113                 return index;
114             }
115             index++;
116             current = current.next;
117         }
118         return -1;
119     }
120     /**
121      * 如果链表中不包含任何元素, 返回true, 如果链表长度大于0则返回false
122      */
123     public isEmpty() {
124         return this.length === 0;
125     }
126     /**
127      * 返回链表包含的元素个数。与数组的length属性类似
128      */
129     public size() {
130         return this.length;
131     }
132     /**
133      * 由于列表项使用了Node类,就需要重写继承自JavaScript对象默认的toString方法,让其只输出元素的值
134      */
135     public toString() {
136         let current = this.head,
137             string = '';
138 
139         while (current) {
140             string += current.element;
141             current = current.next;
142         }
143         return string;
144     }
145     public getHead() {
146         return this.head;
147     }
148     public print() {
149         console.log(this.toString());
150     }
151 }
View Code
// 代码解读:
    // 充分利用引用对象特性,将需要管理的数据加入链表的数据结构中,
    // 完全不依赖系统数组的数据结构,所以避免大量删、增数据数组排序的性能损耗。同时也失去了数组下标取数据的优势。
    // 因此优势劣势非常明显,大量改删数据场景选用链表,大量已知下标去更新数据选用数组

// 抽象:
    // 火车车厢可长可短,随时加入或去掉一节车厢仅仅对操作车厢前后一节有改动,当车厢非常长的时候,这个优势尤为明显,
    // 这仅仅是一种应用场景也可以通过火车的例子去理解这种数据结构的设计理念

// 总结:
    // 链表以引用作为媒介,将大量不相干的数据进行一个有序的链接,同时没有复杂的关联关系,仅仅关系操作数据前后关联数据
    // 面对大量增加删除的场景,链表将是比数组更好的选择

 

JavaScript方式实现源码
  1 var Node = (function () {
  2     function Node(element) {
  3         this.element = element;
  4         this.next = null;
  5     }
  6     return Node;
  7 }());
  8 var LinkedList = (function () {
  9     function LinkedList() {
 10         this.length = 0;
 11         this.head = null;
 12     }
 13     /**
 14      * 向列表尾部添加一个新的项
 15      * @param element
 16      */
 17     LinkedList.prototype.append = function (element) {
 18         var node = new Node(element), current;
 19         if (this.head === null) {
 20             this.head = node;
 21         }
 22         else {
 23             current = this.head;
 24             // 循环列表,知道找到最后一项
 25             while (current.next) {
 26                 current = current.next;
 27             }
 28             // 找到最后一项,将其next赋为node,建立链接
 29             current.next = node;
 30         }
 31         this.length++; // 更新列表长度
 32     };
 33     /**
 34      * 向列表的特定位置插入一个新的项
 35      * @param position
 36      * @param element
 37      */
 38     LinkedList.prototype.insert = function (position, element) {
 39         // 检查越界值
 40         if (position >= 0 && position <= length) {
 41             var node = new Node(element), current = this.head, previous = void 0, index = 0;
 42             if (position === 0) {
 43                 node.next = current;
 44                 this.head = node;
 45             }
 46             else {
 47                 while (index++ < position) {
 48                     previous = current;
 49                     current = current.next;
 50                 }
 51                 node.next = current;
 52                 previous.next = node;
 53             }
 54             this.length++; // 更新列表长度
 55             return true;
 56         }
 57         else {
 58             return false;
 59         }
 60     };
 61     /**
 62      * 从列表的特定位置移除一项
 63      * @param position
 64      */
 65     LinkedList.prototype.removeAt = function (position) {
 66         // 检查越界值
 67         if (position > -1 && position < this.length) {
 68             var current = this.head, previous = void 0, index = 0;
 69             // 移除第一项
 70             if (position === 0) {
 71                 this.head = current.next;
 72             }
 73             else {
 74                 while (index++ < position) {
 75                     previous = current;
 76                     current = current.next;
 77                 }
 78                 // 将previous与current的下一项链接起来:跳过current,从而移除它
 79                 previous.next = current.next;
 80             }
 81             this.length--;
 82             return current.element;
 83         }
 84         else {
 85             return null;
 86         }
 87     };
 88     /**
 89      * 从列表中移除一项
 90      * @param element
 91      */
 92     LinkedList.prototype.remove = function (element) {
 93         var index = this.indexOf(element);
 94         return this.removeAt(index);
 95     };
 96     /**
 97      * :返回元素在列表中的索引。如果列表中没有该元素则返回-1
 98      * @param element
 99      */
100     LinkedList.prototype.indexOf = function (element) {
101         var current = this.head, index = -1;
102         while (current) {
103             if (element === current.element) {
104                 return index;
105             }
106             index++;
107             current = current.next;
108         }
109         return -1;
110     };
111     /**
112      * 如果链表中不包含任何元素, 返回true, 如果链表长度大于0则返回false
113      */
114     LinkedList.prototype.isEmpty = function () {
115         return this.length === 0;
116     };
117     /**
118      * 返回链表包含的元素个数。与数组的length属性类似
119      */
120     LinkedList.prototype.size = function () {
121         return this.length;
122     };
123     /**
124      * 由于列表项使用了Node类,就需要重写继承自JavaScript对象默认的toString方法,让其只输出元素的值
125      */
126     LinkedList.prototype.toString = function () {
127         var current = this.head, string = '';
128         while (current) {
129             string += current.element;
130             current = current.next;
131         }
132         return string;
133     };
134     LinkedList.prototype.getHead = function () {
135         return this.head;
136     };
137     LinkedList.prototype.print = function () {
138         console.log(this.toString());
139     };
140     return LinkedList;
141 }());
View Code

推荐阅读