코디잉

10_연결 리스트(Linked List) 본문

자료구조&알고리즘/이론공부

10_연결 리스트(Linked List)

yong_ღ'ᴗ'ღ 2022. 9. 13. 20:45

- 선형 리스트: 데이터를 순서대로 나열해 놓은 자료구조 

- 스택과 큐도 리스트 구조로 되어 있다.

- 연결 리스트를 구현하기 위한 노드의 구조

class Node<E> {
    E data;		// 데이터
    Node<E> next;	// 다음 노드를 가리키는 포인터
}

└→ 필드 data형인 E는 참조형이다. 

       클래스형 변수인 data는 데이터 그 자체가 아니라 데이터에 대한 '참조' !

 

  • 포인터로 연결 리스트 만들기
public class LinkedList<E> {
    // 노드
    class Node<E> {
        private E data;         // 데이터
        private Node<E> next;   // 다음 노드를 가리키는 포인터

        // 생성자
        public Node(E data, Node<E> next) {
            this.data = data;
            this.next = next;
        }
    }

    private Node<E> head = null;    // 머리 노드 (머리 노드에 대한 참조! 머리 노드 그 자체 아님!)
    private Node<E> crnt = null;    // 현재 선택한 노드 (검색/삭제 등의 용도로 사용)

    // 생성자
    public LinkedList() {
        head = crnt = null;
    }

    // 노드 검색
    public E search(E obj, Comparator<? super E> c) {
        Node<E> node = head;

        while (node != null) {
            // 검색 성공
            if (c.compare(obj, node.data) == 0) {
                crnt = node;
                return node.data;
            }
            // 다음 노드 대입
            node = node.next;
        }
        // 검색 실패
        return null;
    }

    // 머리에 노드 삽입
    public void addFirst(E obj) {
        Node<E> node = head;
        // head에 새로운 노드 넣어주고, next에 기존 head 넣어줌
        head = crnt = new Node<E>(obj, node);
    }

    // 꼬리에 노드 삽입
    public void addLast(E obj) {
        if (head == null)
            addFirst(obj);
        else {
            Node<E> node = head;
            while (node.next != null)
                node = node.next;
            //-- while문 종료 시, node는 꼬리 노드를 가리킴
            node.next = crnt = new Node<E>(obj, null);
        }
    }

    // 머리 노드 삭제
    public void removeFirst() {
        if (head != null)
            head = crnt = head.next;
    }

    // 꼬리 노드 삭제
    public void removeLast() {
        if (head != null) {
            // 머리노드가 꼬리노드일 때,
            if (head.next == null)
                removeFirst();
            else {
                Node<E> node = head;
                Node<E> nodePrev = head;
                while (node.next != null) {
                    nodePrev = node;
                    node = node.next;
                }
                //-- while문 종료 시, node는 꼬리 노드를, nodePrev는 꼬리 노드의 앞 노드를 가리킴
                nodePrev.next = null;
                crnt = nodePrev;
            }
        }
    }
    
    // 노드 p를 삭제
    public void remove(Node p) {
        if (head != null) {
            if (p == head)
                removeFirst();
            else {
                Node<E> node = head;
                while (node.next != p) {
                    node = node.next;
                    if (node == null)   return;     // p가 리스트에 없음
                }
                node.next = p.next;
                crnt = node;
            }
        }
    }
    
    // 선택 노드(crnt)를 삭제
    public void removeCurrentNode() {
        remove(crnt);
    }
    
    // 모든 노드를 삭제
    public void clear() {
        // 노드에 아무것도 없을 때까지 머리노드를 삭제
        while (head != null)
            removeFirst();
        crnt = null;
    }
    
    // 선택 노드(crnt)를 하나 뒤의 노드로 이동
    public boolean next() {
        if (crnt == null || crnt.next == null)
            return false;
        crnt = crnt.next;
        return true;
    }
    
    // 선택 노드(crnt)를 출력
    public void printCurrentNode() {
        if (crnt == null)
            System.out.println("선택 노드 없음");
        else
            System.out.println(crnt.data);
    }
    
    // 모든 노드를 출력
    public void dump() {
        Node<E> node = head;
        while (node != null) {
            System.out.print(node.data + " ");
            node = node.next;
        }
    }
}

 

  • 커서로 연결 리스트 만들기

- 커서(cursor): pointer 역할하는 배열의 index

- 노드 삽입/삭제 시 요소 옮길 필요 없음

- 데이터 수가 크게 바뀌지 않고, 데이터 수의 최댓값 미리 알 수 있을 때 사용

 

  • 원형 리스트(circular list)

