線形リストを作ってみる その1
線形リストは、鎖のようにデータが連なったデータ構造をとります。
--[data]--[data]--[data]--[data]--
線形リストの形には直線型、循環型があり、データを取り出せる方向として一方向、双方向の二種類に分かれます。
一方向の場合は前のノードを参照することができませんので、常に先頭からチェックしなければなりません。
双方向なら前後のノードを知っているため、どのノードからでも簡単にアクセスすることができます。
C++で双方向・循環型の線形リストを実装してみます。各ノードはint型を保持するとしましょう。
ノードクラスの実装
まずはデータを持っているノードクラスを定義します。
ノードクラスが保持する情報は,前後のノードのアドレスとデータそのものになります。
Node.h
#ifndef __Node_H #define __Node_H class Node { public: Node(); ~Node(); Node*& next(); //次のノードのアドレスを取得する Node*& prev(); //前のノードのアドレスを取得する int& data(); //ノードが保持するデータを取得する void print();//メンバ変数を出力する(テスト用) private: Node* m_next; //次のノードのアドレス Node* m_prev; //前のノードのアドレス int m_data; //ノードが保持するデータ }; #endif
Node.cpp
#include "Node.h" Node::Node(void): m_next(0), m_prev(0), m_data(0) { } Node::~Node(void) { } Node*& Node::next(){ return m_next; } Node*& Node::prev(){ return m_prev; } int& Node::data(){ return m_data; } #include <iostream> using namespace std; void Node::print(){ cout <<"prev["<<m_prev<<"]\t"; cout <<"["<<this<<",data="<<m_data<<"]\t"; cout <<"next["<<m_next<<"]"<< endl; }
Nodeクラスの利用
Nodeクラスを利用してみます。
アドレスの参照を介してデータをやり取りすることができるようにしています。
#include "Node.h" int main(){ Node* node1=new Node(); Node* node2=new Node(); node1->print(); node2->print(); node1->data()=3; node2->next()=node1; node1->print(); node2->print(); delete node1; delete node2; return 0; }
prev[00000000] [008D2330,data=0] next[00000000]
prev[00000000] [008D2368,data=0] next[00000000]
prev[00000000] [008D2330,data=3] next[00000000]
prev[00000000] [008D2368,data=0] next[008D2330]
線形リストクラスの実装
線形リストはいくつでもノードを持つことができます。最低限必要な操作として、要素を挿入することと削除することができるものとします。ノードのポインタを引数として、挿入や削除を行う関数を作成しておくと後で応用が効きます。
ノードの挿入などで処理の場合分けをせずに済むように、ダミーノードとしてrootノードを持たせることにします。各メンバについてですが、ひとまずpublicで扱うものとしましょう。
線形リストクラスの基本形
List.h
#ifndef __List_H #define __List_H #include "Node.h" class List { public: List(void); ~List(void); void print(); //要素を表示する //private: void insert_next(Node* node,int data); //指定したnodeの後に追加 void insert_prev(Node* node,int data); //指定したnodeの前に追加 void erase(Node* node); //指定したノードを削除する Node* m_root; int m_size; }; #endif
Listクラスのメンバ関数が決まりましたので、実装部分 List.cppを作成してみたいと思います。
準備
リスト生成時に、rootノードとサイズの初期値を設定します。
#include "List.h" List::List(void): m_size(0) { m_root = new Node(); m_root->next() = m_root; //rootノードの前後を自分自身にしておく m_root->prev() = m_root; }
要素の追加
前の情報を上書きしないように、ノードのリンクの付け替え手順に注意する必要があります。
void List::insert_next(Node* node,int data){ if(node==0){ return; } Node* new_node=new Node();//新たに追加したnode new_node->prev()=node; new_node->next()=node->next(); node->next()->prev()=new_node; node->next()=new_node; new_node->data()=data; //追加したnodeの値を設定 m_size++; } void List::insert_prev(Node* node,int data){ if(node==0){ return; } Node* new_node=new Node();//新たに追加したnode new_node->prev()=node->prev(); new_node->next()=node; node->prev()->next()=new_node; node->prev()=new_node; new_node->data()=data;//追加したnodeの値を設定 m_size++; }
要素の削除
void List::erase(Node* node){ if(node==0 || node==m_root){ //rootは削除しない return; } node->prev()->next()=node->next(); node->next()->prev()=node->prev(); delete node; m_size--; }
後処理
リストを削除するときに、保持しているノードも全て削除しなければなりません。
List::~List(void) { for(Node* node=m_root->next();node!=m_root;){ node=node->next(); delete node->prev(); } //rootノードを初期化する m_root->next()=m_root; m_root->prev()=m_root; m_size=0; delete m_root; }
出力
#include <iostream> using namespace std; //要素を表示する void List::print(){ cout <<"(root) "<<"\t\t"; m_root->print(); int count=0; for(Node* node=m_root->next();node!=m_root;node=node->next()){ cout <<"(node"<<count<<") "<<"\t"; node->print(); count++; } cout << endl; }
実際にテストをしてみます。
#include "List.h" int main(){ List list; list.print(); list.insert_next(list.m_root,1); list.print(); list.insert_next(list.m_root,2); list.print(); list.insert_prev(list.m_root,3); list.print(); list.insert_prev(list.m_root,4); list.print(); list.erase(list.m_root->next()); list.print(); return 0; }
(root) prev[006C2330] [006C2330,data=0] next[006C2330]
(root) prev[006C2368] [006C2330,data=0] next[006C2368]
(node0) prev[006C2330] [006C2368,data=1] next[006C2330]
(root) prev[006C2368] [006C2330,data=0] next[006C17B0]
(node0) prev[006C2330] [006C17B0,data=2] next[006C2368]
(node1) prev[006C17B0] [006C2368,data=1] next[006C2330]
(root) prev[006C17E8] [006C2330,data=0] next[006C17B0]
(node0) prev[006C2330] [006C17B0,data=2] next[006C2368]
(node1) prev[006C17B0] [006C2368,data=1] next[006C17E8]
(node2) prev[006C2368] [006C17E8,data=3] next[006C2330]
(root) prev[006C1820] [006C2330,data=0] next[006C17B0]
(node0) prev[006C2330] [006C17B0,data=2] next[006C2368]
(node1) prev[006C17B0] [006C2368,data=1] next[006C17E8]
(node2) prev[006C2368] [006C17E8,data=3] next[006C1820]
(node3) prev[006C17E8] [006C1820,data=4] next[006C2330]
(root) prev[006C1820] [006C2330,data=0] next[006C2368]
(node0) prev[006C2330] [006C2368,data=1] next[006C17E8]
(node1) prev[006C2368] [006C17E8,data=3] next[006C1820]
(node2) prev[006C17E8] [006C1820,data=4] next[006C2330]