[자료구조] 연결 리스트

2024. 1. 26. 22:14·공부/자료구조 | 알고리즘

링크드 리스트

연결 리스트 (Linked List)

연결 리스트의 개념

  • 연결 리스트 (Linked List) : 데이터와 포인터를 가진 각 노드들을 일렬로 연결시킨 형태의 자료구조
  • 예 : 버킷 리스트, 요일들 등

 

연결 리스트의 특징

  • 메인 메모리상 물리적으로 흩어져있는 데이터들을 서로 연결하여 하나로 묶는 방식
  • 삽입/삭제 시 앞뒤에 있는 데이터들을 이동할 필요없이 해당 데이터들을 연결하는 줄만 수정하면 된다.
  • 첫 번째 데이터만 알면 나머지 데이터들을 연결된 줄로 추적 가능하다.
  • 포인터를 저장하기 위한 추가적인 메모리 공간이 필요하다.

 

연결 리스트의 구조

  • 노드 (node) : 연결되는 상자, 데이터 필드와 링크 필드로 구성
  • 데이터 필드 (data field) : 저장하고 싶은 데이터가 들어감
  • 링크 필드 (link field) : 다른 노드를 가리키는 포인터가 들어감
  • 머리 (head) : 첫 번째 노드
  • 꼬리 (tail) : 마지막 노드

 

연결 리스트의 종류

  • 단순 연결 리스트 (singly linked list) :
    • 하나의 방향으로만 연결되어 있는 연결 리스트
    • 하나의 링크 필드를 가지며 이 링크 필드를 이용하여 모든 노드들이 연결되어 있음
    • 체인(Chain)이라고도 함
    • 마지막 노드의 링크 필드는 NULL 값을 가짐

 

  • 원형 연결 리스트 (circular linked list) :
    • 단순 연결 리스트와 같으나 마지막 노드가 첫 번째 노드를 가리킴
    • 마지막 노드의 링크 필드는 첫 번째 노드 주소를 가리킴
    • 하나의 노드에서 다른 모든 노드로 접근이 가능
    • 원소가 하나밖에 없으면 자기자신을 가리킴

 

  • 이중 연결 리스트 (doubly linked list) :
    • 각 노드마다 2개의 링크가 존재
    • 하나의 링크는 앞의 노드를 가리키고 다른 하나의 노드는 뒤의 노드를 가리킴
    • 단순 연결 리스트의 단점을 보완하여 특정 노드에서 자유롭게 양방향으로 움직일 수 있음

 

연결 리스트의 구현

  • 단순 연결 리스트

public class SinglyLinkedList {

    Node head;
    Node tail;
    int size;

    public SinglyLinkedList() {

        this.head = null;        
        this.tail = null;

        size = 0;
    }

    class Node {
        Object data;
        Node nextNode;

        Node(Object data){
            this.data = data;
            this.nextNode = null;
        }
    }

    // 특정 노드 위치 검색
    public Node search(int idx) {
        Node findNode = head;    // 머리부터 시작함

        for(int i = 0; i < idx; i++) {
            findNode = findNode.nextNode;
        }

        return findNode;
    }

    // 맨 앞에 추가
    public void addFirst(Object data) {
        Node newNode = new Node(data);
        newNode.nextNode = head;
        head = newNode;
        size++;

        if (head.nextNode == null) {
            tail = head;
        }
    }

    // 맨 뒤에 추가
    public void addLast(Object data) {
        Node newNode = new Node(data);

        if (size == 0) {
            addFirst(data);
            return;
        }
        tail.nextNode = newNode;
        tail = newNode;
        size++;
    }

    // 중간에 추가
    public void add(Object data, int idx) {

        if (idx == 0) {
            addFirst(data);
            return;
        }
        if (idx == size) {
            addLast(data);
            return;
        }

        Node preNode = search(idx - 1);
        Node postNode = preNode.nextNode;
        Node newNode = new Node(data);
        preNode.nextNode = newNode;
        newNode.nextNode = postNode;
        size++;
    }

    // 맨 앞에 삭제
    public Object removeFirst() {
        Node headNode = head;
        head = head.nextNode;

        if (head == null) {
            tail = null;
        }
        size--;

        return headNode.data;
    }

