-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
DoublyLinkedList.cs
333 lines (291 loc) · 9.33 KB
/
DoublyLinkedList.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
using System;
using System.Collections.Generic;
using Utilities.Exceptions;
namespace DataStructures.LinkedList.DoublyLinkedList;
/// <summary>
/// Similar to a Singly Linked List but each node contains a refenrence to the previous node in the list.
/// <see cref="System.Collections.Generic.LinkedList{T}" /> is a doubly linked list.
/// Compared to singly linked lists it can be traversed forwards and backwards.
/// Adding a node to a doubly linked list is simpler because ever node contains a reference to the previous node.
/// </summary>
/// <typeparam name="T">Generic type.</typeparam>
public class DoublyLinkedList<T>
{
/// <summary>
/// Initializes a new instance of the <see cref="DoublyLinkedList{T}" /> class.
/// </summary>
/// <param name="data"> Data of the original head of the list.</param>
public DoublyLinkedList(T data)
{
Head = new DoublyLinkedListNode<T>(data);
Tail = Head;
Count = 1;
}
/// <summary>
/// Initializes a new instance of the <see cref="DoublyLinkedList{T}" /> class from an enumerable.
/// </summary>
/// <param name="data"> Enumerable of data to be stored in the list.</param>
public DoublyLinkedList(IEnumerable<T> data)
{
foreach (var d in data)
{
Add(d);
}
}
/// <summary>
/// Gets the amount of nodes in the list.
/// </summary>
public int Count { get; private set; }
/// <summary>
/// Gets or sets the first node of the list.
/// </summary>
private DoublyLinkedListNode<T>? Head { get; set; }
/// <summary>
/// Gets or sets the last node of the list.
/// </summary>
private DoublyLinkedListNode<T>? Tail { get; set; }
/// <summary>
/// Replaces the Head of the list with the new value.
/// </summary>
/// <param name="data"> Value for the new Head of the list.</param>
/// <returns>The new Head node.</returns>
public DoublyLinkedListNode<T> AddHead(T data)
{
var node = new DoublyLinkedListNode<T>(data);
if (Head is null)
{
Head = node;
Tail = node;
Count = 1;
return node;
}
Head.Previous = node;
node.Next = Head;
Head = node;
Count++;
return node;
}
/// <summary>
/// Adds a new value at the end of the list.
/// </summary>
/// <param name="data"> New value to be added to the list.</param>
/// <returns>The new node created based on the new value.</returns>
public DoublyLinkedListNode<T> Add(T data)
{
if (Head is null)
{
return AddHead(data);
}
var node = new DoublyLinkedListNode<T>(data);
Tail!.Next = node;
node.Previous = Tail;
Tail = node;
Count++;
return node;
}
/// <summary>
/// Adds a new value after an existing node.
/// </summary>
/// <param name="data"> New value to be added to the list.</param>
/// <param name="existingNode"> An existing node in the list.</param>
/// <returns>The new node created based on the new value.</returns>
public DoublyLinkedListNode<T> AddAfter(T data, DoublyLinkedListNode<T> existingNode)
{
if (existingNode == Tail)
{
return Add(data);
}
var node = new DoublyLinkedListNode<T>(data);
node.Next = existingNode.Next;
node.Previous = existingNode;
existingNode.Next = node;
if (node.Next is not null)
{
node.Next.Previous = node;
}
Count++;
return node;
}
/// <summary>
/// Gets an enumerable based on the data in the list.
/// </summary>
/// <returns>The data in the list in an IEnumerable. It can used to create a list or an array with LINQ.</returns>
public IEnumerable<T> GetData()
{
var current = Head;
while (current is not null)
{
yield return current.Data;
current = current.Next;
}
}
/// <summary>
/// Gets an enumerable based on the data in the list reversed.
/// </summary>
/// <returns>The data in the list in an IEnumerable. It can used to create a list or an array with LINQ.</returns>
public IEnumerable<T> GetDataReversed()
{
var current = Tail;
while (current is not null)
{
yield return current.Data;
current = current.Previous;
}
}
/// <summary>
/// Reverses the list. Because of how doubly linked list are structured this is not a complex action.
/// </summary>
public void Reverse()
{
var current = Head;
DoublyLinkedListNode<T>? temp = null;
while (current is not null)
{
temp = current.Previous;
current.Previous = current.Next;
current.Next = temp;
current = current.Previous;
}
Tail = Head;
// temp can be null on empty list
if (temp is not null)
{
Head = temp.Previous;
}
}
/// <summary>
/// Looks for a node in the list that contains the value of the parameter.
/// </summary>
/// <param name="data"> Value to be looked for in a node.</param>
/// <returns>The node in the list the has the paramater as a value or null if not found.</returns>
public DoublyLinkedListNode<T> Find(T data)
{
var current = Head;
while (current is not null)
{
if (current.Data is null && data is null || current.Data is not null && current.Data.Equals(data))
{
return current;
}
current = current.Next;
}
throw new ItemNotFoundException();
}
/// <summary>
/// Looks for a node in the list that contains the value of the parameter.
/// </summary>
/// <param name="position"> Position in the list.</param>
/// <returns>The node in the list the has the paramater as a value or null if not found.</returns>
/// <exception cref="ArgumentOutOfRangeException">Thrown when position is negative or out range of the list.</exception>
public DoublyLinkedListNode<T> GetAt(int position)
{
if (position < 0 || position >= Count)
{
throw new ArgumentOutOfRangeException($"Max count is {Count}");
}
var current = Head;
for (var i = 0; i < position; i++)
{
current = current!.Next;
}
return current ?? throw new ArgumentOutOfRangeException($"{nameof(position)} must be an index in the list");
}
/// <summary>
/// Removes the Head and replaces it with the second node in the list.
/// </summary>
public void RemoveHead()
{
if (Head is null)
{
throw new InvalidOperationException();
}
Head = Head.Next;
if (Head is null)
{
Tail = null;
Count = 0;
return;
}
Head.Previous = null;
Count--;
}
/// <summary>
/// Removes the last node in the list.
/// </summary>
public void Remove()
{
if (Tail is null)
{
throw new InvalidOperationException("Cannot prune empty list");
}
Tail = Tail.Previous;
if (Tail is null)
{
Head = null;
Count = 0;
return;
}
Tail.Next = null;
Count--;
}
/// <summary>
/// Removes specific node.
/// </summary>
/// <param name="node"> Node to be removed.</param>
public void RemoveNode(DoublyLinkedListNode<T> node)
{
if (node == Head)
{
RemoveHead();
return;
}
if (node == Tail)
{
Remove();
return;
}
if (node.Previous is null || node.Next is null)
{
throw new ArgumentException(
$"{nameof(node)} cannot have Previous or Next null if it's an internal node");
}
node.Previous.Next = node.Next;
node.Next.Previous = node.Previous;
Count--;
}
/// <summary>
/// Removes a node that contains the data from the parameter.
/// </summary>
/// <param name="data"> Data to be removed form the list.</param>
public void Remove(T data)
{
var node = Find(data);
RemoveNode(node);
}
/// <summary>
/// Looks for the index of the node with the parameter as data.
/// </summary>
/// <param name="data"> Data to look for.</param>
/// <returns>Returns the index of the node if it is found or -1 if the node is not found.</returns>
public int IndexOf(T data)
{
var current = Head;
var index = 0;
while (current is not null)
{
if (current.Data is null && data is null || current.Data is not null && current.Data.Equals(data))
{
return index;
}
index++;
current = current.Next;
}
return -1;
}
/// <summary>
/// List contains a node that has the parameter as data.
/// </summary>
/// <param name="data"> Node to be removed.</param>
/// <returns>True if the node is found. False if it isn't.</returns>
public bool Contains(T data) => IndexOf(data) != -1;
}