문서의 각 단락이 어느 리비전에서 마지막으로 수정되었는지 확인할 수 있습니다. 왼쪽의 정보 칩을 통해 작성자와 수정 시점을 파악하세요.

List | |
정의 | 순서가 있는 데이터 항목들의 모음 |
유형 | 배열 연결 리스트 스택 큐 |
주요 용도 | 데이터의 순차적 저장 및 관리 동적 크기 조절이 필요한 데이터 구조 구현 |
관련 분야 | 자료구조 알고리즘 컴퓨터 과학 |
핵심 연산 | 삽입 삭제 탐색 정렬 |
상세 정보 | |
특징 | 요소 간 순서가 존재함 동일한 값을 가진 요소의 중복 저장이 가능한 경우가 많음 인덱스를 통한 임의 접근이 가능한 구조(배열)와 불가능한 구조(연결 리스트)로 구분 |
구현 예시 | Python: list Java: ArrayList, LinkedList C++: std::vector, std::list |
시간 복잡도 (배열 기준) | 접근: O(1) 탐색: O(n) 삽입/삭제 (끝): O(1) 삽입/삭제 (중간): O(n) |
장점 | 구현이 비교적 단순함 인덱스를 통한 빠른 접근(배열) 동적 크기 조절 가능(많은 현대 구현체) |
단점 | 중간 삽입/삭제 시 비효율적(배열) 메모리 사용이 비연속적일 수 있음(연결 리스트) 크기 조절 시 오버헤드 발생 가능 |

리스트는 순서가 있는 데이터 항목들의 모음을 가리키는 일반적인 용어이다. 자료구조에서 리스트는 데이터를 순차적으로 저장하고 관리하기 위한 핵심적인 추상 데이터 타입으로 사용된다. 이는 배열, 연결 리스트, 스택, 큐 등 다양한 구체적인 자료구조를 포괄하는 상위 개념이다.
리스트의 주요 용도는 데이터의 순차적 저장 및 관리이며, 특히 프로그램 실행 중에 데이터의 개수가 변할 수 있어 동적 크기 조절이 필요한 상황에서 널리 활용된다. 알고리즘과 컴퓨터 과학의 여러 분야에서 기본적인 구성 요소로 사용되며, 리스트를 효율적으로 처리하는 방법은 중요한 연구 주제가 된다.
리스트에서 수행되는 핵심 연산에는 새로운 데이터를 추가하는 삽입, 기존 데이터를 제거하는 삭제, 특정 데이터의 위치를 찾는 탐색, 그리고 데이터 항목들을 특정 기준에 따라 재배열하는 정렬 등이 포함된다. 이러한 연산들의 구현 방식과 효율성은 선택한 구체적인 리스트 자료구조에 따라 크게 달라진다.

리스트는 순서가 있는 데이터 항목들의 모음을 의미하는 추상적인 자료형이다. 이는 배열, 연결 리스트, 스택, 큐 등 구체적인 자료구조의 기반이 되는 개념이다. 리스트의 핵심은 데이터 요소들이 선형적인 순서를 가진다는 점이며, 이를 통해 첫 번째 요소, 마지막 요소, 그리고 각 요소의 앞뒤 관계를 정의할 수 있다. 이러한 순차적 특성은 데이터를 저장하고 관리하는 데 필수적이다.
리스트는 동적 크기 조절이 필요한 상황에서 특히 유용하다. 고정된 크기를 가진 배열과 달리, 대부분의 리스트 구현은 프로그램 실행 중에 요소를 자유롭게 추가하거나 제거할 수 있어 메모리를 효율적으로 활용할 수 있다. 이는 데이터의 양을 미리 알기 어렵거나 빈번한 삽입과 삭제 연산이 발생하는 응용 분야, 예를 들어 작업 대기열 관리나 최근 사용 문서 목록 구현 등에 적합하다.
주요 연산으로는 특정 위치에 새로운 데이터를 넣는 삽입, 기존 요소를 제거하는 삭제, 원하는 값을 찾는 탐색, 그리고 요소들을 특정 기준에 따라 재배열하는 정렬이 있다. 이러한 연산들의 효율성은 선택한 구체적인 자료구조에 크게 의존한다. 예를 들어, 배열 리스트는 탐색이 빠르지만 중간 삽입/삭제는 느릴 수 있으며, 연결 리스트는 그 반대의 특성을 보인다. 따라서 문제의 요구사항에 맞게 배열, 단일 연결 리스트, 이중 연결 리스트, 원형 연결 리스트 등의 적절한 구현체를 선택하는 것이 중요하다.