    // 맨 뒤에 삭제
    public Object removeLast() {
        if (size <= 1) {
            return removeFirst();
        }

        Node secondNode = search(size - 2);
        Node lastNode = tail;
        tail = secondNode;
        tail.nextNode = null;
        size--;

        return lastNode.data;
    }

    // 중간에 삭제
    public Object remove(int idx) {

        if (idx == 0) {
            return removeFirst();
        }
        if (idx == size - 1) {
            return removeLast();
        }

        Node preNode = search(idx - 1);
        Node lastNode = preNode.nextNode;
        preNode.nextNode = preNode.nextNode.nextNode;
        size--;

        return lastNode.data;
    }

    // string으로 데이터 출럭
    public String toString() {
        StringBuffer str = new StringBuffer("[");
        Node node = head;

        if (node != null) {
            str.append(node.data);
            node = node.nextNode;

            while(node != null) {
                str.append(", ");
                str.append(node.data);
                node = node.nextNode;
            }

        }
        str.append("]");
        return str.toString();
    }

    public static void main(String[] args) {
        SinglyLinkedList linkedList = new SinglyLinkedList();

        // 맨 앞에 추가
        linkedList.addFirst(1);
        linkedList.addFirst(2);
        linkedList.addFirst(3);
        System.out.println(linkedList.toString());    // 출력 : [3, 2, 1]

        // 맨 뒤에 추가
        linkedList.addLast(4);
        linkedList.addLast(5);
        System.out.println(linkedList.toString());    // 출력 : [3, 2, 1, 4, 5]

        // 중간에 추가
        linkedList.add(10, 2);
        System.out.println(linkedList.toString());    // 출력 : [3, 2, 10, 1, 4, 5]

        // 맨 앞에 삭제
        linkedList.removeFirst();
        System.out.println(linkedList.toString());    // 출력 : [2, 10, 1, 4, 5]

        // 맨 뒤에 삭제
        linkedList.removeLast();
        System.out.println(linkedList.toString());    // 출력 : [2, 10, 1, 4]

        // 중간에 삭제
        linkedList.remove(2);
        System.out.println(linkedList.toString());    // 출력 : [2, 10, 4]
    }
}

  • 원형 연결 리스트

public class CircularLinkedList {

    Node head;
    Node tail;
    int size;

    public CircularLinkedList() {

        this.head = null;        
        this.tail = null;

        size = 0;
    }

    class Node {
        Object data;
        Node nextNode;

        Node(Object data){
            this.data = data;
            this.nextNode = null;
        }
    }

    // 특정 노드 위치 검색
    public Node search(int idx) {
        Node findNode = head;    // 머리부터 시작함

        for(int i = 0; i < idx; i++) {
            findNode = findNode.nextNode;
        }

        return findNode;
    }

    // 맨 앞에 추가
    public void addFirst(Object data) {
        Node newNode = new Node(data);

        if (size == 0) {
            head = newNode;
            tail = newNode;
            newNode.nextNode = head;
        }
        else {
            newNode.nextNode = head;
            head = newNode;
            tail.nextNode = head;
        }
        size++;
    }

    // 맨 뒤에 추가
    public void addLast(Object data) {
        Node newNode = new Node(data);

        if (size == 0) {
            addFirst(data);
            return;
        }
        tail.nextNode = newNode;
        tail = newNode;
        tail.nextNode = head;
        size++;
    }

    // 중간에 추가
    public void add(Object data, int idx) {

        if (idx == 0) {
            addFirst(data);
            return;
        }
        if (idx == size) {
            addLast(data);
            return;
        }

        Node preNode = search(idx - 1);
        Node postNode = preNode.nextNode;
        Node newNode = new Node(data);
        preNode.nextNode = newNode;
        newNode.nextNode = postNode;
        size++;
    }

    // 맨 앞에 삭제
    public Object removeFirst() {
        if (size == 0) {
            return null;
        }

        Node headNode = head;
        head = head.nextNode;
        tail.nextNode = head; 
        size--;

        if (size == 0) {
            head = null;
            tail = null;
        }

        return headNode.data;
    }

    // 맨 뒤에 삭제
    public Object removeLast() {
        if (size <= 1) {
            return removeFirst();
        }

        Node secondLastNode = search(size - 2);
        Node lastNode = tail;
        tail = secondLastNode;
        tail.nextNode = head; 
        size--;

        return lastNode.data;
    }

