해시테이블
해시함수를 사용해서 특정 해시값을 알아내고(→ 검색)
그 해시값을 인덱스로 변환하여 키값과 데이터를 저장하는 자료구조
해시함수
해시값
이라 함탐색, 삽입, 삭제 속도가 빠름 → O(1)
파이썬에서는 딕셔너리
로 구현
해싱
: 해시함수를 사용해서 인덱스를 구성하는 과정
데이터 → 해시함수에 의해 분류 → 분류된 데이터 해시테이블 안에 들어가는 과정
(장점) 데이터 양에 영향을 덜 받고 성능이 빠름(← 키를 통해 값을 검색)
이중해싱
과 해시의 해시
의 차이
해시탐색
: 데이터의 내용과 저장한 곳의 요소를 미리 연결해 둠으로써 극히 짧은 시간안에 탐색할 수 있도록 고안된 알고리즘
효율적으로 배열을 사용하기 위해 데이터에 일정한 계산을 실시해 결과값과 같은 인덱스를 가진 요소에 보관하는 방법
나중에 데이터를 탐색하기 쉽도록 데이터를 보관하는 단계에서 사전준비를 해두는 것
Linear probing, Quadratic probing, Double hashing
탐색, 삽입, 삭제 → 평균 O(1)
단점
순서가 있는 배열에는 어울리지 않음
공간 효율성이 떨어짐
해시함수의 의존도 높음
키가 들어갈 자리가 없는 경우에 발생
대처방법
체이닝
: 해당 인덱스에 이미 값이 있으면 그 뒤에 체인으로 연결
(장점)
(단점)
오픈어드레싱
: 리니어 프로빙(선형탐사)
파이썬
의 경우 내부적으로 오픈어드레싱 방식 활용로드팩터
를 작게 설정해 성능 저하 문제 해결(장점)
(단점)
적재율 :
로드팩터를 수치로해서 테이블 리사이징을 자동적으로 해주는 형태
더 낮은 로드팩터 = 더 적은 충돌가능성 = 고성능
오픈어드레싱을 사용하면 최대 로드팩터는 1
체이닝을 사용하면 로드팩터(<= 1
)는 오픈어드레싱보다 좋은 성능을 보임
그래프는 vert(노드, 정점)
와 edge(간선)
으로 연결된 자료구조로 object간의 관계 표현에 유용
그래프와 트리의 차이점
정의
방향성
루트노드
부모-자식
그래프는 노드간의 관계, 트리는 노드간의 계층을 표현
directed edge(방향성 간선)
: 그래프가 한쪽 방향(one-way)으로 설명
bidirectional edge(양방향 간선)
undirected edge(무방향성 간선)
: 방향이 따로 지정되어 있지 않음, 서로 상호교환함
인접(adjacent)
부속(incident)
Cyclic graphs(순환 그래프)
: 순환(루프)를 형성할 수 있는 경우
undirected
그래프Acyclic graphs(비순환 그래프)
Directed acyclic graphs(DAGs, 방향성 비순환그래프)
Weighted graphs(가중 그래프)
인접리스트(adjacency lists)
전체 노드 목록을 저장
class Graph:
def __init__(self):
self.vertices = {
"A": {"B"}, # 여기서 {"B"}가 set의 형태이다.
"B": {"C", "D"}, # {"B" : {}}의 형태는 딕셔너리
"C": {"E"}, # 즉, 딕셔너리 안에 set이 있는 것이다.
"D": {"F", "G"},
"E": {"C"},
"F": {"C"},
"G": {"A", "F"}
}
vertices(정점)은 O(1) 상수시간에 각 edge에 접근 가능
(단점) 특정 노드간의 연결관계를 확인하기 위해서는 반복문이 활용되어야 함 → 시간복잡도 O(n) 이상
인접행렬(adjacency matrices)
class Graph:
def __init__(self):
self.edges = [[0,1,0,0,0,0,0],
[0,0,1,1,0,0,0],
[0,0,0,0,1,0,0],
[0,0,0,0,0,1,1],
[0,0,1,0,0,0,0],
[0,0,1,0,0,0,0],
[1,0,0,0,0,1,0]]
인접리스트와 인접행렬 모두 구현 → vertex 및 edge클래스를 포함하여 더 많은 정보 파악 가능
시간복잡도
메모리에서
u와 v라는 edge가 존재하는 지
u에 인접한 모든 노드 v에 대해 특정 액션을 취해라
새로운 엣지(u,v) 삽입
기존 엣지 삭제
그래프 또는 트리처럼 연결된 구조에서 노드를 한번씩 탐색하는 개념
방향에 따라 탐색방법 달라짐
아직 방문하지 않은 노드의 순회 순서 중요
탐색 알고리즘 : DFS
, BFS
→ Day 3
트리의 순회
순회(방문)하면서 탐색하는 탐색 알고리즘, 그래프 탐색 알고리즘
→ 출발지 노드와 그래프/트리 구조에 따라 탐색하는 순서와 노드가 달라질 수 있음
루트 노드에서부터 시작해서 인접한 노드를 먼저 탐색하는 방법
큐
개념 사용(FIFO)
S → 1 → 2 → 3 → 4 → 5 → 6 → 7
노드가 적은 경우 최단경로 탐색할 때 유용
노드가 많아지는 경우 메모리 저장공간이 증가
장점
단점
마지막 노드까지 방문한 다음 역추적해서 방문하지 않은 노드를 방문
스택
개념 사용(LIFO), 재귀함수 사용
1 → 2 → 4 → 5 → 3
다른 경로를 역추적(backtracking)하고 탐색하기 전에 가능한 그래프를 분할
재귀적으로 아래에서부터 탐색하지 않은 정점이 있는지 확인
모든 노드를 방문하는 경우 사용
경로의 특징을 저장해두어야 하는 문제인 경우 사용
장점
단점
# 시작 정점을 방문
# 시작 정점에 인접한 모든 정점들을 우선 방문
# 더 이상 방문할 정점 없으면 한 depth내려가서
# 다시 인접한 모든 정점들 우선 반복
(처음) 시작 정점을 큐에 삽입하고 방문처리한다.
1) 큐에서 하나의 노드를 꺼냄
2) 꺼낸 노드와 인접한 노드 중에 방문하지 않은 노드를 큐에 삽입하고 방문처리한다.
1, 2과정을 큐가 비어있을 때까지 반복한다.
# 스택
# 시작 정점을 방문한 후
# 자식을 모두 탐색
# 이때 연결된 노드가 존재하지 않을 때가지 들어왔다면
# 한 단계 이전으로 돌아가 다시 알고리즘 수행
(처음) 탐색을 시작할 때 첫번째 노드를 스택에 삽입
1) 스택에 삽입된 최상단 노드 주변에 방문하지 않았던 인접노드(가장 작은 번호 먼저)가 있으면 해당 노드를 스택에 삽입하고 방문처리한다.
→ 방문처리 : 스택에 삽입되었던 노드가 중복적으로 또 삽입되지 않기 위함
2) 최상단 노드에 인접한 노드가 없으면 스택에 최상단 노드를 꺼낸다(pop)
1,2 과정을 스택이 빌 때까지 반복한다.
# 재귀
base condition : 파라미터로 넘어온 노드가 이미 방문한 노드라면 return
파라미터로 넘어온 노드가 방문하지 않은 노드라면 방문표시를 하고 인접한 노드에 대해 재귀적으로 함수를 호출하며 탐색을 진행
하나의 문제를 중복되는 서브문제로 나누어 푸는 방법
분할정복과 유사
차이점
특징
중복(반복)되는 서브문제 → 메인과 서브문제를 같은 방법으로 해결
메인문제 해결방법을 서브문제에서 구하여 재사용
하는 구조
→ 최적부분 구조로 구성된 중복되는 서브문제를 분할정복으로 해결
방법론
Memoization(하향식방법, Top-down)
Tabulation(상향식방법, Bottom-up) : 가장 작은 문제를 먼저 해결하고 최종적으로 메인문제를 해결하는 방법
재귀 + 조건 → 분할정복 + 조건 → DP + 조건 → 방법론
동적계획법 적용할 수 있는 문제 유형
최적의 해 또는 근사 해를 구하는 데 사용하는 방법
발견법(heuristic method)의 방법 중 하나
특징
다른 문제들과 독립적 → 다른 노드의 선택에 영향을 고려하지 않음
탐욕 알고리즘의 결과가 최적의 해는 아닐 수 있음 → 순간최적을 위한 답을 찾 방법
구현이 간단하면서 보통 정답에 가까운 정답을 도출해 냄