본문 바로가기

코딩 및 기타/어서와! 자료구조와 알고리즘

재귀 알고리즘 주요 예제

728x90

팩토리얼 (factorial, 계승)

∙ 팩토리얼

- 20보다 같거나 작은 자연수 N이 입력으로 주어질 경우, 1 * 2 * 3* ... *. (N-1)*N 을 계산하는 팩토리얼 함수를 작성하시오

#include <iostream>

using namespace std;

// factorial(20)이 값이 크기 때문에 int형으로 불가능
long long factorial(int n)
{
    if (n == 0)
        return 1;
    else
        return n * factorial(n - 1);
}

int main()
{
    cout << factorial(5) << endl; // 120
    cout << factorial(10) << endl; // 3628800
    cout << factorial(20) << endl; // 2432902008176640000
}

 

 

피보나치 수

∙ 피보나치 수 (Fibonacci numbers)

- 첫째 및 둘째 항이 1이며 그 뒤의 모든 항은 바로 앞 두 항의 합인 수열

ex) 1, 1, 2, 3, 5, 8, 13, 21, 34, 55,...

- 편의상 0번째 항을 0으로 설정하기도 함

#include <iostream>

using namespace std;

long long fibo(int n)
{
    if (n <= 1)
        return n;
    else
        return fibo(n - 1) + fibo(n - 2);
}

int main()
{
    for (int i = 1; i <= 10; i++){
        cout << fibo(i) << ", ";
    }
    cout << endl;
    
    // 40번째 항 출력
    cout << fibo(40) << endl;
    
    // 50번째 항 출력 -> 출력이 늦게 나옴
    // 중복되는 부분 문제로 인해 계산 효율이 떨어짐
    // 캐시를 사용하여 문제를 해결할 수 있음
    cout << fibo(50) << endl;
    
}

 

문자열 뒤집기

∙ 문자열 뒤집기

- 문자열의 각 문자 순서를 역순으로 변경

#include <iostream>
#include <string>

using namespace std;

string reverse(const string& str)
{
    if (str.length() == 0)
        return "";
    else
        return reverse(str.substr(1)) + str[0];
}

int main()
{
    cout << reverse("Hello") << endl;
}

 

 

최대 공약수와 최소 공배수

∙ 최대 공약수

- gcd(a,b) : 두 개의 자연수 a와 b가 있을 때, a와 b 모두의 약수 중에서 가장 큰 정수

- 유클리드 알고리즘을 이용하여 재귀적으로 구할 수 있음

ex) gcd(24, 18) => gcd(18, 6) => gcd(6, 0) => 6

ex) gcd(18, 24) => gcd(24, 18) => gcd(18, 6) => gcd(6, 0) => 6

 

∙ 최소 공배수

- lcm(a, b) : 두 정수 a와 b가 있을 때, a와 b로 모두 나누어 떨어지는 가장 작은 정수

- 두 정수의 곱은 두 정수의 최대 공약수와 최소 공배수의 곱과 같다는 성질을 이용하여 구할 수 있음

 

#include <iostream>
// C++ 17버전부터 사용가능
#include <numeric>

// 최대 공약수
int gcd(int a, int b)
{
    if (b == 0)
        return a;
    else
        return gcd(b, a % b);
}

// 최소 공배수
int lcm(int a, int b)
{
    return a * b / gcd(a,b);
}

int main()
{
    // 정의된 함수 사용
    int gcd1 = gcd(10, 15);
    int lcm1 = lcm(10, 15);
    
    // STL numeric 사용
    int gcd2 = std::gcd(10, 15);
    int lcm2 = std::lcm(10, 15);
    
    std::cout << gcd1 << ", " << gcd2 << std::endl;
    std::cout << lcm1 << ", " << lcm2 << std::endl;
}

 

 

순열

∙ 순열 (permutation)

- n개의 원소로 구성된 집합이 있을 경우, 모든 원소를 서로 다른 순서로 나열하는 순열 방법을 모두 출력하시오

- 순열 재귀 함수 구현 방법

1. 첫 번째 원소를 모든 원소와 각각 맞바꾸기(swap)

2. 첫 번째 원소를 제외한 나머지 원소들의 집합으로 순열 구하기

#include <algorithm>
#include <iostream>
#include <vector>

using namespace std;

template <typename T>
void print_vector(const vector<T>& vec)
{
    for (const auto& e : vec)
        cout << e << " ";
    cout << endl;
}

void permutation(vector<int>& vec, int k)
{
    if (k == vec.size() - 1) {
        print_vector(vec);
        return;
    }

    for (int i = k; i < vec.size(); i++) {
        swap(vec[k], vec[i]);
        permutation(vec, k + 1);
        swap(vec[k], vec[i]);  // 순서 원복
    }
}

int main()
{
    // 재귀 알고리즘 사용 예제
    vector<int> vec {1, 2, 3, 4};
    permutation(vec, 0);

    // std::next_permutation() 사용 예제
    sort(vec.begin(), vec.end());

    do {
        print_vector(vec);
    } while (next_permutation(vec.begin(), vec.end()));
}

 

하노이의 탑

∙ 하노이의 탑

- 퍼즐의 일종

- 세 개의 기둥과 이 기둥에 꽂을 수 있는 크기가 다양한 원판들이 있고, 처음에는 하나의 기둥에 원판들이 작은 것이 위에 있도록 순서대로 쌓여 있음

- 다음 조건을 만족시키면서 원판들이 다른 기둥으로 옮겨서 다시 쌓아야 함

1. 한 번에 하나의 원판만 옮길 수 있다.

2. 기둥의 맨 위에 있는 원판을 다른 기둥의 맨 위로 옮길 수 있다.

3. 큰 원판이 작은 원판 위에 있어서는 안된다.

 

- 하노이의 탑 이동 순서 구하기

> 하노이 탑의 세 기둥을 차례대로 1번, 2번, 3번이라고 할 경우 처음 1번 기둥에 n개의 원판이 있음

> n은 15이하의 자연수이며, 입력으로 주어짐

> 1번 기둥의 모든 원판을 3번 기둥으로 최소 횟수로 옮기는 방법을 출력하시오

#include <vector>
#include <iostream>

using namespace std;

void hanoi(int n, int from, int to, int by)
{
    if (n == 1) {
        cout << from << " -> " << to << endl;
    } else {
        hanoi(n - 1, from, by, to);
        cout << from << " -> " << to << endl;
        hanoi(n - 1, by, to, from);
    }
}

int main()
{
    //hanoi(2, 1, 3, 2);
    hanoi(3, 1, 3, 2);
}

 

'코딩 및 기타 > 어서와! 자료구조와 알고리즘' 카테고리의 다른 글

16 일차  (0) 2023.08.24
15 일차  (0) 2023.08.24
14 일차  (0) 2023.08.22
13 일차  (0) 2023.08.21
12일차  (0) 2023.08.20