    // 중간에 삭제
    public Object remove(int idx) {

        if (idx == 0) {
            return removeFirst();
        }
        if (idx == size - 1) {
            return removeLast();
        }

        Node preNode = search(idx - 1);
        Node lastNode = preNode.nextNode;
        preNode.nextNode = preNode.nextNode.nextNode;
        size--;

        return lastNode.data;
    }

    // string으로 데이터 출럭
    public String toString() {
        StringBuffer str = new StringBuffer("[");
        Node node = head;

        if (node != null) {
            str.append(node.data);
            node = node.nextNode;

            while (node != null && node != head) {
                str.append(", ");
                str.append(node.data);
                node = node.nextNode;
            }
        }
        str.append("]");
        return str.toString();
    }

    public static void main(String[] args) {
        CircularLinkedList linkedList = new CircularLinkedList();

        // 맨 앞에 추가
        linkedList.addFirst(1);
        linkedList.addFirst(2);
        linkedList.addFirst(3);
        System.out.println(linkedList.toString());    // 출력 : [3, 2, 1]

        // 맨 뒤에 추가
        linkedList.addLast(4);
        linkedList.addLast(5);
        System.out.println(linkedList.toString());    // 출력 : [3, 2, 1, 4, 5]

        // 중간에 추가
        linkedList.add(10, 2);
        System.out.println(linkedList.toString());    // 출력 : [3, 2, 10, 1, 4, 5]

        // 맨 앞에 삭제
        linkedList.removeFirst();
        System.out.println(linkedList.toString());    // 출력 : [2, 10, 1, 4, 5]

        // 맨 뒤에 삭제
        linkedList.removeLast();
        System.out.println(linkedList.toString());    // 출력 : [2, 10, 1, 4]

        // 중간에 삭제
        linkedList.remove(2);
        System.out.println(linkedList.toString());    // 출력 : [2, 10, 4]
    }

}
  • 이중 연결 리스트

public class DoublyLinkedList {


    Node head;
    Node tail;
    int size;

    public DoublyLinkedList() {

        this.head = null;        
        this.tail = null;

        size = 0;
    }

    class Node {
        Object data;
        Node preNode;
        Node postNode;

        Node(Object data){
            this.data = data;
            this.preNode = null;
            this.postNode = null;
        }
    }

    // 특정 노드 위치 검색
    public Node search(int idx) {
        Node findNode = head;    // 머리부터 시작함

        for(int i = 0; i < idx; i++) {
            findNode = findNode.postNode;
        }

        return findNode;
    }

    // 맨 앞에 추가
    public void addFirst(Object data) {
        Node newNode = new Node(data);
        newNode.postNode = head;

        if (head != null) {
            head.preNode = newNode;
        }
        head = newNode;
        if (head.postNode == null) {
            tail = head;
        }

        size++;
    }

    // 맨 뒤에 추가
    public void addLast(Object data) {
        Node newNode = new Node(data);

        if (size == 0) {
            addFirst(data);
            return;
        }
        tail.postNode = newNode;
        newNode.preNode = tail;
        tail = newNode;
        size++;
    }

    // 중간에 추가
    public void add(Object data, int idx) {

        if (idx == 0) {
            addFirst(data);
            return;
        }
        if (idx == size) {
            addLast(data);
            return;
        }

        Node leftNode = search(idx - 1);
        Node rightNode = leftNode.postNode;
        Node newNode = new Node(data);
        leftNode.postNode = newNode;
        rightNode.preNode = newNode;
        newNode.preNode = leftNode;
        newNode.postNode = rightNode;
        size++;
    }

    // 맨 앞에 삭제
    public Object removeFirst() {
        Node headNode = head;
        head = head.postNode;

        if (head == null) {
            tail = null;
        }
        else {
            head.preNode = null;
        }

        size--;

        return headNode.data;
    }

    // 맨 뒤에 삭제
    public Object removeLast() {
        if (size <= 1) {
            return removeFirst();
        }

        Node secondNode = search(size - 2);
        Node lastNode = tail;
        tail = secondNode;
        tail.postNode = null;

        size--;

        return lastNode.data;
    }

