Now Loading ...
-
🧩 [Data Structure] 대규모 데이터에서의 게시글 목록 조회
🧩 [Data Structure] 대규모 데이터에서의 게시글 목록 조회.
🍎 Intro.
대규모 데이테이서 게시글 목록 조회는 왜 복잡할까요?
그 이유는 모든 데이터를 한 번에 보여줄 수 없기 때문입니다.
메모리, 네트워크, 성능 등의 제약이 있음.
위와 같은 이유로 인해 페이징이 필요합니다.
✅1️⃣ 페이징 처리란 무엇일까요?
페이징 처리(Paging)란, 많은 양의 데이터를 여러 페이지로 나누어 한 번에 일부 데이터만 표시하는 방법을 말합니다.
주로 웹 애플리케이션, 데이터베이스, UI 설계 등에서 대량 데이터를 효율적으로 표시하고 처리하기 위해 사용됩니다.
✅2️⃣ 페이징 처리가 필요한 이유?
1. 대량 데이터를 효율적으로 처리하기 위해:
데이터가 많을 경우, 한 번에 모든 데이터를 사용자에게 보여주면 성능 저하와 불편한 UI/UX 문제가 발생합니다.
페이징을 사용하면 한 번에 필요한 데이터만 가져와 성능을 향상할 수 있습니다.
2. 사용자 경험(UX) 개선:
데이터를 페이지별로 나누어 보여줌으로써 사용자가 데이터를 쉽게 탐색할 수 있습니다.
예: 쇼핑몰 상품 목록, 블로그 게시글 목록 등
✅3️⃣ 페이징 처리의 구성 요소?
페이징 처리는 주로 다음과 같은 구성 요소로 이루어집니다:
1. 페이지 번호(Page Number):
사용자가 보고자 하는 페이지를 나타냅니다.
예: 1, 2, 3, …
2. 페이지 크기(Page Size):
한 페이지에 표시할 데이터의 개수를 설정합니다.
예: 10개, 20개, 50개 등.
3. 전체 데이터 수(Total Items):
전체 데이터의 개수를 계산하여 몇 페이지까지 필요한지 결정합니다.
4. 전체 페이지 수(Total Pages):
전체 데이터를 페이지 크기로 나눈 값입니다.
계산: 전체 페이지 수 = [전체 데이터 수 / 페이지 크기]
✅4️⃣ 페이징 처리의 예.
🛍️ 쇼핑몰 상품 목록.
예를 들어, 1,000개의 상품이 있고, 한 페이지에 20개의 상품을 표시한다고 가정합니다.
페이지 크기 (Page Size): 20
전체 페이지 수 (Total Pages): [1,000 / 20] = 50
사용자가 각 페이지를 요청하면 다음과 같이 데이터를 나누어 가져옵니다:
1페이지 : 상품 1 ~ 20
2페이지 : 상품 21 ~ 40
…
50페이지 : 상품 981 ~ 1,000
✅5️⃣ 페이징 처리의 구현 방식.
서버 애플리케이션 내의 메모리로 디스크에 저장된 모든 데이터를 가져온 후 특정 페이지만 추출하는 것은 비효율적 입니다.
디스크 접근은 메모리 접근보다 느리기 때문입니다.
디스크 I/O 비용이 듭니다.
디스크에 저장된 데이터는 메모리 용량을 초과할 수 있습니다.
Out of Memory(OOM)
시스템에서 사용 가능한 메모리가 모두 사용되어 더 이상 할당할 수 없는 상황을 말합니다.
데이터베이스에서 특정 페이지의 데이터만 바로 추출하는 방법이 필요합니다.
이것을 “페이징 쿼리”라고 부릅니다.
이러한 페이징 방식은 클라이언트 또는 서비스 특성에 따라서 크게 두 가지로 나뉩니다.
페이지 번호
무한 스크롤
✅1️⃣ “페이지 번호”와 “무한 스크롤”이란?
페이지 번호 방식과 무한 스크롤 방식은 데이터를 페이징 처리하여 사용자에게 보여주는 방법입니다.
둘 다 데이터를 페이지 단위로 나누어 로드하지만, 사용 방식과 사용자 경험(UX)이 크게 다릅니다.
✅2️⃣ 페이지 번호 방식.
페이지 번호 방식은 데이터 전체의 페이지를 번호로 나누고, 사용자가 특정 페이지 번호를 클릭하거나 선택하여 해당 페이지의 데이터를 요청하는 방식입니다.
📌1️⃣ 페이지 번호 방식의 특징.
데이터는 페이지별로 구분됩니다.
사용자는 특정 페이지를 직접 탐색할 수 있습니다.
일반적으로 화면 아래에 페이지 번호(Pagination)가 표시됩니다.
📌2️⃣ 예시: 쇼핑몰 상품 목록.
[1] [2] [3] [4] [5] ... [Next >]
사용자가 “3번 페이지”를 클릭하면, 3번 페이지에 해당하는 데이터를 서버에서 가져옵니다.
📌3️⃣ 페이지 번호 방식의 장점.
1. 사용자가 원하는 페이지를 직접 탐색할 수 있습니다.
네트워크 요청이 명확히 구분되어, 사용자가 로드 과정을 제어 가능합니다.
데이터 로딩 및 성능 관리가 용이합니다.
📌4️⃣ 페이지 번호 방식의 단점.
페이지 이동 시 화면이 갱신되므로 UX가 끊길 수 있습니다.
사용자가 원하는 데이터를 찾기 위해 여러 페이지를 반복 탐색해야 할 수 있습니다.
✅2️⃣ 무한 스크롤 방식.
무한 스크롤 방식은 사용자가 페이지 번호를 선택하지 않고, 화면을 아래로 스크롤할 때마다 자동으로 다음 데이터를 로드하는 방식입니다.
📌1️⃣ 무한 스크롤 방식의 특징.
데이터는 한 번에 하나의 페이지씩 뒤에서 추가 로드됩니다.
사용자는 끊김 없는 스크롤 경험을 얻습니다.
일반적으로 소셜 미디어나 이미지 갤러리에서 많이 사용합니다.
📌2️⃣ 무한 스크롤 방식의 예시: SNS 피드.
사용자가 아래롤 스크롤하면 새로운 게시물이 계속 추가로 로드됩니다.
페이지의 개념이 보이지 않으며, 데이터가 끊기지 않고 계속 표시됩니다.
📌3️⃣ 무한 스크롤 방식의 장정.
UX가매끄럽고 직관적이며, 탐색이 빠릅니다.
사용자가 클릭 없이 데이터를 자연스럽게 탐색 가능합니다.
모바일 확경에 특히 적합합니다.
📌4️⃣ 무한 스크롤 방식의 단점.
네트워크 요청이 많아져서 서버 부하가 증가할 수 있습니다.
사용자가 특정 위치로 돌아가거나 특정 데이터를 다시 찾ㄱ기 어렵습니다.
데이터가 많아질수록 브라우저 메모리 사용량이 증가합니다.
✅3️⃣ 비교: 페이지 번호 vs 무한 스크롤
특징
페이지 번호 방식
무한 스크롤 방식
사용자 경험(UX)
사용자가 원하는 페이지로 직접 이동 가능
자연스러운 탐색, 클릭 없이 데이터 자롱 로드
데이터 로딩 방식
명시적으로 페이지 요청
스크롤 이벤트에 따라 자동으로 추가 로드
네트워크 부하
페이지 요청이 명확, 부하가 적음
스크롤할 때마다 요청 발생, 부하 증가 가능
특정 데이터 탐색
특정 페이지로 바로 이동 가능
특정 위치로 이동이 이려움
적합한 환경
상품 목록, 블로그 게시글, 검색 결과 등
소셜 미디어, 이미지 갤러리, 동적 콘텐츠
✅4️⃣ SQL 예제.
📝1️⃣ 페이지 번호 방식.
-- 페이지 번호 기반 페이징 (페이지 크기: 10, 2번 페이지 데이터)
SELECT *
FROM article
ORDER BY created_at DESC
LIMIT 10 OFFSET 10;
LIMIT 10 ➞ 한 번에 가져올 데이터 개수.
OFFSET 10 ➞ 몇 개를 건너뛰고 가져올지.
📝2️⃣ 무한 스크롤 방식.
-- 무한 스크롤 페이징 (커서 기반, created_at 기준)
SELECT *
FROM article
WHERE created_at < '2025-01-31 12:00:00'
ORDER BY created_at DESC
LIMIT 10;
WHERE created_at < ? ➞ 현재 로드된 마지막 데이터의 시간보다 이전 데이터를 가져옴.
LIMIT 10 ➞ 한 번에 가져올 데이터 개수.
✅5️⃣ 페이지 번호 방싱과 무한 스크롤 방식 정리.
페이지 번호 방식 : 사용자가 페이지를 직접 선택하여 탐색하며, 상품 목록, 검색 결과와 같은 구조에 적합합니다.
무한 스크롤 방식 : 끊김 없는 스크롤 경험을 제공하며, 소셜 미디어 피드, 이미지 갤러리 등 동적 콘텐츠에 적합합니다.
✅6️⃣ 페이징 처리의 장단점.
✅1️⃣ 장점.
1 성능 최적화 : 한 번에 필요한 데이터만 처리하므로 응답 속도가 빨라집니다.
2. 사용자 경험 개선 : 대량 데이터를 탐색하기 쉽고, UI가 간결해집니다.
3. 네트워크 효율성 : 클라이언트와 서버 간 전송되는 데이터 양이 줄어듭니다.
❌2️⃣ 단점.
1. 구현 복잡성 증가 : 서버 및 클라이언트에서 페이징 처리를 추가로 구현해야 합니다.
2. 페이지 이동 시 추가 요청 : 각 페이지를 이동할 때마다 서버와 통신해야 하므로, 느린 네트워크에서는 체감 속도가 떨어질 수 있습니다.
-
🧩 [Data Structure] 로그 시간(logarithmic time)이란?
🧩 [Data Structure] 로그 시간(logarithmic time)이란?
🍎 Intro.
로그 시간(logarithmic time)은 시간 복잡도를 분석할 때 사용하는 용어로, 어떤 알고리즘이나 연산이 수행될 때 입력 크기(N)에 따라 작업 시간이 입력 크기의 로그 값에 비례하는 경우를 의미합니다.
✅1️⃣ 로그 시간의 정의.
로그 시간은 입력 크기 N이 증가하더라도 연산 시간 증가가 매우 느린 경우를 나타냅니다.
시간 복잡도를 표현할 때 O(log N)으로 나타냅니다.
✅2️⃣ 로그 시간의 의미.
만약 입력 크기 N이 2배로 늘어나도, 연산 시간은 1회 정도만 추가되는 방식으로 증가합니다.
이는 로그(logarithm)의 성질에 기인하며, 효율적인 알고리즘의 특징입니다.
✅3️⃣ 로그 시간의 예시.
1️⃣ 이진 탐색(Binary Search) - O(log N)
이진 탐색은 정렬된 배열에서 특정 값을 찾을 때 사용됩니다.
배열을 절반으로 나누는 방식으로 탐색하므로, 입력 크기 N에 대해 시간 복잡도는 O(log N)입니다.
📌2️⃣ 예시.
배열 크기: N = 16
비교 횟수:
첫 번째 비교 ➞ 크기 16 ➞ 8로 줄어듦.
두 번째 비교 ➞ 크기 8 ➞ 4.
세 번째 비교 ➞ 크기 4 ➞ 2.
네 번째 비교 ➞ 크기 2 ➞ 1에서 종료.
총 비교 횟수: log₂(16) = 4
3️⃣ B-Tree와 B+ Tree 검색 - O(log N)
B-Tree와 B+ Tree는 데이터베이스와 파일 시스템에서 사용되는 효율적인 데이터 구조입니다.
트리의 높이가 log에 비례하므로, 검색과 삽입/삭제 연산 모두 O(log N)의 시간 복잡도를 가집니다.
📌4️⃣ 예시.
데이터 개수: N = 1,000
B+ Tree에서 최대 100개의 자식을 가진다면, 트리의 높이는 log₁₀(1,000) = 3
검색 시간은 3단계만에 원하는 데이터를 찾을 수 있습니다.
4️⃣ 이벤트 처리와 분할 정복 알고리즘.
Merge Sort와 같은 분할 정복 알고리즘에서도 로그 시간이 나타납니다.
문제를 절반으로 나누어 재귀적으로 처리하므로, 깊이는 log N에 비례합니다.
병합 과정의 시간은 O(N), 따라서 전체 시간 복잡도는 O(N log N)입니다.
✅4️⃣ 로그 시간의 특징.
1️⃣ 효율성:
로그 시간 복잡도는 대규모 데이터를 처리할 때 매우 효율적입니다.
데이터 크기 N이 기하급수적으로 증가하더라도, 작업 시간은 느리게 증가합니다.
2️⃣ 적용 사례:
검색 알고리즘 : 이진 탐색(Binary Search), B-Tree, AVL Tree, Hash Table 등.
정렬 알고리즘 : Merge Sort, Heap Sort 등.
파일 시스템 및 데이터베이스 : 인덱스 검색(B+ Tree).
3️⃣ 기본 수학:
로그 시간은 log₂(N) 또는 log₁₀(N) 같은 수학적 로그 함수의 성질에 기초합니다.
🚀5️⃣ 정리
로그 시간(logarithmic time)은 O(log N)의 시간 복잡도를 의미하며, 데이터 크기가 증가해도 연산 시간이 느리게 증가합니다.
이는 이진 탐색, 트리 기반 구조, 분할 정복 알고리즘 등에서 나타나는 효율적인 시간 복잡도입니다.
-
🧩 [Data Structure] B-tree와 차수(Degree)란 무엇일까요?
🧩 [Data Structure] B-tree와 차수(Degree)란 무엇일까요?
🍎 Intro.
B-tree(Balanced Multiway Search Tree)는 데이터를 효율적으로 저장하고 검색하기 위한 트리 구조입니다.
데이터베이스 매니지먼트 시스템(DBMS), 인덱스 구조 등에서 널리 사용됩니다.
✅1️⃣ B-Tree의 주요 특징.
1️⃣ 균형 트리(Balanced Tree)
루트에서 리프까지의 경로 길이가 항상 동일합니다.
삽입 및 삭제 시 자동으로 균형을 유지하여 O(log N)의 시간 복잡도를 보장합니다.
2️⃣ 다진 구조(Multiway Tree)
한 노드에 여러 개의 키(Key)와 여러 자식(Child)을 저장할 수 있습니다.
일반적인 이진 트리(Binary Tree)보다 더 많은 데이터를 한 노드에 저장하므로, 트리의 높이를 줄일 수 있습니다.
3️⃣ 디스크 기반 효율성
한 노드에 많은 데이터를 저장하고, 디스크 I/O 횟수를 줄이도록 설계되었습니다.
이를 통해 대규모 데이터베이스와 같은 디스크 기반 시스템에서 높은 성능을 제공합니다.
4️⃣ 정렬된 데이터 저장
모든 노드는 키(Key)가 오름차순으로 정렬된 상태로 저장됩니다.
각 키는 해당 자식 노드 범위의 값을 나타냅니다.
✅2️⃣ B-Tree의 규칙
1️⃣ 키(Key)와 자식(Child) 관계
한 노드에 최대 d-1개의 키를 저장할 수 있습니다.(여기서 d는 트리의 차수)
노드의 키가 N개라면, 해당 노드는 최대 N+1개의 자식 노드를 가질 수 있습니다.
2️⃣ 모든 노드는 정렬된 상태.
노드 내부의 키는 항상 오름차순으로 정렬되어 있습니다.
자식 노드는 각 키의 범위에 따라 데이터를 구분합니다.
3️⃣ 키 개수 범위.
모든 노드는 최소 ceil(d/2)-1개의 키를 가져야 합니다.
루트 노드는 예외적으로 1개의 키를 가질 수 있습니다.
4️⃣ 균형 유지.
트리는 삽입, 삭제 시 자동으로 균형을 유지합니다.
트리의 높이가 급격히 커지는 것을 방지합니다.
✅3️⃣ B-Tree의 구조.
📌1️⃣ 예시: 차수(d) = 3인 B-Tree
차수(Degree)는 B-Tree 또는 B+ Tree에서 한 노드가 가질 수 있는 최대 자식 수를 의미합니다.
또한 한 노드는 최대 d-1개의 키를 저장할 수 있습니다.
차수(d) = 3, 최대로 가질 수 있는 자식 수 ➞ 3
차수(d) = 3, 최대로 저장할 수 있는 키 ➞ 3-1 = 2
📌2️⃣ 특징
1️⃣ 내부 노드의 키.
루트 노드 [30]은 1개의 키를 가지고 2개의 자식을 가짐.
2️⃣ 리프 노드.
리프 노드 [10, 20]와 [40, 50]은 키를 저장하며, 자식 노드가 없음.
✅4️⃣ B-Tree의 주요 연산.
1️⃣ 검색(Search)
루트에서 시작하여 노드를 순차적으로 탐색.
키를 비교하며 자식 노드로 내려감.
시간 복잡도: O(log N)
2️⃣ 삽입(Insert)
데이터를 삽입할 위치를 찾음(리프 노드).
리프 노드에 데이터 추가.
노드가 가득 차면 분할(Split) 수행.
분할된 키를 부모 노드로 이동.
필요한 경우, 부모 노드도 분할(Split).
3️⃣ 삭제(Delete)
데이터를 삭제할 위치를 찾음.
데이터를 삭제한 후, 언더플로우(Underflow) 발생 시:
병합(Merge) 또는 재분배(Redistribute)로 해결.
트리의 균형 유지.
✅5️⃣ B-Tree의 장점.
1. 높은 검색 및 업데이트 성능
트리 높이가 낮아 검색, 삽입, 삭제가 효율적입니다.
2. 디스크 I/O 최적화
한 노드에 많은 키를 저장하므로 디스크 접근 횟수를 줄일 수 있습니다.
3. 균형 유지
항상 균형을 유지하므로, 최악의 경우에도 O(log N)의 성능을 보장합니다.
4. 정렬 데이터 저장
키가 정렬된 상태로 저장되므로, 범위 검색(Range Query)이 효율적입니다.
✅6️⃣ B-Tree가 사용되는 곳.
1. 데이터베이스 인덱스
MySQL(InnoDB), PostSQL 등에서 사용됩니다.
2. 파일 시스템
NTFS, ext4와 같은 파일 시스템의 디렉토리 구조에 사용됩니다.
3. Key-Value 저장소
RocksDB, LevelDB 등에서 B-Tree 기반 인덱스를 사용합니다.
🍎 Intro.
차수(Degree)는 B-Tree 또는 B+ Tree에서 한 노드가 가질 수 있는 최대 자식 수를 의미합니다.
차수(d)가 크면 한 노드에 더 많은 자식을 저장할 수 있으므로, 트리의 높이가 낮아지고 검색 효율이 향상됩니다.
반대로 차수(d)가 작으면 한 노드에 저장할 수 있는 자식 수가 적어 트리의 높이가 커질 수 있습니다.
✅1️⃣ B-Tree에서 “차수(Degree)”의 의미.
B-Tree에서 차수(d)는 노드의 키(Key)와 자식(Child)의 수를 제한하는 중요한 기준이 됩니다.
1️⃣ 한 노드의 최대 키(Key) 개수.
한 노드는 최대 d-1개의 키를 저장할 수 있습니다.
예를 들어, 차수 d=4인 B-Tree라면, 한 노드는 최대 4-1=3개의 키를 저장할 수 있습니다.
2️⃣ 한 노드의 자식(Child) 수.
한 노드의 키(Key) 개수가 N개라면, 해당 노드는 최대 N+1개의 자식을 가질 수 있습니다.
예를 들어, 차수 d=4인 경우:
노드에 3개의 키가 있으면, 최대 4개의 자식(Child)을 가질 수 있습니다.
✅2️⃣ B-Tree에서 차수(Degree)의 규칙.
1. 키(Key) 개수의 범위:
한 노드는 최소 ceil(d/2)-1개의 키를 가져야 하며, 최대 d-1개의 키를 가질 수 있습니다.
예: 차수 d=4라면:
최소 키 개수: ceil(4/2)-1 = 1
최대 키 개수: 4-1 = 3
2. 자식(Child) 수의 범위:
한 노드는 최소 ceil(d/2)개의 자식을 가져야 하며, 최대 d개의 자식을 가질 수 있습니다.
예: 차수 d=4라면:
최소 자식 수: ceil(4/2) = 2
최대 자식 수: 4
✅3️⃣ 차수(d)의 예제.
예: 차수(d) = 4인 B-Tree
[ 30 | 60 ]
/ | \
[10, 20] [40, 50] [70, 80]
1. 내부 노드:
루트 노드 [30 | 60]는 최대 d-1 = 3개의 키를 가질 수 있습니다.
한 노드는 최대 d-1개의 키를 저장할 수 있기 때문입니다.
루트 노드 [30 | 60]은 최소 ceil(4/2)-1 = 1개의 키를 가져야하며, 최대 d-1 = 3개의 키를 가질 수 있습니다.
한 노드는 최소 ceil(d/2)-1개의 키를 가져야하며, 최대 d-1개의 키를 가질 수 있기 때문입니다.
자식 노드 수는 최대 d = 4개.
한 노드의 키(Key) 개수가 N개라면, 해당 노드는 최대 N+1개의 자식을 가질 수 있기 때문입니다.
한 노드는 최소 ceil(4/2) = 2개의 자식을 가져야 하며, 최대 4개의 자식을 가질 수 있습니다.
2. 리프 노드:
리프 노드 [10, 20], [40, 50], [70, 80]은 정렬된 데이터(Key)를 저장합니다.
B-Tree의 규칙 중 “모든 노드는 정렬된 상태”가 있습니다.
노드 내부의 키는 항상 오름차순으로 정렬되어 있기 때문입니다.
자식 노드는 각 키의 범위에 따라 데이터를 구분하기 때문입니다.
B-Tree의 특징 중 “정렬된 데이터 저장”이 있습니다.
모든 노드는 키(Key)가 오름차순으로 정렬된 상태로 저장되기 때문입니다.
각 키(Key)는 해당 자식 노드의 범위 값을 나타냅니다.
3. 트리 높이:
차수(d)가 크면 트리의 높이가 줄어들어 검색이 더 빠릅니다.
차수(d)가 크면 한 노드에 더 많은 자식을 저장할 수 있으므로, 트리의 높이 ⥥가 낮아지고(대신 너비⇔가 넓어짐) 검색 효율이 향상 되기 때문입니다.
반대로 차수(d)가 작으면 한 노드에 저장할 수 있는 자식 수가 적어 트리의 높이가 ⇑높아집니다(너비⇄가 좁아짐).
✅4️⃣ 차수가 작고 큰 경우의 차이.
1️⃣ 차수(d)가 작으면:
한 노드에 저장할 수 있는 키(Key)와 자식 수가 적습니다.
트리의 높이가 커져 검색 성능이 약간 떨어질 수 있습니다.
한 노드에 저장할 수 있는 키(Key)와 자식 수가 적어 트리가 높기 때문에.
메모리 사용량은 적음.
한 노드에 저장할 수 있는 키(Key)와 자식 수가 적어 트리의 높이는 높지만 너비는 좁기 때문에.
2️⃣ 차수(d)가 크면:
한 노드에 더 많은 키(Key)와 자식을 저장할 수 있습니다.
트리의 높이가 낮아져 검색, 삽입, 삭제가 더 빠릅니다.
한 노드에 저장할 수 있는 키(Key)와 자식 수가 많아 트리가 낮기 때문에.
매모리 사용량이 증가할 수 있습니다.
한 노드에 저장할 수 있는 키(Key)와 자식 수가 많아 트리의 높이는 낮지만 너비는 넓기 때문에.
📌1️⃣ 차수(d)와 예제에서의 적용.
1️⃣ 차수(d) = 4인 경우
차수(d)는 B-Tree에서 한 노드가 가질 수 있는 최대 자식 수를 의미합니다.
따라서, 내부 노드(루트 노드 포함)의 자식 노드 수는 최대 4개가 됩니다.
2️⃣ 키(Key) 개수와 d-1 법칙
한 노드가 가질 수 있는 최대 키(Key) 개수는 d-1입니다.
차수 d=4라면:
최대 키 개수 = d-1 = 3개
루트 노드 [30 | 60]는 2개의 키를 가지고 있으므로, 최대 3개를 가질 수 있는 규칙을 준수합니다.
여기서 2개는 “현재 저장된 키 개수”를 나타내며, 3개는 “최대 저장 가능 키 개수”입니다.
3️⃣ 루트 노드의 자식 수
루트 노드는 2개의 키(Key)를 가지므로, 최대 N+1개의 자식을 가질 수 있습니다.
여기서 N = 2(루트 노드의 키 개수)라면:
자식 노드 수는 N+1 = 3개입니다.
루트 노드 [30 | 60]의 자식 노드로 [10, 20], [40, 50]. [70. 80] 3개의 리프 노드가 연결되어 있습니다.
이는 차수(d=4)의 규칙을 충족합니다.
4️⃣ 리프 노드의 구성
리프 노드의 키는 각 노드에 오름차순으로 정렬된 데이터를 저장합니다.
리프 노드는 자식이 없으며, 리프 노드로:
[10, 20]
[40, 50]
[70, 80]
총 3개가 존재합니다.
🚀5️⃣ 결론.
차수(d=4):
내부 노드(루트 노드 포함)의 자식 노드 수는 최대 4개입니다.
키(Key) 개수:
루트 노드 [30 | 60]는 현재 2개의 키를 가지고 있으나, 최대 3개의 키를 가질 수 있습니다.
이는 규칙을 준수합니다.
리프 노드:
자식 노드:
[10, 20]
[40, 50]
[70, 80]
총 3개의 리프 노드가 있으며, 이는 적절한 구성을 따릅니다.
-
-
🧩 [Data Structure] 재귀 구조의 예(1) - 수열
🧩 [Data Structure] 재귀 구조의 예(1) - 수열
✅1️⃣ 수열.
1. 초항(First Term)
↘︎ 정의 : 수열에서 첫 번째 항을 의미함.
↘︎ 기호 : 일반적으로 초항은 $a_1$ 또는 $a$로 나타냄.
↘︎ 예시 :
수열: 2, 4, 6, 8, …
초항: $a_1 = 2$
2. 공차(Common Difference)
↘︎ 정의 : 등차수열에서 연속된 두 항의 차이를 의미함.
↘︎ 기호 : 일반적으로 공차는 $d$로 나타냄.
↘︎ 공식 :
↘︎ $d = a_{n+1} - a_n$
↘︎ 예시 :
↘︎ 수열 : 2, 4, 6, 8, …
↘︎ 공차 : $d = 4 - 2 = 2$
3. 점화식 (Recurrence Relation)
↘︎ 정의 : 수열의 각 항을 이전 항(또는 몇 개의 항)을 이용해 표현한 식임.
↘︎ 기호 : 보통 $a_{n+1} = a_n + d$ 형태로 나타남.
↘︎ 예시 :
↘︎ 수열 : 2, 4, 6, 8, …
↘︎ 점화식 : $a_{n+1} = a_n + 2$
4. 등차수열 (Arithmetic Sequence)
↘︎ 정의 : 연속된 항들의 차이가 일정한 수열.
↘︎ 일반항 공식 :
↘︎ $a_n = a + (n-1)d$
↘︎ $a$ : 초항
↘︎ $d$ : 공차
↘︎ $n$ : 항의 번호
↘︎ 예시 :
↘︎ 수열 : $2, 4, 6, 8, …$
↘︎ 초항 : $a = 2$
↘︎ 공차 : $d = 2$
↘︎ 5번 째 항 : $a_5 = 2 + (5-1) \cdot 2 = 10$
✅2️⃣ 재귀적 구조 알고리즘 예시.
📌 등차수열
↘︎ $a_n = a_{n-1}+3, a_1 = 1$
↘︎ 초항이 1, 공차가 3인 등차수열의 점화식.
↘︎ 수열의 n번째 원소는 자신과 성격이 똑같지만 순서가 하나 작은 $(n-1)$번째 원소에 3을 더한 것임.
↘︎ 첫 번째 원소는 1.
↘︎ 이것을 재귀 알고리즘으로 구현하면 아래와 같다.
seq(n):
if (n = 1)
return 1
else
return seq(n-1) + 3
↘︎ 알고리즘 seq(n)은 seq(n-1)을 호출, seq(n-1)은 seq(n-2)를 호출.
↘︎ seq(n-2)는 seq(n-3)을 호출… seq(2)는 seq(1)을 호출하고, seq(1)은 1을 리턴하고 끝남.
↘︎ seq(1)이 끝나면 역순으로 진행됨.
↘︎ seq(2)는 seq(1)의 리턴 값을 받아 3을 더해 리턴
↘︎ seq(3)은 seq(2)의 리턴 값을 받아 3을 더해 리턴
↘︎ seq(4)는 seq(3)의 리턴 값을 받아 3을 더해 리턴
↘︎ seq(n)은 seq(n-1)의 리턴 값을 받아 3을 더해 리턴하면서 전체가 끝남.
↘︎ 등차수열은 결과를 바로 계산할 수 있는 식이 있어 굳이 이렇게 구할 필요가 없지만 그 속에 재귀적 구조가 있음을 말하려는 것이다.
↘︎ 재귀 알고리즘은 반복해서 호출하다가 언젠가 끝나야 하는데 이를 위한 경계 조건을 항상 갖고 있어야 한다.
↘︎ 위 알고리즘에서는 if(n=1)이 경계 조건이다
↘︎ 수열의 초항에 해당한다.
📌 피보나치 수열
↘︎ 피보나치 수열은 다음과 같다.
↘︎ 첫 두 항은 1이고, 나머지 항은 각각 직전 두 항을 더한 값이다.
↘︎ $f_n = f_{n-1} + f_{n-2}, f_1 = f_2 = 1$
↘︎ 이것을 재귀 알고리즘으로 구현하면 다음과 같다.
fib(n):
if (n = 1 or n = 2)
return 1
else
return fib(n-1) + fib(n-2)
↘︎ 이는 재귀 알고리즘으로 구현한 치명적인 예다.
↘︎ 시간이 너무 많이 걸리기 때문이다.
↘︎ 지수함수적으로 증가한다는 느낌이다.
↘︎ 이렇게 엄청난 시간이 걸리는 이유는 한 번 계산해놓은 결과를 계속 호출하여 지수함수적인 중복을 일으키기 때문이다.
↘︎ 이 문제를 아래와 같이 계산하면 fib_fase(100)은 천만 분의 1초도 걸리지 않는다.
fib_fast(n):
f[1] ← f[2] ← 1 ◀︎ "f[2] ← 1"과 "f[1] ← 1"을 한꺼번에 적어놓은 것
for i ← 3 to n
f[i] ← f[i-1] + f[i-2]
return f[n]
↘︎ 재귀는 문제를 해결하는 유용한 도구이지만 잘못 쓰면 치명적이다.
↘︎ 자료구조와 알고리즘에서는 주로 재귀가 유용할 경우에 사용한다.
-
-
🧩 [Data Structure] 자료구조의 추상데이터 타입
🧩 [Data Structure] 자료구조의 추상데이터 타입
1️⃣ 추상.
‘추상의 레벨이 높다’
사고의 수준이 높다라는 뜻
‘추상의 정의’
관점이 계층화되어 상승한 것.
하위의 개념들을 결합하여 상위의 고급 개념을 만들어나가는 지적 발전의 단계.
계층와의 단계가 높으면 추상의 레벨이 높은 것이다.
추상의 역사
인간의 사고가 진화해온 과정 자체.
원시적이고 낮은 관점이 계층화를 거치면서 점점 차원이 높아져 옴.
객체 지향 언어의 클래스
프로젝트나 문제 해결과정에서는 새로운 의미 단위를 분리하고 계층화하는 작업이 반복됨.
클래스가 대표적 예.
모듈(Module)
의미의 단위.
통상적으로 모듈은 먼저 추상적인 레벨에서 설계되고, 후에 구체화된다.
모듈을 추상화(Abstraction)한다는 것.
세부 사항을 생략하고 가장 중효한 기능만 드러내는 것.
추상화는 ‘위에서-아래로’ 이루어질 수도 있고, ‘아래에서-위로’ 이루어질 수도 있다.
큰 그림을 먼저 그리고 점차 작은 모듈로 나누어가는 과정은 ‘위에서-아래로’ 방식.
최하부 단위들을 준비하고 거기서부터 선택과 조합, 새로움을 가미하여 상위 헤벨의 그림을 만들어내는 과정은 ‘아래에서-위로’ 방식.
일상에서 접하는 추상은 이 두 방식이 혼재함.
2️⃣ 추상 데이터 타임: ADT
자바의 8가지 기초 데이터 타입
byte
정수 타입(1바이트)
short
정수 타입(2바이트)
int
정수 타입(4바이트)
long
정수 타입(8바이트)
float
실수 타입(4바이트)
double
실수 타입(8바이트)
boolean
true
false
논리타입(1비트 true or false)
char
문자 타입(2바이트)
기초 데이터입이 아닌 데이터 타입에는 대부분 추상적 성격이 있다.
어떤 성질을 공유하는 객체들의 집합은 하나의 데이터 타입을 이루기 좋은 후보다.
추상 데이터 타입(ADT, Abstract Data Type)
세부 사항에서 벗어나 추상적으로 정의한 데이터 타입.
즉, 어떤 데이터 타입이 어떤 작업으로 이루어지는지만 표현한 것이다.
예를 들어 원소가 순서를 갖고 저장되는 자료구조인 리스트는 아래 그림의 (a)와 같이 표현 가능.
또는 리스트가 지원하는 작업을(b)와 같이 좀 더 구체적으로 나열해 표현 가능.
“추상적으로 표현한다”
자료구조에서는 이런 식으로 어떤 대상이 지원하는 ‘작업을 나열해 표현’하는 것을 “추상적으로 표현한다”라고 한다.
대상을 그렇게 표현한 것을 추상 데이터 타입이라 한다.
이때 대상이 포함하는 데이터 속성에 제한이 있으면 데이터의 속성을 같이 서술해도 좋다.
위 그림의 (a)에서 3개의 작업을 지원하는 임의의 객체는 추상 데이터 타입(ADT) ‘리스트’에 속한다.
달리 표현하면, ADT 리스트라는 데이터 타입에 속하는 객체는 이 3개의 작업을 지원한다.
자바에서 8가지 기초 데이터 타입을 제외한 나머지는 추상 데이터 타입이다.
추상 데이터 타입은 대략 하나의 클래스에 대응된다.
클래스는 자신의 규칙을 따르는 객체를 만들기 위한 틀이다.
클래스는 (암묵적으로) 그런 객체들의 집합을 의미하기도 해서 하나의 데이터 타입이라 부르기도 한다.
하나의 ADT 아래에 여러 ADT가 포함될 수도 있다.
자바에서 클래스의 상위 개념인 인터페이스(Interface) 기능이 ADT에 가장 잘 대응되는 개념이다.
ADT는 어떤 데이터 타입이 어떤 작업으로 이루어지는지를 세부 사항 없이 표현한다.
이때 아래 그림의 (a)처럼 핵심 작업만으로 표현할 수도 있고, (b)처럼 다른 작업을 더해 표현할 수도 있다.
(a)처럼 최소한으로 표현해놓으면 대개 구현할 때 필요한 작업을 더 한다.
ADT 리스트는 ‘적어도 이 작업들은 포함해야 한다’는 뜻이다.
새 원소를 리스트의 첫자리에 넣거나 끝에 붙이는 것은 “i번째 자리에 새 원소 넣기”로 통합할 수 있다.
그래도 첫자리나 끝자리에 새 원소를 넣는 작업이 아주 흔하면 따로 표현할 수도 있다.
이처럼 특정 대상에 대한 ADT는 유동적이다.
이런 면에서 ADT는 제공하는 사람의 주관적 관점이 반영된다.
ADT는 프로그래밍 작업을 효율적이고 고급스럽게 할 수 있도록 돕기 위한 것이다.
ADT로 표현하는 것이 프로그래밍에 장애가 되어서는 안 된다.
ADT로 시작함으로써 자료구조 내부의 세세한 사항에 신경을 쓰지 않고 시작할 수 있다.
즉, 자료구조와 접점을 이루는 함수들의 세부 사항에 집착하지 않고 자료구조가 하는 일만 생각하며 작업할 수 있다.
이로 인해 협업도 원활해진다.
어떤 클래스 또는 클래스의 집단이 ADT나 ADT에 준할 만큼 잘 표현되어 있으면, 사용자는 그 내부의 세부 구현은 신경 쓰지 않고 ‘이용’할 수 있다.
즉, ADT는 내부에서 작업을 ‘어떻게’하는지 신경 쓰지 않게 하며 ‘무엇을’ 하는지만 신경 쓰게 해준다.
특히 여려 명이 진행하는 프로젝트에서 이 같은 개념의 추상화를 통한 인터페이스가 매우 유용하다.
3️⃣ 추상 데이터 타입과 자바 인터페이스.
인터페이스(Interface)
자바에서 ADT에 가장 가까운 기능
예를 들어 아래 그림의 (a)와 같은 ADT ‘리스트’를 (b)와 같이 인터페이스 기능을 이용해 정의할 수 있다.
그러면 ListInterface란 이름의 인터페이스를 따르는 모든 클래스는 여기 나열된 3개의 작업을 제공하고, 각 작업의 리턴 타입과 파라미터의 수와 타입도 이 정의를 따라야 한다.
따라서 (c)와 같이 implements를 사용해 이 인터페이스를 따르는 클래스를 정의하면 클래스 LinkedList는 ListInterface 인터페이스를 따라 위 3개의 메서드를 지원하고, 리턴 타입과 각 함수의 파라미터 수와 타입도 따라야 한다.
그러면 (d)와 같이 사용자 프로그램에서는 이 인터페이스만 보고 1.LinkedList 객체를 하나 할당받은 다음 2.ListInterface에서 명시된 파라미터 수와 타입에 맞게 함수를 사용하면 된다.
그러면 사용자 프로그램 입장에서는 리스트의 내부가 어떻게 구현되어 있는지 알 필요 없이 “첫 번째 자리에 원소 x를 삽입하라”, “3번째 원소를 삭제하라”와 같은 추상적 수준에서 리스트를 취급할 수 있다.
결국 추상 데이터 타입이 해당 타입의 객체를 ‘추상적인 수준에서’ 사용할 수 있게 해주는 것이다.
규모가 큰 프로젝트라면 많은 ADT로 구성될 텐데, 기존 자료구조를 가져다 쓰기도 하고 새로 만들기도 한다.
아래 그림은 이 경우에 해당하는 ADT에 관한 업무 분담 구조의 예다.
팀장이 ADT를 설계하거나 결정하면 팀원이 그에 부응하는 자료구조를 구현하거나 오픈 소스를 가져다 쓴다.
ADT의 규약만 분명하면 구현 프로그래머과 사용 프로그래머도 이 데이터 타입에 관해 추가로 소통할 필요가 없다.
-
-
🧩 [Data Structure] 자료구조 무엇일까요?
🧩 [Data Structure] 자료구조 무엇일까요?
1️⃣ 자료구조는 데이터를 저장, 조직, 관리하는 방법.
자료구조
자료(데이터)에 효율적으로 접근하고 수정할 수 있도록 저장, 조직, 관리하는 방법에 관한 이론.
2️⃣ 자료구조는 문제 해결에 사용할 부품.
알고리즘
문제 해결 작업을 수행하기 위해 입력을 받아 원하는 출력을 만들어 내는 과정을 기술한 것.
자료구조
문제 해결 작업을 수행하기 위해 입력을 받아 원하는 출력을 만들어 내는 과정에서 부품의 역할.
자료구조 학습시 필요능력
자료구조는 프로그램으로 구현되고 사용되므로 자료구조를 학습하려면 프로그래밍 능력이 기본으로 필요.
자료구조를 구현, 사용, 결합하는 과정에서 수학적 사고도 크게 도움이 됨.
사고가 체계적일수록 자료구조를 사용한 결과물은 가명하고 관리하기 쉬워짐.
자료구조와 밀접한 관련이 있는 수학은 수열, 수학적 귀납법 등을 포함하는 이산 수학임.
아래 그림은 알고리즘, 자료구조, 프로그래밍, 이산 수학의 관계를 나타냄.
자료구조는 운영체제, 컴퓨터 네트워크, 인공지능, 시스템 프로그래밍, 컴파일러 등 컴퓨터 과학의 거의 모든 주제를 구현하기 위한 사고의 빌딩 블록을 제공함.
3️⃣ 자료구조는 생각하는 방법을 훈련하는 도구.
자료구조를 다루는 과정에 포함된 ‘생각하는 방법’도 매우 중요함.
자료구조를 구현하는 과정.
자료구조들을 이용해서 문제를 해결하는 과정.
문제를 해결하는 과정에서 논리의 골격이 구성되는 방법 또는 스타일 등
자료구조에서 시작되는 시 생각하는 방법은 자연스럽게 알고리즘으로 연결됨.
문제 해결을 위한 생각의 과정에서 ‘의미 단위를 잡는 일’은 매우 중요하다.
‘의미의 매듭을 만든다’고도 표현할 수 있다.
큰 프로젝트를 여러 모듈로 분해하면 각 모듈이 ‘의미의 매듭’이 된다.
또 각 모듈은 더 작은 모듈로 나뉠 수 있다.
즉, ‘의미의 매듭;은 여러 크기로 산재할 수 있다.
프로그래밍에서 어떤 작업을 함수로 만드는 것도 ‘모듈화의 일종’이다.
함수로 분리하면 강한 의미 단위가 된다는 뜻이다.
‘의미의 매듭;을 만드는 과정에서 ‘여러 가지 생각의 구조가 개입’될 수 있는데 ‘가장 중요한 구조’중 하나가 ‘재귀’다.
컴퓨터 과학 전반에 걸쳐 가장 중요한 사고 체계 중 하나
재귀
어떤 문제가 자신과 성격이 똑같지만 크기만 더 작은 문제를 포함하고 있는 구조를 말함.
‘큰 의미 매듭’이 ‘같은 모양의 더 작은 의미 매듭’을 ‘1개 이상 포함’하고 있는 것이라 할 수 있다.
아래 그림은 재귀적 구조의 시에르핀스키 삼각형 예로, 같은 구조가 계층적으로 반복되는 것을 볼 수 있다.
4️⃣ 자료구조의 종류와 자바의 컬렉션 패키지.
자료구조는 아래 그림과 같이 종류가 다양하지만 상황과 목적에 맞게 적절한 자료구조를 선택함으로써 효율적인 데이터 관리가 가능.
자바의 경우 클래스 종류별 패키지로 분류되어 있는데 그중 자료구조 관련 클래스를 모아둔 패키지가 아래 그림과 같은 컬랙션 패키지이다.
이 패키지에서 다양한 자료구조를 손쉽게 사져다 쓸 수 있어 같은 시간 동안 옛날보다 훨씬 큰 작업을 수행 가능.
이렇게 미리 만들어둔 것은 범용으로 사용할 수 있도록 지나치게 많은 기능을 제공하거나, 핵심에 집중하기 위해 최소한의 기능만 넣어 놓은 경우도 있음.
따라서 효율적인 코딩을 위해 직접 만드는 것이 더 바람직한 경우도 있음
자료구조를 직접 만들거나 만들어진 것을 목적에 맞게 잘 활용하려면 자료구조 내부의 작동원리를 이해하고 있어야 함.
Touch background to close