1Page 노트정리 연결리스트

1 Page 노트 정리

new repo

Linked List: 개념과 시간 복잡도

개념

Linked List는 데이터 요소들이 연속적으로 저장되지 않고, 각각의 요소가 다음 요소의 주소를 가지고 있는 자료구조입니다. Linked List의 주요 특징은 다음과 같습니다:

  1. 노드(Node): Linked List의 기본 단위로, 데이터와 다음 노드에 대한 참조(링크)를 포함합니다.
  2. Head: Linked List의 시작 노드를 가리킵니다.
  3. Tail: Linked List의 끝 노드를 가리키며, 마지막 노드는 null을 참조합니다.

Linked List의 종류

  1. 단방향 연결 리스트 (Singly Linked List):
    • 각 노드는 다음 노드에 대한 참조를 가집니다.
    • 앞에서 뒤로 순차적으로 접근할 수 있지만, 뒤에서 앞으로의 접근은 불가능합니다.
  2. 양방향 연결 리스트 (Doubly Linked List):
    • 각 노드는 다음 노드와 이전 노드에 대한 참조를 가집니다.
    • 양방향으로 접근이 가능하여 삽입과 삭제가 더 용이합니다.
  3. 양방향 원형 연결 리스트 (Doubly Circular Linked List):
    • 마지막 노드가 첫 노드를 참조하여 원형 구조를 형성합니다.
    • 첫 노드의 이전은 마지막 노드, 마지막 노드의 다음은 첫 노드가 됩니다.

Linked List의 시간 복잡도

Linked List의 주요 연산에 대한 시간 복잡도는 다음과 같습니다:

  1. 삽입 (Insertion):
    • 앞에 삽입 (addFirst): O(1)
    • 뒤에 삽입 (addLast): O(1)
    • 중간에 삽입: O(n) (특정 위치를 찾는 데 O(n), 삽입 자체는 O(1))
  2. 삭제 (Deletion):
    • 앞에서 삭제 (removeFirst): O(1)
    • 뒤에서 삭제 (removeLast): O(1)
    • 중간에서 삭제: O(n) (특정 위치를 찾는 데 O(n), 삭제 자체는 O(1))
  3. 탐색 (Search):
    • 인덱스 접근 (get): O(n) (특정 위치를 찾기 위해 순차적으로 접근)
    • 값 탐색 (contains, indexOf): O(n) (특정 값을 찾기 위해 순차적으로 접근)
  4. 크기 확인 (size): O(1) (단, 크기를 저장하는 변수를 유지하는 경우)

Linked List의 사용 예시

import java.util.LinkedList;
import java.util.Arrays;
import java.util.Iterator;

public class Main {
    public static void main(String[] args) {
        LinkedList<Integer> list = new LinkedList<>();

        list.addFirst(1);  // 데이터 1을 추가
        list.addFirst(2);  // 데이터 2를 맨 앞에 추가

        System.out.println(list); // [2, 1] 출력

        list.add(1, 3); // 1번 인덱스에 데이터 3 추가 
        System.out.println(list); // [2, 3, 1] 출력
        System.out.println(list.get(0)); // 0번 인덱스의 데이터 출력 (2)   

        list.addLast(5); // 데이터 5를 맨 뒤에 추가
        System.out.println(list); // [2, 3, 1, 5] 출력 

        list.removeFirst(); // 가장 첫번째 인덱스 삭제
        list.removeLast();  // 가장 마지막 인덱스 삭제
        list.remove(0);     // 0번 인덱스의 데이터 삭제
        System.out.println(list); // [1] 출력 

        System.out.println(list.size()); // 리스트 크기 출력 (1)        
        System.out.println(list.indexOf(1)); // 데이터 1의 인덱스 출력 (0)

        System.out.println(list.contains(3)); // false
        System.out.println(list.contains(1)); // true
        System.out.println(list.isEmpty()); // false

        list.set(0, 10); // 0번 인덱스의 데이터를 10으로 변경
        System.out.println(list); // [10] 출력

        Object[] arr = list.toArray(); // LinkedList의 데이터를 배열에 저장
        System.out.println(arr[0]);    // 배열의 0번 인덱스 출력 (10)
        System.out.println(Arrays.toString(arr)); // [10] 출력

        Iterator<Integer> it = list.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}

LinkedList는 삽입과 삭제가 빈번하게 일어나는 상황에서 유용하게 사용할 수 있는 자료구조입니다.

연결 리스트 (Linked List), 이중 연결 리스트 (Doubly Linked List), 원형 연결 리스트 (Circular Linked List)

개념 정리

연결 리스트 (Linked List)

각 노드가 데이터와 다음 노드의 주소를 저장하는 구조입니다.

이중 연결 리스트 (Doubly Linked List)

각 노드가 데이터, 이전 노드의 주소, 다음 노드의 주소를 저장하는 구조입니다.

원형 연결 리스트 (Circular Linked List)

마지막 노드가 null을 가리키는 대신 처음 노드를 가리키는 구조입니다.

시간 복잡도

연산 단순 연결 리스트 (Singly Linked List) 이중 연결 리스트 (Doubly Linked List) 원형 연결 리스트 (Circular Linked List)
삽입 O(1) 앞, O(n) 중간 및 뒤 O(1) 앞 및 뒤, O(n) 중간 O(1) 앞 및 뒤, O(n) 중간
삭제 O(1) 앞, O(n) 중간 및 뒤 O(1) 앞 및 뒤, O(n) 중간 O(1) 앞 및 뒤, O(n) 중간
탐색 O(n) O(n) O(n)
인덱스 접근 O(n) O(n) O(n)
크기 확인 O(1) O(1) O(1)

구조 및 장단점

연결 리스트 (Singly Linked List)

