C/C++ 덱
클래스로 구현한 코드
#include <bits/stdc++.h>
#define ll long long
using namespace std;
#define X first
#define Y second
class Deque
{
struct Node // 노드 구조체 정의
{
int data; // 정수 데이터를 저장하기 위한 변수
Node* prev; // 이전 노드를 가리키는 포인터
Node* next; // 다음 노드를 가리키는 포인터
Node(int data) : data(data), prev(nullptr), next(nullptr) {} // 생성자에서 데이터와 포인터 초기화
};
Node* head; // 덱의 첫 번째 노드를 가리키는 포인터
Node* tail; // 덱의 마지막 노드를 가리키는 포인터
int size;
public:
Deque() : head(nullptr), tail(nullptr), size(0) {} // 생성자에서 head와 tail 초기화
~Deque() // 소멸자
{
Node* temp; // 임시 포인터 생성
// 노드를 방문하며 메모리를 해제합니다.
while (head != nullptr)
{
temp = head; // head를 temp에 저장
head = head->next; // head가 두 번째 노드를 가리키도록 설정
delete temp; // 첫 번째 노드 메모리 해제
}
}
void sizze()
{
cout << size << "\n";
}
bool isEmpty() // 덱이 비어있는지 확인
{
return head == nullptr;
}
void push_front(int value) // 앞쪽에 노드를 추가
{
Node* newNode = new Node(value); // 새 노드 생성 및 초기화
if (isEmpty()) // 덱이 비어있을 경우
{
head = tail = newNode; // head와 tail이 새 노드를 가리키게 함
}
else
{
newNode->next = head; // 새 노드의 다음 노드를 현재 head로 설정
head->prev = newNode; // 현재 head의 이전 노드를 새 노드로 설정
head = newNode; // head를 새 노드로 설정
}
size++;
}
void push_back(int value) // 뒤쪽에 노드를 추가
{
Node* newNode = new Node(value); // 새 노드 생성 및 초기화
if (isEmpty()) // 덱이 비어있을 경우
{
head = tail = newNode; // head와 tail이 새 노드를 가리키게 함
}
else
{
newNode->prev = tail; // 새 노드의 이전 노드를 현재 tail로 설정
tail->next = newNode; // 현재 tail의 다음 노드를 새 노드로 설정
tail = newNode; // tail을 새 노드로 설정
}
size++;
}
void pop_front() // 앞쪽 노드를 삭제
{
if (!isEmpty()) // 덱이 비어있지 않다면
{
size--;
Node* temp = head; // 현재 head를 temp에 저장
head = head->next; // head가 다음 노드를 가리키도록 설정
if (head != nullptr)
{
head->prev = nullptr; // 새로운 head의 이전 노드를 nullptr로 설정
}
else
{
tail = nullptr; // 덱이 비어있다면 tail도 nullptr로 설정
}
cout << temp->data << "\n";
delete temp; // 삭제할 노드 메모리 해제
}
else
cout << "-1\n";
}
void pop_back() // 뒤쪽 노드를 삭제
{
if (!isEmpty()) // 덱이 비어있지 않다면
{
size--;
Node* temp = tail; // 현재 tail을 temp에 저장
tail = tail->prev; // tail이 이전 노드를 가리키도록 설정
if (tail != nullptr)
{
tail->next = nullptr; // 새로운 tail의 다음 노드를 nullptr로 설정
}
else
{
head = nullptr; // 덱이 비어있다면 head도 nullptr로 설정
}
cout << temp->data << "\n";
delete temp; // 삭제할 노드 메모리 해제
}
else
cout << "-1\n";
}
int front() // 앞쪽 노드의 데이터를 가져옴
{
return isEmpty() ? -1 : head->data;
}
int back() // 뒤쪽 노드의 데이터를 가져옴
{
return isEmpty() ? -1 : tail->data;
}
};
int main(void) {
ios::sync_with_stdio(false);
cin.tie(NULL);
int n; cin >> n;
Deque q;
for (int i = 0; i < n; i++)
{
string s; cin >> s;
if (s == "push_back")
{
int data; cin >> data;
q.push_back(data);
}
if (s == "push_front")
{
int data; cin >> data;
q.push_front(data);
}
if (s == "pop_front")
{
q.pop_front();
}
if (s == "pop_back")
{
q.pop_back();
}
if (s == "size")
q.sizze();
if (s == "empty")
cout << q.isEmpty() << "\n";
if (s == "front")
cout << q.front() << "\n";
if (s == "back")
cout << q.back() << "\n";
}
return 0;
}
boj 10866번 코드이다.
덱을 구현할때
if (head != nullptr)
{
head->prev = nullptr; // 새로운 head의 이전 노드를 nullptr로 설정
}
else
{
tail = nullptr; // 덱이 비어있다면 tail도 nullptr로 설정
}
이 부분이 중요한데 그 이유는
nullptr을 가리키지 않으면 해당 포인터는 예상치 못한 메모리 위치를 가리키게 됩니다. 이렇게 초기화되지 않은 포인터를 “와일드 포인터(wild pointer)”라고 부릅니다. 이러한 와일드 포인터는 메모리 관련 오류와 같은 런타임 오류의 원인이 될 수 있습니다. 예를 들어, 덱에서 마지막 요소가 삭제되었을 때 head와 tail 포인터를 nullptr로 설정하지 않으면, 이 포인터들은 아직 삭제된 노드를 가리키게 됩니다. 그리고 이 상태에서 다른 메서드를 호출하게 되면, 이미 삭제된 노드에 접근하려고 시도하면서 런타임 오류가 발생할 수 있습니다. 그래서 코드에서 마지막 요소가 삭제된 후 head와 tail을 nullptr로 설정하여 덱이 비어 있음을 표시하고, 다른 메서드가 호출될 때 올바르게 작동하도록 합니다. 이렇게 설정함으로써 “와일드 포인터”가 발생하는 것을 막고 안전한 프로그램 작동을 보장할 수 있습니다.
Leave a comment