C++で配列を循環双方向リンクリストに変換する方法を徹底解説
はじめに
本記事では、配列を循環双方向リンクリスト(循環 doubly linked list)に変換するC++ プログラムの実装方法を、コード例とともにわかりやすく解説します。
課題の内容はシンプルです。入力として与えられた配列の各要素をノード化し、それらを連結して循環双方向リンクリストを構築します。
循環双方向リンクリストとは?
通常の双方向リンクリストでは、各ノードが「次のノード(next)」と「前のノード(prev)」へのポインタを持っています。これに対して循環双方向リンクリストでは、末尾ノードの next が先頭ノードを指し、先頭ノードの prev が末尾ノードを指すため、リスト全体がリング状につながっています。
アルゴリズムの流れ
- 配列の各要素に対して
mallocを使って新しいノードを動的に生成します。 - 最初の要素の場合は、そのノードを先頭(
*start)とし、nextとprevを自分自身に向けて初期化します。 - 2番目以降の要素の場合は、
(*start)->prevで現在の末尾ノードを取得し、その後ろに新しいノードを挿入します。 - すべての要素を追加したら、
print_list関数でリストの中身を出力して結果を確認します。
ここでのポイントは、循環リストでは先頭ノードの prev ポインタが常に末尾ノードを指しているため、末尾をたどるために別途トラバーサルを行う必要がない点です。
C++ 実装例
#include<iostream>
using namespace std;
//双方向リンクリスト用のノード構造体
struct node{
int data;
struct node *next;
struct node *prev;
};
//新しいノードの生成
struct node* getNode(){
return ((struct node *)malloc(sizeof(struct node)));
}
//リストの内容を表示する関数
int print_list(struct node *temp){
struct node *t = temp;
if(temp == NULL)
return 0;
else {
cout<<"List: ";
while(temp->next != t) {
cout<<temp->data<<" ";
temp = temp->next;
}
cout<<temp->data;
return 1;
}
}
//配列を循環双方向リンクリストに変換する関数
void convert_array(int arr[], int n, struct node **start) {
//使用するポインタの宣言
struct node *newNode,*temp;
int i;
//配列の全要素を順に処理
for(i=0;i<n;i++){
newNode = getNode();
newNode->data = arr[i];
if(i==0) {
//最初の要素は先頭ノードとなり、自分自身を指す
*start = newNode;
newNode->prev = *start;
newNode->next = *start;
} else {
//末尾ノードを取得して新しいノードを接続
temp = (*start)->prev;
temp->next = newNode;
newNode->next = *start;
newNode->prev = temp;
temp = *start;
temp->prev = newNode;
}
}
}
int main(){
int arr[] = {1,2,3,4,5};
int n = sizeof(arr) / sizeof(arr[0]);
struct node *start = NULL;
convert_array(arr, n, &start);
print_list(start);
return 0;
}実行結果
List: 1 2 3 4 5
コードのポイント解説
1. ノード構造体の定義
struct node は、格納する値(data)と、次・前のノードへのポインタ(next・prev)を持つシンプルな構造です。
2. 挿入時のリンク更新
新しいノードを末尾に追加する際には、次の4つのポインタ操作が必要です。
- 旧末尾ノードの
nextを新ノードに向ける - 新ノードの
nextを先頭ノードに向ける(循環を維持) - 新ノードの
prevを旧末尾ノードに向ける - 先頭ノードの
prevを新ノード(=新しい末尾)に更新する
3. 計算量
先頭ノードの prev が常に末尾を指しているおかげで、末尾への挿入は O(1) で行えます。配列全体の変換は O(n) で完了します。
まとめ
このように、配列から循環双方向リンクリストへの変換は、「先頭ノードの初期化」と「末尾への挿入時のポインタ更新」を正しく行うことで簡単に実装できます。循環リストは、バッファ管理やターンベースのゲームロジックなど、データを環状に扱いたい場面で活用される重要なデータ構造なので、ぜひマスターしておきましょう。
-
C++で循環リンクリストのノード数をカウントする方法
ノードから構成される循環リンクリスト(Circular Linked List)が与えられ、そのリスト内に存在するノードの総数を求めるのが課題です。 循環リンクリストとは、連結リストの一種であり、最初の要素が最後の要素を指し、最後の要素が最初の要素を指すという特徴を持つデータ構造です。片方向リンクリスト(Singly Linked List)でも双方向リンクリスト(Doubly Linked List)でも、この循環リンクリストとして実装することが可能です。 以下のプログラムでは、片方向リンクリストを循環リンクリストとして実装し、その中に含まれるノード数をカウントする方法を紹介します。 具体
-
C++で双方向リンクリストを使用した優先度付きキューの実装
整数値のデータと優先度が与えられ、その優先度に従って双方向リンクリスト(doubly linked list)を作成し、結果を表示することが本記事の課題です。 優先度付きキューとは? キュー(Queue)はFIFO(First In, First Out:先入れ先出し)方式のデータ構造であり、最初に挿入された要素が最初に取り出されます。優先度付きキュー(Priority Queue)は、要素の優先度に応じて挿入や削除を行えるキューの一種です。キュー、スタック、リンクリストなどのデータ構造を使って実装でき、本記事では双方向リンクリストを用います。 優先度付きキューは、次のルールに従って動作しま