리스트의 구조는 기본적으로 순서가 있는 데이터 항목들의 모음을 어떻게 메모리에 구성하고 관리하는지에 따라 결정된다. 가장 기본적인 구조는 배열로, 연속된 메모리 공간에 데이터를 순차적으로 저장한다. 이 구조는 인덱스를 통한 빠른 접근이 가능하지만, 크기가 고정되어 있어 데이터의 삽입이나 삭제 시 비효율적일 수 있다.
이러한 배열의 단점을 보완한 대표적인 구조가 연결 리스트이다. 연결 리스트는 각 데이터 항목을 노드라는 단위로 저장하며, 각 노드는 실제 데이터와 다음 노드의 위치를 가리키는 포인터로 구성된다. 노드들이 포인터로 연결되어 있기 때문에 물리적으로 연속된 메모리 공간을 필요로 하지 않으며, 데이터의 동적 삽입과 삭제가 상대적으로 용이하다.
리스트 구조의 변형으로는 스택과 큐가 있다. 스택은 후입선출 방식으로 데이터를 관리하는 선형 구조이며, 큐는 선입선출 방식을 따른다. 이들은 특정한 접근 순서를 강제해야 하는 알고리즘이나 시스템에서 널리 활용된다. 이러한 다양한 구조는 모두 데이터의 순차적 저장 및 관리라는 공통된 목적을 가지지만, 내부적인 구성 방식과 성질에 따라 각기 다른 특징과 장단점을 지닌다.

