链表

链表

链表存储有序的元素集合,但不同于数组,链表中的元素在内存中并不是连续放置的。每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(也称指针或链接)组成

如下图:

相对于传统的数组,链表的一个好处在于,添加或移除元素的时候不需要移动其他元素。然而,链表需要使用指针,因此实现链表时需要额外注意。在数组中,我们可以直接访问任何位置的任何元素,而要想访问链表中间的一个元素,则需要从起点(表头)开始迭代链表直到找到所需的元素。

创建链表

 1 // 待实现功能
 2 // push(element):向链表尾部添加一个新元素。
 3 // insert(element, position):向链表的特定位置插入一个新元素。
 4 // getElementAt(index):返回链表中特定位置的元素。如果链表中不存在这样的元素,则返回 undefined。 
 5 // remove(element):从链表中移除一个元素。
 6 // indexOf(element):返回元素在链表中的索引。如果链表中没有该元素则返回-1。
 7 // removeAt(position):从链表的特定位置移除一个元素。
 8 // isEmpty():如果链表中不包含任何元素,返回 true,如果链表长度大于0则返回 false。 
 9 // size():返回链表包含的元素个数,与数组的 length 属性类似。
10 // toString():返回表示整个链表的字符串。
  1 function defaultEquals(a, b){
  2     // 用于比较链表中元素是否相等
  3     return a===b
  4 }
  5 
  6 class Node{
  7     // 节点
  8     constructor(element){
  9         this.element = element
 10         this.next = undefined
 11     }
 12 }
 13 
 14 class LinkedList{
 15     // 链表
 16     constructor(equalsFn = defaultEquals){
 17         this.count = 0      // 长度
 18         this.head = undefined   // 头元素
 19         this.equalsFn = equalsFn
 20     }
 21 
 22     push(element){
 23         // 向链表末尾添加一个元素
 24         // 1.链表为空,添加为头部元素
 25         // 2.链表不为空,追加元素
 26         const node = new Node(element)
 27         let current
 28         if(!this.head){
 29             // 如果没有头元素,则直接插入头部
 30             this.head = node
 31         }else{
 32             // 如果有头元素,则依次查找至末尾,添加到末尾处
 33             current = this.head
 34             while(current.next){
 35                 current = current.next
 36             }
 37             current.next = node
 38         }
 39         this.count++
 40     }
 41     getElementAt(index){
 42         if(index >= 0 && index < this.count){
 43             let node = this.head
 44             for(let i = 0; i < index && node; i++){
 45                 node = node.next
 46             }
 47             return node
 48         }else{
 49             return undefined
 50         }
 51     }
 52     removeAt(index){
 53         // 删除指定索引处的元素,返回删除元素
 54         let current = this.head
 55         if (index === 0){
 56             // 若待删除索引为0,则直接替换头元素
 57             this.head = current.next
 58         }else{
 59             // 若索引不为0,查找至待删除索引上一位,直接连接待删除索引下一位
 60             const previous = this.getElementAt(index-1)
 61             current = previous.next
 62             previous.next = current.next    // 去除index处元素
 63         }
 64         this.count--
 65         return current.element        
 66     }
 67     insert(element, index){
 68         if(0 <= index <= this.count){
 69             const node = new Node(element)
 70             if(index === 0){
 71                 const current = this.head
 72                 node.next = current
 73                 this.head = node
 74             }else{
 75                 const previous = this.getElementAt(index-1)
 76                 const current = previous.next
 77                 node.next = current
 78                 previous.next = node
 79             }
 80             this.count++
 81             return true
 82         }
 83         return false
 84     }
 85     indexOf(element){
 86         let current = this.head
 87         for(let i = 0; i < this.count; i++){
 88             if (this.equalsFn(current.element, element)) {
 89                 return i
 90             }
 91             current = current.next
 92         }
 93         return -1
 94     }
 95     remove(element){
 96         // 移除某个元素
 97         const index = this.indexOf(element)
 98         return this.removeAt(index)
 99     }
100     isEmpty(){
101         return this.count === 0
102     }
103     size(){
104         return this.count
105     }
106     getHead(){
107         return this.head
108     }
109     toString(){
110         // toString方法
111         if(!this.head){
112             return ""
113         }else{
114             let str = `${this.head.element}`
115             let current = this.head.next
116             for(let i = 1; i < this.size() && current !== null; i++){
117                 str = `${str},${current.element}`
118                 current=current.next
119             }
120             return str
121         }
122     }
123 
124 }

 双向链表