    // 중간에 삭제
    public Object remove(int idx) {

        if (idx == 0) {
            return removeFirst();
        }
        if (idx == size - 1) {
            return removeLast();
        }

        Node leftNode = search(idx - 1);
        Node lastNode = leftNode.postNode;
        Node rightNode = lastNode.postNode;

        leftNode.postNode = rightNode;
        rightNode.preNode = leftNode;

        size--;

        return lastNode.data;
    }

    // string으로 데이터 출럭
    public String toString() {
        StringBuffer str = new StringBuffer("[");
        Node node = head;

        if (node != null) {
            str.append(node.data);
            node = node.postNode;

            while(node != null) {
                str.append(", ");
                str.append(node.data);
                node = node.postNode;
            }

        }
        str.append("]");
        return str.toString();
    }

    // string으로 데이터 거꾸로 출럭
    public String toBackString() {
        StringBuffer str = new StringBuffer("[");
        Node node = tail;

        if (node != null) {
            str.append(node.data);
            node = node.preNode;

            while(node != null) {
                str.append(", ");
                str.append(node.data);
                node = node.preNode;
            }

        }
        str.append("]");
        return str.toString();
    }

    public static void main(String[] args) {
        DoublyLinkedList linkedList = new DoublyLinkedList();

        // 맨 앞에 추가
        linkedList.addFirst(1);
        linkedList.addFirst(2);
        linkedList.addFirst(3);
        System.out.println(linkedList.toString());    // 출력 : [3, 2, 1]

        // 맨 뒤에 추가
        linkedList.addLast(4);
        linkedList.addLast(5);
        System.out.println(linkedList.toString());    // 출력 : [3, 2, 1, 4, 5]

        // 중간에 추가
        linkedList.add(10, 2);
        System.out.println(linkedList.toString());    // 출력 : [3, 2, 10, 1, 4, 5]

        // 맨 앞에 삭제
        linkedList.removeFirst();
        System.out.println(linkedList.toString());    // 출력 : [2, 10, 1, 4, 5]

        // 맨 뒤에 삭제
        linkedList.removeLast();
        System.out.println(linkedList.toString());    // 출력 : [2, 10, 1, 4]

        // 중간에 삭제
        linkedList.remove(2);
        System.out.println(linkedList.toString());    // 출력 : [2, 10, 4]

        System.out.println(linkedList.toBackString());    // 거꾸로 - 출력 : [4, 10, 2]
    }

}

 

'공부 > 자료구조 | 알고리즘' 카테고리의 다른 글

[자료구조] 그래프  (1) 2024.01.26
[자료구조] 트리  (0) 2024.01.26
[자료구조] 스택과 큐  (0) 2024.01.26
[C언어로 쉽게 풀어쓴 자료구조] 1장 연습문제  (0) 2023.08.18
[C언어로 쉽게 풀어쓴 자료구조] 1.3 알고리즘의 성능 분석  (0) 2023.08.08
'공부/자료구조 | 알고리즘' 카테고리의 다른 글
  • [자료구조] 그래프
  • [자료구조] 트리
  • [자료구조] 스택과 큐
  • [C언어로 쉽게 풀어쓴 자료구조] 1장 연습문제
2월2
2월2
  • 2월2
    서벅돌의 성장일기
    2월2
  • 전체
    오늘
    어제
    • 분류 전체보기 (120)
      • TIL (2)
      • Server (28)
        • spring (7)
        • node.js (16)
        • 기타 (5)
      • App&Web (17)
        • Web (1)
        • Android (16)
        • iOS (0)
      • 공부 (59)
        • 깃&깃허브 (3)
        • 파이썬 (17)
        • 유니티 (4)
        • 자료구조 | 알고리즘 (15)
        • 자바 (3)
        • 운영체제 (8)
        • AI와 데이터 (9)
      • 대외활동 (12)
        • NPC 동아리 (1)
        • UMC 동아리 (11)
      • 대학교 (1)
        • 교직 (1)
      • 기타 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
    • 글 관리
  • 링크

  • 공지사항

    • Notice
  • 인기 글

  • 태그

    Lua
    Python
    Unity
    자바
    루아
    mysql
    유니티
    코틀린
    Android
    파이썬
    안드로이드
    java
    C
    kotlin
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
2월2
[자료구조] 연결 리스트
상단으로

티스토리툴바