├── .gitignore
├── README.md
└── notes
├── lecture01-intro.md
├── lecture02-data-structure-and-algorithm.md
├── lecture03-algorithm-complexity01.md
├── lecture04-algorithm-complexity02.md
├── lecture05-algorithm-complexity03.md
├── lecture06-sequential-array-list.md
├── lecture07-sequential-data-structures.md
├── lecture08-stack.md
├── lecture09-stack-calculator01.md
├── lecture10-stack-calculator02.md
├── lecture11-queue.md
├── lecture12-linked-list.md
├── lecture13-singly-linked-list01.md
├── lecture14-singly-linked-list02.md
├── lecture15-doubly-linked-list01.md
├── lecture16-doubly-linked-list02.md
├── lecture17-hash01.md
├── lecture18-hash02.md
├── lecture19-hash03.md
├── lecture20-tree.md
├── lecture21-heap01.md
├── lecture22-heap02.md
├── lecture23-heap03.md
├── lecture24-binary-tree.md
├── lecture25-binary-search-tree01.md
├── lecture26-binary-search-tree02.md
├── lecture27-balanced-binary-search-tree01.md
├── lecture28-balanced-binary-search-tree02.md
├── lecture29-balanced-binary-search-tree03.md
└── lecture30-balanced-binary-search-tree04.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | temp
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 한국외국어대학교 컴퓨터공학부 신찬수 교수님의 자료구조 강의(2020-1)
2 |
3 | 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 강의를 정리한 노트입니다.
4 |
5 | ## 안내
6 |
7 | 1. 학습한 날짜를 아래의 표에 정리합니다.
8 | 2. 본 노트는 학습 요약보다는 강의에 집중하기 위한 사적인 필기에 가깝습니다.
9 | 3. 총 55강으로 구성되어 있으나, 현재 저에게 필요한 내용인 AVL 트리에 대한 강의인 30강까지만 학습했습니다.
10 | 4. 항목 2,3 의 이유로, 수업의 내용에 비해 정리가 부족할 수 있습니다.
11 | 5. 노트의 코드는 파이썬 언어의 형태를 하고 있으나, 의사 코드(Psuedo code)가 섞여 있습니다.
12 | 6. 전체 동영상 강의와 교수님의 강의 노트를 기반으로 학습하는 것을 권장합니다.
13 |
14 | ## 동영상 강의 링크와 요약 노트
15 |
16 | 4월 30일부터 5월 6일까지 총 7일간 학습했습니다. 전체 강좌의 링크는 아래에서 확인할 수 있습니다.
17 |
18 | - "자료구조 - Data Structures with Python" 동영상 강의 목록: [https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)
19 |
20 | | 연번 | 동영상 링크 | 노트 | 학습일 |
21 | | -: | --------------------------------- | ----------- | ----------- |
22 | | 1 | [자료구조(2020-1) 과목 소개](https://youtube.com/watch?v=PIidtIBCjEg) | [:memo: note01](./notes/lecture01-intro.md) | 2021.04.30. |
23 | | 2 | [자료구조와 알고리즘](https://youtube.com/watch?v=M2mcJvmYpWY) | [:memo: note02](./notes/lecture02-data-structure-and-algorithm.md) | 2021.05.01. |
24 | | 3 | [알고리즘 시간복잡도1](https://youtube.com/watch?v=jgWyu83DfO0) | [:memo: note03](./notes/lecture03-algorithm-complexity01.md) | 2021.05.01. |
25 | | 4 | [알고리즘 시간복잡도2](https://youtube.com/watch?v=ysn9dLDNLEU) | [:memo: note04](./notes/lecture04-algorithm-complexity02.md) | 2021.05.01. |
26 | | 5 | [알고리즘 시간복잡도 BigO](https://youtube.com/watch?v=0xGJx6qsNCY) | [:memo: note05](./notes/lecture05-algorithm-complexity03.md) | 2021.05.01. |
27 | | 6 | [순차적 자료구조: 배열과 리스트](https://youtube.com/watch?v=Lqd8o7vL2Z8) | [:memo: note06](./notes/lecture06-sequential-array-list.md) | 2021.05.01. |
28 | | 7 | [순차적 자료구조 소개](https://youtube.com/watch?v=buJBlTsWlW0) | [:memo: note07](./notes/lecture07-sequential-data-structures.md) | 2021.05.02. |
29 | | 8 | [스택(Stack)](https://youtube.com/watch?v=OzFXiukhv8o) | [:memo: note08](./notes/lecture08-stack.md) | 2021.05.02. |
30 | | 9 | [스택 활용 - 계산기(1/2)](https://youtube.com/watch?v=G9ujrSGEB4A) | [:memo: note09](./notes/lecture09-stack-calculator01.md) | 2021.05.02. |
31 | | 10 | [스택 활용 - 계산기(2/2)](https://youtube.com/watch?v=MYk4autDAJ0) | [:memo: note10](./notes/lecture10-stack-calculator02.md) | 2021.05.02. |
32 | | 11 | [큐(Queue)](https://youtube.com/watch?v=nqCNk_DmPio) | [:memo: note11](./notes/lecture11-queue.md) | 2021.05.02. |
33 | | 12 | [연결리스트 소개](https://youtube.com/watch?v=sMpsvA5O0xU) | [:memo: note12](./notes/lecture12-linked-list.md) | 2021.05.03. |
34 | | 13 | [한방향 연결리스트 - 삽입,삭제 연산](https://youtube.com/watch?v=kGZoEShMcSQ) | [:memo: note13](./notes/lecture13-singly-linked-list01.md) | 2021.05.03. |
35 | | 14 | [한방향 연결리스트 - 탐색 연산](https://youtube.com/watch?v=aCHwXmpuAkY) | [:memo: note14](./notes/lecture14-singly-linked-list02.md) | 2021.05.03. |
36 | | 15 | [양방향 연결리스트 - splice 연산](https://youtube.com/watch?v=nQhzNRmnmt8) | [:memo: note15](./notes/lecture15-doubly-linked-list01.md) | 2021.05.03. |
37 | | 16 | [양방향 연결리스트 - 삽입,삭제,탐색 연산](https://youtube.com/watch?v=zWrFVf9_YTQ) | [:memo: note16](./notes/lecture16-doubly-linked-list02.md) | 2021.05.04. |
38 | | 17 | [해시 테이블 - 소개, 해시 함수](https://youtube.com/watch?v=Bzmepm6pYQI) | [:memo: note17](./notes/lecture17-hash01.md) | 2021.05.04. |
39 | | 18 | [해시 테이블 - open addressing(linear probing)](https://youtube.com/watch?v=Bj4pd9rJp5c)| [:memo: note18](./notes/lecture18-hash02.md) | 2021.05.04. |
40 | | 19 | [해시 테이블 - 성능 평가, chaining](https://youtube.com/watch?v=ghjWopXXUeA) | [:memo: note19](./notes/lecture19-hash03.md) | 2021.05.04. |
41 | | 20 | [트리구조 소개](https://youtube.com/watch?v=w-1w4ood7Bc) | [:memo: note20](./notes/lecture20-tree.md) | 2021.05.04. |
42 | | 21 | [힙(heap) - 정의](https://youtube.com/watch?v=8XnPN6IB22Y) | [:memo: note21](./notes/lecture21-heap01.md) | 2021.05.04. |
43 | | 22 | [힙(heap) - make_heap 연산](https://youtube.com/watch?v=6VMSTOdHRfI) | [:memo: note22](./notes/lecture22-heap02.md) | 2021.05.04. |
44 | | 23 | [힙(heap) - insert](https://youtube.com/watch?v=gVRDc5NRjjw) | [:memo: note23](./notes/lecture23-heap03.md) | 2021.05.04. |
45 | | 24 | [이진트리 - 정의와 순회](https://youtube.com/watch?v=HDjqrmmpFdU) | [:memo: note24](./notes/lecture24-binary-tree.md) | 2021.05.05. |
46 | | 25 | [이진트리 - 이진탐색트리 정의와 탐색,삽입 연산](https://youtube.com/watch?v=Bhprzw_1kb0) | [:memo: note25](./notes/lecture25-binary-search-tree01.md) | 2021.05.05. |
47 | | 26 | [이진트리 - 이진탐색트리 삭제 연산](https://youtube.com/watch?v=VVhmgQIJCu8) | [:memo: note26](./notes/lecture26-binary-search-tree02.md) | 2021.05.05. |
48 | | 27 | [균형이진탐색트리 - 정의와 회전](https://youtube.com/watch?v=Kuw0f3-E-Hw) | [:memo: note27](./notes/lecture27-balanced-binary-search-tree01.md) | 2021.05.06. |
49 | | 28 | [균형이진탐색트리 - AVL 트리 정의](https://youtube.com/watch?v=dHHjrl6m5CE) | [:memo: note28](./notes/lecture28-balanced-binary-search-tree02.md) | 2021.05.06. |
50 | | 29 | [균형이진탐색트리 - AVL 트리 삽입 연산](https://youtube.com/watch?v=KkgN2xAzmG8) | [:memo: note29](./notes/lecture29-balanced-binary-search-tree03.md) | 2021.05.06. |
51 | | 30 | [균형이진탐색트리 - AVL 트리 삭제 연산](https://youtube.com/watch?v=W3uPlSCzAZM) | [:memo: note30](./notes/lecture30-balanced-binary-search-tree04.md) | 2021.05.06. |
52 |
53 | ## 기타
54 |
55 | - [강의 동영상 목록](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)의 24, 25번째 동영상은 "이진트리 - 정의와 순회"로, 동일한 강의입니다.
56 |
57 | ## 저작권 안내
58 |
59 | 이 저장소는 학습 목적으로 생성한 저장소입니다. 모든 예제 코드와 내용에 대한 저작권은 원저자에게 있습니다.
60 |
--------------------------------------------------------------------------------
/notes/lecture01-intro.md:
--------------------------------------------------------------------------------
1 | # 2020학년도 1학기 - 자료구조 및 실습: 한국외대 컴퓨터공학부
2 |
3 | > 본 포스팅은 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 중 1강 "[인트로](https://youtube.com/watch?v=PIidtIBCjEg)" 강의를 정리한 노트입니다.
4 |
5 | ## 개요
6 |
7 | - 강의 교수: 신찬수 교수(chansu@gmail.com)
8 | - 강의노트: 자료구조(130p) + 파이썬문법(120p) 총 258쪽
9 | - goorm(강의내용, 강의노트, 실습, 과제) + eclass(레포트, 숙제, 공지, 문자소식 등)
10 |
11 | ## Goorm(구름): hufs.goorm.io(수강 초대 이메일 발송 예정)
12 |
13 | - 이론강의: 일부 동영상 포함 + 객관식, 주관식 문제
14 | - 실습강의: 코딩 실습, 코딩 과제 제공
15 | - 채점 문제, 제출 문제, 실행 문제
16 |
17 | ## Python 코딩
18 |
19 | 1. 구름: Python 연습장(Script, interactive)
20 | 2. 개별 노트북
21 | - python.org, ver.3.8.x
22 | - 에디터: notepad, notepad++ 등에서 `test.py`를 만든다면,
23 | - 명령창: cmd에서 `python test.py`로 실행 가능
24 | - 에디터와 명령창을 한번에 사용할 수 있는 Atom이나 Visual Studio Code도 사용 가능
25 |
26 | > 생활코딩: Python&Ruby 강의에서 설정 참고
27 |
28 | ## 강의내용 - Python은 각자 공부 + 자료구조 코딩 과제로 훈련
29 |
30 | 1. 자료구조와 알고리즘: 가상머신, 언어, 코드, 알고리즘/자료구조 복잡도, Big-O 기호
31 | 2. Array(List), Stack, Queue, Dequeue
32 | 3. Linked List(한방향, 양방향)
33 | 4. Hash Table(*): 해시 함수, 해시 충돌 해결 방법
34 | 5. Tree
35 | 6. Graph
36 |
37 | Tree
38 |
39 | - Priority Queue: Heap + HeapSort
40 | - 이진트리: 순회
41 | - 이진타색트리: 삽입, 삭제 등
42 | - 균형이진탐색트리: AVL, 2-3-4, Red-Black, Splay
43 |
44 | Graph
45 |
46 | - 표현법, 순회법(DFS, BFS)
47 | - 최단경로알고리즘(Bellman-Ford, Dijkstra algorithms)
48 |
--------------------------------------------------------------------------------
/notes/lecture02-data-structure-and-algorithm.md:
--------------------------------------------------------------------------------
1 | # 자료구조와 알고리즘
2 |
3 | > 본 포스팅은 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 중 2강 "[자료구조와 알고리즘](https://youtube.com/watch?v=jgWyu83DfO0)" 강의를 정리한 노트입니다.
4 |
5 | ## 개요
6 |
7 | 자료구조
8 |
9 | - 자료(data)를 담기 위한 저장공간과 연산을 통칭
10 | - 자료구조 = 저장공간(memory) + 연산(읽기, 쓰기, 삽입, 삭제 탐색)
11 |
12 | 알고리즘
13 |
14 | - 자료를 입력받아 유한한 횟수의 연산들을 이용해 원하는 결과를 출력하는 것
15 |
16 | > 자료구조와 알고리즘은 뗄레야 뗄 수 없는 바늘과 실 같은 것
17 |
18 | ### 자료구조의 예
19 |
20 | #### 변수(variable)
21 |
22 | ```py
23 | a = 5 # 쓰기 연산
24 | print(a) # 읽기 연산
25 | ```
26 |
27 | #### 배열(array), 리스트(list)
28 |
29 | ```py
30 | A = [3, -1, 5, 7] # 접근: 원소의 index를 이용
31 | # 읽기 쓰기 예: A[3]
32 | # 삽입: A.append(9) 마지막 자리(A[4])에 원소를 추가
33 | # A.intsert
34 | # 삭제: A.pop() 매개변수가 없으면 마지막 값을 반환 후 삭제
35 | # A.pop(2)
36 | ```
37 |
38 | ### 알고리즘의 예
39 |
40 | - 100개의 정수를 리스트 A에 담는다(입력)
41 | - 오름차순 정렬(출력)
42 |
43 | ## 알고리즘
44 |
45 | 인류 최초의 알고리즘: 최대공약수(GCD) 계산 알고리즘(By Euclid)
46 |
47 | 9세기, 페르시아, Algebra 수학자 Al-Khwarizmi -> 유럽에 라틴어로 번역되는 과정에 Algorismus로 번역됨. 수학자의 이름과 라틴어 수인 "Arithmos"이 합쳐져 "Algorithm"이 됐다는 게 정설.
48 |
49 | gcd(8, 12) = max{1, 2, 4} = 4
50 |
51 | ```py
52 | gcd_sub(a, b):
53 | while a != 0 and b != 0:
54 | if a > b: a = a - b
55 | else: b = b - a
56 | return a + b
57 |
58 | # 개선
59 | gcd_mod(a, b):
60 | while a != 0 and b != 0:
61 | if a > b: a = a % b
62 | else: b = b % a
63 | return a + b
64 |
65 | # gcd_rec(recursive)
66 | ```
67 |
--------------------------------------------------------------------------------
/notes/lecture03-algorithm-complexity01.md:
--------------------------------------------------------------------------------
1 | # 알고리즘 시간복잡도 1
2 |
3 | > 본 포스팅은 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 중 3강 "[알고리즘 시간복잡도 1](https://youtube.com/watch?v=M2mcJvmYpWY)" 강의를 정리한 노트입니다.
4 |
5 | ## 자료구조와 알고리즘 성능: 가상컴퓨터 + 가상언어 + 가상코드
6 |
7 | ### 현실의 문제점
8 |
9 | #### 문제점 1)
10 |
11 | - "자료구조 + 알고리즘"을 생각하여 코드(C, Java, Python 등)로 구현하고 나면 실제 컴퓨터에서 동작하게 된다.
12 | - 각 켬퓨터마다 HW/SW 환경이 다를 수 있으므로 다른 성능을 낼 수 있고, 이는 큰 문제가 될 수 있다.
13 |
14 | #### 문제점 2)
15 |
16 | - 입력의 크기가 다양할 수 있다. 다양한 크기의 입력에 대해 코드의 실행 속도를 어떻게 구할 수 있을까.
17 |
18 | ### 해결책
19 |
20 | 가상의 컴퓨터(Virtual Machine) + 가상언어(Pseudo language) + 가상코드(Pseudo Code) 를 가정한다.
21 |
22 | #### 가상 컴퓨터(Virtual Machine/Random Access Machine)
23 |
24 | Turing Machine -> von Neumann: RAM(Random Access Machine)
25 |
26 | RAM = CPU + Memory + 기본연산(단위시간에 수행되는 연산들의 모음)
27 |
28 | 기본연산이란: 배정, 대입, 북사연산 등으로 이들은 1 단위 시간이 드는 것으로 본다.
29 |
30 | - `A = B`(Read and Write)
31 | - 산술연산: +, -, *, / (%, 내림, 올림, 반올림 등은 기본 연산으로 보지 않으나, 수업에서는 이들도 단위시간에 들어가는 것으로 간주)
32 | - 비교연산: >, >=, <, <=, ==, !=
33 | - 논리연산: AND, OR, NOT
34 | - 비트연산: bit-AND, OR, NOT
35 |
36 | #### 가상 언어(Pseudo/Virtual Languages)
37 |
38 | - 배정, 산술, 비교, 논리, bit-논리 연산 등 기본연산을 표현할 수 있으면 됨
39 | - 비교: if, if else, if elseif... else 등
40 | - 반복: for, while 등
41 | - 함수: 정의, 호출, 반환 문법
42 |
43 | #### 가상 코드(Pseudo Code)
44 |
45 | 실제 돌아가는 코드가 아닌 가상머신(RAM)에서 돌아가는 코드이므로 자유롭게 기술할 수 있다.
46 |
47 | ```py
48 | # input: n개의 정수를 갖는 배열 A
49 | # output: A의 수 중에서 최대값 리턴
50 | alogirithm arrayMax(A, n):
51 | currentMax = A[0]
52 | for i = 1 to n-1 do # 가상의 코드.
53 | if currentMax < A[i]: # C 언어: for(int i = 1; i < n; i++){}
54 | currentMax = A[i] # Python: for i in range(A, n):
55 | return currentMax
56 | ```
57 |
58 | `A = [3, -1, 9, 2, 12], n = 5`일때 기본연산은 7회(`for` 선언 내부에서 쓰는 i와 관련한 연산은 제외)
59 |
--------------------------------------------------------------------------------
/notes/lecture04-algorithm-complexity02.md:
--------------------------------------------------------------------------------
1 | # 알고리즘 시간 복잡도(time complexity)
2 |
3 | > 본 포스팅은 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 중 4강 "[알고리즘 시간 복잡도(time complexity)](https://youtube.com/watch?v=ysn9dLDNLEU)" 강의를 정리한 노트입니다.
4 |
5 | ## 알고리즘 시간 복잡도를 계산하는 방법
6 |
7 | 1. 모든 입력에 대해 기본연산 횟수를 더한 후 평균(__현실적으로 불가능__)
8 | 2. 가장 안 좋은 입력(Worstcase input)에 대한 기본연산 횟수를 측정(__worstcase time complexity__)
9 |
10 | 2번 방법의 경우 어떤 입력에 대해서도 W.T.C.보다 수행시간이 크지 않다. 즉 연산횟수가 보장된다.
11 |
12 | > 알고리즘의 시간 복잡도를 정의하는 방법
13 | >
14 | > 알고리즘의 수행시간 = 최악의 입력에 대한 기본연산 횟수
15 |
16 | ```py
17 | # input: n개의 정수를 갖는 배열 A
18 | # output: A의 수 중에서 최대값 리턴
19 | alogirithm arrayMax(A, n):
20 | currentMax = A[0]
21 | for i = 1 to n-1 do
22 | if currentMax < A[i]:
23 | currentMax = A[i]
24 | return currentMax
25 | ```
26 |
27 | 위 코드에서 `currentMax < A[i]` 조건이 항상 참일 경우 가장 연산횟수가 크다.
28 |
29 | 예시: `A = [2, 5, 7, 9, 15, 26]`
30 |
31 | `for`문이 n - 1만큼 실행할 때 기본연산은 2번 수행씩 된다.
32 |
33 | - 대입연산: `1` 단위시간
34 | - 반복문: `(n - 1) * 2` = `2n - 2` 단위시간
35 |
36 | > T(n) = 2n - 1
37 |
38 | `n = 6` 일때, `T(6) = 12 - 1 = 11`
39 |
40 | ### 시간 복잡도 예시
41 |
42 | #### 예시 1
43 |
44 | ```py
45 | algorithm sum1(A, n):
46 | sum = 0 # 대입연산 (1 단위시간)
47 | for i = 0 to n - 1 do
48 | if A[i] % 2 == 0: # 짝수이면 (1 단위시간)
49 | sum += A[i] # 더하고 대입하라(2 단위시간)
50 | return sum
51 | ```
52 |
53 | `A`의 모든 값이 짝수이면 worstcase 입력
54 |
55 | > T(n) = 4n + 1
56 |
57 | `n`이 커지면 `n`에 비례해 수행시간이 커진다.
58 |
59 | #### 예시 2
60 |
61 | ```py
62 | algorithm sum2(A, n)
63 | sum = 0
64 | for i = 0 to n - 1 do
65 | for j = i to n - 1 do
66 | sum += A[i] * A[j]
67 | return sum
68 | ```
69 |
70 | | i | j |
71 | |------|-------|
72 | | 0 | n |
73 | | 1 | n - 1 |
74 | | 2 | n - 2 |
75 | | n -1 | 1 |
76 |
77 | ```txt
78 | 1 + 2 + ... + n = (n(n + 1) / 2) * 3
79 |
80 | T(n) = ((3/2)n)(n+1) + 1
81 | = (3/2)n^2 - (3/2)n + 1
82 | ```
83 |
84 | `n`이 커지면 `n^2`에 비례해 수행시간이 커진다.
85 |
--------------------------------------------------------------------------------
/notes/lecture05-algorithm-complexity03.md:
--------------------------------------------------------------------------------
1 | # 알고리즘 시간 복잡도(Big-O 표기법)
2 |
3 | > 본 포스팅은 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 중 5강 "[알고리즘 시간 복잡도(Big-O 표기법)](https://youtube.com/watch?v=0xGJx6qsNCY)" 강의를 정리한 노트입니다.
4 |
5 | ## 앞서 4강에서 본 3개의 함수의 시간 복잡도:
6 |
7 | - Algorithm1(arrayMax): T1(n) = 2n - 1
8 | - Algorithm2(sum1): T2(n) = 4n + 1
9 | - Algorithm3(sum2): T3(n) = (3/2)n2 - (3/2)n + 1
10 |
11 | ### 도출할 수 있는 명제:
12 |
13 | 1. Algorithm2가 Algorithm1보다 2배 느리다.
14 | 2. Algorithm3는 `n < (5/3) `이면 Algorithm2보다 빠르다.
15 | 3. Algorithm3는 모든 n에 대해서 Algorithm1보다 느리다.
16 | 4. Algorithm3는 `n > 5/3`이면 항상 Algorithm2보다 느리다.
17 |
18 | - T1(n), T2(n)은 n에 대해 선형적으로 증가. 최고차항이 1차항
19 | - T3(n)은 n에 대해 제곱으로 증가. 최고차항이 n의 2제곱
20 |
21 | > n에 대한 최고차항 = 단위 시간의 증가율을 결정
22 |
23 | ## Big-O 표기법
24 |
25 | 이를 이용해 알고리즘의 수행시간을 간단하게 표기할 수 있다. 함수값을 결정하는 가장 중요한 최고차항만 표시하여 함수의 수행시간을 대략적으로 표현한다.
26 |
27 | - T1(n) = 2n - 1 은 T1(n) = O(n)
28 | - T2(n) = 4n + 1은 T2(n) = O(n)
29 | - T3(n) = (3/2)n2 - (3/2)n + 1은 T2(n) = O(n2)
30 |
31 | ### 표기방법
32 |
33 | 1. 최고차항만 남긴다
34 | 2. 최고차항 계수(상수)는 생략
35 | 3. Big-O(최고차항)
36 |
37 | ### 집합으로 이해하기
38 |
39 | - T1(n) = O(n)
40 | - T2(n) = O(n)
41 |
42 | 위 두 함수는 O(n) 집합 안에 들어있는 것으로 보면 된다. 즉,
43 |
44 | - T1(n) ∈ O(n)
45 | - T2(n) ∈ O(n)
46 |
47 | ### 예시
48 |
49 | #### 예시1
50 |
51 | ```py
52 | def inclrement_one(a):
53 | return a + 1
54 | ```
55 |
56 | T(n) = 1 는 O(1) = O(n0)
57 |
58 | #### 예시2
59 |
60 | ```py
61 | def number_of_bits(n):
62 | count = 0
63 | while n > 0 :
64 | n = n // 2
65 | count += 1
66 | return count
67 | ```
68 |
69 | n / (2count) = 1 는 log2N = count
70 |
71 | T(n) = c·log2N + 1 = O(log2N)
72 |
--------------------------------------------------------------------------------
/notes/lecture06-sequential-array-list.md:
--------------------------------------------------------------------------------
1 | # 순차적 자료구조: 배열과 리스트
2 |
3 | > 본 포스팅은 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 중 6강 "[순차적 자료구조: 배열과 리스트](https://youtube.com/watch?v=Lqd8o7vL2Z8)" 강의를 정리한 노트입니다.
4 |
5 | ## 배열(array) VS. 리스트(list) <- Python
6 |
7 | - 가장 기본적인 순차적(sequential) 자료구조(매우 기본, 중요)
8 |
9 | ```c
10 | int A[4] = {2, 4, 0, 5};
11 | ```
12 |
13 | `A[0]`에는 첫번째 요소의 바이트 주소가 저장되어 있다.
14 |
15 | `A[2] = A[2] + 1`: `A[2]`에 대한 읽기/쓰기 수행. `A[2]`의 값이 업데이트(산술연산), 대입(`=`) 연산. 이 연산은 모두 상수시간(O(1))이 걸림. 계산의 편의상 읽기/쓰기를 카운트하지 않았으나 원래는 1 단위시간이 걸린다.
16 |
17 | RAM이므로 `A[2]`의 주소가 있으면 값을 바로 가져올 수 있다.
18 |
19 | `A[0]`의 주소에서 + (4bytes * 2개)
20 |
21 | `A[0]`의 주소가 `100`이라면 `A[2]`는 `108`. 산술연산 두 번으로 구할 수 있으므로, `O(1)`로 볼 수 있다.
22 |
23 | > 배열은 `index`를 이용해 특정 위치의 값을 상수시간 내에 읽고 쓸 수 있게 해주는 자료구조이다.
24 |
25 | ### Python의 list
26 |
27 | `index`를 이용해 값을 읽고 쓸 수 있으며, 더 많은 함수를 제공한다.
28 |
29 | ```py
30 | A = [2, 4, 0, 5]
31 | ```
32 |
33 | 파이썬의 리스트는 각각의 값을 다른 메모리에 저장한다. `2`, `4`, `0`, `5`는 객체가 되고, 따로 저장되며, `A[0]`은 `2`가 저장된 곳의 주소를 가리키고, `A[1]`은 `4`가 저장된 곳의 주소를 가리킨다.
34 |
35 | `A[2] = A[2] + 1`을 수행하면 `A[2]`가 가리키던 `0` 객체를 더이상 가리키지 않고, `1`을 갖고 있는 객체를 가리키게 된다.
36 |
37 | #### Python list의 함수들
38 |
39 | - `A.append(6)`: list의 맨 뒤에 `6`을 삽입(이전과 마찬가지로 마지막 위치에 `6`을 가리키는 주소를 저장)
40 | - `A.pop()`: list 맨 뒤의 값을 삭제하고 반환
41 | - `A.pop(1)`: `A[1]`을 제거하고 반환. `A[1]` 이후의 요소 `A[n]`은 `A[n -1]`으로 이동
42 |
43 | `append()`와 `pop()`은 기본으로 제공되는 삽입/삭제 연산
44 |
45 | - `A.insert(1, 10)`: `A[1]`에 `10`을 삽입. 기존의 `A[1]` 이후의 요소 `A[n]`은 `A[n + 1]`로 이동
46 | - `A.remove(value)`: `A`에서 왼쪽으로부터 첫번째 `value`를 찾아 제거. 삭제한 `A[v]` 이후의 `A[n]`은 `A[n - 1]`로 이동
47 | - `A.index(value)`: `A`를 왼쪽부터 검색해 첫번째 `value`의 인덱스 반환
48 | - `A.index(value)`: `A`를 왼쪽부터 검색해 해당 `value`의 개수 반환
49 |
50 | 파이썬의 list는 다양한 연산을 제공해 편의성이 높다.
51 |
52 | #### list의 이점
53 |
54 | - 용량(capacity)를 자동으로 조절한다. 공간이 부족하면 더 큰 용량을 할당하여 값을 저장.
55 | - `pop()`에 의해 요소들이 삭제가 되면 더 작은 용량을 할당하여 작은 리스트를 유지.
56 |
57 | > dynamic array: 자동으로 용량을 조정하는 배열. C 언어에서는 제공하지 않는다.
58 |
59 | ```c
60 | int A[4] = {2, 4, 0, 5}
61 | A[4] = 10; // error
62 |
63 | // 배열의 크기를 바꾸려면
64 | int *b = (int *)malloc(sizeof(int) * 4); // 동적 할당
65 | int *c = (int *)malloc(sizeof(int) * 6); // 동적 할당
66 | free (b);
67 | b = c;
68 | free(c);
69 | ```
70 |
71 | 아래 코드로 배열의 크기가 변경되는 것을 확인할 수 있다.
72 |
73 | ```py
74 | import sys
75 |
76 | a = [] # empty list
77 | print(sys.getsizeof(A)) # 28bytes(운영체제에 따라 다를 수 있음)
78 | A.append(10)
79 | print(sys.getsizeof(A)) # 44bytes(운영체제에 따라 다를 수 있음)
80 | ```
81 |
82 | #### list는 클래스(class)
83 |
84 | list A가 있다면 그 안에는:
85 |
86 | - `capacity`: 용량
87 | - `n`: 현재 저장된 값의 개수
88 |
89 | `capacity`가 `100`일 때, `append()` 연산이 반복되면 `n`이 증가.
90 |
91 | `A.append(x)`의 동작을 개념적으로 살펴보면:
92 |
93 | ```py
94 | if A.n < A.capacity:
95 | A[n] = x
96 | A.n = n + 1
97 | else: A.n == A.capacity
98 | B = A.capacity * 2 # 2배 크기의 리스트 새로 할당
99 | for i in range(n):
100 | B[i] = A[i] # A의 값을 B로 복사 O(n)만큼의 시간 소모
101 | del A
102 | A = B
103 | A[n] = x
104 | A.n = n + 1
105 | ```
106 |
107 | #### list의 연산별 시간복잡도(Big-O)
108 |
109 | - `A.append()`, `A.pop`: O(1) 평균
110 | - `A.insert()`, `A.remove()`: O(n) w.c
111 | - `A.index()`, `A.count()`: O(n) w.c
112 |
--------------------------------------------------------------------------------
/notes/lecture07-sequential-data-structures.md:
--------------------------------------------------------------------------------
1 | # 순차적 자료구조 소개
2 |
3 | > 본 포스팅은 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 중 7강 "[순차적 자료구조 소개](https://youtube.com/watch?v=buJBlTsWlW0)" 강의를 정리한 노트입니다.
4 |
5 | ## 1. array, list
6 |
7 | - `index`로 임의의 원소에 접근할 수 있다.
8 | - 연산자 `[]`로 접근할 수 있다. 상수시간(O(1))에 값을 알 수 있다.
9 | - 삽입(`append`, `insert`)
10 | - 삭제(`pop`, `remove`)
11 |
12 | `append`와 `pop`은 O(1)이지만, `insert`와 `remove`는 O(n)
13 |
14 | ## 2. stack, queue, dequeue
15 |
16 | - 제한된 접근(삽입, 삭제)만 허용한다.
17 |
18 | ### stack
19 |
20 | - LIFO(Last In First Out)
21 | - 자루처럼 먼저 넣은 것이 밑에 있고, 꺼낼 때 나중에 넣은, 가장 위의 것이 나온다.
22 | - `push`: 삽입. 아래에서부터 차곡차곡 삽입된다.
23 | - `pop`: 삭제. 맨 위 값(가장 나중에 들어온 값)에서부터 삭제한다.
24 |
25 | 제한된 연산을 가진 자료구조가 많이 쓰인다.
26 |
27 | ### queue
28 |
29 | - FIFO(First In First Out)
30 | - 선착순(가장 먼저 온 사람이 가장 먼저 서비스를 받는다)
31 | - 삽입은 아래서부터 차곡차곡 쌓인다.
32 | - 삭제 역시 아래의 값(가장 먼저 들어온 값)부터 수행한다.
33 |
34 | ### dequeue
35 |
36 | - stack + queue
37 | - 삽입은 위, 아래로 수행할 수 있다.
38 | - 삭제 역시 위, 아래로 수행할 수 있다.
39 |
40 | ## 3. linked list(연결 리스트)
41 |
42 | - 파이썬의 list는 배열과 유사. linked list와는 다르다.
43 | - 값이 연속된 공간이 아닌 메모리 공간에 독립적으로 저장된다.
44 | - 다음 값이 저장된 주소를 갖고 있다(link).
45 | - 각각의 값은 자기자신의 값과 함께 다음 값의 주소를 갖고 있다.
46 | - 마지막 요소는 다음 값의 주소 대신 `NULL`(Python에서는 `None`)을 갖고 있다.
47 | - `index`로 접근할 수 없음.
48 | - n번째 값을 가져오고 싶다면 첫번째로부터 순차적으로 조회해야한다.
49 |
--------------------------------------------------------------------------------
/notes/lecture08-stack.md:
--------------------------------------------------------------------------------
1 | # 스택(stack)
2 |
3 | > 본 포스팅은 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 중 8강 "[스택(stack)](https://youtube.com/watch?v=OzFXiukhv8o)" 강의를 정리한 노트입니다.
4 |
5 | 자료구조는 삽입, 삭제, 탐색, 이 세 가지 연산은 어느 자료구조든지 제공을 해야한다.
6 |
7 | ## 스택의 연산
8 |
9 | - 삽입: `push`
10 | - 삭제: `pop`
11 |
12 | 스택은 아래에서부터 저장이 되고, 최근에 들어온 값부터 제거가 되는 형식이다(LIFO)
13 |
14 | 스택은 `push`와 `pop` 연산으로만 값을 변경할 수 있다. 따라서 파이썬 리스트를 간접적으로 사용하면서, 두 연산으로만 값을 변경하도록 한다.
15 |
16 | ### 편의상 제공하는 연산
17 |
18 | - `top`: 가장 위에 있는 값을 반환. `pop`과 달리 삭제를 수행하지 않는다.
19 | - `len`: 요소의 개수를 반환하는 함수
20 |
21 | ## 스택 클래스 구현
22 |
23 | ```py
24 | class Stack:
25 | def __init__(self):
26 | self.items = [] # 데이터 저장을 위한 리스트 준비
27 |
28 | def push(self, val):
29 | self.items.append(val)
30 |
31 | def pop(self):
32 | try: # pop할 아이템이 없으면
33 | return self.items.pop()
34 | except IndexError: # indexError 발생
35 | print("Stack is empty")
36 |
37 | def top(self):
38 | try:
39 | return self.items[-1]
40 | except IndexError:
41 | print("Stack is empty")
42 |
43 | def __len__(self): # len() 호출하면 stack의 item 수 반환
44 | return len(self.items)
45 | ```
46 |
47 | 사용 예시
48 |
49 | ```py
50 | S = Stack()
51 | S.push(10)
52 | S.push(2)
53 | print(S.pop()) # 2
54 | print(S.top()) # 10
55 | print(len(S)) # 1 -> 이렇게 호출하면 파이썬은 S.__len__()을 호출함
56 | ```
57 |
58 | ### 연산의 수행시간
59 |
60 | - `push`: O(1)(상수 시간)
61 | - `pop`: O(1)
62 | - `top`: O(1)
63 | - `len`: O(1) (리스트에서 개수를 항상 알고 있으므로 값만 리턴한다)
64 |
65 | ## 스택의 사용예 1) 괄호 맞추기
66 |
67 | > 보기1: (2 + 5) * 7 - ((3 - 1) / 2 + 7) # 쌍이 맞음
68 | >
69 | > 보기2: (()()) # 쌍이 맞음
70 | >
71 | > 보기3: (()))( # 쌍이 맞지 않음
72 |
73 | - 문제:
74 | - 입력: 왼쪽과 오른쪽 괄호의 문자열
75 | - 출력: 쌍이 맞으면 True, 아니면 False 반환
76 |
77 | 보기2를 보면,
78 |
79 | > ( ( ) ( ) )
80 | > 1 2 2 3 3 1
81 |
82 | 좌측 괄호를 만나면 `push`, 우측 괄호를 만나면 `pop`을 수행한다. 끝까지 수행한 후 스택에 아무것도 남지 않았다면 짝이 맞는 것. True.
83 |
84 | > ( ) )
85 | > 1 1 2
86 |
87 | 위 경우, 마지막에 `pop`을 수행하려 할 때 에러가 발생. 왼쪽괄호가 하나 모자른 것이다. False.
88 |
89 | > ( ) (
90 | > 1 1 2
91 |
92 | 끝까지 수행한 후 무언가 남아있다면 오른쪽 괄호 하나가 모자른 경우. False.
93 |
94 | ```py
95 | for p in parSeq:
96 | if p == '(': S.push(p)
97 | elif p == ')': S.pop() # 에러 발생시 오른쪽 괄호가 더 많은 것으로 보고 return False
98 | else: print("Not allowed Symbol")
99 | if len(S) > 0: return False
100 | else: return True
101 | ```
102 |
--------------------------------------------------------------------------------
/notes/lecture09-stack-calculator01.md:
--------------------------------------------------------------------------------
1 | # 스택(stack)
2 |
3 | > 본 포스팅은 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 중 9강 "[스택(stack)](https://youtube.com/watch?v=G9ujrSGEB4A)" 강의를 정리한 노트입니다.
4 |
5 |
6 |
7 | ## 스택의 사용예 2) 계산기 코드 작성
8 |
9 | ```txt
10 | "2 + 3 * 5" - > '2', '+', '3', '*', '5'
11 | ^연산자operator ^피연산자operand
12 | ```
13 |
14 | - 이항연산자(binary operator): 2 + 3, 3 * 5
15 | - 단항연산자(unary operator): +3, -6
16 |
17 | 예시에서는 이항연산자만 다룸.
18 |
19 | - infix 수식: "2 + 3 * 5" 처럼 연산자가 두 피연산자 사이에 있는 형식
20 | - postfix로 변경하면: "2 3 5 * +"
21 |
22 | infix를 postfix로 변경하는 규칙
23 |
24 | 1. 우선순위에 따라 괄호를 친다: "(2 + (3 * 5))"
25 | 2. 연산자의 오른쪽 활호 다음으로 연산자 이동: (2 (3 5) * ) +
26 | 3. 괄호 지우기: 2 3 5 * +
27 |
28 | 연습 해보기
29 |
30 | - infix: `3 * (2 + 5) * 4`
31 | - postfix: `3 2 5 + * 4 *`
32 |
33 | 1. 괄호치기: `((3 * (2 + 5)) *4)`
34 | 2. 연산자 이동: `((3 (2 5)+)* 4)*`
35 | 3. 괄호 지우기: `3 2 5 + * 4 *`
36 |
--------------------------------------------------------------------------------
/notes/lecture10-stack-calculator02.md:
--------------------------------------------------------------------------------
1 | # 스택(stack)
2 |
3 | > 본 포스팅은 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 중 10강 "[스택(stack)](https://youtube.com/watch?v=MYk4autDAJ0)" 강의를 정리한 노트입니다.
4 |
5 | ## 스택의 사용예 2) 계산기 코드 작성2
6 |
7 | - 문제:
8 | - 입력: +, -, *, /, (, ), 숫자(영문자)로 구성된 infix 수식
9 | - 출력: postfix 수식
10 |
11 | - `A + B * C -> A B C * +`
12 |
13 | 분석
14 |
15 | 1. 피연산자의 순서는 그대로
16 | 2. 자신보다 연산자 우선순위가 낮은 연산자가 나올 때까지 대기(이때 스택에서 대기)
17 |
18 | - `A * B + C -> A B * C +`
19 | - `A + B + C -> A B C * +`
20 |
21 | 괄호가 있으면 괄호도 스택에 담는다. 오른쪽 괄호는 우선순위가 가장 높고 왼쪽 괄호는 우선순위가 가장 낮다.왼쪽괄호가 나오면 무조건 스택에 연산자를 담다가, 오른쪽 괄호가 오는 순간 왼쪽 괄호가 나올 때까지 `pop`을 한다.
22 |
23 | ### 구현하기
24 |
25 | - 리스트: outstack
26 | - 스택: opstack
27 |
28 | ```py
29 | # psuedo code
30 |
31 | for each token in expr:
32 | if token == operand:
33 | outstack.append(token)
34 | if token == '(':
35 | opstack.push(token)
36 | if token == ')':
37 | opstack에서 '('가 나올때까지 pop하면서 outstack에 `append`
38 | if token in '+*-/`:
39 | opstack에 token보다 우선순위 높은 연산자는 모두 `pop`한 후 자신을 `push`
40 | opstack에 남은 연산자 모두 `pop`한 후 outstack에 `append`
41 | ```
42 |
43 | 위 psuedo code에 따라 아래 수식을 postfix 수식으로 변환하기
44 |
45 | > `6 + (3 - 2) * 4`
46 |
47 | 왼쪽 괄호가 나오면 무조건 스택에 넣었다가 오른쪽 괄호가 나오는 시점에 왼쪽 괄호가 나올 때까지 pop 해준다. 이 부분 중요.
48 |
49 | > `6 3 2 - 4 * +`
50 |
51 | ### postfix 수식을 이용해 계산하기
52 |
53 | ```py
54 | # psuedo code
55 |
56 | if token == operand
57 | S.push(token)
58 | if token in '+-*/':
59 | a = S.pop()
60 | b = S.pop()
61 | S.push(a token b)
62 | ```
63 |
64 | 위 puedo code를 이용해 계산하면 10의 결과를 얻을 수 있다.
65 |
--------------------------------------------------------------------------------
/notes/lecture11-queue.md:
--------------------------------------------------------------------------------
1 | # 큐(queue)
2 |
3 | > 본 포스팅은 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 중 11강 "[큐(queue)](https://youtube.com/watch?v=nqCNk_DmPio)" 강의를 정리한 노트입니다.
4 |
5 | FIFO(First in First out) 규칙의 순차적 자료 구조
6 |
7 | - 삽입: `enqueue`
8 | - 삭제: `dequeue`
9 |
10 | ```py
11 | enqueue(5)
12 | enqueue(-2)
13 | dequeue() # 5
14 | enqueue(10)
15 | dequeue() # -2
16 | ```
17 |
18 | queue는 두 개의 인덱스가 필요하다.
19 |
20 | - `enqueue`는 현재 어디까지 쌓여있는지 알아야 한다. (rear index)
21 | - `dequque`는 다음에 나갈 인덱스를 알아야 한다. (front index)
22 |
23 | ```py
24 | class Queue:
25 | def __init__(self):
26 | self.items = []
27 | self.front_index = 0
28 | def enque(self, val):
29 | self.items.append(val)
30 | def dequeue(self):
31 | if self.front_index == len(self.items): # 현재 dequeue할 수 있는 값이 없음
32 | print("Queue is empty")
33 | return None
34 | else:
35 | x = self.items[front_index]
36 | self.front_index += 1
37 | return x
38 | ```
39 |
40 | ## 큐 활용 예시: Josephus Problem
41 |
42 | 여섯 명(n)중에 2번째(k) 사람마다 죽는다면 몇 번의 사람이 살아남는가?
43 |
44 | ```txt
45 | n = 6, k = 2
46 | 1
47 | 6 2
48 | 5 3
49 | 4
50 | ```
51 |
52 | 아래처럼 동작하면 된다.
53 |
54 | ```py
55 | Josephus(n, k):
56 | return 최종 생존자의 번호
57 | ```
58 |
59 | `n = 9, k = 3`이라면, `k - 1 = 2`는 `dequeue` 하자마자 다시 `enqueue`를 한다. `k` 번째가 나오면 `dequeue`를 하여 삭제한다. 이 로직을 마지막 살아남는 사람이 나올 때까지 반복한다. 코드는 강의 노트를 참고할 것.
60 |
61 | ## 덱(deque)
62 |
63 | stack + queue 형태. 덱은 양쪽 끝에서 insert와 delete가 이루어진다.
64 |
65 | - 삽입: `append`, `append_left`
66 | - 삭제: `pop`, `pop_left`
67 |
68 | 파이썬에서는 `deque` 클래스를 제공한다. 덱 관련한 내용은 온라인 QnA에서 따로 설명한다.
69 |
--------------------------------------------------------------------------------
/notes/lecture12-linked-list.md:
--------------------------------------------------------------------------------
1 | # 연결리스트(Linked List)
2 |
3 | > 본 포스팅은 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 중 12강 "[연결리스트(Linked List)](https://youtube.com/watch?v=sMpsvA5O0xU)" 강의를 정리한 노트입니다.
4 |
5 | 한방향 vs. 양방향
6 |
7 | ## 배열과 연결리스트 비교
8 |
9 | 배열은 메모리 공간에 순차적으로 배치되므로 `[]` 연산자로 상수시간 접근이 가능하다. 하지만 연결리스트는 메모리상에서 흩어져 있어서 상수시간 접근이 불가능하다.
10 |
11 | 연결리스트의 마지막 요소에는 None(C에서의 NULL)이 있다. 이는 다음 값이 없다는 것을 의미하고 그 앞의 값이 마지막 값이 된다.
12 |
13 | 연결리스트의 각 노드는 data 값(key)과 다음 주소의 값(link) 쌍을 항상 갖고 있어야 한다. 이 쌍을 노드(node)라고 부른다. 노드와 링크로 연결된 구조를 연결리스트라고 부른다.
14 |
15 | 가장 앞의 노드를 헤드 노드(head node)라고 부른다. n번째의 값을 가져오려면 헤드 노드로부터 링크를 따라가야만 한다.
16 |
17 | 연결리스트의 장점
18 |
19 | - 배열에서 값과 값 사이에 새 값을 넣으려면 삽입한 값 이후의 모든 요소를 한 칸씩 이동해야 한다. 하지만 연결리스트는 중간에 새 노드를 넣고 다시 연결해주면 된다. 두 개의 링크 주소만 바꾸면 되므로 insert 작업에는 상수시간이 걸린다. 단 교체해야할 노드를 알고 있을 때에 상수시간이다.
20 |
21 | ## 한 방향 연결 리스트 vs 양 방향 연결 리스트
22 |
23 | - 한 방향: 링크가 한쪽 방향으로만 연결되어 있으므로, 한 방향으로만 갈 수 있고 반대 방향으로는 갈 수 없다.
24 | - 양 방향: 양쪽 방향으로 링크가 있어서 노드의 양쪽 방향으로 모두 이동할 수 있다.
25 |
26 | ## 구현해보기
27 |
28 | ```py
29 | class Node:
30 | def __init__(self, key = None):
31 | self.key = key
32 | self.next = None
33 | def __str__(self):
34 | return str(self.key) # print(v.key) 대시 print(v)로 쓸 수 있다.
35 | ```
36 |
37 | 사용 예시
38 |
39 | ```py
40 | a = Node(3)
41 | b = Node(9)
42 | c = Node(-1)
43 |
44 | a.next = b
45 | b.next = c
46 | ```
47 |
48 | `c` 노드를 테일 노드(tail node)라고 부른다.
49 |
50 | 헤드 노드의 앞에 `head`와 `size`를 갖고 있는 객체를 만들어서 헤드 노드 앞에 붙이려 한다.
51 |
52 | ```py
53 | class SinglyLinkedList:
54 | def __init__(self):
55 | self.head = None
56 | self.size = 0
57 | .
58 | .
59 | .
60 | ```
61 |
62 | 한방향 연결리스트에 대해서는 다음 동영상에서 본격적으로 살펴본다.
63 |
--------------------------------------------------------------------------------
/notes/lecture13-singly-linked-list01.md:
--------------------------------------------------------------------------------
1 | # 한방향 연결리스트
2 |
3 | > 본 포스팅은 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 중 13강 "[한방향 연결리스트](https://youtube.com/watch?v=kGZoEShMcSQ)" 강의를 정리한 노트입니다.
4 |
5 | ## `pushFront`와 `pushBack`
6 |
7 | ```py
8 | class SinglyLinkedList:
9 | def __init__(self):
10 | self.head = None
11 | self.size = 0
12 | def __len__(self):
13 | return self.size
14 | def pushFront(self, key):
15 | new_node = Node(key)
16 | new_node.next = self.head;
17 | self.head = new_node
18 | self.size += 1
19 | def pushBack(self, key):
20 | v = Node(key)
21 | if len(self) == 0:
22 | self.head = v
23 | else:
24 | tail = self.head
25 | while tail.next != None:
26 | tail = tail.next
27 | tail.next = v
28 | self.size += 1
29 |
30 | ```
31 |
32 | 사용 예시
33 |
34 | ```py
35 | L = SinglyLinkedList()
36 | L.pushFront(-1) # [-1]->0
37 | L.pushFront(9) # [9]->[-1]->0
38 | L.pushFront(3) # [3]->[9]->[-1]->0
39 | L.pushFront(5) # [5]->[3]->[9]->[-1]->0
40 | L.pushBack(4) # [5]->[3]->[9]->[-1]->[4]->0
41 | ```
42 |
43 | ## 삭제 연산 `popFront`와 `popBack`
44 |
45 | > [5]->[3]->[9]->[1]->0
46 |
47 | `popFront()`
48 |
49 | ```py
50 | def popFront(self):
51 | if len(self) == 0:
52 | return None
53 | else:
54 | x = self.head
55 | key = x.key
56 | self.head = x.next
57 | self.size -= 1
58 | del x
59 | return key
60 | ```
61 |
62 | `popBack()`
63 |
64 | ```py
65 | def popBack(self):
66 | if len(self) == 0: return None
67 | else: # running techinque
68 | prev, tail = None, self.head
69 | while tail.next != None:
70 | prev = tail
71 | tail = tail.next
72 | if len(self) == 1:
73 | self.head = None
74 | else
75 | prev.next = tail.next # None
76 | key = tail.key
77 | del tail
78 | self.size -= 1
79 | return key
80 | ```
81 |
82 | ## 한방향 연결리스트의 수행 시간
83 |
84 | - `pushFront`: O(1)
85 | - `popFront`: O(1)
86 | - `pushBack`: O(n)
87 | - `popBack`: O(n)
88 |
--------------------------------------------------------------------------------
/notes/lecture14-singly-linked-list02.md:
--------------------------------------------------------------------------------
1 | # 한방향 연결리스트
2 |
3 | > 본 포스팅은 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 중 14강 "[한방향 연결리스트](https://youtube.com/watch?v=aCHwXmpuAkY)" 강의를 정리한 노트입니다.
4 |
5 | ## 추가 연산들: 탐색(search) + 제너레이터(generator)
6 |
7 | ### 서치
8 |
9 | ```py
10 | def search(self, key):
11 | # key 값의 노드를 리턴, 없으면 None 리턴
12 | v = self.head
13 | while v.next != None:
14 | if v.key == key:
15 | return v
16 | v = v.next
17 | return None # or return v (== None)
18 | ```
19 |
20 | ### 제너레이터
21 |
22 | ```py
23 | A = [3, 5, -1, 9]
24 | for x in A: # A의 요소를 순서대로 x에 대입해준다.
25 | print(x)
26 | ```
27 |
28 | 리스트처럼 `for` 루프에서 사용할 수 있게 해주는 것이 제너레이터.
29 |
30 | ```py
31 | def __iterator__(self):
32 | v = self
33 | while v != None:
34 | yield v
35 | v = v.next
36 | ```
37 |
38 | `yeild`가 있는 함수를 가리켜 제너레이터라고 한다. `L`이라는 이름의 연결리스트에 대해 아래처럼 쓸 수 있다.
39 |
40 | ```py
41 | for x in L:
42 | print(x)
43 | ```
44 |
45 | 연결리스트의 이터레이터가 종료되면 `StopIterator`라는 에러 메시지가 자동으로 생성된다. 이 에러 메시지가 발생하면 자동으로 이터레이터를 빠져 나온다.
46 |
--------------------------------------------------------------------------------
/notes/lecture15-doubly-linked-list01.md:
--------------------------------------------------------------------------------
1 | # 양방향 연결리스트(Doubly Linked List)
2 |
3 | > 본 포스팅은 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 중 15강 "[양방향 연결리스트(Doubly Linked List)](https://youtube.com/watch?v=nQhzNRmnmt8)" 강의를 정리한 노트입니다.
4 |
5 | ## 한방향 연결리스트의 단점
6 |
7 | - 한쪽으로만 연결이 되어 있기 때문에 테일 노드에 대한 조작을 하고 싶을 때 직전의 노드(previous node)를 알고 있어야 한다. 그래서 O(n)의 연산 시간을 사용한다.
8 |
9 | 양방향 연결리스트를 이용해 이 문제를 해결할 수 있다.
10 |
11 | ## 양방향 연결리스트
12 |
13 | 테일 노드와 헤드 노드가 각각 서로를 가리키도록 하면 원형 양방향 연결리스트(Circulary Doubly L.L)라고 부른다. 본 강좌에서는 양방향 연결리스트라고 하면 원형 연결리스트를 지칭하는 것으로 한다.
14 |
15 | ## 원형 연결리스트
16 |
17 | 원형 연결리스트의 빈 리스트는 더미 노드(dummy node)라고 부른다. 더미 노드는 원형 연결리스트의 시작을 알리는 마커로서, key로 None을 넣어준다. 더미 노드는 헤드 역할을 하므로 헤드 노드라고 부르기도 한다.
18 |
19 | ## 구현
20 |
21 | ```py
22 | class Node:
23 | def __init__(self, key = None):
24 | self.key = key
25 | self.next = self
26 | self.prev = self
27 | def __str__(self):
28 | # implement
29 | def __len__(self):
30 | # implement
31 | ```
32 |
33 | ```py
34 | class DoublyLinkedList:
35 | def __init__(self):
36 | self.head = Node()
37 | self.size = 0
38 | def __iter__(self):
39 | # implement
40 | def __str__(self):
41 | # implement
42 | def __len__(self):
43 | # implement
44 | ```
45 |
46 | ## 양방향 연결리스트의 다양한 삽입 삭제 연산
47 |
48 | ### splice 연산
49 |
50 | ```py
51 | def splice(self, a, b, x): # 세 개의 노드 a, b, x
52 | ap = a.prev, bn = b.next, xn = x.next
53 | ap.next = bn # cut
54 | bn.prev = ap # cut
55 | xn.next = a
56 | a.prev = x
57 | b.next = xn
58 | xn.prev = b
59 | ```
60 |
61 | a와 b를 포함하는 노드를 cut 해서 x의 다음에 연결한다. 이때 x는 양방향 연결리스트 자기 자신일 수도 있고 다른 양방향 연결리스트일 수도 있다.
62 |
63 | - 조건1: a -> ... -> b 형태로 배치
64 | - 조건2: a와 b 사이에 head 노드가 없어야 함
65 | - 조건2: a와 b 사이에 x 노드가 없어야 함
66 |
67 | 6개의 링크를 변경하면 cut과 재연결을 할 수 있다.
68 |
--------------------------------------------------------------------------------
/notes/lecture16-doubly-linked-list02.md:
--------------------------------------------------------------------------------
1 | # 양방향 연결리스트 삽입-삭제-탐색 연산
2 |
3 | > 본 포스팅은 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 중 16강 "[양방향 연결리스트 삽입-삭제-탐색 연산](https://youtube.com/watch?v=zWrFVf9_YTQ)" 강의를 정리한 노트입니다.
4 |
5 | ```txt
6 | |------------------v
7 | head [.]->[0]<->[3]<->[-1]<->[9]
8 | size [3] ^------------------|
9 | ```
10 |
11 | ## 이동 연산
12 |
13 | - `moveAfter(self, a, x)` -> `splice(a, a, x)`
14 | - `moveBefore(a, x)` -> `splice(a, a, x.prev)`
15 |
16 | ## 삽입 연산
17 |
18 | 삽입
19 |
20 | - `insertAfter(x, key)` -> `moveAfter(Node(key), x)`
21 | - `insertBefore(x, key)` -> `moveBefore(Node(key), x)`
22 |
23 | 푸시
24 |
25 | - `pushFront(key)` -> `insertAfter(self.head, key)`
26 | - `pushBack(key)` -> `insertBefore(self.head, key)`
27 |
28 | ## 탐색 연산
29 |
30 | 탐색
31 |
32 | ```py
33 | def search(self, key):
34 | v = self.head # dummy node
35 | while v.next != self.head:
36 | if v.key == key:
37 | return v
38 | v = v.next
39 | return None
40 | ```
41 |
42 | ## 삭제 연산
43 |
44 | 삭제
45 |
46 | ```py
47 | def remove(x) # node x를 삭제
48 | if x == None or x == self.head:
49 | return
50 | x.prev.next = x.next
51 | x.next.prev = x.prev
52 | del x
53 | ```
54 |
55 | popFront
56 |
57 | ```py
58 | def popFront()
59 | if self.size == 0:
60 | return None
61 | remove(self.head.next)
62 | ```
63 |
64 | popBack
65 |
66 | ```py
67 | def popBack()
68 | if self.size == 0:
69 | return None
70 | remove(self.head.prev)
71 | ```
72 |
73 | ## 그외의 함수
74 |
75 | - `join`: 두 연결리스트를 하나로 합침
76 | - `split`: 하나의 연결리스트를 어떤 노드 x를 기준으로 나눔
77 |
78 | ## 양방향 연결리스트 연산의 수행시간
79 |
80 | - `moveAfter/moveBefore`: O(1) # splice 활용
81 | - `insertAfter/insertBefore`: O(1) # splice 활용
82 | - `pushFront/pushBack`: O(1) # splice 활용
83 | - `remove(x)`: O(1)
84 | - `popFront/popBack`: O(1)
85 | - `search(key)`: O(n)
86 | - `splice(a, b, x)`: O(1)
87 |
88 | `remove`는 `search`를 먼저 호출해야하므로 시간이 더 걸리 수 있다. 한 방향 연결리스트는 테일 노드를 수정하는 연산이 O(n)이었지만 원형 양방향 연결리스트는 O(1)이다.
89 |
--------------------------------------------------------------------------------
/notes/lecture17-hash01.md:
--------------------------------------------------------------------------------
1 | # 해시 테이블
2 |
3 | > 본 포스팅은 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 중 17강 "[해시 테이블](https://youtube.com/watch?v=Bzmepm6pYQI)" 강의를 정리한 노트입니다.
4 |
5 | ## 해시테이블 소개 및 해시 함수
6 |
7 | 해시 테이블(Hash Table): 매우 빠른 평균 삽입, 삭제, 탐색 연산 제공
8 |
9 | 딕셔너리 예시
10 |
11 | ```py
12 | D = {}
13 | D['2019317'] = "신찬수"
14 | D['2019209'] = "홍길동"
15 | D = { 2019317: "신찬수", 2019209: "홍길동" }
16 | ```
17 |
18 | ### 파이썬에서 key/value를 저장하는 방식
19 |
20 | - "key: 2019317, value: "신창수""를 받으면 2019317 % 10의 위치에 저장
21 | - "key: 2019209, value: "홍길동""을 받으면 2019209 % 10의 위치에 저장
22 |
23 | "%10"은 열 칸 중에 하나에 저장하기 위해서임. 10칸은 임의의 예시.
24 |
25 | 만약 "2019235"를 찾으면 2019235 % 10 위치에 찾아가 확인한 후, 없음을 반환한다.
26 |
27 | ### 해시 함수(hash function)
28 |
29 | 저장하는 장소를 정할 때 key 값을 index 로 매핑한다. 이 매핑 과정에서 함수(여기서는 나머지 연산)가 쓰인다. 이 함수를 해시 함수라고 부른다.
30 |
31 | 새로 들어온 값을 저장하려 할 때, 그 곳에 기존의 값이 있으면 충돌(collistion) 발생. 이를 해결하는 걸 충돌 해결(collision resolustion)이라고 부르며 이 해결함수를 충돌 해결 방법이라고 부른다.
32 |
33 | 세 가지 주요 요소
34 |
35 | 1. Table: list
36 | 2. Hash function
37 | 3. Collision resolution method
38 |
39 | #### 해시 함수 특징과 종류
40 |
41 | ```py
42 | H, (H) = m # m은 slots 개수
43 | ```
44 |
45 | 앞서 예시처럼 나머지 연산으로 구분하는 해시 함수를 Division hash function이라고 부른다.
46 |
47 | ```py
48 | f(k) = (k % P) % m # P는 소수(Prime)
49 | ```
50 |
51 | 이 방식은 간단하지만 충돌이 많이 발생한다. 각 슬롯에 골고루 분산되도록 해주는 해시 함수가 좋은 함수인데 이런 함수를 perfect hash function이라고 부른다. 가장 이상적인 형태의 해시 함수
52 |
53 | ```txt
54 | key ----> slot
55 | 1:1
56 | ```
57 |
58 | Universal hash function: 충돌이 일어날 확률이 1/m인 해시 함수
59 |
60 | ```py
61 | Pr(f(x) == f(y)) = 1/m
62 | ```
63 |
64 | C-universal hash function: 충돌이 일어날 확률이 c/m인 해시 함수
65 |
66 | ```py
67 | Pr(f(x) == f(y)) = c/m
68 | ```
69 |
70 | 다양한 해시 함수 종류
71 |
72 | - Division
73 | - Multiplication
74 | - Folding
75 | - Mid-squares
76 | - Extraction
77 |
78 | 키 값이 문자열일 경우
79 |
80 | - Addictive: key[i] 번째 문자를 전부 더해서 m으로 나머지 연산
81 | - Rotating: 값 h를 계산하는데 initial_value를 임의로 준 후 for문으로 키값 문자열의 길이만큼 `h = (h << 4) ^ (h >> 28) ^ key[i]`한후 `h%P%m`을 반환
82 | - Universal: 값 h 계산에서 `((h * a) + key[i])) % P`를 한후 `h%m`을 반환. `a`는 임의의 값. C++ STL 해시 함수로 사용되고 있음. 자바에서도 활용.
83 |
84 | #### 좋은 해시 함수
85 |
86 | 1. Less collision
87 | 2. Fast computation
88 |
89 | 이 두 가지는 트레이드오프인 측면이 있다. 적절하게 만드는 게 중요하다.
90 |
--------------------------------------------------------------------------------
/notes/lecture18-hash02.md:
--------------------------------------------------------------------------------
1 | # 해시 테이블
2 |
3 | > 본 포스팅은 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 중 18강 "[해시 테이블](https://youtube.com/watch?v=Bj4pd9rJp5c)" 강의를 정리한 노트입니다.
4 |
5 | ## 충돌회피방법(Collision resolution method)
6 |
7 | ### Open addressing
8 |
9 | 가고자하는 슬롯이 비어 있지 않을 경우 주위를 살펴보는 방식 세 가지
10 |
11 | - linear probing: 바로 아래의 슬롯부터 확인해서 빈칸을 발견하면 저장
12 | - quadratic probing
13 | - double hashing
14 |
15 | 오픈 어드레싱과 대비되는 방법: Chaining
16 |
17 | 클러스터: 비슷한 키 값이 모여있는 단위를 말함. 클러스터가 크면 삽입/탐색에 시간이 많이 소요됨. 클러스터는 가급적 피해야 한다.
18 |
19 | ### 연산
20 |
21 | #### set
22 |
23 | 1. key값이 H에 있으면 value를 업데이트
24 | 2. key값이 H에 없으면 (key, value)를 insert
25 |
26 | `set`에 사용할 `find_slot` 함수
27 |
28 | ```py
29 | find_slot(key): # key값이 있으면 slot 번호 리턴
30 | # key값이 없으면 key값이 삽입될 slot 번호 리턴
31 | i = f(key)
32 | start = i
33 | while ( H[i] == occupied) and (H[i].key != key)
34 | i = (i + 1)%m
35 | if i == start: return Full
36 | return i
37 | ```
38 |
39 | set 함수
40 |
41 | ```py
42 | set(key, value = None):
43 | i = find_slot(key)
44 | if i == Full: return None # 반환 전에 H를 키워야 함!
45 | if H[i].is_occupied:
46 | H[i].value = value
47 | else:
48 | H[i].key, H[i].value = key, value
49 | return key
50 | ```
51 |
52 | #### search
53 |
54 | ```py
55 |
56 | search(key):
57 | ```
58 |
59 | set과 비슷하게 구현하면 된다. 교재 참고
60 |
61 | #### remove
62 |
63 | 클러스터에서 사이에 있는 값이 사라지면 클러스터가 끊기지 않도록 아래에 있는 값을 올려줘야 한다. 이때 원래 자기 자리에 있는 값은 올려선 안 된다.
64 |
65 | 올려야 하는 값을 검색할 때는 클러스터의 끝까지 확인해줘야 한다.빈칸 또는 끝칸을 만날 때까지 작업을 한다.
66 |
67 | `k = f(H[j].key)`일 때, 현재 타겟 i가 k와 j 사이에 있다면 (`k < i <= j`)라면 올릴 수 있다.
68 |
69 | ```py
70 | remove(key):
71 | i = find_slot(key)
72 | if H[i].is_occupied: return None
73 | j = i # H[i]: 빈 slot, H[j]: 이사해야할 slot
74 | while True:
75 | H[i] = None
76 | while True: # H[j] 찾기
77 | j = (j + 1) % m
78 | if H[j].is_occupied: return key
79 | k = f(H[j].key)
80 | if (k < i <= j):break
81 | H[i] = H[j]
82 | i = j
83 | ```
84 |
85 | ### 성능
86 |
87 | - 성능은 클러스터의 길이에 비례한다.
88 | - 클러스터의 길이를 결정하는 것은 해시 함수.
89 | - 충돌 해소를 어떤 방식으로 하는지 여부
90 |
--------------------------------------------------------------------------------
/notes/lecture19-hash03.md:
--------------------------------------------------------------------------------
1 | # 해시 테이블
2 |
3 | > 본 포스팅은 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 중 19강 "[해시 테이블](https://youtube.com/watch?v=ghjWopXXUeA)" 강의를 정리한 노트입니다.
4 |
5 | ## Open addressing
6 |
7 | - linear probing: 해시 충돌시 한칸씩 내려가는 방식
8 | - quadratic probing: k + 12, k + 22, k + 32 순으로 내려가는 방식. remove 함수가 복잡해진다.
9 | - double hashing: 해시 함수를 두 개 쓰는 방식. f(key) -> f(key) + g(key) -> f(key) + 2g(key).. 순으로 내려간다.
10 |
11 | ## 성능 평가
12 |
13 | - set, remove, search: 클러스터 사이즈의 영향을 직접적으로 받는다. 클러스터 사이즈는 해시 함순 충돌 해결 방식에 영향을 받는다. 또한, 로드 팩터(load factor)의 영향도 받는다.
14 |
15 | 로드 팩터 확인
16 |
17 | - load factor: n / m (M: |H|=slot갯수, n: H에 저장된 item 갯수)
18 | - 0 <= n/m < 1
19 | - load factor가 1에 가까울수록 더 많은 연산이 필요하다.
20 |
21 | 충돌 비율 확인
22 |
23 | - (collision 횟수) / n = 충돌 비율
24 |
25 | 오픈 어드레싱 방식은 평균적으로 `m >= 2n`(최소 50% 이상 빈 슬롯이라면) 데이터 이사비용을 합치더라도 클러스터의 평균 사이즈가 O(1)이 되도록 유지할 수 있다.
26 |
27 | ## Chaining
28 |
29 | 하나의 슬롯에 여러 개의 아이템을 저장하는 방식. 각 슬롯에 한방향 연결리스트를 만든다. 꼭 한방향일 필요는 없다.
30 |
31 | `set` 함수가 항상 O(1) 시간 보장. `search`, `remove` 함수는 각 슬롯내의 노드 갯수만큼 O(n) 시간. O(충돌 key의 평균 갯수)
32 |
33 | c-universal 해시 함수를 사용하면 `set`, `search`, `remove`가 O(1). C-universal 해시 함수는 c/n만큼의 충돌을 보장하는 함수.
34 |
35 | ## 결론
36 |
37 | 해시 함수는 C-universal을 쓰고 충분한 로드 팩터를 확보하면 상수시간 내에 `set`, `remove`, `search`를 수행할 수 있다.
38 |
--------------------------------------------------------------------------------
/notes/lecture20-tree.md:
--------------------------------------------------------------------------------
1 | # 트리(tree)
2 |
3 | > 본 포스팅은 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 중 20강 "[트리(tree)](https://youtube.com/watch?v=w-1w4ood7Bc)" 강의를 정리한 노트입니다.
4 |
5 | ## 순차적 자료구조
6 |
7 | 배열, 인덱스, i = 0, 1..., 연결리스트 링크(next, prev) 등이 있는 자료구조.
8 |
9 | ## 트리
10 |
11 | 부모, 자식 노드를 갖고 데이터를 표현하는 자료구조. 자식이 하나만 있거나 없는 경우를 연결리스트라고 부른다. 연결리스트는 트리의 특별한 유형이라고 할 수도 있다.
12 |
13 | 자식이 최대 두 개인 트리를 이진 트리(binary tree)라고 부른다. 삼진 트리도 가능하지만 이진 트리가 가장 간단하고 많이 쓰이는 구조이다.
14 |
15 | - 노드: 각각의 요소
16 | - 링크: 노드를 연결하는 선, 엣지라고 부르기도 한다.
17 | - 루트 노드: 최고의 조상
18 | - 리프 노드(leaf node): 자식이 하나도 없는 노드
19 | - 레벨: 루트 노드가 레벨 0. 루트로부터 각각 레벨 1, 2, 3...
20 | - 높이: 루트부터 리프까지 가장 깊은 레벨까지의 통과하는 노드의 수. 레벨 수 == 트리 높이
21 | - 경로(path): 노드 v에서 w까지 거치는 노드들. 출발노드-경유노드-도착노드
22 | - 경로길이(path length): 경로에 포함되는 링크(엣지)의 개수
23 | - 자식(child) 노드와 부모(parent) 노드, 형제 노드
24 |
25 | ## 구현
26 |
27 | ```txt
28 | a
29 | / \
30 | b c
31 | \ / \
32 | d e f
33 | ```
34 |
35 | - 표현법 1) 리스트: `A = [a, b, c None, d, e, f, None, None, h, i, g]`
36 | - 표현법 2) 리스트(재귀적): `[a, [ [b, [], [d, [], []] ], [c, [e, [], []] [f, [], [] ]]]`
37 | - 표현법 3) 노드 클래스를 이용: 다음에 다시 살펴볼 것
38 |
--------------------------------------------------------------------------------
/notes/lecture21-heap01.md:
--------------------------------------------------------------------------------
1 | # 힙(heap)
2 |
3 | > 본 포스팅은 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 중 21강 "[힙(heap)](https://youtube.com/watch?v=8XnPN6IB22YU)" 강의를 정리한 노트입니다.
4 |
5 | 힙 성질(heap property) 만족하는 이진트리
6 |
7 | ```txt
8 | a - 0 level
9 | / \
10 | b c - 1
11 | \ / \
12 | d e f - 2
13 | ```
14 |
15 | ```py
16 | H = [a, b, c, None, d, e, f]
17 | # ^ ^--^ ^----------^
18 | # 0 1 2
19 | ```
20 |
21 | - H[0]의 왼쪽 자식노드 H[1]
22 | - H[0]의 오른쪽 자식노드 H[2]
23 | - H[2]의 왼쪽 자식노드 H[2 * 2 + 1] = H[5] = e
24 | - H[2]의 오른쪽 자식노드 H[2 * 2 + 2] = H[6] = f
25 |
26 | > H[k]의 왼쪽 자식노드: H[2 * k + 1]
27 | >
28 | > H[k]의 오른쪽 자식노드: H[2 * k + 2]
29 | >
30 | > H[k]의 부모노드: H[(k - 1) // 2]
31 |
32 | 모두 상수시간에 계산 가능. 단점은 비어있는 곳을 None으로 표시하기 위해 자리를 차지하는 불필요한 공간이 생긴다. 트레이드오프. 레벨을 꽉꽉 채워주면 이 낭비를 없앨 수 있다. 꽉꽉 채워둔 이진트리를 힙이라고 부른다.
33 |
34 | ## 예시
35 |
36 | ```py
37 | A = [2,8,6,1,10,15,3,12,11]
38 | ```
39 |
40 | 위 리스트를 이진트리로 변환하면
41 |
42 | ```txt
43 | 2
44 | / \
45 | 8 6
46 | / \ / \
47 | 1 10 15 3
48 | / \
49 | 12 11
50 | ```
51 |
52 | 힙을 만족하려면,
53 |
54 | - 모양: 레벨마다 꽉 채워진 상태
55 | - 힙 성질: 모든 부모노드의 키값은 자식노드의 키값보다 작지 않다.
56 |
57 | 위의 이진트리는 힙 성질을 만족하지 못 한다.
58 |
59 | 아래 노드는 힙 성질을 만족한다. 아래 A는 힙이다.
60 |
61 | ```py
62 | A = [15,12,6,11,10,2,3,1,5]
63 | ```
64 |
65 | ```txt
66 | 15
67 | / \
68 | 12 6
69 | / \ / \
70 | 11 10 2 3
71 | / \
72 | 1 5
73 | ```
74 |
75 | 힙의 특징: 힙의 루트노드에는 가장 큰 값이 들어있다. A[0]가 가장 큰 값.
76 |
77 | ## 힙의 연산
78 |
79 | 1. `insert` # O(logN)
80 | 2. `find_max`: `return A[0]` # O(1)
81 | 3. `delete_max`: # O(logN)
82 |
83 | `make_heap`: 힙 성질을 만족하도록 요소를 재배치 하는 함수
84 |
--------------------------------------------------------------------------------
/notes/lecture22-heap02.md:
--------------------------------------------------------------------------------
1 | # 힙(Heap)
2 |
3 | > 본 포스팅은 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 중 22강 "[힙(Heap)](https://youtube.com/watch?v=6VMSTOdHRfI)" 강의를 정리한 노트입니다.
4 |
5 | ```py
6 | A = [2,8,6,1,10,15,3,12,11]
7 | ```
8 |
9 | ```txt
10 | 2
11 | / \
12 | 8 6
13 | / \ / \
14 | 1 10 15 3
15 | / \
16 | 12 11
17 | ```
18 |
19 | 위와 같은 힙 성질을 만족하지 않는 리스트를 힙 성질을 만족하도록 변경하는 것을 make_heap이라고 한다. 이를 위해서는 `heapify_down`을 반복적으로 수행
20 |
21 | 1, 12, 11을 보면,
22 |
23 | - 1: A[3]
24 | - 12: A[3 * 2 + 1] = A[7]
25 | - 11: A[3 * 2 + 2] = A[8]
26 |
27 | 셋중 12가 가장 크므로, 1과 12를 바꾼다. A[3]과 A[7]을 바꾸면,
28 |
29 | ```py
30 | A = [2,8,6,12,10,15,3,1,11]
31 | ```
32 |
33 | 6, 15, 3을 보면, 15가 가장 크므로 6과 15를 바꾼다.
34 |
35 | ```py
36 | A = [2,8,15,12,10,6,3,1,11]
37 | ```
38 |
39 | 8, 12, 10을 보면 12가 가장 크므로 8과 12를 바꾼다.
40 |
41 | ```py
42 | A = [2,12,15,8,10,6,3,1,11]
43 | ```
44 |
45 | 8, 1, 11을 보면 11이 가장 크므로 8과 11을 바꾼다.
46 |
47 | ```py
48 | A = [2,12,15,11,10,6,3,1,8]
49 | ```
50 |
51 | A[0]인 2를 자기자리를 찾으려면 2, 15를 바꾸고, 또 6과 2를 바꾼다.
52 |
53 | ```py
54 | A = [15,12,6,11,10,2,3,1,8]
55 | ```
56 |
57 | 이렇게 하면 힙 성질을 만족한다.
58 |
59 | 결론: 자식노드와 비교하여 가장 큰 값을 부모 노드 자리로 옮기면 make_heap이 된다.
60 |
61 | ```py
62 | make_heap(A):
63 | n = len(A)
64 | for k in range(n-1, -1, -1):
65 | #A[k] -> heap 성질 만족하는 곳으로 내려보낸다.
66 | heapify_down(k, n)
67 | ```
68 |
69 | ```py
70 | heapify_down(k, n):
71 | # A[k], n값
72 | while A[k] != leaf:
73 | L, R = 2*k+1, 2*k+2
74 | m = index_max(A[k], A[L], A[R])
75 | if k != m:
76 | # A[K], A[m]을 swap
77 | else:
78 | break
79 | ```
80 |
81 | ## 성능
82 |
83 | `heapify_down`은 `A[k]`를 `n`번 부른다. 상수 연산을 루트로부터 가장 깊은 리프노드까지 가는, 트리의 높이(height)만큼 반복하면 최악의 케이스이다.
84 |
85 | > `T = O(nh) # h = height`
86 |
87 | n개의 노드를 가진 힙의 높이 h는?
88 |
89 | - level0 : 1
90 | - level1 : 2
91 | - level2 : 22
92 | - level3 : 23
93 | - level h - 1 : 2h - 1
94 | - level h : < 2h
95 |
96 | 다 더하면 1 + 2 + 2^2 +..... + 2^(k-1) + 1 <= n
97 |
98 | 1 + 2 + 2^2 +.....+ 2^(k-1) = (2h -1 / 2 - 1) + 1 = 2h <= n
99 |
100 | `h <= log2n
101 |
102 | - heapify_down: O(h) = O(logN)
103 | - make_heap: o(nh) = O(nlogN) (사실은 O(n) 시간에도 된다)
104 |
--------------------------------------------------------------------------------
/notes/lecture23-heap03.md:
--------------------------------------------------------------------------------
1 | # 힙(Heap)
2 |
3 | > 본 포스팅은 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 중 23강 "[힙의 insert 연산](https://youtube.com/watch?v=gVRDc5NRjjw)" 강의를 정리한 노트입니다.
4 |
5 | ## 힙의 insert 연산
6 |
7 | ```py
8 | A = [15,12,6,11,10,2,3,1,8]
9 | ```
10 |
11 | ```txt
12 | 15
13 | / \
14 | 12 6
15 | / \ / \
16 | 11 10 2 3
17 | / \
18 | 1 8
19 | ```
20 |
21 | `insert(14)` -> 10의 왼쪽 끝에 들어가므로, 삽입하고 나면,
22 |
23 | ```py
24 | A = [15,12,6,11,10,2,3,1,8,14]
25 | ```
26 |
27 | ```py
28 | insert(14)
29 | A.append(14)
30 | ```
31 |
32 | 자리를 두번 바꿔야 한다.
33 |
34 | ```py
35 | A = [15,12,6,11,14,2,3,1,8,10]
36 | ```
37 |
38 | ```py
39 | A = [15,14,6,11,12,2,3,1,8,10]
40 | ```
41 |
42 | 14가 자기자리를 찾도록 재배치하면 전체적으로 힙이 완성된다. 부모노드로 가면서 자신의 자리를 찾도록 만드는 함수를 `heapify_up`이라고 부른다.
43 |
44 | ```py
45 | insert(14)
46 | A.append(14)
47 | A.heapify_up(9)
48 | # A[k]를 root 방향으로 이동하면서 heapify를 해준다.
49 |
50 | heapify(k):
51 | while k > 0 and A[(k -1)//2)] < A[k]:
52 | A[k], A[(k-1) //2] = A[(k-1) //2], A[k]
53 | k = (k - 1) // 2
54 | ```
55 |
56 | 최악의 경우 리프에서 루트까지 연산. `insert`와 `heapify_up` 모두 h = O(logN).
57 |
58 | ## 힙의 find_max 연산
59 |
60 | ```py
61 | A = [15,12,6,11,10,2,3,1,8]
62 | ```
63 |
64 | ```txt
65 | 15
66 | / \
67 | 12 6
68 | / \ / \
69 | 11 10 2 3
70 | / \
71 | 1 8
72 | ```
73 |
74 | ```py
75 | find_max:
76 | return A[0] # O(1)
77 | ```
78 |
79 | ## 힙의 delete_max 연산
80 |
81 | A[0] 을 없애면서 가장 마지막 리프노드를 루트로 옮긴다. 그 후 A[0]를 `heapify_down(0, n)` 해준다.
82 |
83 | ```txt
84 | 15
85 | / \
86 | 12 6
87 | / \ / \
88 | 11 10 2 3
89 | / \
90 | 1 8
91 | ```
92 |
93 | ```py
94 | delete_max: # O(logN)
95 | if len(A) == 0: return None
96 | key = A[0]
97 | A[0], A[len(A) - 1] = A[len(A) - 1], A[0]
98 | A.pop()
99 | heapify_down(0, len(A))
100 | return key
101 | ```
102 |
103 | ### 성능
104 |
105 | - `make_heap`: O(n), O(nlogN)
106 | - `insert`: O(logN)
107 | - `find_max`: O(1)
108 | - `delete_max`: O(logN)
109 | - `heapify_down`: O(h) = O(logN)
110 | - `heapify_up`: O(h) = O(logN)
111 |
112 | ### 기타
113 |
114 | 힙에는 `search` 함수가 없다. 정리가 되어 있는 게 아니다보니 알 방법이 없어서 무조건 모두 탐색해야한다. `search`는 효율이 없고 `insert`, `find_max`, `delete_max`를 이용하는 경우에 힙이 좋다.
115 |
116 | `find_min`과 `delete_min`을 갖는 힙인 min_heap을 만들 수 있다. 우리가 만든 힙은 max_heap이다.
117 |
118 | 정렬을 하는 경우, 힙 소트를 할 수 있다. `make_heap()`은 O(nlogN)이므로 정렬 후 `delete_max()`의 반환과 유사한 형식을 이용해 할 수 있다. (자세한 내용은 나중에 다시 확인할 것!)
119 |
--------------------------------------------------------------------------------
/notes/lecture24-binary-tree.md:
--------------------------------------------------------------------------------
1 | # 이진트리 - 정의와 순회
2 |
3 | > 본 포스팅은 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 중 24강 "[이진트리 - 정의와 순회](https://www.youtube.com/watch?v=HDjqrmmpFdU)" 강의를 정리한 노트입니다.
4 |
5 | ## 이진트리(Binary Tree)
6 |
7 | 이진트리의 표현법
8 |
9 | - 배열과, 리스트를 이용한 표현법
10 | - 노드와 링크를 클래스로 직접 표현. Node 클래스와 BT class를 이용한 표현법
11 |
12 | ### 클래스를 이용한 표현
13 |
14 | ```py
15 | class Node:
16 | def __init__(self, key):
17 | self.key = key
18 | self.parent = self.left = self.right = None
19 | def __str__(self):
20 | return str(self.key)
21 | ```
22 |
23 | 사용 예시
24 |
25 | ```py
26 | a = Node(6)
27 | b = Node(9)
28 | c = Node(1)
29 | d = Node(5)
30 | a.left = b
31 | a.right = c
32 | b.parent = c.parent = a
33 | b.left = d
34 | d.paren = b
35 | ```
36 |
37 | ```txt
38 | 6
39 | / \
40 | 9 1
41 | \
42 | 5
43 | ```
44 |
45 | ### 이진트리 순회(traversal)
46 |
47 | 이진트리의 노드를 빠짐없이 출력하는 세가지 방법
48 |
49 | ```txt
50 | F
51 | / \
52 | B G
53 | / \ \
54 | A D I
55 | /\ /
56 | C E H
57 | ```
58 |
59 | - preorder(MLR): F B A D C E G I H
60 | - inorder(LMR): A B C D E F G H I
61 | - postorder(LRM): A C E D B I H G F
62 |
63 | M: 현재 노드, L: 좌측 서브트리, R: 우측 서브트리
64 |
65 | ### 구현
66 |
67 | ```py
68 | class Node:
69 | def __init__(self, key):
70 | self.key = key
71 | self.parent = self.left = self.right = None
72 | def __str__(self):
73 | return str(self.key)
74 | def preorder(self): #현재 방문중인 노드 == self
75 | if self != None: # MLR
76 | print(self.key)
77 | if self.left:
78 | self.left.preorder()
79 | if self.right:
80 | self.right.preorder()
81 | def inorder(self):
82 | if self != None: # LMR
83 | if self.left:
84 | self.left.inorder()
85 | print(self.key)
86 | if self.right:
87 | self.right.inorder()
88 | def postorder(self):
89 | if self != None: # LRM
90 | if self.left:
91 | self.left.postorder()
92 | if self.right:
93 | self.right.postorder()
94 | print(self.key)
95 | ```
96 |
97 | ### reconstruct
98 |
99 | `preorder`, `inorder`, `postorder` 중 두 개 이상의 출력된 결과를 기반으로 이진트리의 모양을 추측할 수 있다.
100 |
--------------------------------------------------------------------------------
/notes/lecture25-binary-search-tree01.md:
--------------------------------------------------------------------------------
1 | # 이진탐색트리 - 정의와 탐색, 삽입 연산
2 |
3 | > 본 포스팅은 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 중 25강 "[이진탐색트리 - 정의와 탐색, 삽입 연산](https://www.youtube.com/watch?v=Bhprzw_1kb0)" 강의를 정리한 노트입니다.
4 |
5 | ## 이진탐색트리
6 |
7 | 특정 키 값을 찾는 연산을 효율적으로 할 수 있도록 조직화한 트리
8 |
9 | 이진탐색트리의 정의
10 |
11 | - 각 노드의 왼쪽 서브트리의 key 값은 노드의 key 값보다 작거나 같아야 한다.
12 | - 각 노드의 오른쪽 서브트리의 key 값은 노드의 key 값보다 커야 한다.
13 |
14 | 이진탐색트리의 예
15 |
16 | ```txt
17 | 15
18 | / \
19 | 4 20
20 | / / \
21 | 2 17 32
22 | \
23 | 19
24 | ```
25 |
26 | 모든 노드에 대해서 위 조건이 만족되어 있어야 한다.
27 |
28 | - 15의 왼쪽 노드: 2, 4
29 | - 15의 오른쪽 노드: 20, 17, 32, 19
30 | - 20의 왼쪽 노드: 17, 19
31 | - 20의 오른쪽 노드: 32
32 |
33 | 이렇게 구성하면 현재의 key 값을 기준으로 검색해야할 노드의 방향을 결정할 수 있다.
34 |
35 | 탐색 시 level by level로 내려가므로 위 예시에서는 height가 3이고, 수행시간은 O(h)이다.
36 |
37 | ## 구현
38 |
39 | ### BST 클래스
40 |
41 | ```py
42 | class BST:
43 | def __init__(self):
44 | self.root = None
45 | self.size = 0
46 | def __len__(self):
47 | return self.size
48 | def __iter__(self):
49 | return self.root.__iter__() # Node클래스에 구현된대로 방문.
50 | # generator에 대해 별도 학습할 것
51 | ```
52 |
53 | 사용 예시
54 |
55 | ```py
56 | T = BST()
57 | T.insert(15) # 아직은 구현 전인 함수
58 | T.insert(4)
59 | ```
60 |
61 | ### 탐색
62 |
63 | `find_loc()`
64 |
65 | ```py
66 | def find_loc(self, key):
67 | if self.size == 0: return None
68 | p = None # p is parent of v
69 | v = self.root
70 | while v: # while v != None
71 | if v.key == key: return v
72 | elif v.key < key:
73 | p = v
74 | v = v.right
75 | else:
76 | p = v
77 | v = v.left
78 | retern p
79 | ```
80 |
81 | `search()`
82 |
83 | ```py
84 | def search(self, key):
85 | v = self.find_loc(key)
86 | if v == None:
87 | return None
88 | else:
89 | return v
90 | ```
91 |
92 | ### 삽입
93 |
94 | - 탐색트리는 규칙을 갖고 있으므로 key가 들어갈 위치가 정해진다.
95 |
96 | `insert()`
97 |
98 | ```py
99 | def insert(self, key):
100 | p = self.find_loc(key) # find_loc() 은 O(h)
101 | if p == None or p.key != key:
102 | v = Node(key)
103 | if p == None:
104 | self.root = v
105 | else:
106 | v.parent = p
107 | if p.key >= key: # left/right
108 | p.left = v
109 | else:
110 | p.right = v
111 | self.size = self.size + 1
112 | return v
113 | else:
114 | print("key is already in the tree")
115 | return p # 중복 key를 허용하지 않으면 None 리턴 p == None
116 | ```
117 |
118 | `insert()` 함수는 O(h)
119 |
--------------------------------------------------------------------------------
/notes/lecture26-binary-search-tree02.md:
--------------------------------------------------------------------------------
1 | # 이진탐색트리 - 삭제 연산
2 |
3 | > 본 포스팅은 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 중 26강 "[이진탐색트리 - 삭제 연산](https://www.youtube.com/watch?v=VVhmgQIJCu8)" 강의를 정리한 노트입니다.
4 |
5 | ## 삭제 연산
6 |
7 | 삭제 연산의 두 종류
8 |
9 | - deleteByMerging
10 | - deleteByCopying
11 |
12 | ### deleteByMerging
13 |
14 | 강의에서는 deleteByMerging을 살펴 본다.
15 |
16 | x노드를 지울 때 x의 왼쪽노드를 L, 오른쪽 노드를 R이라고 했을 때, x 자리에 L을 두고, L의 가장 큰 노드의 자식 노드로 R을 연결하는 방식이다.
17 |
18 | 경우들:
19 |
20 | 1. L == None : R을 x 로 대체한다.
21 | 2. x == root : root 노드를 업데이트 해줘야 한다.
22 |
23 | ```py
24 | deleteByMerging(self, x): # 노드 x 를 삭제
25 | a = x.left, b = x.right
26 | pt = x.parent
27 | # c == x를 대체할 노드
28 | # m == L에서 가장 큰 노드
29 | if a != None:
30 | c = a
31 | m = a
32 | while m.right:
33 | m = m.right
34 | if b != None:
35 | b.parent = m
36 | m.right = b
37 | else: # a == None
38 | c = b
39 | if pt != None:
40 | if c: c.parent = pt
41 | if pt.key < c.key:
42 | pt.right = c
43 | else:
44 | pt.left = c
45 | else: # pt == None (root == x)
46 | self.root = c
47 | if c: c.parent = None
48 | self.size -= 1
49 | # return은 다음에 다시 설명하기로 함
50 | ```
51 |
52 | 강의 노트의 코드를 보면 충분히 이해할 수 있다.
53 |
54 | ### deleteByCopying
55 |
56 | x 노드를 지울 때 x의 왼쪽노드를 L, 오른쪽 노드를 R이라고 했을 때, x를 지우는 게 아니라 값을 복사하는 방식으로 변경해주는 방법. L에서 가장 큰 값(m)을 찾아서 x에 복사해준다. 원래 m의 자리에는 m의 왼쪽 서브트리가 들어온다.
57 |
58 | deletebyCopying은 직접 구현해볼 것.
59 |
60 | ### 삭제 연산의 수행 시간
61 |
62 | - deleteByMerging: O(h)
63 | - deleteByCopying: O(h)
64 |
65 | 두 함수 모두 m을 찾는데 드는 수행시간이 절대적으로 대부분을 차지.
66 |
67 | - insert
68 | - search(find_loc)
69 | - delete(merging, copying)
70 |
71 | 위 함수 모두 O(h) 시간이 든다.
72 |
73 | ```py
74 | # case 1
75 | insert(1)
76 | insert(2)
77 | insert(3)
78 | insert(4)
79 |
80 | # case 2
81 | insert(2)
82 | insert(1)
83 | insert(3)
84 | insert(4)
85 | ```
86 |
87 | 같은 키 값을 저장해도 height의 크기가 전혀 다를 수 있다. 이는 수행시간의 큰 차이를 만들기 때문에 어떤 순서로 키값이 삽입되는지와 상관없이 가능하면 작게 유지하기 위한 서치트리를 균형이진탐색트리(balanced binary search tree)라고 부른다.
88 |
--------------------------------------------------------------------------------
/notes/lecture27-balanced-binary-search-tree01.md:
--------------------------------------------------------------------------------
1 | # 균형이진탐색트리(Balanced BST) - 정의와 회전
2 |
3 | > 본 포스팅은 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 중 27강 "[균형이진탐색트리 - 정의와 회전](https://www.youtube.com/watch?v=Kuw0f3-E-Hw)" 강의를 정리한 노트입니다.
4 |
5 | ## 이진탐색트리
6 |
7 | - 각 노드 x의 좌측에는 x보다 작거나 같은 값이, 우측에는 x보다 큰 값이 있다는 조건을 만족해야 한다
8 | - 탐색, 삽입, 삭제에 O(height) 시간만큼 소요된다. 삽입, 삭제는 탐색의 비용에 영향을 받는다.
9 | - 비효율적으로(연결리스트처럼) 저장되면 "h <= n - 1"이 될 수 있다.
10 |
11 | ```txt
12 | 1
13 | \
14 | 10
15 | /
16 | 8
17 | /
18 | 5
19 | \
20 | 7
21 | ```
22 |
23 | 반면, 힙의 높이는 항상 "log(n+1) <= h <= n - 1"을 보장.
24 |
25 | ## 균형이진탐색트리
26 |
27 | 힙처럼 항상 트리의 높이가 O(logN) 정도로 보장이 되는 트리를 균형이진탐색트리라고 한다.
28 |
29 | ### 균형이진탐색트리의 예
30 |
31 | - AVL트리
32 | - 레드블랙(Red-Black) 트리
33 | - (2,3,4)-트리
34 | - 스프레이(splay) 트리
35 |
36 | ### 균형을 맞추는 방법
37 |
38 | - 트리의 높이가 커질 때 트리를 조정해 항상 높이를 O(logN) 정도로 유지
39 | - 이때 사용하는 함수가 회전(Rotation)
40 |
41 | ```txt
42 | z <-------- x
43 | / \ left / \
44 | x c [rotation] a z
45 | / \ right / \
46 | a b ---------> b c
47 | ```
48 |
49 | ### 균형이진탐색트리의 회전
50 |
51 | 우측 회전
52 |
53 | - 왼쪽의 일부가 높이 레벨 1만큼 증가하고, 오른쪽의 일부가 높이 레벨 1만큼 감소한다
54 | - x: level 1 증가, z: level 1 감소
55 | - 총 6개의 링크를 변경해야 한다
56 |
57 | 좌측 회전
58 |
59 | - 왼쪽의 일부가 높이 레벨 1만큼 감소하고, 오른쪽의 일부가 높이 레벨 1만큼 증가한다
60 | - x: level 1 감소, z: level 1 증가
61 | - 총 6개의 링크를 변경해야 한다
62 |
63 | ### 회전 구현
64 |
65 | ```py
66 | def rotateRight(self, z):
67 | if not z: return
68 | x = z.left
69 | if x == None: return
70 | b = x.right
71 | x.parent = z.parent # 1st update
72 | if z.parent:
73 | if z.parent.left == z:
74 | z.parent.left = x # 2nd update
75 | else:
76 | z.parent.right.x
77 | x.right = z # 3rd update
78 | z.parent = x # 4th update
79 | x.left = b # 5th update
80 | if b:
81 | b.parent = z # 6th update
82 | if self.root == z:
83 | self.root = x
84 | ```
85 |
86 | `rotateLeft`는 `rotateRight`에 대칭적으로 구현하면 된다.
87 |
88 | ### 회전 연산의 수행 시간
89 |
90 | 균형이진탐색트리의 회전은 O(1) 시간이 든다.
91 |
--------------------------------------------------------------------------------
/notes/lecture28-balanced-binary-search-tree02.md:
--------------------------------------------------------------------------------
1 | # 균형이진탐색트리(Balanced BST) - AVL 트리 정의
2 |
3 | > 본 포스팅은 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 중 28강 "[균형이진탐색트리 - AVL 트리 정의](https://www.youtube.com/watch?v=dHHjrl6m5CE)" 강의를 정리한 노트입니다.
4 |
5 | ## AVL(Adelson-Velsky, Londis) 트리 개요
6 |
7 | - 모든 노드에 대해서, 노드의 왼쪽 서브트리와 오른쪽 서브트리의 높이차가 1 이하인 BST
8 | - 1964년에 발표
9 |
10 | ```txt
11 | 10
12 | / \
13 | 3 12
14 | / \
15 | 1 5
16 | / \
17 | 4 7
18 | ```
19 |
20 | 10을 기준으로, '왼쪽 서브트리의 높이'는 2, '오른쪽 서브트리의 높이'는 0이며, 이 경우에는 AVL 트리의 조건을 만족하지 못한다. 반면 3을 기준으로 보면 좌우 서브트리의 높이가 각각 0, 1로 AVL 트리의 조건을 만족한다. 12를 기준으로 했을 때 역시 좌우가 None이므로 조건을 만족한다(리프 노드는 모두 조건을 만족한다).
21 |
22 | 만약 12 노드 좌측 하단 노드에 11이 있다면 AVL 트리의 조건을 만족한다.
23 |
24 | ```txt
25 | # 높이(h)가 0인 AVL트리 (자식 노드가 하나도 없는 트리) : 1개 유형
26 |
27 | # h = 1: 3개 유형
28 |
29 | node node node
30 | / \ / \
31 | node node node node
32 |
33 | # h = 2(아래 모양에서 유형별로 만들어 볼 것)
34 |
35 | node
36 | / \
37 | node node
38 | / \ / \
39 | node node node node
40 | ```
41 |
42 | ## AVL 트리의 최소 노드 갯수 Nh
43 |
44 | 위 경우에서 노드수가 가장 적은 경우들을 보면,
45 |
46 | - h = 0일 때, 노드 수는 1개
47 | - h = 1일 때, 노드 수는 2개
48 | - h = 2일 때, 노드 수는 4개
49 |
50 | AVL 노드에서 각 서브트리가 최소의 노드를 가진 경우를 본다면,
51 |
52 | h = 3 일때, 최소 노드를 가진 경우를 만들려고 하면 좌우 서브트리의 노드가 최소여야 한다.
53 |
54 | - 좌측(또는 우측) 서브트리는 h = 2, 따라서 최소 노드 갯수는 4개
55 | - 우측(또는 좌측) 서브트리는 h = 1, 최소 노드 갯수는 2개
56 |
57 | Nh: 높이가 h인 AVL 트리 중에서 최소 노드 갯수
58 |
59 | - N0 = 1
60 | - N1 = 2
61 | - N2 = 4
62 | - N3 = 7
63 |
64 | ## AVL 트리의 탐색 시간
65 |
66 | 그림을 그려서 점화식을 얻어보면,
67 |
68 | > Nh = Nh-1 + Nh-2 + 1
69 |
70 | 위의 점화식으로부터,
71 |
72 | > Nh >= 2Nh-2 + 1
73 | >
74 | > Nh >= Nh-2
75 | >
76 | > Nh >= 2h/2 * N0
77 | >
78 | > Nh >= 2h/2
79 |
80 | 높이가 h이고 노드 갯수가 n인 AVL트리를 가정했을 때,
81 |
82 | "h <= c·log2N"을 증명
83 |
84 | ```txt
85 | N >= Nh >= 2h/2
86 | h / 2 <= log2N
87 | h <= 2·log2N
88 |
89 | ∴ h = O(logN)
90 | ```
91 |
--------------------------------------------------------------------------------
/notes/lecture29-balanced-binary-search-tree03.md:
--------------------------------------------------------------------------------
1 | # 균형이진탐색트리(Balanced BST) - AVL 트리 삽입 연산
2 |
3 | > 본 포스팅은 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 중 29강 "[균형이진탐색트리 - AVL 트리 삽입 연산](https://youtube.com/watch?v=KkgN2xAzmG8)" 강의를 정리한 노트입니다.
4 |
5 | ## AVL(Adelson-Velsky, Londis) 트리 구현
6 |
7 | ### AVL 트리에 새 노드 삽입
8 |
9 | - AVL 트리: 모든 노드에 대해서, 노드의 왼쪽 서브트리와 오른쪽 서브트리의 높이차 <= 1
10 |
11 | - class Node: BST 와 동일 <- 각 노드에는 key, left, right, parent에 height 변수 추가
12 | - class BST: 사용. insert, deleteByMerging, deleteByCopying, search
13 | - class AVL(BST)
14 |
15 | > insert, delete 함수는 연산에 따라 height가 변하므로 수행시 height 정보를 업데이트 해야 한다.
16 |
17 | ```py
18 | class AVL(BST):
19 | def insert(self, key):
20 | super(AVL, self).insert(key)
21 | v = super(AVL, self).insert(key) # step.1
22 | .
23 | .
24 | ```
25 |
26 | - `AVL` 클래스에 `__init__`이 없으면 부모 클래스 `BST`의 생성자가 호출된다.
27 | - `insert`는 BST와 똑같이 동작한 후, 높이차 조건에 위배되는 경우에는 조치를 취한다.
28 | - 위배되는 경우를 바로잡는 것을 rebalance라고 한다.
29 |
30 | > `super` 키워드: 첫번째인자(클래스)의 객체(self)의 부모를 호출
31 |
32 | ```txt
33 | # insert(9)
34 |
35 | 5 <- z
36 | \
37 | 7 <- y
38 | \
39 | 9 <- x 또는 v
40 | ```
41 |
42 | - 9: 삽입된 새로운 값. v라고 부름
43 | - 5: v로부터 올라가다가 AVL 트리의 조건을 만족하지 않는 첫번째 노드. z라고 부름
44 | - 7: z의 자식노드. y라고 부른다. y의 자식노드인 v는 x라고 부른다.
45 |
46 | > rebalance(x,y,z)
47 |
48 | z에서 밸런스가 깨졌을 때, y는 z의 자식, x는 y의 자식이다.
49 |
50 | ### insert
51 |
52 | > insert(self, key):
53 |
54 | 1. `v = super(AVL, self).insert(key)`
55 | 2. `find x, y, z`
56 | 3. `w = rebalance(x, y, z)`. w는 원래 z 위치에 있었던 노드
57 | 4. `if w.parent == Node: self.root = w`
58 |
59 | `insert` 이후의 두 가지 케이스
60 |
61 | 1. z, y, x가 왼쪽 자식으로만 구성되거나, 오른쪽 자식으로만 구성되는 경우
62 | 2. z, y, x가 일직선이 아니고 각각 다른 방향의 자식일 경우, 세 점이 삼각형 모양으로 되는 경우.
63 |
64 | ### rebalance
65 |
66 | 1. z, y, x가 왼쪽으로 구성된 경우: `rotateRight(z)`
67 | 2. z, y, x가 일직선이 아니고 각각 왼쪽자식, 오른쪽 자식일 경우: `rotateLeft(y)`, `rotateRight(z)`
68 |
69 | ### 수행시간
70 |
71 | 1. `v = super(AVL, self).insert(key)` -> O(h) = O(logN)
72 | 2. `find x, y, z` -> O(h)
73 | 3. `w = rebalance(x, y, z)` -> O(1)
74 | 4. `if w.parent == Node: self.root = w` -> O(1)
75 |
76 | 삽입에는 O(logN) 시간이 소요된다.
77 |
--------------------------------------------------------------------------------
/notes/lecture30-balanced-binary-search-tree04.md:
--------------------------------------------------------------------------------
1 | # 균형이진탐색트리(Balanced BST) - AVL 트리 삭제연산
2 |
3 | > 본 포스팅은 한국외대 컴퓨터공학부 신찬수 교수님의 "[자료구조 - Data Structures with Python](https://www.youtube.com/playlist?list=PLsMufJgu5933ZkBCHS7bQTx0bncjwi4PK)" 중 30강 "[균형이진탐색트리 - AVL 트리 삭제연산](https://www.youtube.com/watch?v=W3uPlSCzAZM)" 강의를 정리한 노트입니다.
4 |
5 | ## AVL(Adelson-Velsky, Londis) 트리 구현
6 |
7 | ### AVL 트리의 노드 삭제
8 |
9 | ```txt
10 | # 1. u 삭제(불균형 상태)
11 |
12 | o
13 | / \
14 | o z
15 | /| / \
16 | oo y o
17 | || / \ /
18 | oo x o u
19 | || |\
20 | oo oo
21 | ||
22 | oo
23 |
24 | # 2. rotateRight(z)
25 |
26 | o
27 | / \
28 | o y
29 | /| / \
30 | oo x z
31 | || |\ / \
32 | oo oo o o
33 | ||
34 | oo
35 | ||
36 | oo
37 | ```
38 |
39 | x y z를 정할 때는 height가 높은 쪽으로 간다. `rotateRight(z)`는 z의 부모노드(이 경우 root)의 균형을 깨뜨리게 된다. 이렇게 균형을 맞추려 하면 계속 부모의 균형에 영향을 주므로, 부모로 올라가면서 계속 맞춰야 한다.
40 |
41 | `insert`의 경우에는 1회 또는 2회면 가능했으나 `delete`는 최악의 경우 모든 높이 레벨에서 다 회전을 수행해야 한다. O(logN) 회전이 필요할 수 있다.
42 |
43 | ### delete 구현
44 |
45 | ```py
46 | delete(self, u):
47 | v = super(AVL, self).deleteByCopying(u)
48 | # v는 u를 삭제함으로써 균형이 깨질 수 있는(가능성이 있는) 가장 깊은 곳의 노드
49 | # 앞에서 살펴본 케이스에서는 u의 부모였던 노드가 v로 반환
50 | # 반환된 v로부터 균형이 깨진 노드를 다시 찾음
51 | # 강의 노트도 참고할 것
52 | while v != None:
53 | if v is_not_balanced:
54 | z = v
55 | if z.left.height >= z.right.height:
56 | y = z.left
57 | else:
58 | y = z.right
59 | if y.left.height >= y.right.height:
60 | x = y.left
61 | else:
62 | x = y.right
63 | v = rebalance(x, y, z) #
64 | w = v # w는 예전에 v가 있었던 자리. 즉 v의 자식
65 | v = v.parent
66 | # while을 빠져나오면 v == None, w == root
67 | selft.root = w
68 | ```
69 |
70 | ### delete 수행시간
71 |
72 | - 높이: <= 2logN : O(logN)
73 | - insert: O(logN)
74 | - 노드 삽입 : O(logN)
75 | - rebalance: 1회 또는 2회 회전 O(1)
76 | - delete
77 | - 노드 제거: O(logN)
78 | - rebalance: 매 level에서 O(logN) 회전
79 |
80 | AVL트리는 insert, delete 를 모두 O(logN)만에 수행하는 균형이진 탐색트리의 대표적인 예 중 하나이다.
81 |
--------------------------------------------------------------------------------