# 연습 문제 ## 개념 확인 ### 배열과 연결 리스트에서 원소 찾기 배열과 단일 연결 리스트에 모두 `[A, B, C, D, E]`가 순서대로 저장되어 있다. 네 번째 원소 `D`를 읽으려고 한다. 1. 배열에서는 어떤 인덱스를 바로 사용할 수 있는가? 2. 단일 연결 리스트에서는 머리 노드 `A`부터 `next`를 따라 어떤 노드를 차례대로 거쳐야 하는가? 3. 읽으려는 원소가 뒤쪽에 있을수록 두 구조에 필요한 단계 수는 어떻게 변하는가? 위치를 기준으로 반복해서 읽을 때는 어느 구조가 더 적합하며, 그 이유는 무엇인가? ??? success "참고 답안" 1. 인덱스가 0부터 시작한다면 네 번째 원소의 인덱스는 3이므로, 배열에서는 `arr[3]`을 바로 읽을 수 있다. 2. 단일 연결 리스트는 맨 앞부터 시작해야 한다. 접근 경로는 `A → B → C → D`이며, `next`를 따라 3번 이동해야 한다. 3. 배열은 시작 주소와 인덱스로 원소의 위치를 바로 계산할 수 있으므로 위치를 기준으로 접근하는 시간 복잡도가 $O(1)$이다. 단일 연결 리스트에서 $k$번째 노드에 접근하려면 머리 노드부터 `next`를 따라 $k-1$번 이동해야 하므로, 최악의 경우 $O(n)$ 시간이 필요하다. 여기서는 위치를 기준으로 읽는 연산만 비교한 것이며, 연결 리스트가 모든 연산에서 더 느리다는 뜻은 아니다. ### 배열과 연결 리스트에 원소 삽입하기 배열과 단일 연결 리스트에 모두 `A, B, C, D`가 저장되어 있다. `B` 뒤에 `X`를 삽입하려고 한다. - 배열의 용량은 5이고 현재 상태는 `[A, B, C, D, _]`다. - 연결 리스트는 `A → B → C → D`이며, 노드 `B`를 가리키는 참조를 이미 가지고 있다. 1. 배열에서는 어떤 원소를 이동해야 하는가? 삽입 후 배열을 적어 보자. 2. 연결 리스트에서는 `X.next`와 `B.next`를 어떤 순서로 수정해야 하는가? 삽입 후 연결 리스트를 적어 보자. 3. 삽입 효율성을 비교할 때 ‘노드 B의 참조를 이미 가지고 있다’는 조건을 강조하는 이유는 무엇인가? ??? success "참고 답안" 1. 배열에서는 먼저 `D`를 오른쪽으로 한 칸 옮기고, 다음으로 `C`를 오른쪽으로 한 칸 옮긴 뒤, `X`를 인덱스 2에 넣는다. 결과는 `[A, B, X, C, D]`다. 2. `B.next`는 원래 `C`를 가리킨다. 먼저 `X.next = B.next`로 설정하여 `X`가 `C`를 가리키게 하고, 다음으로 `B.next = X`로 설정한다. 결과는 `A → B → X → C → D`다. 기존 연결을 저장하지 않은 채 `B.next`를 먼저 덮어쓰면 `C`를 찾지 못할 수 있다. 3. `B`의 위치를 알고 있다면 연결 리스트의 삽입은 연결 두 개만 수정하면 되므로 $O(1)$ 시간에 완료할 수 있다. 맨 앞부터 `B`를 찾아야 한다면 탐색 자체에 $O(n)$ 시간이 걸릴 수 있다. ### 리스트의 용량은 어떻게 늘어나는가 배열로 구현한 리스트의 현재 내용은 `[A, B, C]`이고, 길이는 `size = 3`, 용량은 `capacity = 4`다. 용량이 부족해지면 새 배열의 용량을 기존의 2배로 늘리기로 한다. 1. `D`를 추가한 뒤 리스트의 길이와 용량은 각각 얼마인가? 용량을 늘려야 하는가? 2. 이어서 `E`를 추가하면 용량은 얼마가 되는가? 기존 원소를 몇 개 복사해야 하는가? 3. 내부 배열의 길이는 바꿀 수 없는데, 리스트의 용량은 어떻게 늘어나는 것처럼 보이는가? ??? success "참고 답안" 1. `D`는 마지막 빈자리에 넣을 수 있다. 내용은 `[A, B, C, D]`가 되고, `size = 4`, `capacity = 4`이며 용량 확장은 필요 없다. 2. 이어서 `E`를 추가할 때는 빈자리가 없으므로 용량이 8인 새 배열을 만들어야 한다. 기존 원소 `A, B, C, D` 네 개를 복사한 뒤 `E`를 넣는다. 이때 `size = 5`, `capacity = 8`이다. 3. 원래 배열 자체의 길이가 늘어난 것은 아니다. 더 큰 새 배열을 만들고 기존 원소를 복사한 뒤, 새 배열을 내부 저장 공간으로 사용하기 때문에 사용자에게는 용량이 늘어난 것으로 보인다. ## 프로그래밍 연습 ### 배열로 표현한 큰 정수에 1 더하기 배열 `digits`에는 음이 아닌 정수의 각 자릿수가 왼쪽부터 저장되어 있다. 예를 들어 `[3, 0, 8]`은 308을 나타낸다. 숫자 0은 `[0]`으로 나타내며, 그 밖의 입력은 첫 번째 자릿수가 0이 아니다. 십진수의 세로셈 덧셈 과정을 따라 이 정수에 1을 더한 뒤, 결과를 같은 배열 형식으로 반환하라. `digits`를 직접 수정해도 된다. 맨 앞에 새로운 자리 올림이 생기면 더 긴 배열을 반환할 수 있다. ??? tip "풀이 힌트" 1. 세로셈 덧셈처럼 배열의 마지막 자릿수부터 시작한다. 2. 현재 자릿수가 9보다 작으면 1을 더한 뒤 바로 반환할 수 있다. 3. 현재 자릿수가 9라면 0으로 바꾼다. 모든 자릿수가 9였다면 맨 앞에 1을 추가해야 한다. [LeetCode](https://leetcode.com/problems/plus-one/){ .rounded-button .exercise-button target="_blank" rel="noopener noreferrer" } ### 단일 연결 리스트 뒤집기 단일 연결 리스트의 머리 노드 `head`가 주어진다. 각 노드에는 값과 다음 노드를 가리키는 `next`가 있다. 반복 방식으로 모든 노드 사이의 연결 방향을 뒤집고, 뒤집힌 리스트의 머리 노드를 반환하라. 새로운 연결 리스트 노드를 만들어서는 안 된다. ??? tip "풀이 힌트" 1. 먼저 종이에 연결된 노드 세 개와 prev, cur 두 포인터를 그려 보자. 2. cur.next를 바꾸기 전에 nxt에 원래 다음 노드를 반드시 저장해야 한다. 3. cur.next의 방향을 뒤집은 뒤 prev = cur, cur = nxt로 설정하고 원래 리스트의 다음 노드를 계속 처리한다. [LeetCode](https://leetcode.com/problems/reverse-linked-list/){ .rounded-button .exercise-button target="_blank" rel="noopener noreferrer" }