How to Implement a Doubly Linked List in C++

Jinku Hu Feb 02, 2024
  1. Use struct to Implement Doubly Linked List in C++
  2. Use the std::list Container as Doubly Linked List in C++
How to Implement a Doubly Linked List in C++

This article will demonstrate multiple methods about how to implement a doubly linked list in C++.

Use struct to Implement Doubly Linked List in C++

Linked lists are considered one of the most common data structures you can encounter in programming. These structures are linked in the sense that each node contains the address of another. Linked lists have two types: singly-linked lists and doubly linked lists. A singly-linked list contains nodes that only point to the next node in the list; thus, it makes the traversal of the structure one-directional. On the other hand, doubly linked lists provide two-directional access from each node in the list.

In this case, we implement a doubly linked list using the struct command, making all its data members public and defining the element manipulation functions separately. Note that one may prefer an object-oriented version with member functions providing the element manipulation routines and some record-keeping data members.

The following example code includes the driver code in the main function that tests the basic usage scenario, where the string vector elements are inserted into a list, and then the list is deallocated. We utilize the new operator to dynamically allocated each node, and consequently, the freeNodes function iterates through the list to call delete on each Node.

The method of separately allocating each Node could be inefficient if the user wants to design a relatively high-performance linked-list structure. One might consider a bulk allocation of memory resources to add new nodes quickly and, when not needed, deallocate multiples at the same time. In this design, we start building the list with the valid element representing the first node in the structure. Some implementations may create a dummy element, which denotes the head of the list but does not contain the element data.

#include <iostream>
#include <string>
#include <vector>

using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::vector;

struct Node {
  struct Node *next{};
  struct Node *prev{};
  string data;
};

template <typename T>
struct Node *addNewNode(struct Node *node, T &data) {
  auto new_node = new Node;
  if (node) node->next = new_node;
  new_node->prev = node;
  new_node->next = nullptr;
  new_node->data = data;
  return new_node;
}

void freeNodes(struct Node *node) {
  struct Node *tmp = nullptr;
  while (node) {
    tmp = node;
    node = node->next;
    delete tmp;
  }
}

void printNodeData(struct Node *node) {
  cout << "data: " << node->data << endl;
}

int main() {
  struct Node *tmp, *root = nullptr;
  struct Node *end = nullptr;

  vector<string> data_set = {"Rocket Lake", "Alder Lake", "Tiger Lake",
                             "Meteor Lake"};

  root = addNewNode(root, data_set.at(0));
  tmp = root;
  for (auto it = data_set.begin() + 1; it != data_set.end(); ++it) {
    tmp = addNewNode(tmp, *it);
    if (it == data_set.end() - 1) end = tmp;
  }

  printNodeData(root);
  printNodeData(end);

  freeNodes(root);
  return EXIT_SUCCESS;
}

Output:

data: Rocket Lake
data: Meteor Lake

You can utilize linked lists as building blocks of more specialized data structures like stacks, deques, queues, or circular lists. The latter is interesting because it’s essentially a doubly linked list where the last node points to the first element as the next node, and correspondingly the first one points to the last element as the previous node. Notice that the following code snippet adds the printNodes function to display the contents of each node in the constructed list.

#include <iostream>
#include <string>
#include <vector>

using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::vector;

struct Node {
  struct Node *next{};
  struct Node *prev{};
  string data;
};

struct Node *addNewNode(struct Node *node, string &data) {
  auto new_node = new Node;
  if (node) node->next = new_node;
  new_node->prev = node;
  new_node->next = nullptr;
  new_node->data = data;
  return new_node;
}

void freeNodes(struct Node *node) {
  struct Node *tmp = nullptr;
  while (node) {
    tmp = node;
    node = node->next;
    delete tmp;
  }
}

void printNodes(struct Node *node) {
  auto count = 0;
  while (node) {
    cout << "node " << count << " - data: " << node->data << endl;
    node = node->next;
    count++;
  }
}

int main() {
  struct Node *tmp, *root = nullptr;
  struct Node *end = nullptr;

  vector<string> data_set = {"Rocket Lake", "Alder Lake", "Tiger Lake",
                             "Meteor Lake"};

  root = addNewNode(root, data_set.at(0));
  tmp = root;
  for (auto it = data_set.begin() + 1; it != data_set.end(); ++it) {
    tmp = addNewNode(tmp, *it);
    if (it == data_set.end() - 1) end = tmp;
  }

  printNodes(root);

  freeNodes(root);
  return EXIT_SUCCESS;
}

Output:

node 0 - data: Rocket Lake
node 1 - data: Alder Lake
node 2 - data: Tiger Lake
node 3 - data: Meteor Lake

Use the std::list Container as Doubly Linked List in C++

Alternatively, one can utilize the std::list container from C++ STL, which is usually implemented as the doubly linked list and provides various element manipulation functions. Additionally, the std::list container is implemented to support quite powerful algorithms included in the standard library so the user can save time on development if some very special performance characteristics are not required.

#include <iostream>
#include <list>
#include <string>

using std::cin;
using std::cout;
using std::endl;
using std::list;
using std::string;

template <typename T>
void printList(std::list<T> l) {
  for (const auto &item : l) {
    cout << item << "; ";
  }
  cout << endl;
}

int main() {
  std::list<int> l = {1, 2, 3, 4};
  l.push_back(5);
  l.push_front(0);
  printList(l);

  auto it = std::find(l.begin(), l.end(), 3);
  if (it != l.end()) {
    l.insert(it, 99);
  }
  printList(l);

  return EXIT_SUCCESS;
}

Output:

0; 1; 2; 3; 4; 5;
0; 1; 2; 99; 3; 4; 5;
Author: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn Facebook

Related Article - C++ Data Structure