단일 연결 리스트는 가장 기본적인 형태의 연결 리스트이다. 각 노드는 데이터 필드와 다음 노드를 가리키는 하나의 포인터로 구성된다. 이 포인터를 통해 노드들이 일렬로 연결되어 선형적인 순서를 이루며, 리스트의 시작점은 헤드 포인터가 가리킨다. 마지막 노드의 포인터는 NULL 값을 가지며, 이를 통해 리스트의 끝을 식별할 수 있다.
단일 연결 리스트의 주요 특징은 데이터 항목들이 물리적으로 연속된 메모리 공간에 저장되지 않는다는 점이다. 각 노드는 독립적으로 할당되며, 포인터를 통해 논리적인 순서만을 유지한다. 이로 인해 배열과 달리 데이터의 동적 삽입과 삭제가 비교적 효율적이다. 특히 리스트의 시작 부분에서의 삽입과 삭제 연산은 상수 시간(O(1))에 수행 가능하다.
그러나 단일 연결 리스트는 단방향으로만 연결되어 있어, 특정 노드의 이전 노드에 직접 접근할 수 없다는 단점이 있다. 따라서 리스트의 중간이나 끝에서의 삭제나 삽입을 위해서는 원하는 위치의 직전 노드를 찾기 위해 헤드부터 순차적으로 탐색해야 할 수 있다. 이러한 순차 접근 방식은 임의 접근이 가능한 배열에 비해 특정 위치 데이터 검색 시 비효율적일 수 있다.
이러한 구조적 특성 때문에 단일 연결 리스트는 주로 삽입과 삭제가 빈번하게 발생하는 경우나, 데이터의 크기가 예측 불가능하여 동적 메모리 할당이 필요한 상황에서 활용된다. 또한 스택이나 간단한 큐와 같은 다른 선형 자료구조를 구현하는 기초가 되기도 한다.
이중 연결 리스트는 각 노드가 데이터와 함께 두 개의 포인터를 가지는 연결 리스트이다. 하나의 포인터는 다음 노드를 가리키고, 다른 하나는 이전 노드를 가리킨다. 이로 인해 단일 연결 리스트와 달리 양방향으로 순회가 가능하다는 특징을 가진다.
이 구조는 리스트의 앞뒤에서 노드를 추가하거나 제거하는 연산이 효율적이다. 특히 리스트 중간의 특정 노드를 삭제할 때, 그 노드의 이전 노드와 다음 노드를 쉽게 찾아 연결을 수정할 수 있어 상대적으로 간편하다. 하지만 각 노드가 추가적인 포인터를 저장해야 하므로 메모리 사용량이 단일 연결 리스트보다 많다.
이중 연결 리스트는 주로 양방향 탐색이나 수정이 빈번하게 필요한 경우에 사용된다. 대표적인 예로는 웹 브라우저의 방문 기록 앞뒤로 이동하는 기능, 혹은 음악 플레이어의 재생 목록에서 다음 곡과 이전 곡을 선택하는 기능 등을 구현하는 데 활용된다. 또한 더 복잡한 자료구조인 덱의 기반이 되기도 한다.
원형 연결 리스트는 연결 리스트의 한 종류로, 마지막 노드가 첫 번째 노드를 가리키는 구조를 가진다. 이로 인해 노드들이 원형으로 연결되어 있다고 볼 수 있다. 단일 연결 리스트나 이중 연결 리스트와 달리 리스트의 끝이라는 개념이 명확하지 않으며, 순회를 시작한 노드로 다시 돌아올 수 있다는 특징이 있다. 이러한 구조는 리스트의 처음과 끝을 구분할 필요가 없는 응용에 유용하게 사용된다.
원형 연결 리스트는 단일 연결 리스트를 기반으로 하는 원형 단일 연결 리스트와, 이중 연결 리스트를 기반으로 하는 원형 이중 연결 리스트로 구분할 수 있다. 원형 단일 연결 리스트의 각 노드는 데이터와 다음 노드를 가리키는 포인터 하나를 가지며, 마지막 노드의 포인터가 헤드 노드를 가리킨다. 원형 이중 연결 리스트의 각 노드는 데이터와 함께 다음 노드와 이전 노드를 가리키는 두 개의 포인터를 가지며, 이로 인해 양방향 순회가 가능하고 삽입 및 삭제 연산이 더 유연해진다.
원형 연결 리스트의 주요 연산인 삽입과 삭제는 일반 연결 리스트와 유사하지만, 리스트의 끝 부분을 처리하는 로직이 다르다. 예를 들어, 리스트의 맨 앞에 노드를 삽입할 때 마지막 노드의 포인터를 새로운 노드로 갱신해야 한다. 리스트가 비어 있는 경우를 처리하는 것도 중요한데, 이때는 새 노드가 자기 자신을 가리키도록 초기화한다. 이러한 특성 때문에 구현 시 경계 조건에 대한 주의가 필요하다.
이러한 구조는 운영체제의 프로세스 스케줄링, 멀티플레이어 게임에서 플레이어 순서 관리, 버퍼 구현 등 순환적인 데이터 처리가 필요한 다양한 알고리즘과 시스템에서 응용된다.

