양방향 연결
단일 연결리스트와 다르게 양방향으로 접근이 가능하다
각 노드들은 포인터로 이어져있는 관계

노드 크기를 판단한 size변수
data -> 실제 저장하는 데이터 값
*previous -> 현재의 이전 노드를 참조하는 포인터
*next -> 현재의 다음 노드를 참조하는 포인터
노드의 처음 부분을 가리키는 head 포인터
노드의 마지막 부분을 가리키는 tail포인터
#include <iostream>
using namespace std;
template <typename T>
class DoubleLinkedList
{
private:
int size;
struct Node
{
T data;
Node* previous;
Node* next;
};
Node* head;
Node* tail;
public:
DoubleLinkedList()
{
size = 0;
head = nullptr;
tail = nullptr;
}






// 앞에 데이터 넣기
void PushFront(T data)
{
Node* newnode = new Node;
newnode->data = data;
newnode->next = nullptr;
newnode->previous = nullptr;
// 노드가 하나도 없을때
if (head == nullptr)
{
head = newnode;
tail = newnode;
}
else
{
head->previous = newnode;
newnode->next = head;
head = newnode;
}
size++;
}








void PopFront()
{
if (head == nullptr)
{
cout << "DoubleLinkedList is Empty" << endl;
}
else
{
Node* deleteNode = head;
// 노드가 하나 있으면
if (head == tail)
{
head = nullptr;
tail = nullptr;
}
else
{
deleteNode->next->previous = nullptr;
head = head->next;
}
delete deleteNode;
size--;
}
}
-> newnode 생성 후 데이터 넣어준 뒤 head와 tail을 같은 노드에 가리키게 함




void PushBack(T data)
{
Node* newnode = new Node;
newnode->data = data;
newnode->next = nullptr;
newnode->previous = nullptr;
// 하나도 없을 때
if (head == nullptr)
{
head = newnode;
tail = newnode;
}
else
{
tail->next = newnode;
newnode->previous = tail;
tail = newnode;
}
size++;
}

void PopBack()
{
// 노드 하나도 없을때
if (tail == nullptr)
{
cout << "Double Linked List is Empty" << endl;
}
else
{
Node* deleteNode = tail;
// 노드 하나일때
if (head == tail)
{
head = nullptr;
tail = nullptr;
}
// 노드 여러개일때
else
{
tail->previous->next = nullptr;
tail = tail->previous;
}
delete deleteNode;
size--;
}
}
void Show()
{
Node* currentNode = head;
while (currentNode != nullptr)
{
cout << currentNode->data << " ";
currentNode = currentNode->next;
}
}
const int & Size()
{
return size;
}
~DoubleLinkedList()
{
while (head != nullptr)
{
PopFront();
}
}

int main()
{
DoubleLinkedList <int> doubleLinkedList;
doubleLinkedList.PushFront(30);
doubleLinkedList.PushFront(20);
doubleLinkedList.PushFront(10);
doubleLinkedList.PushBack(40);
doubleLinkedList.PushBack(50);
doubleLinkedList.PopBack();
doubleLinkedList.PopFront();
doubleLinkedList.Show();
cout << endl << "size: " << doubleLinkedList.Size() << endl;
return 0;
}
결과값:
삽입/삭제 -> O(1) 탐색 -> O(n) ( head와 tail을 알 경우 O(1) )
단일 연결 리스트에서는 맨 마지막 노드를 탐색하려면 head부터 순차적으로 접근했지만, 양방향 연결 리스트는 양방향에서 접근이 가능하여 뒤에서부터도 접근이 가능함
-> 맨 마지막 노드를 탐색해야 하는데 tail을 알고 있을 경우 시간복잡도 O(1)
이전 노드의 정보를 알고있기에 중간 노드 삭제가 효율적이다