  • 구조: 각 노드는 데이터와 다음 노드의 주소를 저장.
  • 장점: 단순 구조, 메모리 효율적.
  • 단점: 단방향 탐색만 가능, 특정 요소 접근 시 탐색 시간 증가.

이중 연결 리스트 (Doubly Linked List)

  • 구조: 각 노드는 데이터, 이전 노드의 주소, 다음 노드의 주소를 저장.
  • 장점: 양방향 탐색 가능, 노드 삽입 및 삭제 용이.
  • 단점: 더 많은 메모리 사용, 구현 복잡성 증가.

원형 연결 리스트 (Circular Linked List)

  • 구조: 마지막 노드가 처음 노드를 가리키는 구조.
  • 장점: 연속적인 순회 가능, 단일 포인터로 머리와 꼬리 노드 추가 가능.
  • 단점: 구현 복잡성 증가.

요세푸스 문제

문제

요세푸스 문제는 다음과 같다.

1번부터 N번까지 N명의 사람이 원을 이루면서 앉아있고, 양의 정수 K(≤ N)가 주어진다.
이제 순서대로 K번째 사람을 제거한다.
한 사람이 제거되면 남은 사람들로 이루어진 원을 따라 이 과정을 계속해 나간다.
이 과정은 N명의 사람이 모두 제거될 때까지 계속된다.
원에서 사람들이 제거되는 순서를 (N, K)-요세푸스 순열이라고 한다.
예를 들어 (7, 3)-요세푸스 순열은 <3, 6, 2, 7, 5, 1, 4>이다.
N과 K가 주어지면 (N, K)-요세푸스 순열을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 N과 K가 빈 칸을 사이에 두고 순서대로 주어진다. (1 ≤ K ≤ N ≤ 5,000)

출력

예제와 같이 요세푸스 순열을 출력한다.

예제 입력 1

7 3

예제 출력 1

<3, 6, 2, 7, 5, 1, 4>
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;
import java.util.stream.IntStream;

public class BJ1158 {

    public static ArrayList getJosephusPermutation(int N, int K) {
        Queue queue = new LinkedList();
        ArrayList result = new ArrayList();

        IntStream.range(1, N + 1).forEach(x -> queue.add(x));

        int cnt = 0;
        while (!queue.isEmpty()) {
            int data = (int)queue.remove();
            cnt += 1;
            if (cnt % K == 0) {
                result.add(data);
            } else {
                queue.add(data);
            }
        }

        return result;
    }

