You have a two primary problems with your code generally, and Delete_at()
cannot simply call Dequeue()
. As a general note, your code contains duplicated expressions that can simply be consolidated. For example, Enqueue()
can be written succinctly as:
void Enqueue(int d) {
struct node *tmp = new node;
tmp->next = nullptr;
tmp->data = d;
if (rear == nullptr) {
front = rear = tmp;
size = 1;
}
else {
rear->next = tmp;
rear = tmp;
size += 1;
}
}
Your Dequeue()
function will segfault checking front->data
BEFORE checking if front == nullptr
. You must check before you dereference, e.g.
int Dequeue() {
struct node *tmp = front;
int data;
if (front == nullptr) { /* (must check before dereference (front->data) */
return 0;
}
data = front->data;
size -= 1;
if (tmp->next != nullptr) {
front = front->next;
}
else {
front = nullptr;
rear = nullptr;
}
delete tmp;
return data;
}
Your Delete_at()
function must remove the node at a specific index. This requires that you maintain your ->next
links throughout your list, updating the prev->next
before the deleted node to point to the node after the one you are deleting. You do that by iterating with both the address of the node and a pointer to node. When you reach the index to remove, you simply replace what is currently at the address of that node with the next node and delete the current, see: Linus on Understanding Pointers
void Delete_at (size_t index) {
struct node *pnode = front, /* pointer to node */
**ppnode = &front; /* address of node */
if (index >= Size()) { /* validate with >= Size() */
std::cerr << '\n' << "Error: out of bounds !";
return;
}
while (index--) { /* loop index times */
ppnode = &pnode->next; /* address of next node */
pnode = pnode->next; /* pointer to next node */
}
*ppnode = pnode->next; /* replace struct at address with next */
delete pnode; /* delete removed node */
size -= 1;
}
Your Size()
function simply reduces to a "getter" function:
size_t Size() {
return size;
}
Updating your example a bit and being mindful of Why is “using namespace std;” considered bad practice? your full code could now be:
#include <list>
#include <iostream>
class Queue{
private:
struct node {
int data;
struct node *next;
};
struct node *front = nullptr;
struct node *rear = nullptr;
size_t size;
public:
void Enqueue(int d) {
struct node *tmp = new node;
tmp->next = nullptr;
tmp->data = d;
if (rear == nullptr) {
front = rear = tmp;
size = 1;
}
else {
rear->next = tmp;
rear = tmp;
size += 1;
}
}
int Dequeue() {
struct node *tmp = front;
int data;
if (front == nullptr) { /* (must check before dereference (front->data) */
return 0;
}
data = front->data;
size -= 1;
if (tmp->next != nullptr) {
front = front->next;
}
else {
front = nullptr;
rear = nullptr;
}
delete tmp;
return data;
}
void Display() {
struct node *temp = front;
if (front == nullptr) {
std::cout << "Queue is empty" << '\n';
return;
}
while (temp != nullptr) {
std::cout << temp->data << '\n';
temp = temp->next;
}
}
size_t Size() {
return size;
}
void Delete_at (size_t index) {
struct node *pnode = front, /* pointer to node */
**ppnode = &front; /* address of node */
if (index >= Size()) { /* validate with >= Size() */
std::cerr << '\n' << "Error: out of bounds !";
return;
}
while (index--) { /* loop index times */
ppnode = &pnode->next; /* address of next node */
pnode = pnode->next; /* pointer to next node */
}
*ppnode = pnode->next; /* replace struct at address with next */
delete pnode; /* delete removed node */
size -= 1;
}
};
int main() {
Queue q;
q.Enqueue(2);
q.Enqueue(6);
q.Enqueue(7);
q.Enqueue(1);
q.Enqueue(2);
q.Enqueue(4);
q.Display();
std::cout << "\nq.Delete_at(2)\n\n";
q.Delete_at(2);
q.Display();
}
Example Use/Output
$ ./bin/queue_delete_at
2
6
7
1
2
4
q.Delete_at(2)
2
6
1
2
4
Look things over and let me know if you have further questions.
Edit With Additional Constraints From Comments
Per you comments, you have constraints of only being able to use Dequeue()
and Enqueue()
in Delete_at()
and no pointers, etc... You can do that, but understand it will be horribly inefficient compared to simply removing the node at the index. You will essentially have to save (Dequeue()
) your entire queue data in an allocated block of memory, omitting the index to remove. You will then need to iterate over all saved values calling Enqueue()
to repopulated your list.
You can do that as:
void Delete_at (size_t index) {
if (index >= Size()) { /* validate with >= Size() */
std::cerr << '\n' << "Error: out of bounds !";
return;
}
size_t nelem = Size();
int *arr = new int [nelem],
n = 0;
for (size_t i = 0; i < nelem; i++) {
int tmp = Dequeue();
if (i != index && tmp)
arr[n++] = tmp;
}
for (size_t i = 0; i < (size_t)n; i++)
Enqueue (arr[i]);
delete[] arr;
}
(same output)
For a less readable more C++'ized presentation, you can replace the first loop with:
for (int i = 0, j = Dequeue(); j; i++, j = Dequeue())
if (static_cast<size_t>(i) != index)
arr[n++] = j;
It would be nice to have utilized at least a separate list pointer, so you could build the new list while simultaneously deleting the old, but your class/struct isn't setup to use additional pointers. So you are basically left with buffering all values except the index to remove and then recreating your queue.