## 알고리즘 - Computer Science 는 곧 컴퓨터로 문제를 해결하기 위한 알고리즘을 공부하는 것과 같음 - 유한한 시간 내에 유한한 저장 공간, 데이터를 가지고 문제를 푸는 절차 - 명령은 **모호하지 않고(unambiguous), (컴퓨터에서) 실행 가능해야 함(feasible)** - 분석, 구체화 → 알고리즘 짜기 → 코드로 구현 → 유지보수 <br> ## 반복문 - **Count-Controlled Loops** - 특정한 횟수만큼 반복하는 반복문 (for) - 시작하는 값으로 control 변수 초기화 - control 변수가 미리 정한 횟수와 같은지 - 아니라면 control 변수 증가 (보통 1 증가) - **Event-Cotrolled Loops** - 반복문 body 내에서 발생하는 이벤트에 의해 반복 횟수가 결정되는 반복문 (while) - 이벤트 초기화 - 이벤트 조건 확인 - 이벤트 업데이트 #### 제곱근 구하는 알고리즘 ![[square_root_pseuo_code.png|center|600]] ![[square_root_algorithm.png|center|600]] <br> ## 배열과 구조체 - 배열(Array) - 같은 데이터 타입의**(homogenenous)** 값을 가지는 집합 - 인덱스를 통해 접근, 인덱스는 0부터 시작함 - 구조체(Records) - 다른 데이터 타입의**(heterogeneous)** 값을 가지는 집합 - 이름을 통해 접근 - 서로 관련된 데이터들을 모아서 관리하는데 유용한 자료구조 <br> ## Searching Algorithms - Sequntial Search - 순차적으로 하나하나 다 비교해보기 - O(n) - **Binary Search** - (아이템들이 정렬되어 있는 경우) **==divide and conquer==** 를 이용함 - 중간값 K를 고른 후 내가 찾고자 하는 값보다 작으면 0~(K-1) 인덱스까지, 크면 (K+1)~N까지 다시 binary search를 수행함 → 찾고자 하는 값을 찾을 때까지 반복 - O(log n) ![[binary_search_pseudo_code.png|center|250]] ![[binary_search_sample.png|center|600]] <br> ## Sorting Algorithms ✨ 컴퓨팅에서는 미리 정렬을 해두면 검색에 쓰이는 시간을 드라마틱하게 줄일 수 있음 <br> #### Selection Sort(선택 정렬) ![[selection_sort.png|center|500]] - **정렬 방법** 1. 배열 순회 : 현재 순회 중인 위치부터 배열의 끝까지 중에서 가장 작은 값 찾기 2. 교환 : 찾은 최소값과 현재 순회 중인 위치의 원소를 swap 3. 다음 순회로 이동 :다음 위치로 이동하여 순회를 반복 처음 위치부터 N-1번째 위치까지 위의 과정을 반복하면 전체 배열이 정렬된다 - **시간 복잡도** - 선택 정렬의 시간 복잡도는 항상 **O(n^2)** - **특징** - 간단하게 구현 가능하지만, 배열의 크기가 큰 경우 손해 → 실제로는 잘 사용되지 않음 - in-place 정렬 : 별도의 메모리 공간을 사용하지 않음 - **슈도코드** ``` procedure selectionSort(arr: Array) n = length(arr) for i from 0 to n - 1 // 최소값을 찾기 위해 현재 위치부터 배열의 끝까지 검색 minIndex = i for j from i + 1 to n - 1 if arr[j] < arr[minIndex] minIndex = j // 최소값을 현재 위치와 교환 if i ≠ minIndex swap(arr[i], arr[minIndex]) end for end procedure ``` ![[selection_sort_pesudo_code.png]] <br> > **XOR swap** (temp 변수를 두지 않고 swap 하기) ``` def swapWithoutTemp(a, b): a = a ^ b b = a ^ b a = a ^ b return a, b # 사용 예시 x = 5 y = 10 x, y = swapWithoutTemp(x, y) print("x =", x) print("y =", y) ``` <br> #### Bubble Sort ![[bubble_sort.png|center|500]] - **정렬 방법** 1. **배열 순회:** 배열의 첫 번째 원소부터 시작하여 **인접한 두 원소**를 비교하기 2. **비교 및 교환:** 인접한 두 원소를 비교하여 순서가 바뀌어 있으면 교환 큰 값을 뒤로 보내는 방식이므로 오름차순 정렬의 경우에는 큰 값을 뒤로 보내는 것이고, 내림차순 정렬의 경우에는 작은 값을 뒤로 보냄 3. **반복:** 이러한 과정을 전체 배열이 정렬될 때까지 반복 한 번의 순회가 끝나면 가장 큰(또는 작은) 원소가 배열의 마지막으로 이동함 - **시간 복잡도** - 최악 O(n^2) - **특징** - 마찬가지로 배열의 크기가 커질수록 효율이 낮아짐 - **슈도코드** ``` procedure bubbleSort(arr: Array) n = length(arr) for i from 0 to n-1 // 각 순회에서 가장 큰 원소를 배열의 끝으로 이동 for j from 0 to n-i if arr[j] > arr[j+1] // 인접한 원소를 비교하여 순서가 바뀌어 있으면 교환 swap(arr[j], arr[j+1]) end if end for end for end procedure ``` ![[bubble_sort_pesudo_code.png]] <br> #### Insertion Sort ![[insertion_sort.png|center|500]] - **정렬 방법** 1. **배열 순회**: 첫 번째 원소(인덱스 0)는 이미 정렬된 것으로 간주하고 다음 원소부터 시작 해당 원소를 이미 정렬된 부분과 비교 2. **원소 삽입**: 정렬된 부분에서 적절한 위치를 찾아 현재 원소 삽입 3. **반복**: 정렬된 부분은 계속해서 확장되며, 모든 원소가 삽입될 때까지 반복됨 - **시간 복잡도** - 최선의 경우에도 O(n^2) - **특징** - 작은 크기의 데이터나 이미 정렬된 부분이 있는 경우에 성능이 우수함 - in-place 정렬 - **슈도코드** ``` procedure insertionSort(arr: Array) n = length(arr) for i from 1 to n-1 key = arr[i] j = i - 1 while j >= 0 and arr[j] > key // 현재 원소보다 큰 원소들을 오른쪽으로 이동 arr[j + 1] = arr[j] j = j - 1 // 현재 원소를 정렬된 부분 배열에 삽입 arr[j + 1] = key end for end procedure ``` ![[insertion_sort_pseudo_code.png|center|350]] <br> ## Recursive Algorithms **recursion** : 알고리즘이 스스로를 호출함. 일반적으로 현재 문제의 작은 버전으로 스스로를 호출함. - 재귀조건(general case)과 종료조건(base case)이 있어야 함 <br> #### Recursive Factorial - 재귀조건(general case) : N! = N * (N-1)! - 종료조건(base case) : 0! = 1 ![[recursion_factorial_pseudo_code.png|center|500]] <br> #### Recursive Binary Search ![[binary_search_pesudo_code.png|center|350]] ``` function binarySearch(arr: Array, target: Integer, low: Integer, high: Integer): Integer if low <= high mid = (low + high) / 2 // 중간 원소가 찾는 값인 경우 if arr[mid] == target return mid // 중간 원소보다 작으면 왼쪽 부분 배열에서 탐색 if arr[mid] > target return binarySearch(arr, target, low, mid - 1) // 중간 원소보다 크면 오른쪽 부분 배열에서 탐색 return binarySearch(arr, target, mid + 1, high) // 탐색 범위가 더 이상 없을 때 return -1 ``` - 함수의 매개변수로는 배열 `arr`, 찾고자 하는 값 `target`, 탐색 범위의 시작 인덱스 `low`, 그리고 탐색 범위의 끝 인덱스 `high`가 있음 <br><br> > **Divide and Conquer (분할 정복)** > >알고리즘 설계 패러다임 중 하나로, 큰 문제를 작은 부분 문제로 분할하고, 각각의 작은 부분 문제를 해결한 다음, 이 해답을 합쳐서 전체 문제를 해결하는 방법론 >이를 통해 전체적으로 문제를 해결하는 과정을 단순화하고, 재귀적으로 문제를 해결하는 특징을 갖고 있음 <br> #### Merge Sort - **정렬 방법** 1. **분할(Divide):** 주어진 배열을 중간 지점을 기준으로 반으로 나눈다 2. **정복(Conquer):** 나뉜 각 배열에 대해 재귀적으로 합병 정렬을 수행 각 배열은 더 이상 나눌 수 없을 정도로 작아질 때까지 계속 분할하고 정렬한다 3. **합병(Merge):** 정렬된 두 개의 하위 배열을 합병하여 하나의 정렬된 배열을 만든다 이때 두 배열의 요소를 비교하여 작은 값을 새로운 배열에 차례대로 넣음 - **시간 복잡도** - 모든 경우에 대해 O(n log n) - **특징** - 안정적이지만 **추가적인 메모리 공간**이 필요함 ``` <br> #### Quicksort ✨ ![[quicksort.png|center|400]] - **정렬 방법** 1. **피벗 선택:** 배열에서 하나의 원소를 선택하여 피벗으로 지정 피벗의 선택 방법은 다양한데, 보통은 배열의 중간 원소를 선택하거나 무작위로 선택한다 2. **파티션 분할:** 배열을 피벗을 기준으로 두 부분으로 분할 피벗보다 작은 값은 피벗의 왼쪽으로, 큰 값은 오른쪽으로 이동시킨다 3. **재귀적 정렬:** 분할된 두 부분에 대해 각각 재귀적으로 퀵소트를 수행함 이를 반복하면서 각 부분이 정렬됩니다. - **시간 복잡도** - 평균 O(n log2 n), 최악 O(n^2), - **특징** - 각 부분 배열은 이미 정렬되어 있으므로 합병 과정이 필요없음 - in-place sort - 특히 큰 데이터셋에 대해 빠른 정렬을 제공함 - 다만 입력 데이터에 따라 성능이 달라짐 (이미 정렬된 경우나 거의 정렬된 경우 성능이 떨어짐) - **슈도코드** ``` procedure quickSort(arr: Array, low: Integer, high: Integer) if low < high // 배열을 분할하고 pivot의 위치를 찾음 pivotIndex = partition(arr, low, high) // pivot을 기준으로 왼쪽과 오른쪽 부분 배열을 재귀적으로 정렬 quickSort(arr, low, pivotIndex - 1) quickSort(arr, pivotIndex + 1, high) procedure partition(arr: Array, low: Integer, high: Integer): Integer // 피벗 선택 (예: 배열의 가운데 원소) pivot = arr[(low + high) / 2] // low와 high를 가운데서 출발하여 교차할 때까지 원소를 찾아 위치 교환 while low <= high while arr[low] < pivot low = low + 1 while arr[high] > pivot high = high - 1 if low <= high swap(arr[low], arr[high]) low = low + 1 high = high - 1 return low ``` ![[quicksort_pseudo_code.png|center|400]] <br> #### 하노이탑 문제 ``` procedure hanoi(n, 출발기둥, 목표기둥, 보조기둥): if n == 1: // 이동할 원반의 개수가 1개일 경우, 직접 이동 수행 이동(출발기둥, 목표기둥) else: // 1. n-1개의 원반을 보조기둥으로 옮김 hanoi(n-1, 출발기둥, 보조기둥, 목표기둥) // 2. 남은 1개의 원반을 목표기둥으로 옮김 이동(출발기둥, 목표기둥) // 3. 보조기둥에 있는 n-1개의 원반을 목표기둥으로 옮김 hanoi(n-1, 보조기둥, 목표기둥, 출발기둥) procedure 이동(출발기둥, 목표기둥): // 실제로 원반을 옮기는 동작을 수행하는 함수 출력("원반을", 출발기둥, "에서", 목표기둥, "으로 이동") ``` 이 함수는 4개의 매개변수를 가진다. - 이동할 원반의 개수 `n` - 출발 기둥 `출발기둥` - 목표 기둥 `목표기둥` - 보조 기둥 `보조기둥` 만약 이동할 원반의 개수가 1개라면 직접 이동을 수행하고, 그렇지 않으면 다음 세 단계를 수행한다. 1. `n-1`개의 원반을 보조 기둥으로 옮김 2. 남은 1개의 원반을 목표 기둥으로 옮김 3. 보조 기둥에 있는 `n-1`개의 원반을 목표 기둥으로 옮김 `이동` 함수는 실제로 원반을 옮기는 동작을 수행한다. <br> <br> <br> <br>