1
+ import Node from './NodeLL.ts' ;
2
+
3
+ export default class LinkedList < T > {
4
+ private head : Node < T > | null ;
5
+ private tail : Node < T > | null ;
6
+ private length : number ;
7
+
8
+ constructor ( ) {
9
+ this . head = null ;
10
+ this . tail = null ;
11
+ this . length = 0 ;
12
+ }
13
+
14
+ public getLength ( ) : number {
15
+ return this . length ;
16
+ }
17
+
18
+ public isEmpty ( ) : boolean {
19
+ return this . length === 0 ;
20
+ }
21
+
22
+ public append ( value : T ) : boolean {
23
+ const newNode = new Node ( value ) ;
24
+
25
+ if ( ! this . tail ) {
26
+ this . head = newNode ;
27
+ this . tail = newNode ;
28
+ }
29
+ else {
30
+ this . tail . setNext ( newNode ) ;
31
+ this . tail = this . tail . getNext ( ) ;
32
+ }
33
+ ++ this . length ;
34
+
35
+ return true ;
36
+ }
37
+
38
+ public prepend ( value : T ) : boolean {
39
+ const newNode = new Node ( value ) ;
40
+
41
+ if ( ! this . head ) {
42
+ this . head = newNode ;
43
+ this . tail = newNode ;
44
+ } else {
45
+ newNode . setNext ( this . head ) ;
46
+ this . head = newNode ;
47
+ }
48
+ ++ this . length ;
49
+
50
+ return true ;
51
+ }
52
+
53
+ public insert ( value : T , atIndex : number ) : boolean | null {
54
+ if ( atIndex < 0 || atIndex > this . length ) return null ;
55
+ if ( ! this . head ) {
56
+ this . prepend ( value ) ;
57
+ return true ;
58
+ }
59
+ if ( atIndex === this . length ) {
60
+ this . append ( value ) ;
61
+ return true ;
62
+ }
63
+
64
+
65
+ const newNode = new Node ( value ) ;
66
+
67
+ let currentNode : Node < T > = this . head ;
68
+
69
+ for ( let i = 0 ; i < atIndex - 1 ; ++ i ) {
70
+ currentNode = currentNode . getNext ( ) ;
71
+ }
72
+
73
+ newNode . setNext ( currentNode . getNext ( ) ) ;
74
+ currentNode . setNext ( newNode ) ;
75
+
76
+ ++ this . length ;
77
+
78
+ return true ;
79
+ }
80
+
81
+ public removeElementAtIndex ( index : number ) : T | null {
82
+ if ( index > this . length - 1 || index < 0 ) return null ;
83
+ if ( this . length === 0 || ! this . head ) return null ;
84
+
85
+ let value = null ;
86
+
87
+ if ( index === 0 ) {
88
+ value = this . head . getValue ( ) ;
89
+ this . head = this . head . getNext ( ) ;
90
+ }
91
+ else {
92
+ let currentNode = this . head ;
93
+
94
+ for ( let i = 0 ; i < index ; ++ i ) { // Traverse linked list to find Node index-1
95
+ if ( i === index - 1 ) {
96
+ value = currentNode . getNext ( ) . getValue ( ) ; // Store Node value
97
+ break ;
98
+ }
99
+ currentNode = currentNode . getNext ( ) ;
100
+ }
101
+ currentNode . setNext ( currentNode . getNext ( ) . getNext ( ) ) ; // Manipulate pointers to mark deleted Node for garbage collection
102
+ }
103
+
104
+ -- this . length ; // Decrement length
105
+
106
+ return value ;
107
+ }
108
+
109
+ public getValueAtIndex ( index : number ) : T | null {
110
+ if ( index > this . length || index < 0 ) return null ; // Validate input
111
+ if ( this . length === 0 || ! this . head || ! this . tail ) return null ; // Verify that list is not empty
112
+ if ( index === this . length - 1 ) return this . tail . getValue ( ) ; // Optimization when retrieving last element
113
+
114
+ let currentNode = this . head ;
115
+
116
+ for ( let i = 0 ; i <= index ; ++ i ) {
117
+ if ( i === index ) return currentNode . getValue ( ) ;
118
+ currentNode = currentNode . getNext ( ) ;
119
+ }
120
+
121
+ return null ;
122
+ }
123
+
124
+ /**
125
+ * Find the index of the desired element if it exists.
126
+ * WARNING: Usable only for trees of primitive types, i.e. number, string
127
+ * @param value Value to search for, i.e. desired element
128
+ * @returns Numerical index of element position
129
+ */
130
+ public searchFor ( value : T ) : number | null {
131
+ if ( this . length === 0 || ! this . head ) return null ;
132
+
133
+ let currentNode = this . head ;
134
+
135
+ for ( let i = 0 ; ! ! currentNode ; ++ i ) {
136
+ if ( currentNode . getValue ( ) === value ) return i ; // Value matches, so return index
137
+ currentNode = currentNode . getNext ( ) ; // Otherwise, continue searching
138
+ }
139
+ return null ;
140
+ }
141
+
142
+ /**
143
+ * Remove all nodes from list. Constant Time O(1)
144
+ */
145
+ public empty ( ) : boolean {
146
+ this . head = null ;
147
+ this . tail = null ;
148
+ this . length = 0 ;
149
+ return true ;
150
+ }
151
+
152
+ public toString ( nodesPerGroup ?: number ) : string {
153
+ if ( this . length === 0 || ! this . head ) {
154
+ return "" ;
155
+ }
156
+
157
+ nodesPerGroup = nodesPerGroup ? nodesPerGroup : 8 ;
158
+
159
+ const LABEL_HEAD = 'HEAD' ;
160
+ const LABEL_TAIL = 'TAIL' ;
161
+
162
+ let listAsString : string = `--- Node Count: ${ this . length } \n` ;
163
+ let currentNode : Node < any > = this . head ;
164
+
165
+ for ( let i = 0 ; i < this . length ; ++ i ) {
166
+ listAsString += `${ Array ( i % nodesPerGroup ) . fill ( '\t' ) . join ( '' ) } ${ i === 0 ? LABEL_HEAD : '–' } ${ currentNode } ${ i === this . length - 1 ? LABEL_TAIL : '' } \n` ;
167
+
168
+ if ( currentNode . hasNext ( ) ) {
169
+ currentNode = currentNode . getNext ( ) ;
170
+ }
171
+ }
172
+
173
+ return listAsString ;
174
+ }
175
+ }
176
+
177
+ function printLinkedList ( linkedList : LinkedList < any > ) : void {
178
+ if ( linkedList . isEmpty ( ) ) {
179
+ console . log ( 'Empty linked list -_-' ) ;
180
+ return ;
181
+ }
182
+ console . log ( linkedList . toString ( ) ) ;
183
+ }
184
+
185
+ function printSearchFor ( value : any , linkedList : LinkedList < any > ) : void {
186
+ console . log ( JSON . stringify ( value ) , 'found at index:' , linkedList . searchFor ( value ) ) ;
187
+ }
188
+
189
+ function printGetValueAtIndex ( index : number , linkedList : LinkedList < any > ) {
190
+ console . log ( 'Element at index' , index + ':' , linkedList . getValueAtIndex ( index ) ) ;
191
+ }
192
+
193
+ function printRemoveIndex ( index : number , linkedList : LinkedList < any > ) {
194
+ console . log ( 'Removing element at index' , index + ':' , JSON . stringify ( linkedList . removeElementAtIndex ( index ) ) ) ;
195
+ }
196
+
197
+
198
+
199
+ //---------------------------------------------------------------------
200
+ // ---------- MAIN PROGRAM ----------
201
+ //---------------------------------------------------------------------
202
+ if ( import . meta. main ) {
203
+
204
+ const ATLA = new LinkedList < string > ( ) ;
205
+
206
+ ATLA . append ( 'Sokka' ) ;
207
+ ATLA . append ( 'Katara' ) ;
208
+ ATLA . prepend ( 'Appa' ) ;
209
+
210
+ printLinkedList ( ATLA ) ;
211
+
212
+ ATLA . insert ( 'Aang' , 2 ) ;
213
+ ATLA . insert ( 'Zuko' , 1 ) ;
214
+ ATLA . insert ( 'Iroh' , 4 ) ;
215
+
216
+ printLinkedList ( ATLA ) ;
217
+
218
+ printSearchFor ( 'Iroh' , ATLA ) ;
219
+ printSearchFor ( 'Sok' , ATLA ) ;
220
+ printSearchFor ( 'Sokka' , ATLA ) ;
221
+ printSearchFor ( 'Appa' , ATLA ) ;
222
+ printSearchFor ( 'Zuko' , ATLA ) ;
223
+ printGetValueAtIndex ( 2 , ATLA ) ;
224
+ printGetValueAtIndex ( 5 , ATLA ) ;
225
+ console . log ( '----------------------------------' ) ;
226
+
227
+ printRemoveIndex ( 1 , ATLA ) ;
228
+ printLinkedList ( ATLA ) ;
229
+ printRemoveIndex ( 0 , ATLA ) ;
230
+ printLinkedList ( ATLA ) ;
231
+ printRemoveIndex ( 2 , ATLA ) ;
232
+ printLinkedList ( ATLA ) ;
233
+ printRemoveIndex ( 2 , ATLA ) ;
234
+ printLinkedList ( ATLA ) ;
235
+ printRemoveIndex ( 1 , ATLA ) ;
236
+ printRemoveIndex ( 0 , ATLA ) ;
237
+ printLinkedList ( ATLA ) ;
238
+ console . log ( '----------------------------------' ) ;
239
+
240
+ ATLA . insert ( 'Katara' , 0 ) ;
241
+ printLinkedList ( ATLA ) ;
242
+ ATLA . append ( 'Aang' ) ;
243
+ printLinkedList ( ATLA ) ;
244
+
245
+ // RUN: deno run Data-Structures/Linked-Lists/LinkedList.ts
246
+ }
247
+
248
+
249
+ // --------------------------- Terminal Output: ---------------------------
250
+ // --- Node Count: 3
251
+ // HEAD { value: "Appa", next: true }
252
+ // – { value: "Sokka", next: true }
253
+ // – { value: "Katara", next: false } TAIL
254
+ //
255
+ // --- Node Count: 6
256
+ // HEAD { value: "Appa", next: true }
257
+ // – { value: "Zuko", next: true }
258
+ // – { value: "Sokka", next: true }
259
+ // – { value: "Aang", next: true }
260
+ // – { value: "Iroh", next: true }
261
+ // – { value: "Katara", next: false } TAIL
262
+ //
263
+ // "Iroh" found at index: 4
264
+ // "Sok" found at index: null
265
+ // "Sokka" found at index: 2
266
+ // "Appa" found at index: 0
267
+ // "Zuko" found at index: 1
268
+ // Element at index 2: Sokka
269
+ // Element at index 5: Katara
270
+ // ----------------------------------
271
+ // Removing element at index 1: "Zuko"
272
+ // --- Node Count: 5
273
+ // HEAD { value: "Appa", next: true }
274
+ // – { value: "Sokka", next: true }
275
+ // – { value: "Aang", next: true }
276
+ // – { value: "Iroh", next: true }
277
+ // – { value: "Katara", next: false } TAIL
278
+ //
279
+ // Removing element at index 0: "Appa"
280
+ // --- Node Count: 4
281
+ // HEAD { value: "Sokka", next: true }
282
+ // – { value: "Aang", next: true }
283
+ // – { value: "Iroh", next: true }
284
+ // – { value: "Katara", next: false } TAIL
285
+ //
286
+ // Removing element at index 2: "Iroh"
287
+ // --- Node Count: 3
288
+ // HEAD { value: "Sokka", next: true }
289
+ // – { value: "Aang", next: true }
290
+ // – { value: "Katara", next: false } TAIL
291
+ //
292
+ // Removing element at index 2: "Katara"
293
+ // --- Node Count: 2
294
+ // HEAD { value: "Sokka", next: true }
295
+ // – { value: "Aang", next: false } TAIL
296
+ //
297
+ // Removing element at index 1: "Aang"
298
+ // Removing element at index 0: "Sokka"
299
+ // Empty linked list -_-
300
+ // ----------------------------------
301
+ // --- Node Count: 1
302
+ // HEAD { value: "Katara", next: false } TAIL
303
+ //
304
+ // --- Node Count: 2
305
+ // HEAD { value: "Katara", next: true }
306
+ // – { value: "Aang", next: false } TAIL
0 commit comments