All is well

[YYBASIC0304/얌얌코딩] AddNode 구현, 링크드리스트 출력 본문

C++/YYBASIC

[YYBASIC0304/얌얌코딩] AddNode 구현, 링크드리스트 출력

D0YUN 2025. 3. 3. 14:29

링크드 리스트 시작

// 노드 구조체 정의
struct Node
{
    int data;   // 데이터 저장
    Node* next; // 다음 노드를 가리키는 포인터
};

// 연결 리스트의 헤드와 테일 포인터 선언
Node* head = nullptr; // 리스트의 첫 번째 노드를 가리킴
Node* tail = nullptr; // 리스트의 마지막 노드를 가리킴
  • 링크드 리스트를 구현하려면 기본적으로 `head`(첫 번째 노드)와 `tail`(마지막 노드) 가 필요합니다.
  • 처음에는 리스트가 비어 있기 때문에 `head` 와 `tail` 모두 `nullptr` 로 초기화하여 가독성을 높이고, 이후 노드가 추가되면서 값을 채워 나갑니다.

링크드 리스트에 노드 추가

#include <iostream>
using namespace std;

// 새로운 노드를 추가하는 함수
void AddNode(int data)
{
    if (head == nullptr) // 리스트가 비어 있는 경우 (첫 번째 노드 추가)
    {
        // 새로운 노드 생성
        head = new Node;
        head->data = data;
        head->next = nullptr; // 다음 노드가 없음을 명시

        // tail도 head를 가리키도록 설정 (첫 번째 노드이므로)
        tail = head;
    }
    else // 기존 노드가 존재하는 경우 (새로운 노드를 끝에 추가)
    {
        // 새로운 노드를 생성하여 현재 tail의 next에 연결
        tail->next = new Node;
        tail->next->data = data;
        tail->next->next = nullptr; // 다음 노드가 없음을 명시

        // tail을 새로 추가한 노드로 갱신
        tail = tail->next;
    }
}

// 리스트가 비어 있는 경우 `(head == nullptr)`

  • `head` 가 `nullptr` 인 경우, 즉 리스트가 비어 있다면 새로운 `Node` 객체를 생성하여 `head` 에 추가합니다.
  • 이후 `tail`도 `head`를 가리키도록 설정하여 첫 번째 노드가 추가되었음을 나타냅니다.

 

// 기존 노드가 있는 경우 (head != nullptr)

  • 리스트에 이미 노드가 존재한다면, 새로운 노드는 항상 마지막 노드인 `tail` 뒤에 추가됩니다.
  • 즉, `tail->next` 에 새로운 `Node` 객체를 생성하고 데이터를 할당합니다.
  • 이후 새로 추가한 노드가 마지막 노드가 되므로 `tail` 을 갱신하여 `tail` 이 새로운 노드를 가리키도록 합니다.
  • 마지막 노드의 `next` 는 `nullptr` 로 설정하여 다음 노드가 없음을 명확히 합니다.

링크드 리스트 출력 - 순차적으로 모든 노드 탐색

  • 링크드 리스트는 배열과 다르게 각 노드가 연결된 형태이므로, 처음(`head`)부터 차례대로 따라가며 데이터를 출력해야 합니다.
  • 이를 위해 `while` 문`for` 문을 이용한 탐색 방법을 살펴보겠습니다.

// `while` 문을 이용한 출력 (종료 조건: `break` 사용)

  • 먼저 무한 루프(`while (true)`)를 사용한 방식입니다.
  • 루프 안에서 `nullptr` 여부를 확인한 후 `break` 문으로 탈출하는 방식입니다.
Node* p = head;
while (true)
{
    if (p == nullptr)  // p가 nullptr이면 반복 종료
        break;

    cout << p->data << " ";  // 현재 노드의 데이터 출력
    p = p->next;  // 다음 노드로 이동
}
  • 실행 흐름은 다음과 같습니다.
    1. `p` 가 `head` 를 가리키도록 설정
    2. `nullptr` 을 만날 때까지 `data` 를 출력하고 `next` 로 이동
    3. `nullptr` 이 되면 `break` 로 루프 종료
  • 장점: `break` 를 사용하여 명확하게 종료되는 지점을 표현했습니다.
  • 단점: `while (true)` 를 사용하므로 일반적인 조건 기반 루프보다 직관성이 떨어질 수 있습니다.