- 리스트의 꼬리 노드의 next가 머리 노드를 가리켜 고리 모양으로 나열

 

  • 원형 이중 연결 리스트 (=양방향 리스트, doubly linked list)

-  다음 노드를 가리키는 next와 이전 노드를 가리키는 prev가 있음

- head의 이전 노드 → tail

  tail의 다음 노드 → head

public class DblLinkedList<E> {
    class Node<E> {
        private E data;
        private Node<E> prev;
        private Node<E> next;

        Node() {
            data = null;
            prev = next = null;
        }

        Node(E obj, Node<E> prev, Node<E> next) {
            data = obj;
            this.prev = prev;
            this.next = next;
        }
    }

    private Node<E> head;    // 머리 노드(더미 노드)
    //--> 노드의 삽입/삭제 처리를 원활하게 하도록 리스트의 머리에 계속 존재하는 더미 노드
    private Node<E> crnt;

    public DblLinkedList() {
        head = crnt = new Node<E>();    // 더미 노드를 생성
    }

    // 리스트가 비어 있는가?
    public boolean isEmpty() {
        return head.next == head;
    }

    // 노드를 검색
    public E search(E obj, Comparator<? super E> c) {
        Node<E> ptr = head.next;    // 현재 스캔 중인 노드
        //-- 실질적은 머리노드는 더미 노드의 다음 노드이므로 검색 시작 → head.next 부터!

        while (ptr != head) {
            if (c.compare(obj, ptr.data) == 0) {
                crnt = ptr;
                return ptr.data;
            }
            ptr = ptr.next;
        }
        return null;
    }

    // 선택 노드를 출력
    public void printCurrentNode() {
        if (isEmpty())
            System.out.println("선택 노드 없음");
        else
            System.out.println(crnt.data);
    }

    // 모든 노드 출력
    public void dump() {
        Node<E> ptr = head.next;    // 더미 노드의 다음 노드 (실질적 머리노드)

        // ptr이 더미노드가 아닐 때 까지 반복
        while (ptr != head) {
            System.out.print(ptr.data + " ");
            ptr = ptr.next;
        }
        System.out.println();
    }

    // 모든 노드를 거꾸로 출력
    public void dumpReverse() {
        Node<E> ptr = head.prev;    // 더미 노드의 앞쪽 노드

        while (ptr != head) {
            System.out.print(ptr.data + " ");
            ptr = ptr.prev;
        }
        System.out.println();
    }

    // 선택 노드를 하나 뒤로 이동
    public boolean next() {
        if (isEmpty() || crnt.next == head)
            return false;
        crnt = crnt.next;
        return true;
    }

    // 선택 노드를 하나 앞으로 이동
    public boolean prev() {
        if (isEmpty() || crnt.prev == head)
            return false;
        crnt = crnt.prev;
        return true;
    }
    
    // 선택 노드의 바로 뒤에 노드 삽입
    public void add(E obj) {
        Node<E> node = new Node<E>(obj, crnt, crnt.next);
        crnt.next = crnt.next.prev = node;
        crnt = node;
    }
    
    // 머리에 노드 삽입
    public void addFirst(E obj) {
        crnt = head;
        add(obj);
    }
    
    // 꼬리에 노드 삽입
    public void addLast(E obj) {
        crnt = head.prev;
        add(obj);
    }
    
    // 선택 노드 삭제
    public void removeCurrentNode() {
        if (!isEmpty()) {
            crnt.prev.next = crnt.next;
            crnt.next.prev = crnt.prev;
            crnt = crnt.prev;
            if (crnt == head) crnt = head.next;
        }
    }
    
    // 노드 p를 삭제
    public void remove(Node p) {
        Node<E> ptr = head.next;
        
        while (ptr != head) {
            if (ptr == p) {
                crnt = p;
                removeCurrentNode();
                break;
            }
            ptr = ptr.next;
        }
    }
    
    // 머리 노드 삭제
    public void removeFirst() {
        crnt = head.next;
        removeCurrentNode();
    }
    
    // 꼬리 노드 삭제
    public void removeLast() {
        crnt = head.prev;
        removeCurrentNode();
    }
    
    // 모든 노드 삭제
    public void clear() {
        while (!isEmpty())
            removeFirst();
    }
}

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

12_트리  (0) 2022.09.14
11_해시  (0) 2022.09.13
09_문자열 검색  (0) 2022.09.13
08_집합  (0) 2022.09.13
07_정렬 ① 버블정렬/단순선택정렬/단순삽입정렬  (0) 2022.08.26
Comments