doubly linked list data structure c with illustration
二重リンクリストに関する詳細なチュートリアル。
二重リンクリストは、単一リンクリストのバリエーションです。単一リンクリストはノードのコレクションであり、各ノードにはデータ部分と次のノードを指すポインタがあることを認識しています。
二重にリンクされたリストもノードのコレクションです。ここでの各ノードは、データ部分と2つのポインターで構成されます。 1つのポインターは前のノードを指し、2番目のポインターは次のノードを指します。
=> ここで詳細なC ++トレーニングチュートリアルを確認してください。
学習内容:
C ++で二重にリンク
単一リンクリストと同様に、二重リンクリストにも先頭と末尾があります。これが最初のノードであるため、ヘッドの前のポインターはNULLに設定されます。これが最後のノードであるため、テールノードの次のポインタはNULLに設定されます。
二重リンクリストの基本的なレイアウトを次の図に示します。
上の図では、各ノードに2つのポインターがあり、1つは前のノードを指し、もう1つは次のノードを指していることがわかります。最初のノード(ヘッド)のみの前のノードがnullに設定され、最後のノード(テール)の次のポインターがnullに設定されます。
二重にリンクされたリストには、前と次の2つのポインターが含まれているため、前後の方向にトラバースできます。これは、単一リンクリストに対する二重リンクリストの主な利点です。
Windows7に最適なPCクリーナー
宣言
Cスタイルの宣言では、二重リンクリストのノードは次のように表されます。
struct node { struct node *prev; int data; struct node *next; };
上記の宣言とは別に、二重リンクリスト内のノードをC ++のクラスとして表すこともできます。 C ++でSTLを使用すると、二重にリンクされたリストがクラスとして表されます。 Javaのクラスを使用して、二重リンクリストを実装することもできます。
基本操作
以下は、二重にリンクされたリストで実行できる操作の一部です。
挿入
二重リンクリストの挿入操作により、リンクリストに新しいノードが挿入されます。新しいノードを挿入する位置に応じて、次の挿入操作を実行できます。
- フロントへの挿入– 最初のノードとして新しいノードを挿入します。
- 最後に挿入– 最後のノードとして最後に新しいノードを挿入します。
- ノードの前への挿入– ノードを指定すると、このノードの前に新しいノードを挿入します。
- ノードの後の挿入– ノードを指定すると、このノードの後に新しいノードを挿入します。
削除
削除操作は、二重リンクリストの特定の位置からノードを削除します。
- 最初のノードの削除– リストの最初のノードを削除します
- 最後のノードの削除– リストの最後のノードを削除します。
- データが与えられたノードの削除– データが与えられると、操作はそのデータをリンクリスト内のノードデータと照合し、そのノードを削除します。
トラバーサル
トラバーサルは、リンクリスト内の各ノードにアクセスする手法です。二重リンクリストには、方向が異なる2つのポインターがあるため、2種類のトラバーサルがあります。
- フォワードトラバーサル– トラバーサルは、順方向にある次のポインターを使用して実行されます。
- 後方トラバーサル– トラバーサルは、逆方向である前のポインターを使用して行われます。
逆行する
この操作では、二重リンクリスト内のノードが逆になり、最初のノードが最後のノードになり、最後のノードが最初のノードになります。
探す
二重リンクリストの検索操作は、リンクリストの特定のノードを検索するために使用されます。この目的のために、一致するデータが見つかるまでリストをトラバースする必要があります。
挿入
前面にノードを挿入します
リストの先頭に新しいノードを挿入する方法を上に示します。ご覧のとおり、前の新しいノードNはnullに設定されています。ヘッドは新しいノードを指します。 Nの次のポインタはN1を指し、以前はNullを指していたN1の前のポインタはNを指すようになりました。
最後にノードを挿入します
二重リンクリストの最後にノードを挿入するには、新しいノードNの次のポインタをnullにポイントします。 Nの前のポインタはN5を指しています。 N5の「次へ」ポインタはNを指しています。
指定されたノードの前後にノードを挿入します
上図に示すように、特定のノードの前後にノードを追加する必要がある場合は、新しいノードを適切に指すように、前後のノードの前後のポインターを変更します。また、新しいノードポインタは既存のノードを適切に指し示します。
次のC ++プログラムは、二重リンクリストにノードを挿入するための上記のすべての方法を示しています。
#include using namespace std; // A doubly linked list node struct Node { int data; struct Node* next; struct Node* prev; }; //inserts node at the front of the list void insert_front(struct Node** head, int new_data) { //allocate memory for New node struct Node* newNode = new Node; //assign data to new node newNode->data = new_data; //new node is head and previous is null, since we are adding at the front newNode->next = (*head); newNode->prev = NULL; //previous of head is new node if ((*head) != NULL) (*head)->prev = newNode; //head points to new node (*head) = newNode; } /* Given a node as prev_node, insert a new node after the given node */ void insert_After(struct Node* prev_node, int new_data) { //check if prev node is null if (prev_node == NULL) { coutnext = prev_node->next; //set next of prev node to newnode prev_node->next = newNode; //now set prev of newnode to prev node newNode->prev = prev_node; //set prev of new node's next to newnode if (newNode->next != NULL) newNode->next->prev = newNode; } //insert a new node at the end of the list void insert_end(struct Node** head, int new_data) { //allocate memory for node struct Node* newNode = new Node; struct Node* last = *head; //set last node value to head //set data for new node newNode->data = new_data; //new node is the last node , so set next of new node to null newNode->next = NULL; //check if list is empty, if yes make new node the head of list if (*head == NULL) { newNode->prev = NULL; *head = newNode; return; } //otherwise traverse the list to go to last node while (last->next != NULL) last = last->next; //set next of last to new node last->next = newNode; //set last to prev of new node newNode->prev = last; return; } // This function prints contents of linked list starting from the given node void displayList(struct Node* node) { struct Node* last; while (node != NULL) { coutnext; } if(node == NULL) cout 出力:
二重リンクリストは次のとおりです。
1020304050NULL
上記のプログラムは、前にノードを挿入し、最後にノードを挿入し、指定されたノードの後にノードを挿入するという3つの挿入方法を使用してノードを挿入することにより、二重リンクリストを作成します。
次に、Java実装と同じ操作を示します。
// Java Class for Doubly Linked List class Doubly_linkedList { Node head; // list head /* Doubly Linked list Node*/ class Node { int data; Node prev; Node next; //create a new node using constructor Node(int d) { data = d; } } // insert a node at the front of the list public void insert_front(int new_data) { /* 1. allocate node * 2. put in the data */ Node new_Node = new Node(new_data); /* 3. Make next of new node as head and previous as NULL */ new_Node.next = head; new_Node.prev = null; /* 4. change prev of head node to new node */ if (head != null) head.prev = new_Node; /* 5. move the head to point to the new node */ head = new_Node; } //insert a node after the given prev node public void Insert_After(Node prev_Node, int new_data) { //check that prev node is not null if (prev_Node == null) { System.out.println('The previous node is required,it cannot be NULL '); return; } //allocate new node and set it to data Node newNode = new Node(new_data); //set next of newNode as next of prev node newNode.next = prev_Node.next; //set new node to next of prev node prev_Node.next = newNode; //set prev of newNode as prev node newNode.prev = prev_Node; //set prev of new node's next to newnode if (newNode.next != null) newNode.next.prev = newNode; } // Add a node at the end of the list void insert_end(int new_data) { //allocate the node and set the data Node newNode = new Node(new_data); Node last = head; //set last as the head //set next of new node to null since its the last node newNode.next = null; //set new node as head if the list is null if (head == null) { newNode.prev = null; head = newNode; return; } //if list is not null then traverse it till the last node and set last next to last while (last.next != null) last = last.next; last.next = newNode; //set last next to new node newNode.prev = last; //set last as prev of new node } // display the contents of linked list starting from the given node public void displaylist(Node node) { Node last = null; while (node != null) { System.out.print(node.data + ''); last = node; node = node.next; } if(node == null) System.out.print('null'); System.out.println(); } } class Main{ public static void main(String() args) { /* Start with the empty list */ Doubly_linkedList dll = new Doubly_linkedList(); // Insert 40. dll.insert_end(40); // Insert 20 at the beginning. dll.insert_front(20); // Insert 10 at the beginning. dll.insert_front(10); // Insert 50 at the end. dll.insert_end(50); // Insert 30, after 20. dll.Insert_After(dll.head.next, 30); System.out.println('Doubly linked list created is as follows: '); dll.displaylist(dll.head); } }
出力:
作成される二重リンクリストは次のとおりです。
別の電話をスパイするための最高のアプリ
1020304050null
削除
ノードは、フロント、エンド、その他の特定の位置または特定のデータなど、任意の位置から二重リンクリストから削除できます。
二重リンクリストからノードを削除するときは、最初にその特定のノードを指すポインターを再配置して、前後のノードが削除するノードに接続しないようにします。その後、ノードを簡単に削除できます。
3つのノードA、B、Cを持つ次の二重リンクリストについて考えてみます。ノードBを削除する必要があると考えてみましょう。
上記の一連の図に示されているように、指定されたリンクリストからノードBが削除されることを示しました。ノードが最初または最後であっても、操作の順序は同じままです。注意が必要なのは、最初のノードが削除された場合に、2番目のノードの前のポインタがnullに設定されることだけです。
同様に、最後のノードが削除されると、前のノードの次のポインタがnullに設定されます。ノード間のノードが削除された場合、シーケンスは上記のようになります。
二重リンクリストからノードを削除するプログラムを終了します。実装は挿入実装の行にあることに注意してください。
逆二重リンクリスト
二重にリンクされたリストを逆にすることは重要な操作です。ここでは、すべてのノードの前のポインターと次のポインターを交換し、ヘッドポインターとテールポインターも交換します。
以下に二重にリンクされたリストを示します。
次のC ++実装は、逆二重リンクリストを示しています。
#include using namespace std; //node declaration for doubly linked list struct Node { int data; struct Node *prev, *next; }; Node* newNode(int val) { Node* temp = new Node; temp->data = val; temp->prev = temp->next = nullptr; return temp; } void displayList(Node* head) { while (head->next != nullptr) { cout next; } cout next = *head; (*head)->prev = temp; (*head) = temp; } // reverse the doubly linked list void reverseList(Node** head) { Node* left = *head, * right = *head; // traverse entire list and set right next to right while (right->next != nullptr) right = right->next; //swap left and right data by moving them towards each other till they meet or cross while (left != right && left->prev != right) { // Swap left and right pointer data swap(left->data, right->data); // Advance left pointer left = left->next; // Advance right pointer right = right->prev; } } int main() { Node* headNode = newNode(5); insert(&headNode, 4); insert(&headNode, 3); insert(&headNode, 2); insert(&headNode, 1); cout << 'Original doubly linked list: ' << endl; displayList(headNode); cout << 'Reverse doubly linked list: ' << endl; reverseList(&headNode); displayList(headNode); return 0; }
出力:
元の二重リンクリスト:
1 2 3 4 5
逆二重リンクリスト:
5 4 3 2 1
ここでは、左右のポインタを入れ替えて、互いに出会うか交差するまで、互いに向かって移動します。次に、最初と最後のノードが交換されます。
次のプログラムは、二重にリンクされたリストを逆にするためのJava実装です。このプログラムでも、前のプログラムで行ったように、左右のノードのスワッピングを利用します。
// Java Program to Reverse a doubly linked List using Data Swapping class Main{ static class Node { int data; Node prev, next; }; static Node newNode(int new_data) { Node temp = new Node(); temp.data = new_data; temp.prev = temp.next = null; return temp; } static void displayList(Node head) { while (head.next != null) { System.out.print(head.data+ ' '); head = head.next; } System.out.println( head.data ); } // Insert a new node at the head of the list static Node insert(Node head, int new_data) { Node temp = newNode(new_data); temp.next = head; (head).prev = temp; (head) = temp; return head; } // Function to reverse the list static Node reverseList(Node head) { Node left = head, right = head; // traverse the list, set right pointer to end of list while (right.next != null) right = right.next; // move left and right pointers and swap their data till they meet or cross each other while (left != right && left.prev != right) { // Swap data of left and right pointer int t = left.data; left.data = right.data; right.data = t; left = left.next; // Advance left pointer right = right.prev; // Advance right pointer } return head; } public static void main(String args()) { Node headNode = newNode(5); headNode = insert(headNode, 4); headNode = insert(headNode, 3); headNode = insert(headNode, 2); headNode = insert(headNode, 1); System.out.println('Original doubly linked list:'); displayList(headNode); System.out.println('Reversed doubly linked list:'); headNode=reverseList(headNode); displayList(headNode); } }
出力:
元の二重リンクリスト:
1 2 3 4 5
逆リンクリスト:
5 4 3 2 1
単一リンクリストに対する長所/短所
単一リンクリストに対する二重リンクリストの長所と短所のいくつかについて説明しましょう。
利点:
- 二重リンクリストは、順方向にのみトラバースできる単一リンクリストとは異なり、順方向と逆方向にトラバースできます。
- 二重リンクリストでの削除操作は、特定のノードが指定されている場合の単一リストと比較すると、より効率的です。単一リンクリストでは、特定のノードを削除するために前のノードが必要なため、前のノードを見つけるためにリストをトラバースする必要がある場合があります。これはパフォーマンスに影響します。
- 挿入操作は、単一リンクリストと比較すると、二重リンクリストで簡単に実行できます。
短所:
- 二重リンクリストにはもう1つの追加のポインタが含まれているため、つまり以前の場合、二重リンクリストが占めるメモリスペースは、単一リンクリストと比較して大きくなります。
- 前と次の2つのポインターが存在するため、二重にリンクされたリストで実行されるすべての操作は、これらのポインターを処理して維持する必要があり、それによってパフォーマンスのボトルネックが発生します。
二重リンクリストのアプリケーション
二重にリンクされたリストは、以下で説明するように、さまざまな実際のシナリオやアプリケーションに適用できます。
- ゲーム内のカードのデッキは、二重にリンクされたリストの典型的な例です。デッキ内の各カードには前のカードと次のカードが順番に配置されているため、このカードのデッキは二重にリンクされたリストを使用して簡単に表すことができます。
- ブラウザの履歴/キャッシュ–ブラウザのキャッシュにはURLのコレクションがあり、進むボタンと戻るボタンを使用してナビゲートできます。これは、二重リンクリストとして表すことができるもう1つの良い例です。
- 最近使用された(MRU)は、二重リンクリストとして表すこともできます。
- スタック、ハッシュテーブル、バイナリツリーなどの他のデータ構造も、二重リンクリストを使用して構築またはプログラミングできます。
結論
二重リンクリストは、単一リンクリストのバリエーションです。これは、各ノードに次のポインターとともに前のノードへの追加のポインターが含まれているという点で、単一リンクリストとは異なります。
この追加のポインタの存在により、二重リンクリストでの挿入、削除操作が容易になりますが、同時に、これらの追加のポインタを格納するために追加のメモリが必要になります。
すでに説明したように、二重リンクリストは、ブラウザキャッシュ、MRUなどのリアルタイムシナリオでさまざまな用途があります。二重リンクリストを使用して、ツリー、ハッシュテーブルなどの他のデータ構造を表すこともできます。
次のチュートリアルでは、Circular LinkedListについて詳しく学習します。
=> ここで人気のあるC ++トレーニングシリーズを読んでください。
推奨読書