    public static void main(String[] args) throws IOException {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer stringTokenizer = new StringTokenizer(bufferedReader.readLine());
        int n = Integer.parseInt(stringTokenizer.nextToken());
        int k = Integer.parseInt(stringTokenizer.nextToken());
        // Test code
        ArrayList<Integer> result = getJosephusPermutation(n, k);

        // 결과를 원하는 형식으로 출력하기
        StringBuilder sb = new StringBuilder();
        sb.append("<");
        for (int i = 0; i < result.size(); i++) {
            sb.append(result.get(i));
            if (i < result.size() - 1) {
                sb.append(", ");
            }
        }
        sb.append(">");
        System.out.println(sb.toString());
    }
}
7 3
<3, 6, 2, 7, 5, 1, 4>

출처 : 백준, https://www.acmicpc.net/problem/1158

문제 설명 운영체제의 역할 중 하나는 컴퓨터 시스템의 자원을 효율적으로 관리하는 것입니다. 이 문제에서는 운영체제가 다음 규칙에 따라 프로세스를 관리할 경우 특정 프로세스가 몇 번째로 실행되는지 알아내면 됩니다.

  1. 실행 대기 큐(Queue)에서 대기중인 프로세스 하나를 꺼냅니다.
  2. 큐에 대기중인 프로세스 중 우선순위가 더 높은 프로세스가 있다면 방금 꺼낸 프로세스를 다시 큐에 넣습니다.
  3. 만약 그런 프로세스가 없다면 방금 꺼낸 프로세스를 실행합니다. 3.1 한 번 실행한 프로세스는 다시 큐에 넣지 않고 그대로 종료됩니다.

예를 들어 프로세스 4개 [A, B, C, D]가 순서대로 실행 대기 큐에 들어있고, 우선순위가 [2, 1, 3, 2]라면 [C, D, A, B] 순으로 실행하게 됩니다.

현재 실행 대기 큐(Queue)에 있는 프로세스의 중요도가 순서대로 담긴 배열 priorities와, 몇 번째로 실행되는지 알고싶은 프로세스의 위치를 알려주는 location이 매개변수로 주어질 때, 해당 프로세스가 몇 번째로 실행되는지 return 하도록 solution 함수를 작성해주세요.

제한사항

  • priorities의 길이는 1 이상 100 이하입니다.
  • priorities의 원소는 1 이상 9 이하의 정수입니다.
  • priorities의 원소는 우선순위를 나타내며 숫자가 클 수록 우선순위가 높습니다.
  • location은 0 이상 (대기 큐에 있는 프로세스 수 - 1) 이하의 값을 가집니다.
  • priorities의 가장 앞에 있으면 0, 두 번째에 있으면 1 … 과 같이 표현합니다.

입출력 예

priorities location return
[2, 1, 3, 2] 2 1
[1, 1, 9, 1, 1, 1] 0 5

입출력 예 설명 예제 #1

  • 문제에 나온 예와 같습니다.

예제 #2

  • 6개의 프로세스 [A, B, C, D, E, F]가 대기 큐에 있고 중요도가 [1, 1, 9, 1, 1, 1] 이므로 [C, D, E, F, A, B] 순으로 실행됩니다. 따라서 A는 5번째로 실행됩니다.
import java.util.*;

class Solution {
        public int solution(int[] priorities, int location) {
            int answer = 0;
            LinkedList<Integer> ll = new LinkedList<>();

            // LinkedLis로 priorities 넣기
            for(int n: priorities){
                ll.add(n);
            }

            // ll 순회
            while(!ll.isEmpty()){
                // max 값 찾기
                int max = 0;
                for(Integer n : ll){
                    if (n > max){
                        max = n;
                    }
                }

                // 0번 인덱스 조회
                // 0번 인덱스 값이 max인경우,
                if(ll.peek() == max){
                    ll.poll();
                    answer++;
                    // location이 0인 경우, 종료
                    // 아닌 경우, poll로 인한 재배열
                    // location--
                    if(location == 0){
                        break;
                    }else{
                        location--;
                    }
                }
                // 0번 인덱스 값이 max가 아닌 경우,
                else{
                    // 맨 앞의 요소를 뒤로 보냄
                    ll.add(ll.poll());

                    // location이 0인경우,
                    // 다시 뒤부터 시작
                    // 아닌 경우, 앞으로 당김
                    if(location == 0){
                        location = ll.size()-1;
                    }else{
                        location--;
                    }
                }
            }

            return answer;
        }
}

출처 : 프로그래머스, https://school.programmers.co.kr/learn/courses/30/lessons/42587

Leave a comment