Deque

A deque is a double-ended queue in which elements can be added to either the front or the back. Removal can also occur from either the front or back.

Since adding and deleting must be implemented on both sides, a doubly linked list can be used as the basis for implementation. Thus, each element in the deque will be represented by the following class:

1
2
3
4
5
6
7
8
9
10
public class DoublyNode<T>
{
public DoublyNode(T data)
{
Data = data;
}

public T Data { get; set; }

// FIXED: Changed ‘DoubleNode’ to match the actual class name ‘DoublyNode’
public DoublyNode<T>? Previous { get; set; }
public DoublyNode<T>? Next { get; set; }
}

The deck class will be as follows:

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
using System;
using System.Collections;
using System.Collections.Generic;

namespace SimpleAlgorithmsApp
{
public class Deque<T> : IEnumerable<T>
{
private DoublyNode<T>? head; // head/first element
private DoublyNode<T>? tail; // last/tail element
private int count; // number of elements in the list

// adding an element to the back
public void AddLast(T data)
{
DoublyNode<T> node = new DoublyNode<T>(data);

if (head == null)
{
head = node;
tail = node; // FIXED: If empty, tail must also point to the new node
}
else
{
tail!.Next = node; // Safe bang operator used because tail won’t be null here
node.Previous = tail;
tail = node;
}
count++;
}

// adding an element to the front
public void AddFirst(T data)
{
DoublyNode<T> node = new DoublyNode<T>(data);

if (head == null)
{
head = node;
tail = node; // FIXED: If empty, tail must also point to the new node
}
else
{
node.Next = head;
head.Previous = node;
head = node;
}
count++;
}

public T RemoveFirst()
{
if (count == 0 || head == null)
throw new InvalidOperationException(“Deque is empty.”);

T output = head.Data;
if (count == 1)
{
head = null;
tail = null;
}
else
{
head = head.Next;
head!.Previous = null;
}
count–;
return output;
}

public T RemoveLast()
{
if (count == 0 || tail == null)
throw new InvalidOperationException(“Deque is empty.”);

T output = tail.Data;
if (count == 1)
{
head = null;
tail = null;
}
else
{
tail = tail.Previous;
tail!.Next = null;
}
count–;
return output;
}

// FIXED: ‘Public’ changed to lowercase ‘public’
public T First
{
get
{
if (IsEmpty || head == null)
throw new InvalidOperationException(“Deque is empty.”);
return head.Data;
}
}

public T Last
{
get
{
if (IsEmpty || tail == null)
throw new InvalidOperationException(“Deque is empty.”);
return tail.Data;
}
}

public int Count => count;
public bool IsEmpty => count == 0;

public void Clear()
{
head = null;
tail = null;
count = 0;
}

// FIXED: Handled potential NullReferenceException using generic EqualityComparer
public bool Contains(T data)
{
DoublyNode<T>? current = head;
EqualityComparer<T> comparer = EqualityComparer<T>.Default;

while (current != null)
{
if (comparer.Equals(current.Data, data))
return true;
current = current.Next;
}
return false;
}

// FIXED: Eliminated infinite recursion/StackOverflowException
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}

public IEnumerator<T> GetEnumerator()
{
DoublyNode<T>? current = head;
while (current != null)
{
yield return current.Data;
current = current.Next;
}
}
}
}

Unlike the queue class, there are two methods defined here for adding and two for removing.

To add to the head of the queue, we reset the reference to the head variable:

1
2
3
4
5
6
7
8
9
10
11
12
public void AddFirst(T data)
{
DoublyNode<T> node = new DoublyNode<T>(data);

if (head == null)
{
// FIXED: Explicitly handle the empty-list state
head = node;
tail = node;
}
else
{
// Link the new node to point forward to the current head
node.Next = head;
// Link the current head to point backward to the new node
head.Previous = node;
// Move the head pointer to the new node
head = node;
}

count++;
}

Adding an element to the end of a deque causes a similar reset of the tail variable:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void AddLast(T data)
{
DoublyNode<T> node = new DoublyNode<T>(data);

if (head == null)
{
// FIXED: Explicitly link both structural pointers for an empty state
head = node;
tail = node;
}
else
{
// Safe to use the null-forgiving operator (!) since head != null guarantees tail != null
tail!.Next = node;
node.Previous = tail;
tail = node; // Move the tail pointer to the newly appended node
}

count++;
}

When deleting from the beginning of a deck, you need to reset the reference to the first element:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public T RemoveFirst()
{
// 1. Added head == null check to satisfy modern compiler null-safety analysis
if (count == 0 || head == null)
throw new InvalidOperationException(“The deque is empty.”);

T output = head.Data;

if (count == 1)
{
head = null;
tail = null;
}
else
{
head = head.Next;
// 2. Added the null-forgiving operator (!) because we’ve proven head isn’t null
head!.Previous = null;
}

count–;
return output;
}

When the last element is removed, the tail variable is reset to the second-to-last element:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public T RemoveLast()
{
// 1. Added tail == null check to satisfy modern compiler null-safety analysis
if (count == 0 || tail == null)
throw new InvalidOperationException(“The deque is empty.”);

T output = tail.Data;

if (count == 1)
{
head = null;
tail = null;
}
else
{
tail = tail.Previous;
// 2. Added the null-forgiving operator (!) because we’ve proven tail isn’t null
tail!.Next = null;
}

count–;
return output;
}

The algorithmic complexity of all methods for adding and removing from a deque is O(1).

 

365

Application of the deck:

1
2
3
4
5
6
7
8
9
10
11
12
13
Deque<string> usersDeck = new Deque<string>();
usersDeck.AddFirst(“Alice”);
usersDeck.AddLast(“Kate”);
usersDeck.AddLast(“Tom”);

foreach (string s in usersDeck)
Console.WriteLine(s);

string removedItem = usersDeck.RemoveFirst();
Console.WriteLine(“\n Removed: {0}\n”, removedItem);

foreach (string s in usersDeck)
Console.WriteLine(s);

Console output:

Alice
Kate
Tom

Removed: Alice

Kate
Tom

Explore More IT Terms


Share this term: Facebook X LinkedIn WhatsApp Email

Leave a Reply

Your email address will not be published. Required fields are marked *