Skip to content

Commit bec0c9c

Browse files
BarklimBarklim
authored andcommitted
Create 0206.js
1 parent 02a3ca7 commit bec0c9c

File tree

5 files changed

+587
-28
lines changed

5 files changed

+587
-28
lines changed

dStructure/linkedList.js

Lines changed: 363 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,363 @@
1+
class Comparator {
2+
/**
3+
* Constructor.
4+
* @param {function(a: *, b: *)} [compareFunction] - It may be custom compare function that, let's
5+
* say may compare custom objects together.
6+
*/
7+
constructor(compareFunction) {
8+
this.compare = compareFunction || Comparator.defaultCompareFunction;
9+
}
10+
11+
/**
12+
* Default comparison function. It just assumes that "a" and "b" are strings or numbers.
13+
* @param {(string|number)} a
14+
* @param {(string|number)} b
15+
* @returns {number}
16+
*/
17+
static defaultCompareFunction(a, b) {
18+
if (a === b) {
19+
return 0;
20+
}
21+
22+
return a < b ? -1 : 1;
23+
}
24+
25+
/**
26+
* Checks if two variables are equal.
27+
* @param {*} a
28+
* @param {*} b
29+
* @return {boolean}
30+
*/
31+
equal(a, b) {
32+
return this.compare(a, b) === 0;
33+
}
34+
35+
/**
36+
* Checks if variable "a" is less than "b".
37+
* @param {*} a
38+
* @param {*} b
39+
* @return {boolean}
40+
*/
41+
lessThan(a, b) {
42+
return this.compare(a, b) < 0;
43+
}
44+
45+
/**
46+
* Checks if variable "a" is greater than "b".
47+
* @param {*} a
48+
* @param {*} b
49+
* @return {boolean}
50+
*/
51+
greaterThan(a, b) {
52+
return this.compare(a, b) > 0;
53+
}
54+
55+
/**
56+
* Checks if variable "a" is less than or equal to "b".
57+
* @param {*} a
58+
* @param {*} b
59+
* @return {boolean}
60+
*/
61+
lessThanOrEqual(a, b) {
62+
return this.lessThan(a, b) || this.equal(a, b);
63+
}
64+
65+
/**
66+
* Checks if variable "a" is greater than or equal to "b".
67+
* @param {*} a
68+
* @param {*} b
69+
* @return {boolean}
70+
*/
71+
greaterThanOrEqual(a, b) {
72+
return this.greaterThan(a, b) || this.equal(a, b);
73+
}
74+
75+
/**
76+
* Reverses the comparison order.
77+
*/
78+
reverse() {
79+
const compareOriginal = this.compare;
80+
this.compare = (a, b) => compareOriginal(b, a);
81+
}
82+
}
83+
84+
class LinkedListNode {
85+
constructor(value, next = null) {
86+
this.value = value;
87+
this.next = next;
88+
}
89+
90+
toString(callback) {
91+
return callback ? callback(this.value) : `${this.value}`;
92+
}
93+
}
94+
95+
class LinkedList {
96+
/**
97+
* @param {Function} [comparatorFunction]
98+
*/
99+
constructor(comparatorFunction) {
100+
/** @var LinkedListNode */
101+
this.head = null;
102+
103+
/** @var LinkedListNode */
104+
this.tail = null;
105+
106+
this.compare = new Comparator(comparatorFunction);
107+
}
108+
109+
/**
110+
* @param {*} value
111+
* @return {LinkedList}
112+
*/
113+
prepend(value) {
114+
// Make new node to be a head.
115+
const newNode = new LinkedListNode(value, this.head);
116+
this.head = newNode;
117+
118+
// If there is no tail yet let's make new node a tail.
119+
if (!this.tail) {
120+
this.tail = newNode;
121+
}
122+
123+
return this;
124+
}
125+
126+
/**
127+
* @param {*} value
128+
* @return {LinkedList}
129+
*/
130+
append(value) {
131+
const newNode = new LinkedListNode(value);
132+
133+
// If there is no head yet let's make new node a head.
134+
if (!this.head) {
135+
this.head = newNode;
136+
this.tail = newNode;
137+
138+
return this;
139+
}
140+
141+
// Attach new node to the end of linked list.
142+
this.tail.next = newNode;
143+
this.tail = newNode;
144+
145+
return this;
146+
}
147+
148+
/**
149+
* @param {*} value
150+
* @param {number} index
151+
* @return {LinkedList}
152+
*/
153+
insert(value, rawIndex) {
154+
const index = rawIndex < 0 ? 0 : rawIndex;
155+
if (index === 0) {
156+
this.prepend(value);
157+
} else {
158+
let count = 1;
159+
let currentNode = this.head;
160+
const newNode = new LinkedListNode(value);
161+
while (currentNode) {
162+
if (count === index) break;
163+
currentNode = currentNode.next;
164+
count += 1;
165+
}
166+
if (currentNode) {
167+
newNode.next = currentNode.next;
168+
currentNode.next = newNode;
169+
} else {
170+
if (this.tail) {
171+
this.tail.next = newNode;
172+
this.tail = newNode;
173+
} else {
174+
this.head = newNode;
175+
this.tail = newNode;
176+
}
177+
}
178+
}
179+
return this;
180+
}
181+
182+
/**
183+
* @param {*} value
184+
* @return {LinkedListNode}
185+
*/
186+
delete(value) {
187+
if (!this.head) {
188+
return null;
189+
}
190+
191+
let deletedNode = null;
192+
193+
// If the head must be deleted then make next node that is different
194+
// from the head to be a new head.
195+
while (this.head && this.compare.equal(this.head.value, value)) {
196+
deletedNode = this.head;
197+
this.head = this.head.next;
198+
}
199+
200+
let currentNode = this.head;
201+
202+
if (currentNode !== null) {
203+
// If next node must be deleted then make next node to be a next next one.
204+
while (currentNode.next) {
205+
if (this.compare.equal(currentNode.next.value, value)) {
206+
deletedNode = currentNode.next;
207+
currentNode.next = currentNode.next.next;
208+
} else {
209+
currentNode = currentNode.next;
210+
}
211+
}
212+
}
213+
214+
// Check if tail must be deleted.
215+
if (this.compare.equal(this.tail.value, value)) {
216+
this.tail = currentNode;
217+
}
218+
219+
return deletedNode;
220+
}
221+
222+
/**
223+
* @param {Object} findParams
224+
* @param {*} findParams.value
225+
* @param {function} [findParams.callback]
226+
* @return {LinkedListNode}
227+
*/
228+
find({ value = undefined, callback = undefined }) {
229+
if (!this.head) {
230+
return null;
231+
}
232+
233+
let currentNode = this.head;
234+
235+
while (currentNode) {
236+
// If callback is specified then try to find node by callback.
237+
if (callback && callback(currentNode.value)) {
238+
return currentNode;
239+
}
240+
241+
// If value is specified then try to compare by value..
242+
if (value !== undefined && this.compare.equal(currentNode.value, value)) {
243+
return currentNode;
244+
}
245+
246+
currentNode = currentNode.next;
247+
}
248+
249+
return null;
250+
}
251+
252+
/**
253+
* @return {LinkedListNode}
254+
*/
255+
deleteTail() {
256+
const deletedTail = this.tail;
257+
258+
if (this.head === this.tail) {
259+
// There is only one node in linked list.
260+
this.head = null;
261+
this.tail = null;
262+
263+
return deletedTail;
264+
}
265+
266+
// If there are many nodes in linked list...
267+
268+
// Rewind to the last node and delete "next" link for the node before the last one.
269+
let currentNode = this.head;
270+
while (currentNode.next) {
271+
if (!currentNode.next.next) {
272+
currentNode.next = null;
273+
} else {
274+
currentNode = currentNode.next;
275+
}
276+
}
277+
278+
this.tail = currentNode;
279+
280+
return deletedTail;
281+
}
282+
283+
/**
284+
* @return {LinkedListNode}
285+
*/
286+
deleteHead() {
287+
if (!this.head) {
288+
return null;
289+
}
290+
291+
const deletedHead = this.head;
292+
293+
if (this.head.next) {
294+
this.head = this.head.next;
295+
} else {
296+
this.head = null;
297+
this.tail = null;
298+
}
299+
300+
return deletedHead;
301+
}
302+
303+
/**
304+
* @param {*[]} values - Array of values that need to be converted to linked list.
305+
* @return {LinkedList}
306+
*/
307+
fromArray(values) {
308+
values.forEach((value) => this.append(value));
309+
310+
return this;
311+
}
312+
313+
/**
314+
* @return {LinkedListNode[]}
315+
*/
316+
toArray() {
317+
const nodes = [];
318+
319+
let currentNode = this.head;
320+
while (currentNode) {
321+
nodes.push(currentNode);
322+
currentNode = currentNode.next;
323+
}
324+
325+
return nodes;
326+
}
327+
328+
/**
329+
* @param {function} [callback]
330+
* @return {string}
331+
*/
332+
toString(callback) {
333+
return this.toArray().map((node) => node.toString(callback)).toString();
334+
}
335+
336+
/**
337+
* Reverse a linked list.
338+
* @returns {LinkedList}
339+
*/
340+
reverse() {
341+
let currNode = this.head;
342+
let prevNode = null;
343+
let nextNode = null;
344+
345+
while (currNode) {
346+
// Store next node.
347+
nextNode = currNode.next;
348+
349+
// Change next node of the current node so it would link to previous node.
350+
currNode.next = prevNode;
351+
352+
// Move prevNode and currNode nodes one step forward.
353+
prevNode = currNode;
354+
currNode = nextNode;
355+
}
356+
357+
// Reset head and tail.
358+
this.tail = this.head;
359+
this.head = prevNode;
360+
361+
return this;
362+
}
363+
}

0 commit comments

Comments
 (0)