// `while` 문을 이용한 출력 (보다 직관적인 종료 조건 사용)

  • `while (true)` 대신, 루프 조건 자체를 `p != nullptr` 로 설정하는 방식입니다.
  • 이 방식이 더 일반적인 연결 리스트 순회 방식이며 가독성이 좋습니다.
Node* p = head;
while (p != nullptr)  // p가 nullptr이 아닐 때만 실행
{
    cout << p->data << " ";
    p = p->next;
}
  • 실행 흐름은 다음과 같습니다.
    1. `p` 가 `head` 를 가리키도록 설정
    2. `nullptr` 이 아닌 동안 `data` 를 출력하고 `next` 로 이동
    3. `nullptr` 을 만나면 자동으로 루프 종료
  • 장점: 조건 자체가 종료 조건을 포함하고 있어 코드가 직관적입니다.
  • 단점: `nullptr` 검사를 매번 수행해야 하지만, 큰 성능 차이는 없습니다.

// `for` 문을 이용한 출력 (더 간결한 방식)

  • 위의 `while` 문을 더 간결하게 표현한 방법입니다.
  • 반복 변수 `p` 를 선언하고, `nullptr` 이 될 때까지 반복합니다.
for (Node* p = head; p != nullptr; p = p->next)
{
    cout << p->data << " ";
}
  • 장점: 반복문에 초기화, 조건 검사, 증감식이 한 줄로 정리되어 깔끔합니다.
  • 단점: `while` 문에 비해 익숙하지 않은 사람에게는 조금 낯설 수 있습니다.

최종 코드

#include <iostream>
using namespace std;

// 노드 구조체 정의
struct Node
{
    int data;   // 데이터 저장
    Node* next; // 다음 노드를 가리키는 포인터
};

// 연결 리스트의 헤드와 테일 포인터 선언
Node* head = nullptr; // 리스트의 첫 번째 노드를 가리킴
Node* tail = nullptr; // 리스트의 마지막 노드를 가리킴

// 새로운 노드를 추가하는 함수
void AddNode(int data)
{
    if (head == nullptr) // 리스트가 비어 있는 경우 (첫 번째 노드 추가)
    {
        // 새로운 노드 생성
        head = new Node;
        head->data = data;
        head->next = nullptr; // 다음 노드가 없음을 명시

        // tail도 head를 가리키도록 설정 (첫 번째 노드이므로)
        tail = head;
    }
    else // 기존 노드가 존재하는 경우 (새로운 노드를 끝에 추가)
    {
        // 새로운 노드를 생성하여 현재 tail의 next에 연결
        tail->next = new Node;
        tail->next->data = data;
        tail->next->next = nullptr; // 다음 노드가 없음을 명시

        // tail을 새로 추가한 노드로 갱신
        tail = tail->next;
    }
}

// 연결 리스트의 노드들을 출력하는 함수
void PrintNode()
{
    // p 포인터를 사용하여 head를 이동시키며 출력 (원본 head 유지)
    Node* p = head;

    // while 문을 이용한 출력 (break 사용)
    cout << "Linked List with 'while' : ";
    while (true)
    {
        if(p == nullptr) // p가 nullptr이면 종료
            break;
        cout << p->data << " "; // 현재 노드의 데이터 출력
        p = p->next; // 다음 노드로 이동
    }
    cout << "\\n";

    // while 문을 이용한 출력 (더 직관적인 조건 사용)
    p = head;
    cout << "Linked List with 'while' : ";
    while(p != nullptr) // p가 nullptr이 아닐 때 반복
    {
        cout << p->data << " "; // 현재 노드의 데이터 출력
        p = p->next; // 다음 노드로 이동
    }
    cout << "\\n";

    // for 문을 이용한 출력 (간결한 표현)
    cout << "Linked List with 'for' : ";
    for (Node* p = head; p != nullptr; p = p->next) // p를 head에서 시작해 nullptr이 아닐 때까지 이동
        cout << p->data << " "; // 현재 노드의 데이터 출력
    cout << "\\n";
}

int main()
{
    // 노드 추가
    AddNode(3);
    AddNode(4);
    AddNode(5);

    // 연결 리스트 출력
    PrintNode();

    return 0;
}

// 디버깅

 

// 실행 결과

 

LV10 AddNode 구현, 링크드 리스트 출력