C++ で二重リンクリストを実装する

胡金庫 2023年10月12日
  1. C++ で struct を使用して二重リンクリストを実装する
  2. std::list コンテナを C++ の二重リンクリストとして使用する
C++ で二重リンクリストを実装する

この記事では、C++ で二重リンクリストを実装する方法に関する複数の方法を示します。

C++ で struct を使用して二重リンクリストを実装する

リンクリストは、プログラミングで遭遇する可能性のある最も一般的なデータ構造の 1つと見なされています。これらの構造は、各ノードに別のノードのアドレスが含まれているという意味でリンクされています。リンクリストには、単一リンクリストと二重リンクリストの 2つのタイプがあります。単一リンクリストには、リスト内の次のノードのみを指すノードが含まれます。したがって、構造のトラバーサルが一方向になります。一方、二重にリンクされたリストは、リスト内の各ノードからの双方向アクセスを提供します。

この場合、struct コマンドを使用して二重リンクリストを実装し、そのすべてのデータメンバーを公開し、要素操作関数を個別に定義します。要素操作ルーチンといくつかの記録保持データメンバーを提供するメンバー関数を備えたオブジェクト指向バージョンを好む場合があることに注意してください。

次のサンプルコードには、基本的な使用シナリオをテストする main 関数のドライバーコードが含まれています。このシナリオでは、string ベクトル要素がリストに挿入され、リストの割り当てが解除されます。new 演算子を使用して各ノードを動的に割り当てます。その結果、freeNodes 関数はリストを繰り返し処理して、各 Nodedelete を呼び出します。

ユーザーが比較的高性能のリンクリスト構造を設計したい場合、各 Node を個別に割り当てる方法は非効率的である可能性があります。新しいノードをすばやく追加し、不要な場合は同時に複数の割り当てを解除するために、メモリリソースの一括割り当てを検討する場合があります。この設計では、構造の最初のノードを表す有効な要素を使用してリストの作成を開始します。一部の実装では、ダミー要素を作成する場合があります。これは、リストの先頭を示しますが、要素データは含まれていません。

#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;
}

出力:

data: Rocket Lake
data: Meteor Lake

リンクリストは、スタック、両端キュー、キュー、循環リストなど、より特殊なデータ構造の構成要素として利用できます。後者は本質的に二重リンクリストであり、最後のノードが次のノードとして最初の要素を指し、それに対応して最初のノードが前のノードとして最後の要素を指すため、後者は興味深いものです。次のコードスニペットは、構築されたリストの各ノードの内容を表示するための printNodes 関数を追加していることに注意してください。

#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;
}

出力:

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

std::list コンテナを C++ の二重リンクリストとして使用する

あるいは、C++ STL の std::list コンテナを利用することもできます。これは通常、二重リンクリストとして実装され、さまざまな要素操作機能を提供します。さらに、std::list コンテナは、標準ライブラリに含まれる非常に強力なアルゴリズムをサポートするように実装されているため、非常に特別なパフォーマンス特性が必要ない場合でも、ユーザーは開発にかかる時間を節約できます。

#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;
}

出力:

0; 1; 2; 3; 4; 5;
0; 1; 2; 99; 3; 4; 5;
著者: 胡金庫
胡金庫 avatar 胡金庫 avatar

DelftStack.comの創設者です。Jinku はロボティクスと自動車産業で8年以上働いています。自動テスト、リモートサーバーからのデータ収集、耐久テストからのレポート作成が必要となったとき、彼はコーディングスキルを磨きました。彼は電気/電子工学のバックグラウンドを持っていますが、組み込みエレクトロニクス、組み込みプログラミング、フロントエンド/バックエンドプログラミングへの関心を広げています。

LinkedIn Facebook

関連記事 - C++ Data Structure