경일 메타버스 20220509 6주차 1일 수업내용. 자료구조와 알고리즘, 재귀 함수, 자료구조
자료구조의 내용은 시간 부족, 집중력 부족 등의 이유로 제대로 숙지를 못하였으니 후에 자습 복습 예습이 필요하다고 생각한다.
자료
https://docs.google.com/document/d/1lAZizYg8AjfkM-UqPlmkseWIs8Fagvqpo307hQA3xl4/edit
자료구조 : 데이터의 표현 및 저장 방법, 데이터를 조직하는 방법
재귀 : 함수가 자신을 호출하는 것.
기저 조건(Base Condition) : 재귀를 중단시키는 조건
재귀 조건(Recursion Condition) : 기저 조건까지 수렴할 동안 반복시키는 조건
재귀의 사용 이유
문제에 따라 전체를 한 번에 해결하기보다 같은 유형의 하위 작업으로 분할하여 작은 문제부터 해결하는 방법이 효율적일 수 있기 때문이다.
재귀의 단점
함수 호출에 따른 오버헤드가 있다는 것이다. 재귀가 너무 깊어지면 그만큼 호출 스택의 공간을 더 많이 사용하게 되고, 끝내는 스택 오버플로가 발생할 수 있다.
오버헤드(overhead) : 어떤 처리를 하기 위해 들어가는 간접적인 처리 시간 · 메모리 등.
스택 오버플로(stack overflow) : 스택이 가용할 수 있는 공간을 벗어나는 것.
재귀의 팁
반복할 내용과 관련된 변수는 매개변수로 가져가야 한다.
점화식을 사용한다.
호출 등의 순서를 조정하는 것으로 다양한 것이 가능하다.
재귀를 이용해서 어떤 값을 올려야 할 때 전역 변수를 이용하면 구현 복잡도가 내려감.
4-1. 다만, 멀티 스레드 상황에서 데이터 경쟁이 일어날 수 있다. 이를 방지하기 위해 반환값을 이용할 줄도 알아야 한다.
자료구조는 크게 선형 구조(linear structure)와 비선형 구조(non-linear structure)로 나눌 수 있으며, 선형 구조에는 리스트, 스택, 큐가 있고, 비선형 구조에는 그래프와 트리가 있다. 또, 대부분의 자료구조는 네 가지 기본 방법을 사용하며 이를 연산이라고 한다.
읽기 : 자료구조 내 특정 위치를 찾아보는 것이다.
검색 : 자료구조 내 특정 값을 찾는 것이다.
삽입 : 자료구조에 새로운 값을 추가하는 것이다.
삭제 : 자료구조 내 특정 값을 삭제하는 것이다.
자료구조를 구현하는 방법에는 크게 순차 자료구조(contiguous data structure)와 연결 자료구조(linked data structure)가 있다.
지역성(locality) : CPU가 기억장치의 특정 부분에 위치한 데이터나 프로그램 코드를 집중적으로 액세스하는 현상이다.
알고리즘 : 문제를 해결하기 위한 절차.
문제를 해결하는 방법에는 여러가지가 존재할 수 있으며, 우리는 이 중 최선을 선택해야 한다. 다시 말해 알고리즘을 분석하고 비교할 줄 알아야 한다. 이는 알고리즘의 자원(resource)*의 사용량을 분석하는 것이며, 자원을 적게 사용할 수록 효율적인 알고리즘이라고 할 수 있다.
자원
알고리즘의 분석에 있어 가장 중요한 부분은 실행 시간이다.
알고리즘의 실제 실행은 하드웨어 및 소프트웨어 환경에 따라 천차만별이므로 단순 측정으로는 실행 시간을 분석할 수 없다.
시간 복잡도 : 하드웨어와 소프트웨어 환경을 배제한 객관적인 지표
알고리즘을 수행하는 데 필요한 연산이 몇 번 실행되는지를 숫자로 표기한다.
이 연산의 개수는 상수가 아니라 입력한 데이터의 개수를 나타내는 n에 따라 변하게 된다. 그래서 연산의 개수 n의 값에 따라 시간 복잡도를 나타낸 것을 시간 복잡도 함수라고 하며 수식으로는 T(n)이라고 표기한다.
입력값이 작을 때는 그 차이가 크지 않았지만, 입력값이 점점 커질 수록 엄청나게 차이가 벌어진다.
다항식의 시간 복잡도 함수에서 입력값이 커져감에 따라 각 항의 결과값에 관여하는 정도가 달라지게 됨
시간 복잡도와 뒤에 나올 공간 복잡도를 표시할 때는 각 함수의 모든 항을 표시하지 않으며 상수 계수와 중요하지 않은 항목을 제거한 점근적 표기법(asymptotic notation)을 사용한다.
이중 가장 많이 사용되는 Big-O 표기법은 점근적 상한선을 제공한다.
다음의 비교에서 왼쪽에 위치한 시간 복잡도일수록 해당 알고리즘은 빠르다고 할 수 있다.
O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3) < O(2n) < O(n!)
컴퓨터 내의 자원 공간을 얼마나 사용하는지 또한 알고리즘의 효율성을 평가할 때 고려해야 한다.
공간 복잡도(space complexity) : 알고리즘을 수행하는 데 필요한 자원 공간의 양
공간 복잡도 함수는 고정 공간 요구 + 가변 공간 요구로 나타낼 수 있으며 수식으로는 S(P)=c+Sp(n)으로 표기한다.
고정 공간 : 입력과 출력의 횟수나 크기와 관계 없는 공간
가변 공간 : 문제를 해결하기 위해 요구되는 추가 공간
Big-O 표기법 상에서는, 가변 공간만을 고려하게 된다.
효율적인 알고리즘은 실행 시간이 적게 걸리며, 자원을 적게 소모하는 것
시간과 공간은 반비례적인 경향이 있다.
보통 알고리즘의 척도는 시간 복잡도를 위주로 판단
대부분의 언어는 기본 자료구조와 알고리즘을 제공하고 있다.
C++의 경우는 STL(standard template library)이라고 하며, 3가지로 구성되어 있다.
컨테이너는 앞으로 배우게 될 자료구조의 구현체
반복자는 컨테이너의 요소에 접근할 수 있는 포인터
- 이렇게 나뉘게 된 이유는 컨테이너 각각에 알고리즘을 구현하려면 알고리즘 코드의 개수가 엄청 많이 필요한데, 반복자의 존재로 각 요소에 추상적으로 접근할 수 있어 구현 개수를 많이 줄일 수 있었다.
리스트(list) : 순서를 갖고 있는 자료구조
순차 자료구조엔 선형 리스트가 있다.
연결 자료구조엔 단일 연결 리스트, 원형 연결 리스트, 이중 연결 리스트가 있다.
선형 리스트(linear list)는 순서 리스트(ordered list)라고도 한다.
임의 접근이 가능하다는 특징을 가지고 있다.
읽기
선형 리스트는 임의 접근이 가능하기 때문에 O(1)의 시간이 걸린다.
검색
하나하나 원소를 비교해가야 하므로 O(n)의 시간이 걸린다. 정렬되어 있다면 이진 검색을 사용할 수 있으며 이 경우 O(logn)이다.
삽입
원소를 어디에 삽입하냐에 따라 시간이 달라진다. 맨 끝에 데이터를 추가할 경우 O(1)이지만, 처음이나 중간이라면 모든 데이터를 이동해야 하기에 O(n)이 걸린다.
삭제
삽입과 마찬가지로 원소를 어디에서 삭제하냐에 따라 시간이 달라진다. 맨 끝의 데이터를 삭제할 경우 O(1)이지만, 처음이나 중간이라면 모든 데이터를 이동해야 하기에 O(n)이 걸린다.
선형 리스트와 다르게 임의 접근이 불가능하다.
읽기
연결 리스트는 임의 접근이 불가능해 요소 하나하나를 탐색해야 하므로 O(n)의 시간이 걸린다. 하지만 처음과 끝이라면 구현에 따라 O(1)이 될 수 있다.
검색
하나하나 원소를 비교해가야 하므로 O(n)의 시간이 걸린다. 선형 리스트와 다르게 이진 검색을 사용할 수 없다.
삽입
원소를 어디에 삽입하냐에 따라 시간이 달라진다. 앞이나 뒤에 데이터를 추가할 경우 O(1)이지만, 중간이라면 해당 위치까지 검색해야 하기 때문에 O(n)이 걸린다.
삭제
삽입과 마찬가지로 원소를 어디에서 삭제하냐에 따라 시간이 달라진다. 앞이나 뒤의 데이터를 삭제할 경우 O(1)이지만, 중간이라면 O(n)이 걸린다.
스택(stack) : 리스트의 일종으로 연산이 한 쪽 끝에서만 이뤄지는 자료구조
가장 나중에 들어간 것이 처음에 나온다고 하여 LIFO(Last-In First-Out, 후입선출)구조라고도 한다.
스택의 끝을 위(top), 스택의 시작을 밑(bottom)이라 한다.
괄호가 올바른지 검사 / 후위 표기식 / DFS 등에 사용될 수 있다.
STL에서는 std::stack으로 구현되어 있다.
큐(queue)도 리스트의 일종으로 스택과 다르게 가장 처음에 들어간 데이터가 처음에 나오는 FIFO(First-In First-Out, 선입선출) 자료구조다.
삽입이 일어나는 쪽을 뒤(rear), 삭제가 일어나는 쪽을 앞(front)라고 한다.
프린트 큐 / CPU 스케줄링 / 데이터 버퍼 / BFS 등에 사용된다.
STL에서는 std::queue, std::deque로 구현되어 있다.
입력된 순서가 유지되어야 할 때 사용 가능.
관계에 특화된 자료구조
정점(vertex)과 간선(edge)으로 구성되어 있다.
정점 : 고유하게 식별되는 객체
간선 : 정점 간의 관계를 표현
간선이 방향성을 가지는 가
방향 그래프(directed graph)
무방향 그래프(undirected graph)
가중치 그래프(weighted graph)* : 간선이 단순 연결 이상의 정보(가중치)를 가진다.
네트워크(network)라고도 한다.
그래프의 종류
https://pangtrue.tistory.com/147
깊이 우선 탐색(depth first search)
너비 우선 탐색(breadth first search)
최단 경로(shortest path)
가중치 그래프에서 두 정점을 연결하는 경로 중 가중치의 합이 최소인 경로
최단 경로를 구하기 위해 간선이 없다면 무한대 값으로 저장하며, 가중치가 음수여선 안된다.
최단 경로를 구하는 알고리즘으로는 다익스트라 알고리즘(발전형 : A*, JPS)과 플로이드 알고리즘이 있다.
트리(tree)
그래프의 일종으로 계층형 자료구조(hierarchical data structure)
트리는 데이터가 저장된 노드(node)와 노드 간 관계를 표현하는 간선(edge)으로 구성된다.
STL에서는 std::set / std::map / std::multiset / std::multimap 으로 구현이 되어 있다.
재귀적.
이진 검색 트리(binary search tree)
한 노드를 기준으로 왼쪽 서브 트리는 모두 그 노드보다 작은 값을 가지고 있으며, 오른쪽 서브 트리는 모두 그 노드보다 큰 값을 가지고 있는 이진 트리(binary tree)이다.
수식으로 표현하면 왼쪽 자식 노드의 값 <= 부모 노드의 값 <= 오른쪽 자식 노드의 값이 된다.
힙(heap)
완전 이진 트리에 있는 노드 중에서 키값이 가장 큰 노드나 키값이 가장 작은 노드를 찾기 위해 만든 자료구조
최대 힙(max heap) : 키값이 가장 큰 노드를 찾기 위한 힙
최소 힙(min heap) : 가장 작은 노드를 찾기 위한 힙. 우선순위 큐(priority queue)라고도 한다.
STL에서는 std::priority_queue로 구현이 되어 있다.
해싱(hashing) : 입력의 크기에 상관 없이 일정 크기의 값으로 변환하는 것. 이에 사용되는 함수를 해시 함수(hash function)라 한다.
균일성 : 충돌이 적은 것을 의미한다.
효율성 : 계산하기 쉬워야 한다.
결정적 : 같은 입력에는 같은 값을 내놓아야 한다.
해시 테이블(hash table) : 해싱을 활용해 데이터를 저장하는 검색을 위한 자료구조. 연관 배열(associative array)이라고도 한다. 해시 값을 테이블의 인덱스로 활용한다.