출처: https://jjalbang.today/view/%EB%AD%90%EC%A7%80/4696
공부하고 알아야 할 만한 기본적인 자료구조에 대해 정리합니다
python으로 작성된 코드의 예시들이 있지만, JS와는 큰 차이가 없기 때문에 예시 코드로는 파이썬을 넣어 두었습니다
대학 정규 교육 과정으로 오랜 기간 공부해서 배운 내용이 아니기 때문에 깊이 면에서 부족할 수 있지만,
자료구조와 그에 따른 예시를 간단히 작성하여 자료 구조는 어떤 것들이 있는지 알기 위해 정리하고 있습니다
이 게시글만으로 자료구조를 정복할 수는 없습니다, 관심이 생기신다면 강의나 책을 통해서 공부해보시는 것을 추천드려요 😁
자료구조란 대량의 데이터를 효율적으로 관리할 수 있는 데이터의 구조를 의미합니다
코드 상에서 효율적으로 데이터를 처리하기 위해, 데이터 특성에 따라, 체계적으로 데이터를 구조화해야 합니다
어떤 데이터 구조를 사용하느냐에 따라, 코드의 효율이 달라질 수 있습니다
우편번호: 5자리 우편번호로 국가의 기초구역을 제공
5자리 우편번호에서 앞 3자리는 시, 군, 자치구를 표기, 뒤 2자리는 일련번호로 구성
학생 관리: 학년, 반, 번호를 학생에게 부여해서, 학생부를 관리
XX학년, X반, X번 학생
만약 위 관리 기법이 없다면, 3000명 학생 중 특정 학생을 찾기 위해, 전체 학생부를 모두 훑어야 함
자료구조 | 자료구조 |
---|---|
선형 구조 | 비선형 구조 |
리스트 스택 큐 덱 | 트리 그래프 |
출처: https://boycoding.tistory.com/32
선형 구조 (Linear Structure)
데이터들이 일렬로 쭉 저장되어 있는 형태
비 선형 구조 (Non-Linear Structure)
데이터가 트리 형태로 저장되어 있다고 생각하고 사용하는 자료구조
리스트는 왜 필요한가요?
장점
[0]
에서 상대적인 위치로 데이터에 접근이 가능하다 (인덱스 번호로 접근이 가능하다)단점
make a list using C
#include <stdio.h>
int main(int argc, char * argv[])
{
char country[3] = "US";
printf ("%c%c\n", country[0], country[1]);
printf ("%s\n", country);
return 0;
}
make a list using python
country = 'US'
print (country)
>>> US
country = country + 'A'
print(country)
>>> USA
1 차원 배열
# 1차원 배열: 리스트로 구현시
data_list = [1, 2, 3, 4, 5]
print(data_list)
>>>
[1,2,3,4,5]
2 차원 배열
# 2차원 배열: 리스트로 구현시
data_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(data_list)
[
[1,2,3],
[4,5,6],
[7,8,9]
]
Queue 구조
queue 라이브러리에는 다양한 큐 구조로 ① Queue(), ② LifoQueue(), ③ PriorityQueue() 제공
프로그램을 작성할 때 프로그램에 따라 적합한 자료 구조를 사용
Queue(): 가장 일반적인 큐 자료 구조
# 큐 (Queue)
import queue
# FIFO QUEUE (일반적인 구조의 큐) 사용하기
data_queue = queue.Queue()
print('data_queue:', data_queue)
print('type:', type(data_queue))
print()
# 데이터 삽입 (put)
data_queue.put(1)
data_queue.put(2)
data_queue.put(3)
print('data_queue.qsize():', data_queue.qsize()) >>> 3
print()
# 데이터 추출 (get)
print('data_queue.get():', data_queue.get()) >>> 1
print('data_queue.get():', data_queue.get()) >>> 2
print('data_queue.get():', data_queue.get()) >>> 3
LifoQueue(): 나중에 입력된 데이터가 먼저 출력되는 구조 (스택 구조라고 보면 됨)
import queue
# LIFO QUEUE (스택과 같은 구조의 큐) 사용하기
data_queue = queue.LifoQueue()
print('data_queue:', data_queue)
print('type:', type(data_queue))
print()
# 데이터 삽입 (put)
data_queue.put(1)
data_queue.put(2)
data_queue.put(3)
print('data_queue.qsize():', data_queue.qsize()) >>> 3
print()
# 데이터 추출 (get)
print('data_queue.get():', data_queue.get()) >>> 3
print('data_queue.get():', data_queue.get()) >>> 2
print('data_queue.get():', data_queue.get()) >>> 1
PriorityQueue(): 데이터마다 우선순위를 넣어서, 우선순위가 높은 순으로 데이터 출력
# PriorityQueue() 우선 순위가 있는 큐 만들기
import queue
data_queue = queue.PriorityQueue()
# - 튜플 형식 ( , )으로 데이터를 삽인한다
# - 숫자가 낮을수록 우선순위가 높다 (1 <<<<< 우선 순위가 높음 , .... , 15 <<<< 우선 순위가 낮음)
# - 우선순위가 높은 튜플이 먼저 Dequeue 된다
data_queue.put((10, "korea"))
data_queue.put((5, 1))
data_queue.put((15, "china"))
print()
print(data_queue.qsize()) # >>> 3
print()
print(data_queue.get()) # >>> (5,1)
print(data_queue.get()) # >>> (10, 'korea')
print(data_queue.get()) # >>> (15, 'china')
멀티 태스킹을 위한 프로세스 스케쥴링 방식을 구현하기 위해 많이 사용된다
queue_list = list()
def enqueue(data):
queue_list.append(data)
def dequeue():
data = queue_list[0]
del queue_list[0]
return data
for index in range(10):
enqueue(index)
print('queue_list:', queue_list)
# >>> queue_list: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
dequeue()
print('queue_list:', queue_list)
# >>> queue_list: [1, 2, 3, 4, 5, 6, 7, 8, 9]
Stack 구조
큐(Queue) | 스택(Stack) |
---|---|
F.I.F.O First In First Out | L.I.F.O Last In First Out |
스택 구조
스택은 LIFO(Last In, Fisrt Out) 또는 FILO(First In, Last Out) 데이터 관리 방식을 따름
LIFO: 마지막에 넣은 데이터를 가장 먼저 추출하는 데이터 관리 정책
FILO: 처음에 넣은 데이터를 가장 마지막에 추출하는 데이터 관리 정책
대표적인 스택의 활용
컴퓨터 내부의 프로세스 구조의 함수 동작 방식
주요 기능
push(): 데이터를 스택에 넣기
pop(): 데이터를 스택에서 꺼내기
스택의 장단점
장점 🔥
단점 🔥
데이터 최대 개수를 미리 정해야 한다
파이썬의 경우 재귀 함수 호출은 1000번까지 가능하다
저장 공간의 낭비가 발생할 수 있다
미리 최대 개수만큼 저장 공간을 확보해야 한다
스택은 단순하고 빠른 성능을 위해 사용되므로, 보통 배열 구조를 활용해서 구현하는 것이 일반적임
list 자료형의 내장 메서드로 append(push), pop() 메서드를 제공한다
# 스택 (Stack)
# 큐와 달리 파이썬, JS 에서 기본 제공하는 리스트를 통해서 구현이 가능하다
# 큐의 경우 data = queue.Queue() 와 같이 인스턴스로 만들어서 사용했었음
# append: 삽입하기 (push)
# pop: 꺼내기 (pop)
data_stack = list()
data_stack.append(1)
data_stack.append(2)
print('data_stack:', data_stack) >>> [1,2]
print()
data_stack.pop()
print('① data_stack.pop():', data_stack) >>> [1]
print()
data_stack.pop()
print('② data_stack.pop():', data_stack) >>> [0]
리스트 변수로 스택을 다루는 pop, push 기능 구현해보기
stack_list = list()
def push(data):
stack_list.append(data)
def pop():
data = stack_list[-1]
del stack_list[-1]
return data
for elem in range(10):
push(elem)
print('stack_list', stack_list)
# >>> stack_list [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print()
pop()
print('stack_list', stack_list)
# >>> stack_list [0, 1, 2, 3, 4, 5, 6, 7, 8]
Linked List 구조
링크드 리스트 기본 구조와 용어
노드(Node): 데이터 저장 단위 (데이터값, 포인터) 로 구성
포인터(pointer): 각 노드 안에서, 다음이나 이전의 노드와의 연결 정보를 가지고 있는 공간
일반적인 링크드 리스트 형태
(출처: wikipedia, https://en.wikipedia.org/wiki/Linked_list)
링크드 리스트의 장단점 (전통적인 C 언어에서의 배열과 링크드 리스트)
장점
단점
더블 링크드 리스트(Doubly linked list) 기본 구조
(출처: wikipedia, https://en.wikipedia.org/wiki/Linked_list)
해쉬 테이블(Hash Table) 구조
해쉬 테이블(Hash Table): 키(Key)에 데이터(Value)를 저장하는 데이터 구조
알아둘 용어
해쉬 테이블의 장단점과 주요 용도
장점
단점
주요 용도
트리(Tree) 구조
트리: 노드(node)와 브랜치(branch)를 이용해서, 사이클을 이루지 않도록 구성한 데이터 구조
실제로 어디에 많이 사용되나?
이진 트리: 노드의 최대 브랜치가 2개인 트리
이진 탐색 트리: 이진 트리에 다음과 같은 추가적인 조건이 있는 트리
완전 이진 트리
: 노드를 삽입할 때 최하단의 왼쪽 노드부터 차례대로 삽입하는 트리출처: http://sjkitpro.blogspot.com/2018/07/heap.html
프로그램이 실행되면 아래 그림과 같이 4개의 메모리 영역을 가지게 된다.
이중 Heap 영역은 사용자가 동적할당을 할 경우 메모리에 저장된다.
C언어에서는 malloc, java에서는 new 키워드로 Heap 영역에 메모리를 할당할 수 있다.
Heap 메모리의 해제는 C에서는 free 함수로 해제하며, java에서는 JVM에서 가비지 컬렉터가 지우는 시점에 해제된다.
힙은 완전 이진 트리이다. 최소 값 혹은 최대 값을 찾아내는 연산에 적합하다.
힙에는 최소 힙과 최대 힙이 있다. 최대 힙은 루트노드로 갈수록 값이 커지며, 최소 힙은 루트노드로 갈수록 값이 작아진다.
힙을 사용하는 이유
시간 복잡도 순서
O(1) < O( 𝑙𝑜𝑔𝑛 ) < O(n) < O(n 𝑙𝑜𝑔𝑛 ) < O( 𝑛2 ) < O( 2𝑛 ) < O(n!)
① 각 노드의 값은 해당 노드의 자식 노드가 가진 값보다 크거나 같다. (최대 힙의 경우) - 최소 힙의 경우는 각 노드의 값은 해당 노드의 자식 노드가 가진 값보다 크거나 작음
② 완전 이진 트리 형태를 가진다
공통점: 힙과 이진 탐색 트리는 모두 이진 트리임 (자식 노드가 최대 2개가 있는 트리 구조)
차이점:
그래프 (Graph) 란?
노드(Node) = 정점(Vertex): 위치를 말함
간선(Edge) = 링크 = 브랜치: 위치 간의 관계를 표시한 선으로 노드를 연결한 선이라고 보면 됨 (link 또는 branch라고도 함)
인접 정점(Adjacent Vertex): 간선으로 직접 연결된 정점(또는 노드)
눈여겨 볼 그래프 종류
<A, B>
로 표기 (<B, A>
는 B -> A
로 가는 간선이 있는 경우이므로 <A, B>
와 다름)연결 그래프 (Connected Graph)
비연결 그래프 (Disconnected Graph)
사이클 (Cycle)
비순환 그래프 (Acyclic Graph)
트리는 그래프 중에 속한 특별한 종류라고 볼 수 있음 (트리는 그래프의 한 종류이다)
그래프 | 트리 | |
---|---|---|
정의 | 노드와 노드를 연결하는 간선으로 표현되는 자료 구조 | 그래프의 한 종류, 방향성이 있는 비순환 그래프 |
방향성 | 방향 그래프, 무방향 그래프 둘 다 존재함 | 방향 그래프만 존재함 |
사이클 | 사이클 가능함, 순환 및 비순환 그래프 모두 존재함 | 비순환 그래프로 사이클이 존재하지 않음 |
루트 노드 | 루트 노드 존재하지 않음 | 루트 노드 존재함 |
부모/자식 관계 | 부모 자식 개념이 존재하지 않음 | 부모 자식 관계가 존재함 |