리스트에서의 삽입 연산은 새로운 데이터 항목을 리스트 내의 특정 위치에 추가하는 작업이다. 이 연산은 리스트의 핵심 기능 중 하나로, 데이터 구조의 동적인 특성을 활용할 수 있게 해준다. 배열과 같은 고정 크기 구조와 달리, 대부분의 리스트 구현은 삽입 시 메모리를 동적으로 할당하여 크기를 유연하게 조절할 수 있다.
삽입 연산의 구체적인 동작 방식은 리스트의 종류에 따라 다르다. 단일 연결 리스트에서는 새로운 노드를 생성한 후, 삽입할 위치의 이전 노드와 다음 노드의 참조(포인터)를 조정하여 연결한다. 이중 연결 리스트에서는 앞뒤 노드의 참조를 모두 업데이트해야 하므로 연산이 약간 더 복잡해질 수 있다. 배열을 기반으로 한 리스트에서는 삽입 위치 이후의 모든 요소를 한 칸씩 뒤로 이동시켜 공간을 만드는 작업이 필요하며, 이는 성능에 영향을 미칠 수 있다.
삽입 위치에 따라 연산의 복잡도가 달라진다. 리스트의 맨 앞(헤드)에 삽입하는 것은 일반적으로 빠른 연산이다. 반면, 리스트 중간이나 특정 조건을 만족하는 위치를 찾아 삽입하려면 먼저 탐색 연산이 선행되어야 하므로 전체적인 수행 시간이 증가한다. 이러한 특성은 알고리즘을 설계할 때 고려해야 할 중요한 요소가 된다.
삽입 연산은 스택의 push 연산이나 큐의 enqueue 연산과 같이 추상 자료형의 기본 동작을 구현하는 데에도 사용된다. 또한 데이터가 지속적으로 추가되어야 하는 데이터베이스 기록, 실시간 로그 수집, 혹은 사용자 입력을 관리하는 프로그램 등 다양한 응용 분야에서 필수적이다.
리스트에서의 삭제 연산은 특정 위치에 있는 데이터 항목을 제거하는 과정이다. 이 연산의 구체적인 방법과 성능은 사용하는 리스트의 종류에 따라 크게 달라진다. 예를 들어, 배열 기반 리스트에서는 중간 요소를 삭제할 경우 뒤의 모든 요소를 앞으로 한 칸씩 이동시켜야 하므로 시간 복잡도가 O(n)이 될 수 있다. 반면, 연결 리스트에서는 삭제할 노드의 앞뒤 포인터 연결만 변경하면 되므로, 삭제 위치에 직접 접근할 수 있다면 O(1)의 시간에 연산을 완료할 수 있다.
단일 연결 리스트에서의 삭제는 일반적으로 삭제할 노드의 이전 노드를 찾아 그 노드의 다음 포인터를 삭제할 노드의 다음 노드를 가리키도록 갱신한다. 이중 연결 리스트에서는 삭제할 노드의 앞뒤 노드들의 포인터를 모두 조정하여 서로 연결해주어야 한다. 원형 연결 리스트의 경우에도 기본 원리는 유사하지만, 리스트의 끝과 시작이 연결되어 있다는 점을 고려하여 포인터를 처리해야 한다.
삭제 연산을 구현할 때는 몇 가지 주의사항이 있다. 먼저, 리스트가 비어있는 경우를 체크해야 한다. 또한, 삭제하려는 위치가 리스트의 범위를 벗어나지 않았는지 확인하는 것이 중요하다. 메모리 관리 측면에서, 동적 할당을 사용하는 연결 리스트의 경우 삭제된 노드가 차지하던 메모리를 명시적으로 해제해 주어야 메모리 누수를 방지할 수 있다. 이러한 삭제 연산은 스택의 pop 연산이나 큐의 dequeue 연산과 같은 더 추상화된 자료구조의 핵심을 이루기도 한다.
탐색 연산은 리스트에서 특정 데이터 항목을 찾는 과정이다. 배열과 연결 리스트 모두에서 기본적으로 제공되지만, 그 성능과 구현 방식은 자료구조의 종류에 따라 크게 달라진다.
배열에서의 탐색은 인덱스를 통한 직접 접근이 가능하기 때문에 매우 빠르다. 특정 위치의 요소를 찾는 데 걸리는 시간은 항상 일정하다. 반면, 특정 값을 가진 요소를 찾기 위해서는 처음부터 끝까지 순차적으로 검색해야 할 수 있으며, 이 경우 최악의 경우 리스트 전체를 확인해야 한다. 이러한 순차 탐색은 연결 리스트에서도 마찬가지로 적용된다.
연결 리스트에서는 인덱스 접근이 불가능하므로, 특정 위치나 값을 찾기 위해서는 항상 헤드 노드부터 시작하여 다음 노드에 대한 포인터를 따라가며 원하는 노드를 찾을 때까지 순회해야 한다. 이는 탐색 시간이 리스트의 길이에 비례하게 됨을 의미한다. 따라서 탐색이 빈번한 응용 프로그램에서는 배열이 더 유리한 경우가 많다.
탐색의 효율성을 높이기 위해 이진 탐색과 같은 알고리즘을 사용할 수 있지만, 이는 리스트가 정렬되어 있고 인덱스 접근이 가능한 배열과 같은 구조에서만 효과적이다. 연결 리스트에서는 중간 요소에 대한 직접 접근이 불가능하여 일반적으로 이진 탐색을 적용하기 어렵다.

