Professional Documents
Culture Documents
• 해석
--------------------2~3주차. Kotlin-------------------- - println(o1) 명령어는 println(o1.toString()) 과 동일
■ Variable& Data type - 즉, 클래스에 특별히 정의해두지 않아도 toString() 에서
• 변수 선언: var, val 는 멤버변수 ‘이름=값’의 형태로 만들어진 String을 리턴
• Data types 해주는 동작을 수행
- Number: Double, Float, Long, Int, Short, Byte - TestDataClass는 상속받은 클래스가 존재하지 않음에도
- Character: Char, String 불구하고, toString()을 포함한 몇 가지 멤버함수들이 존재
- Other: Boolean, Array
• NULL • Why? 왜 이런 특이한 Data Class 라는 게 존재할까?
- 객체지향프로그래밍 (OOP) 관점에서 클래스 = {멤버변수,
■ Conditional statement 멤버함수} 라고 볼 수 있으며, 결국 대부분의 ‘객체’는
• if-else 문 서로 밀접한 값/데이터를 멤버변수로 모아놓은 것
- else 문은 필요시에만 작성 -> 이러한 객체들을 사용하는 개발자들이 반복적으로 만들
• if – else if - else 던 함수들이 존재
• when -> else 문 -> 이러한 함수들을 기본적으로 포함하는 클래스를 제공하
- 뒤에는 반드시 1문장 또는 { … } 면 편리
■ Interface
• Abstract class와 유사하게, ‘미완성’인 함수와 그렇지 - Time과 Space는 “두 마리 토끼”같은 존재이나, 둘 모
않은 함수들을 가짐, 함수들이 기본적으로 미완성이라는 두 놓치는 많은 알고리즘이 존재
전제가 깔려있어서, abstract 키워드를 붙이지 않아도 됨 (둘 중의 하나라도 확실히 이점을 가지거나, 적절히 균형을 맞춘
알고리즘들이 널리 사용됨)
■ Class 상속
• Kotlin에서는, 일반 class (추상 클래스, Interface가 아닌) ■ Big O notation
는 기본적으로 상속이 불가능 (final 클래스라고 지칭) • 알고리즘의 효율을 표현하는 표기법 중 하나
•‘open’ 키워드를 통해 상속 가능하도록 만들 수 있음 • Big O: 상한선 표기
(해당 알고리즘 실행 효율의 최악의 case를 표기)
■ Getter/Setter
■ 문제풀이
1. Data class 는 객체 생성을 할 수 없는 특수한 클래스이
다. X
■ Time Complexity
2. Abstract class 는 객체 생성을 할 수 없는 특수한 클래
• Constant time
스이다. O
- 데이터양에 관계없이 동일한 실행시간
3. Abstract class 의 변수들 중에 abstract 키워드가 붙은
- 리스트의 첫 번째 item이 존재할 경우에만 출력
변수의 초기값을 설정할 수 있다. X
- Time Complexity: O(1)
4. Interface 는 초기값이 주어진 멤버변수를 가질 수 없다.
O
• Linear time
- 데이터양이 증가함에 따라 실행시간도 선형 비례 증가
----------3주차. Complexity, Linked List, Stack----------
- Time Complexity: O(n) (n: 데이터 개수)
■ Complexity Dimensions
- 참고: O(2n + 3) à O(2n) à O(n) 이라고 표기
• Dimension ‘Time’
·Time complexity: Expensive 알고리즘 (Time 효율이 안
• Quadratic time
좋은)은 데이터양이 많아짐에 따라 “cost” (실행에 걸리
- 데이터양이 증가함에 따라 실행시간도 “제곱”(n
는 시간) 크게 증가
squared) 비례 증가
-‘실행을 마치기까지 걸리는 시간’이라고 지칭할 수
- Time Complexity: O(n^m)
있긴 하지만, 엄밀히 말하면 시간을 지칭한다기보다는
- 예시: 아래 프로그램의 경우, multiplicationBoard 함수는
operation의 횟수(수량)를 기반으로 실행시간을 정량화한
것 O( ) (n: 데이터 개수)
■ Space Complexity
• removeAfter
• 정수 정렬 알고리즘: Space complexity O(1)
- 임의 위치의 item을 삭제(및 값 리턴)
• Space complexity를 줄이기 위해, Time complexity를 희
생하기도 함.
• Time complexity
■ 문제 풀이
1. 아래 프로그램의 Time complexity를 Big O notation으로
나타내보자.
정답: O( )
■ 요약
- Time complexity와 Space complexity는 알고리즘의
scalability에 대한 measure이다.
- Time complexity: Expensive 알고리즘 (Time 효율이 안
좋은)은 데이터 양이 많아짐에 따라 “cost” (실행에 걸리
는 시간) 크게 증가
- Time complexity가 작다고 해서, 무조건적으로 실행시간
이 짧게 되지는 않는다.
- Space complexity: 알고리즘이 사용하는 메모리
■ Array
• Array: 동일한 유형의 “값”이 순서대로 모인 집합
• Q> 만약, removeAt(Int) 함수가 있었다면, removeAt 함수 • 양방향 Linked List
의 복잡도는? O(n)
• printInReverse
- item들을 역순으로 화면에 출력하는 함수
- Time complexity = O(n)
• getMiddle
- 순서상 가운데에 위치한 item을 화면에 출력하는 함수
- Time complexity = O(n)
• reversed
- item들을 역순으로 가진 새로운 LikedList를 생성해서 리
턴하는 함수
- Time complexity = O(n)
• mergeSorted
- A객체에서 B객체에 대하여, B객체의 item들 사이에 A객
체의 item들을 (대소비교하여) 적절한 위치에 삽입한 결과
(즉, merge한 결과)를 가지는 새로운 LinkedList 객체를 리
턴
• 배열에 만약 push() 함수가 존재한다면, time complexity - Time complexity = O(m+n)
가 어떨까? (참고: push() 함수는 맨 앞에 Node 1개를 추가 (m: # of nodes (this)
해주는 함수) n: # of nodes (otherList))
-> Array: “동일한 크기를 가진 item”들이 “메모리 상에
서 실제로 연속”된다는 특징 ■ Stack
-> time complexity: O(n) • LIFO: Last-in First-out
• push, pop의 Time complexity: O(1)
• 존재하는 top 위치의 item 을 pop하지 않고 엿보는(?) 함
수
• 괄호 개수가 짝이 맞는지 체크 하는 함수
-> Time complexity: O(n) (n: 주어진 문자열의 길이)
■ 문제 풀이 • Sealed 클래스 (interface로도 가능)
1. inkedList에서 제일 뒤에 item을 추가하는 작업의 Time - 여러 다른 클래스를 묶어서 사용할 목적의 클래스
complexity는 O(n) 이다. (X) - Sealed class 를 상속받은 클래스들을 묶음
-> 추가하는 작업은 tail의 next에 할당해 주기만 하면 된다. 그래 - 어떤 클래스들이 sealed class를 상속받았는지를 명시적
서 답은 O(1) 으로 천명하는 효과
2. Stack의 pop() 기능의 Time complexity는 O(1) 이다. (O) - 상속받는 클래스들은 같은 파일에 존재해야만 함
3. Stack 에 가장 나중에 넣은 item은 가장 먼저 나오게 된 - 추상 클래스이므로 객체 생성이 불가능(생성자는 기본적
다. (O) 으로 private)
-`sealed` 키워드를 사용해서 정의
■ 요약 • Enum class
- LinkedList는 임의 위치의 item들에 대한 접근을 허용함 상수들의 모음
- LinkedList는 Node들을 순서에 따라서 가지고 있는 구조 Q. “그냥 사용하고자 하는 상수(const val)를 만들어 쓰면
- Stack은 item을 넣는 위치와 빼는 위치가 동일 되는 거 아닌가?”
- Stack에 가장 먼저 넣은 item이 가장 나중에 나옴 A. “ㅇㅇ 맞음. 근데 불편”
밀접한 상수들을 모아서 관리하자!
• Inner class
• 함수 ‘also’: ‘let’과 매우 유사하나, 객체 자신 리턴
- Nested 클래스와 달리, Outer 객체 생성해야 함
- (참고) 객체의 멤버변수 수정 시, 원본의 값이 변함
- Nested 클래스와는 달리, ‘inner’ 키워드를 사용하여
클래스가 정의됨
• 함수 ‘apply’
- Nested 클래스와는 달리, Outer class에 접근 가능
-‘also’와 유사하게 객체 자신을 리턴
- 심지어, private으로 보호되는 member 변수/함수에게도,
-‘run’처럼 주어진 객체를 this로 표기
companion object에게도 접근 가능
(즉, 소속된 객체에 대한 접근은 불가능)
■ 요약 • Queue: using LinkedList
- Companion object는 Outer 클래스의 ‘동반자’ - LinkedList 기반의 Queue
- Outer 객체 생성 없이도 사용 가능
- Companion object 자체도 별도의 생성 없이 사용 가능
- Nested 클래스는 거의 남남
- Inner 클래스는 Outer 객체 생성해야 함
- 한 클래스 안에 여러 버전의 생성자를 만들 수 있음
■ Queue
• FIFO (First-In First-Out)
• 종류 별로 alias 존재
- Class에 대한 type
• Queue를 반대로 출력하고 싶다면?
(예) typealias NodeSet = Set<Tree.Node>
- Stack에 Queue 내부의 값을 모두 push하고 다시 반대로
(예) typealias FileTable<T> = MutableMap<T, MutableList<File>>
pop한 값을 enque하면 됨
(예) typealias ManInner = Man.Inner
Inner 클래스를 ‘지칭’할 수 있음 (객체를 생성한 것이 아니므
■ Stack vs. Queue 로, Outer 클래스의 이름인 ‘Man’을 사용하여 Man.Inner 라고
• Q> Queue와 Stack의 차이점은? 지칭)
- Queue는 FIFO이고, Stack인 LIFO이다. - Function에 대한 type
• Q> 실생활에서 Queue, Stack 개념에 해당되는 예시? (예) typealias TheFunc = (Int, String, Any) -> Unit
- Stack: 총 탄창, 그림판에서 ‘되돌리기’ 리턴 타입이 Unit 인 것은, 리턴 값이 없다는 의미
- Queue: 자동차 세차장, 영화관에서 표 사려고 줄서기 (예) typealias TheFunc<T> = (T) -> Boolean
■ Deque ■ forEach
• Deque (double-ended queue) • 주어진 item sequence (예: List, Collection 등) 객체에서
- 덱(deck)이라고 발음 item 들을 하나씩 꺼내주는 구문
- Queue와 유사, 양쪽 끝 부분에서 삽입/삭제가 모두 가능
(Queue와 Stack의 특징을 모두 가지고 있다고도 볼 수 있음) • return@NAME
- 구현한 방식이 Array 기반인지, List 기반인지에 따라서 -> return@forEach: for문의 continue와 비슷
한 쪽 방향에 item들이 꽉 차게 되면 새로운 공간을 할당 -> return@run: for문의 break로 활용하고 싶을 때 사용
할 지 여부가 결정됨
■ Elvis operator
• Deque을 이용한 알고리즘 문제 풀기 - 기호 ?: 를 사용하여, 앞에 등장한 객체가 null일 경우에
- Sliding window 방식 취할 default 값을 명시할 수 있음
: Window를 왼쪽에서 오른쪽으로 한 칸씩 옮긴다는 것은,
기존 Window에서 맨 왼쪽 item을 제거하고 맨 오른쪽에 ■ Naming convention
새로운 item을 추가하는 것 - Class: Camel case 사용, 첫 글자는 대문자이고 나머지는
à Deque로 구현 가능 소문자이되, 단어 시작도 대문자
- 1번째 풀이 - Function, Variable: Camel case 사용 (단, 첫 글자는 소문
Sliding window 이 옮겨갈 때마다(O(N)), Deque 안의 L개의 자)
item 에 대하여 최소값을 찾기 (O(L)) - Constant: 모두 대문자이고, 단어 사이를 언더바(_)로 구
à 따라서, 결국 O(NL) 분
- 2번째 풀이: “최소값 Chain” 이용하기
맨 왼쪽부터 sliding window 를 옮겨가면서 (O(N)) 최소값의 ■ Java import
index (위치)를 chain 으로 관리 à 따라서, 결국 O(N) - 코틀린에서 Java 패키지를 import 가능
- Java에서도 코틀린 패키지 사용 가능
■ 문제 풀이
1. 코틀린 ArrayList를 사용해서 Queue를 구현하면, ■ 요약
enqueue 기능의 Time complexity는 항상 O(n) 이다. (X) - Type alias를 통해 자주 사용되는 타입을 손쉽게 다룰 수
2. Ring buffer 구조를 사용하여 Queue를 구현하면, 있다.
LinkedList의 장점을 취하면서도 LinkedList의 단점이었던 - Elvis operator를 이용하여 단순 null 체크 하는 코드를
“부가적인 작업으로 인한 load”를 감소할 수 있게 된다. 단순화할 수 있다.
(O) - 코틀린은 기본적으로 Java 언어와 유사한 Naming
convention을 따른다. (Camel case 사용)
- 코틀린에서 Java 패키지를 import 하여 사용할 수 있다.
■ Traversal algorithms ■ Binary search trees
• (Tree의 Node에는 값 O)값을 write 하거나 read 하기 위 • Binary Search Tree (BST)
해서는, 해당 노드(노드 위치)를 찾기 위해 Tree의 Node들 - 빠른 lookup (찾기), insertion (삽입), removal (삭제)
을 어떤 순서로 visit(access) 할지 결정해야 함 - 조건: Left child < Parent < Right child
• Pre-order traversal
- 동작은 DFT와 동일함 ■ BinaryNode::isBinarySearchTree
- 자신을 ‘visit’ 한다. - 지금까지 살펴본 Binary Search Tree (BST)는 멤버변수로
- Left child node를 재귀적으로 traverse 한다. BinaryNode(root 노드)를 가지고 있으며, insert, remove 등
- Right child node를 재귀적으로 traverse 한다. 의 함수를 제공하되 BST로서의 구조를 유지하도록 구현되
어 있었음
• Post-order traversal -> 따라서, BinaryNode로 이루어진 Binary Tree도 BST로서
- Left child node 를 재귀적으로 traverse 한다. 의 구조를 가지고 있을 가능성이 있음
- Right child node 를 재귀적으로 traverse 한다.
-> BinaryNode 클래스 안에, 이를 체크하는 함수의 Time
- 자신을 ‘visit’ 한다.
complexity = O(n)
-> 개선 후 Time Complexity = O(1) • Trie: Collection(sequence가 가까운 표현) 형태로 저장될
수 있는 데이터를 저장하는 특수한 Tree
• lists property
- Trie에 들어있는 모든 word들을 모은 collection을 리턴
- `lists’에 대한 getter 를 정의할 때, 내부 함수를 사용하
여 얻은 결과값(리턴값)을 그대로 리턴하도록 구현
• count property
- Trie에 존재하는 Unique word 개수를 리턴하는 property
• isEmpty property
- Trie에 word가 존재하는지 여부를 리턴하는 property
중간고사까지의 과제(퀴즈) 5. (단반향)Linked List에서 맨 앞 노드(head), 맨 뒤 노드
(tail)를 직접 접근이 가능한 상황일 때 Linked List를 사용
1. 구구단 2단부터 9단까지를 화면에 출력하려고 한다. 이 해서 Stack을 구현할 경우, 틀린 것은? ①
를 for { ... for { ... } ... }와 같이 중첩 for문으로 구현하였 ① Linked List의 맨 앞에 push, pop이 되도록 구현하면
다. ‘곱셈’ opreration을 complexity 대상으로 생각했을 O(n)이 되므로 비효율적이다.
때, 이 코드의 Time complexity는? -> 둘 다 O(1)이므로 비효율적이라 할 수 없다.
-> 정답: O( ) ② Push되는 아이템 개수가 갑작스럽게 커지는 경우가 많
을 경우에는, Array 기반의 Stack보다는 Linked List 기반의
2. Linked List에 대한 설명으로 틀린 것은? ② Stack이 메모리를 재할당하지 않아도 된다는 면에서 장점
① 맨 앞에 노드를 1개 추가하는 것은 O(1)이다. 을 지닐 것이다.
② 맨 뒤(tail) 노드가 주어져 있을 경우, 이 노드 뒤에 새 ③ Linked List의 맨 뒤에 push, pop이 되도록 구현이 가능
로운 노드를 1개 추가하는 것은 O(n)이다. 할 것이다.
-> O(n)이 아니라 O(1) ④ Array 기반의 Stack보다 전반적으로 메모리 공간을 더
③ 주어진 임의의 노드의 뒤에 새로운 노드 1개를 추가하 많이 소비하게 될 것이다.
는 것은 O(1)이다.
④ 임의의 i번째에 위치한 노드에 접근하는 것은 O(i)(또는, 6. Linked List를 사용하여 Queue를 구현하였다. 특히,
O(n))이다. Linked List의 head 부분에서 dequeue가 수행되도록 구현
하였다. 이러한 Queue의 Time complexity 중에 옳지 않은
3. Linked List와 Array에 대한 설명으로 틀린 것은? ② 것은? ②
① (단방향) Linked List의 맨 뒤(tail)에 있는 노드를 삭제하 ① Queue가 비어있는지 여부 체크: O(1)
는 것은 O(n)이다. ② Queue의 가장 앞쪽(dequeue 하는 곳)에 있는 아이템 엿
4. Array를 사용하여 구현된 Stack에 대한 설명으로 틀린 ② Ring buffer의 최대 용량을 넘어서는 개수의 element
것은? ① 저장은 불가능할 것이다.
① Stack에 새로운 아이템을 추가하는 작업은 O(n)이다. ③ 동일한 개수의 element를 저장하는 Queue에 대하여,
-> O(n)이 아니라 O(1) Ring buffer에 비해 Linked List를 사용했을 때 더 적은 양