双向链表和普通链表的区别在于,在链表中,一个节点只有链向下一个节点的链接;而在双向链表中,链接是双向的:一个链向下一个元素,另一个链向前一个元素

对单向链表进行扩展,主要重写插入和删除方法,需要考虑每个节点有向前的指针

 1 class DoubleLinkedList extends LinkedList{
 2     constructor(equalsFn = defaultEquals){
 3         super(equalsFn)
 4         this.tail = undefined   // 最后一个节点
 5     }
 6     insert(element, index){
 7         // 在任意位置添加新元素
 8         if(index >=0 && index <= this.count){   // 索引是否超限
 9             const node = new DoubleNode(element)
10             let current = this.head
11             if (index === 0) {  
12                 // 若索引为0
13                 if (!this.head) {
14                     // 空链表则直接加到头部
15                     this.head = node
16                     this.tail = node
17                 }else{
18                     node.next = this.head
19                     current.prev = node
20                     this.head = node
21                 }
22             }else if(index === this.count){
23                 // 若索引为最后
24                 current =this.tail
25                 current.next = node
26                 node.prev = current
27                 this.tail = node
28             }else{
29                 // 其余索引
30                 const previous = this.getElementAt(index - 1)
31                 current = previous.next
32                 node.next = current
33                 node.prev = previous
34                 previous.next = node
35                 current.prev = node
36             }
37             this.count++
38             return true
39         }
40         return false
41     }
42     removeAt(index){
43         // 按索引删除
44         if(index >= 0 && index < this.count){    // 索引是否超限
45             let current = this.head     
46             if(index === 0){
47                 // 删除头元素
48                 this.head = current.next
49                 if (this.count === 1) {
50                     // 若只有一个元素
51                     this.tail = undefined
52                 }else{
53                     this.head.prev = undefined
54                 }
55             }else if(index === this.count - 1){
56                 // 删除末尾元素
57                 current = this.tail
58                 this.tail = current.prev
59                 this.tail.next = undefined
60             }else{
61                 current = this.getElementAt(index)
62                 const previous = current.prev
63                 previous.next = current.next
64                 current.next.prev = previous
65             }
66             this.count--
67             return current.element
68         }
69         return undefined
70     }
71 }

 有序链表

有序链表是指保持元素有序的链表结构。新元素将按照固定的排序算法插入
 1 function defaultEquals(a, b){
 2     // 用于比较链表中元素是否相等
 3     return a===b
 4 }
 5 
 6 const compare = {
 7     Less_Than : -1,
 8     Bigger_Than : 1
 9 }
10 
11 function defaultCompare(a, b){
12     // 比较大小
13     if(a === b){
14         return 0
15     }
16     return a < b? compare.Less_Than : compare.Bigger_Than
17 }
18 
19 class SortedLinkedList extends LinkedList{
20     constructor(equalsFn = defaultEquals, compareFn = defaultCompare){
21         super(equalsFn)
22         this.compareFn = compareFn
23     }
24 
25     // 因为自动排序,所以要重写insert
26     insert(element){
27         if(this.isEmpty){
28             // 若是空链表,则直接插入即可
29             return super.insert(element, 0)
30         }else{
31             // 若不是空链表,则插入排序后的位置
32             return super.insert(element, getIndexNextSortedElement(element))
33         }
34 
35     }
36 
37     getIndexNextSortedElement(element){
38         // 排序,找到元素的位置
39         let current = this.head
40         let i = 0
41         for(; i < this.size() && current; i++){
42             const comp = this.compareFn(element, current.element)
43             if (comp === compare.Less_Than) {
44                 return i
45             }
46             current = current.next
47         }
48         return i
49     }
50 
51 
52 }

 使用链表实现栈数据结构

 1 class StackLinkedList{
 2     constructor(){
 3         this.items = new DoubleLinkedList()
 4     }
 5     push(element){
 6         this.items.insert(this.items.size())
 7     }
 8     pop(){
 9         if (!this.items.isEmpty()) {
10             return undefined
11         }
12         this.items.removeAt(this.size()-1)
13     }
14     // 使用双向链表实现栈数据结构比单向链表简单的多,同样也可实现队列
15 
16 }

 

 

posted @ 2021-08-02 15:41  邢韬  阅读(51)  评论(0编辑  收藏  举报