리스트는 다양한 자료구조의 기반이 되며, 각각의 구현 방식에 따라 뚜렷한 장점과 단점을 가진다. 가장 기본적인 배열과 비교했을 때, 연결 리스트의 주요 장점은 동적 메모리 할당을 통한 유연한 크기 조절이다. 데이터를 미리 정해진 크기로 할당하는 배열과 달리, 연결 리스트는 프로그램 실행 중에 필요에 따라 노드를 추가하거나 제거할 수 있어 메모리를 효율적으로 사용할 수 있다. 또한, 리스트 중간에 데이터를 삽입하거나 삭제하는 연산이 배열보다 일반적으로 빠르다. 배열의 경우 요소들을 이동시켜야 하는 오버헤드가 발생하지만, 연결 리스트는 포인터만 변경하면 되기 때문이다.
반면, 리스트의 가장 큰 단점은 임의 접근이 불가능하다는 점이다. 배열은 인덱스를 통해 즉시 원하는 요소에 접근할 수 있는 반면, 단일 연결 리스트나 이중 연결 리스트는 처음부터 순차적으로 탐색해야 하므로 접근 시간이 느리다. 이는 탐색 연산의 성능을 저하시키는 주요 원인이다. 또한, 각 노드가 데이터와 함께 포인터를 저장해야 하므로, 데이터 자체를 저장하는 데 필요한 메모리 외에 추가적인 메모리 공간이 필요하다.
스택과 큐와 같은 추상 자료형을 리스트로 구현할 때는 이러한 특성이 다른 영향을 미친다. 예를 들어, 연결 리스트로 구현한 스택은 크기 제한이 없고 동적이라는 장점이 있지만, 각 연산마다 노드의 할당과 해제가 발생하여 오버헤드가 있을 수 있다. 배열로 구현한 리스트는 캐시 지역성으로 인해 접근 속도가 빠르고 메모리 오버헤드가 적지만, 크기가 고정되어 있어 공간이 부족할 경우 재할당 비용이 크다.
따라서 리스트를 사용할 때는 응용 프로그램의 요구사항, 즉 데이터의 삽입/삭제 빈도, 탐색 빈도, 메모리 제약 조건 등을 종합적으로 고려하여 배열 기반 리스트와 연결 리스트 중 적합한 구현 방식을 선택해야 한다. 알고리즘의 효율성은 사용된 자료구조에 크게 의존하기 때문이다.

리스트는 다양한 알고리즘과 소프트웨어의 기본 구성 요소로 널리 응용된다. 가장 직접적인 응용은 데이터의 순차적 저장 및 관리이다. 예를 들어, 음악 재생 목록이나 브라우저의 방문 기록, 파일 시스템의 디렉토리 내 파일 목록 등은 리스트를 통해 순서를 유지하며 관리된다. 또한 배열과 달리 동적 크기 조절이 용이하기 때문에, 실행 시간에 데이터 양이 자주 변하는 상황에서 효율적으로 사용된다.
운영체제에서는 연결 리스트가 프로세스 관리에 핵심적으로 활용된다. 준비 큐나 대기 큐와 같은 시스템 자료구조는 리스트를 바탕으로 구현되어, 실행 대기 중인 프로세스들을 순서대로 관리한다. 메모리 관리에서도 사용 가능한 메모리 블록을 연결 리스트로 관리하는 기법이 존재한다.
더 복잡한 자료구조를 구현하는 데에도 리스트가 기반이 된다. 스택과 큐는 리스트의 특수한 형태로 볼 수 있으며, 그래프를 표현할 때 인접 리스트 방식으로 사용된다. 또한 해시 테이블에서 충돌을 해결하기 위한 체이닝 기법도 각 버킷을 연결 리스트로 관리한다. 이러한 광범위한 응용은 리스트가 컴퓨팅의 근간을 이루는 유연한 도구임을 보여준다.
