입력으로 It is time to study 라는 문자열이 들어오면

가장 긴 단어를 출력하는 유형 

 

 split()

String[] s = str.split(" ");

매개변수로 기준을 정해준다. 지금은 띄어쓰기를 기준으로 한다.

int m = Integer.MIN_VALUE;
for(String x : s){
	int len = x.length();
    if(len>m){
    	m = len;
        answer = x;
        }
}

m은 최소값으로 설정해두고

for each 문을 이용하여 탐색한다. 

for each문을 쓸 수 있는 경우는 부모가 Collection인 경우에 가능하다.

(Set, List, 등등 Map은 아니지만 keySet()같은 함수를 쓴다면 가능)

지금은 String배열이기 때문에 각각 요소를 length함수를 써서 길이를 구하고

m보다 크면 m을 len으로 초기화하고 그때 요소를 answer에 담는다.

 

indexof()

파라미터로 들어온 문자의 인덱스를 반환한다.(첫 번째로 만난) 

못찾은 경우 -1 반환

두번째 파라미터에 시작위치를 지정할 수 있다.

 

substring()

문자열을 자를때 쓰는 함수이다.

파라미터로

(시작위치)

(시작위치, 끝위치)

정할 수 있다. 끝위치 -1 까지의 값이 반환된다.

'알고리즘 with 자바 > 알고리즘' 카테고리의 다른 글

순열  (0) 2021.09.15
멱집합  (0) 2021.09.13
Recursion의 응용 : n queen problem  (0) 2021.09.09
Recursion의 응용 : Counting Cells in a Bob  (0) 2021.09.09
Recursion의 개념과 기본 예제들 3  (0) 2021.09.08

임의의 집합 data에 대해서 원소들의 모든 가능한 순열을 출력하라

 

data = {a,b,c,d} 

4! = 24개

 

a b c d

a b d c

a c b d

a d b c

. . . . 

 

a를 고정 - b,c,d의 모든 순열에 a를 앞에 추가하여 출력

b를 고정 - a,c,d의 모든 순열에 b를 앞에 추가하여 출력

. . . . 

a,b를 고정 - c,d의 모든 순열에 a,b를 앞에 추가하여 출력

. . . 

a,b,c를 고정 - d에 a,b,c를 앞에 추가하여 출력

 

void printPerm(a prefix string, a set S)
{
	if S is 0
    	print the pre fix string;
    else
    	for each element x in S
        	printPerm(the prefix string + x, S-{x});
}

S의 모든 순열들을 생성한 후 각각에 prefix String을 앞에 붙여서 출력한다.

 

int data[] = {'a','b','c','d'};
int n = 4;

void perm(int k) {
	if(k==n) {
    	print data[0...n-1];
        return;
    }
    
    for(int i=k; i<n; i++) {
    	swap(data, k, i);
        perm(data, k+1; n);
        swap(data, k, i);
        }
    }

perm이라는 함수의 미션은 매개변수 k를 받아서 data[a..k-1]prefix로 정하고 data[k..n]으로 만들수 있는 모든 순열을

프린트하되, 배열 data에 저장된 값들의 순서는 그대로 유지한다.

 


 

멱집합(순서무관)과 순열 나열(순서상관)의 응용

 

배낭문제 

-무게와 가격이 정해진 n개의 아이템과 용량이 w이 배낭이 있다.

배낭의 용량을 초과하지 않으면서 가격의 합이 최대가 되도록 아이템을 배낭에 넣으려면?

 

TSP

- 서울 대전 대구 부산 광주 강릉 속초 등의 n개의 도시들이 있을때

한 도시에서 출발하여 모든 도시들을 지나서 다시 집으로 돌아올때 방문 순서에따라

이동 거리가 달라지는데 길이가 가장 짧은 투어를 고르는 문제.

 

 

임의의 집합 data의 모든 부분집합을 출력하라

 

data = {a,b,c,d} 

 

공집합

a

b

c

d

a b

a c

a d

b c . . . 

2의 4(원소갯수)승 = 16개

 

{a,b,c,d,e,f}의 모든 부분집합을 나열하려면

a를 제외한 {b,c,d,e,f}의 모든 부분집합들을 나열하고

{b,c,d,e,f}의 모든 부분집합에 {a}를 추가한 집합들을 나열한다. 

 

powerSet(S)
if s is an empty set
	print nothing;
else
	let t be the first element of s;
    find all subsets of s-[t] by calling powerSet(s-{t});
    print the subsets;
    print thet subsets with adding t;

Pseudo code

 

입력으로 하나의 집합 s가 주어진다.

s의 멱집합을 출력하는 것이 미션이다.

 

base case 

만약 s가 공집합이라면 공집합을 출력한다.

 

그렇지 않다면

 

집합s에서 한 원소t에 대해서 t를 제외한 집합의 부분집합을 구한다.

그 부분집합을 출력하고

제외했던 원소를 추가하여 출력한다

 

s-{t}의 부분집합을 구한 후 그 부분집합을 한번은 그대로 출력하고 한번은 t를 추가해 출력하려면

powerSet함수는 여러개의 집합들을 return 해야한다. 어떻게 해야할까

 

powerSet(P,S)
if S is an empty set
	print p;
else 
	let t be the first element of S;
    powerSet(P, S-{t});
    powerSet(Pu{t}, S-{t});

처음 호출할때 P를 공집합 S가 전체데이터 집합 이라면 

S의 멱집합을 구한 후 각각에 집합 P를 합집합하여 출력하도록 설계해야한다.

 

base case

만약 s가 공집합이라면 집합 P 출력

그렇지 않다면 t에 대해서 s에서 t를 제외하여 호출하고

t를 포함하여 P에 합쳐두어 출력하게 만든다.

 

함수가 두 개의 집합을 매개변수로 받도록 설계해야한다는 의미이다.

두 개의 집합의 모든 부분집합들에 첫접째 집합을 합집합 하여 출력한다.

 

두 개의 집합으로 나뉘게되면 

집합 S : k번째 부터 마지막 원소까지 연속된 원소들이다.

집합 P : 처음부터 k-1째 원소들 중 일부이다.

 

boolean배열을 사용하여 

집합 P를 구현할 수 있다.

 

private static char data[] = {'a','b''c','d','e','f'};
private static int n = data.length;
private static boolean [] include = new boolean[n];

public static void powerSet(int k) {
	if(k==n){
    	for(int i =0; i<n; i++)
        	if(include[i]) System.out.print(data[i] + " ");
        System.out.println();
        retrun;
	}
include[k] = false;
powerSet(k+1);
include[k] = true;
powerSet(k+1);
}

실제 코드는 이렇게 된다.

 

k는 매개변수로 넘겨주고

배열은 전역변수로 어디에서든 읽을 수 있게한다.

 

매개변수 k는 전체집합에서 k번째 원소부터 나머지 원소가 집합s이고

나머지 원소들 중 include가 true 인 원소들만 포함시켜 출력하라는 의미이다.

 

base case는 s가 공집합이라면 p를 출력한다. == 배열 include값이 true인 원소들을 출력한다.

 

일반적인 경우에는 

data[k] 를 포함하지 않는경우와

data[k] 를 포함하는 경우로 나누어서 재귀를 돌린다.

 

https://www.youtube.com/watch?v=nkeMRRIVW9s&list=PL52K_8WQO5oUuH06MLOrah4h05TZ4n38l&index=7

상태공간 트리로 a,b,c의 모든 멱집합을 구하는 과정을 시각화 한 것이다.

 

a를 포함하는 경우와 안하는경우 

b를 포함하는 경우와 안하는 경우 이런식으로 아래로 내려간다.

 

 

 

 

- 입력으로 n을 받는다.

- n*n 2차월 배열을 생성

- n개의 퀸을 놓을때 어떠한 행,열,대각에 겹치는 퀸이 없게 놓는 방법.

- 퀸은 대각,행,열 한방향으로 끝까지 이동 가능

 

Backtracking

내가 결정을 하며 내려가다가 안된다는 것이 분명해 지면 내가 최근에 내린 결정을 번복하고

다른 결정을 내리는 알고리즘

상태 공간 트리를 깊이 우선 방식으로 탐색하여 해를 찾는 알고리즘을 말한다.

 

상태 공간 트리

 

https://www.youtube.com/watch?v=xKGbWC-DPT4&list=PL52K_8WQO5oUuH06MLOrah4h05TZ4n38l&index=6

 

상태공간트리란 ? 찾는 해를 포함하는 트리

즉 해가 존재한다면 그것은 반드시 이 트리의 어떤 한 노드에 해당함

따라서 이 트리를 체계적으로 탐색하면 해를 구할 수 있음.

 

상태공간 트리의 모든 노드를 탐색해야 하는 것은 아니다.

 


Design Recursion

 

return type queens(arguments)
{
    if non-promising
    	report failure and return;
    else if sucess
    	report answer and return;
    else
    	visit children recursively;
}

 

내가 어떤 노드에 도착하면 제일먼저 생각해야할 것은

 

이미 더이상 갈 필요가 없는 노드인가 아닌가를 생각해야한다.

아니라면 되돌아가면 그만이다.

 

두번째로 지금 도착한 노드가 내가 찾고있던 그 노드인가를 확인한다.

 

그렇지 않다면 

이 노드의 자식노드들을 방문해보는것이다.

 

int[] cols = new int [N+1];
boolean queens(int level)
{
    if non-promising
    	report failure and return;
    else if sucess
    	report answer and return;
    else
    	visit children recursively;
}

어떤 매개변수를 넘겨줄 것인가?

내가 현재 어떤 노드인지 알아야 하기때문에 깊이 정보를 넘겨준다.

 

2차원 배열을 사용할 필요가 없다.

 

cols[1] : 1번 말이 놓인 열 번호

cols[2] : 2번 말이 놓인 열 번호

cols[level] : level번째 말이 놓인 열 번호

 

int[] cols = new int [N+1];
boolean queens(int level)
{
    if(!promising(level))
        return false;
    else if (level == N)
    	return true;
    for(int i = 1; i<=N; i++){
    	cols[level+1] = i;
        if(queens(level+1))
        	return true;
       }
       return false;
}

level + 1 값을 재호출한다.

'알고리즘 with 자바 > 알고리즘' 카테고리의 다른 글

순열  (0) 2021.09.15
멱집합  (0) 2021.09.13
Recursion의 응용 : Counting Cells in a Bob  (0) 2021.09.09
Recursion의 개념과 기본 예제들 3  (0) 2021.09.08
Recursion의 개념과 기본 예제들 2  (0) 2021.09.08

- 입력으로 하나의 Binary 이미지를 받는다.

- 각 픽셀은 background pixel 이거나 혹은 image pixel

- 서로 연결된 image pixel들의 집합을 blob이라고 부름

- 상하좌우 및 대각방향으로도 연결된 것으로 간주

 

https://www.youtube.com/watch?v=HHJFlVT1tBw&list=PL52K_8WQO5oUuH06MLOrah4h05TZ4n38l&index=5

입력 : n*n 크기의 2차원 그리드 , 하나의 좌표(x,y)

 

출력 : 픽셀 (x,y)가 포함된 blob의 크기, (x,y)가 어떤 blob에도 속하지 않는 경우는 0

 


 

Recursive Thinking

 

현재 픽셀이 이 속한 blob의 크기를 카운트 하려면

    현재 픽셀이 image color가 아니라면

        0을반환

    현재 픽셀이 image color 라면

        먼저 현재 픽셀을 카운트한다.

        현재 픽셀이 중복 체크되는 것을 방지하기 위해 다른 색으로 칠한다.

        현재 픽셀에 이웃한 모든 픽셀들에 대해서 

            그 픽셀이 속한 blob의 크기를 카운트하여 카운터에 더해준다.

        카운터를 반환한다.

 

	private static void dfs(int x,int y) {
		
		if(x < 0 || x >= n || y < 0 || y >= n) {
			return;
		}
		
		visited[x][y] = true;
		count++;
		
		for(int i=0; i<8; i++) {
			int nx = x+dx[i];
			int ny = y+dy[i];
			
			if(nx < 0 || nx >= n || ny < 0 || ny >= n)
				continue;
			if(arr[nx][ny]==1&&!visited[nx][ny]) {
				dfs(nx,ny);
			}
		}
	}

직접 짜본 코드이다. 강의 보다 더 보기쉽게 구현하였다.  

순환적 알고리즘 설계

 

적어도 하나의 base case가 있어야한다.

모든 case는 결국 base case로 수렴해야 한다.

 


 

암시적 매겨변수를 

명시적 매개변수로 바꾸어라 

이 말이 무슨 뜻일까?

 

순차 탐색을 예로 들어보자.

int search(int [] data, int n, int target) {
	for(int i=0; i<n; i++)
    	if(data[i] == target)
        	return i;
    return -1;
}

 

이 함수의 미션은 data[0] ~ data[n-1] 사이에서 target을 검색하는 것이다.

하지만 검색 구간의 시작 인덱스 0은 보통 생략한다. 즉 암시적 매개변수이다.

 

일반적으로 recursion을 사용하지 않는 함수에서는 이렇게 암시적으로 생략할 수 있다면 

생략하는 것이 코드의 간결성 관점에서 좋다.

하지만 recursion 프로그래밍을 할때는 일반적으로 저렇게 하지 않는 것이 좋다.

 

int search(int [] data, int begin, int end, int target){
	if(begin>end)
    	return -1;
    else if(target==items[begin])
    	return begin;
    else
    	return search(data, begin+1, end, target);
}

이 코드는 위의 코드를 recursive하게 변경한 코드이다.

 

배열 데이터가 있고 target의 인덱스를 찾고 싶다면 

이전 코드에는 순서대로 검사 후 찾아내는 절차적 프로그래밍 이라면

 

첫번째 값이 타겟이라면 찾은것이고 그 경우는 첫번째 값의 인덱스를 리턴하고

아니라면 나머지 구간을 검색하면 된다.

 

두 코드의 차이는 검색할 구간의 명시이다.

두번째 코드는 시작위치 마저 명시되어있다. 이게 더좋은 이유는

다시 자기자신을 호출할때 다른 일을 하게 만들어야 하기때문에 

그렇게 설계해야만 한다. 맨처음의 일만 생각하여 매개변수를 넣으면

안된다.

 

거꾸로 검색해도 같은 결과를 얻을 수 있다.

그때는 begin을 고정으로 두고 end값을 줄여가며 검색할 수 있다.

 


int search(int [] data, int begin, int end, int target){
	if(begin>end)
    	return -1;
    else{
    	int middle = (begin+end)/2;
        if(data[middle]==target)
        	return middle;
        int index = search(data, begin, middle-1, target);
        if(index != -1)
        	return index;
        else
        	return search(data, middle+1, end, target);
        }
}

순차 탐색의 다른 버전이다.

이진 탐색과는 다르다.

시작점과 끝점의 가운데 위치가 타겟이라면 리턴하고

아니라면 시작점과 중간점-1로 두고 검사한다.

 


매개변수의 명시화 : 최대값 찾기

int findMax(int [] data, int begin, int end){
	if(begin==end)
    	return data[begin];
    else
    	return Math.max(data[begin], findMax(data,begin+1, end);
}

일반적으로 우리가 데이터들이 어떤 배열에 저장되어있을때

최대값을 찾을때의 함수의 매개변수는 일반적으로

findMax(int[] data, int n) 이렇게 설계한다.

recursion을 이용한다면 배열 데이터에 관심있는 데이터의 시작위치와 끝 위치의 매개변수가 필요하다.

 


Binary Search

 

public static int binarySearch(String[] items, String target, int begin, int  end){
	if(begin>end)
    	return -1;
    else{
    	int middle = (begin+end)/2;
        int compResult = target.compareTo(items[middle]);
        if(comResult == 0)
        	return middle;
        else if(compResult<0)
        	return binarySerach(items, target, begin, middle-1);
        else
        	return binarySearch(items, target, middle+1 end);
        }
}

이진 탐색이란 데이터들이 크기순으로 정렬되어있을때 적용할 수 있는 검색방법이다.

먼저 가운데 데이터를 비교하여 동일하다면 리턴하고

그보다 크다면 시작점을 중간점 이후로 이동한다.

그보다 작다면 종료점을 중간점 이전으로 이동한다.

 

이과정을 recursive하게 동작하면 된다.

 

이진 탐색은 그 자체로 recursive하다.

 


 

순환알고리즘을 설계하는데에 있어서 암시적 매개변수를 명시적 매개변수로 바꾸는 것이 중요하다.

Recursive Thinking 

순환적으로 사고하기!

 

Recursion은 수학 함수 계산에만 유용할까?

 

수학함수뿐 아니라 다른 많은 문제들을 

recursion으로 해결할 수 있다.

 

for문 대신에 recursion을 통해 해결 할 수도 있다.

 


문자열의 길이 계산

 

라이브러리 함수를 써도 되지만 

라이브러리 함수는 어떻게 동작할까?

 

앞에서부터 문자하나하나씩 세면 된다.

이것이 절차적인 프로그램이라고도 이야기할 수 있다.

 

for문 같은 반복문을 통해 해결 할 수 있지만

 

문자열의 길이가 0이라면 리턴하고(base case)

그렇지 않다면 

원래 문자열에서 첫번째 문자열을 제거한 그 문자열의 길이를 제거하고

1을 더하면 된다.

 

public static int length(String str){
	if(str.eqauls(""))
    	return 0;
    else
    	return 1+length(str.substring(1));
}

이렇게 구현할 수 있다.

 


문자열의 프린트

 

public static void printChars(String str){
	if(str.length()==0)
    	return;
    else{
    	System.out.print(str.charAt(0));
        printChars(str.substring(1));
       	}
}

위와 같은 방식으로 프린트도 할 수 있다.

 


문자열의 뒤집어 프린트

 

public static void printCharsReverse(String str) {
	if(str.length()==0)
    	return;
    else{
    	printCharsReverse(str.substring(1));
        System.out.print(str.charAt(0));
        }
}

else문 안에서 순서만 바꾸어 주면 가능한 문제이다.

 


2진수로 변환하여 출력

 

 

public void printTnBinary(int n){
	if(n<2)
    	System.out.print(n);
    else{
    	printInBinary(n/2);
        System.out.print(n%2);
        }
}

음이 아닌 정수 n을 이진수로 변환하여 인쇄한다.

n을 2로 나눈 몫을 먼저 2진수로 변환하여 인쇄한 후

n을 2로 나눈 나머지를 인쇄한다.

 


배열의 합 구하기

 

public static int sum(int n, int [] data){
	if(n<=0)
    	return 0;
    else
    	return sum(n-1, data) + data[n-1];
}

data[0]에서 data[n-1]까지의 합을 구하여 반환한다.

 


데이터파일로 부터 n개의 정수 읽어오기

 

public void readFrom(int n, int [] data, Scanner in){
	if(n==0)
    	return;
    else{
    	readFrom(n-1,data,in);
        data[n-1] = in.nextInt();
        }
}

 

일반적으로 모든 순환함수는 반복문으로 변경 가능하다.

그 역도 성립한다. 즉 모든 반복문은 recursion으로 표현 가능하다.

순환함수는 복잡한 알고리즘을 단순하고 알기쉽게 표현하는 것을 가능하게 한다.

하지만 함수 호출에 따른 오버헤드가 있다.

'알고리즘 with 자바 > 알고리즘' 카테고리의 다른 글

Recursion의 응용 : Counting Cells in a Bob  (0) 2021.09.09
Recursion의 개념과 기본 예제들 3  (0) 2021.09.08
Recursion의 개념과 기본 예제들 1  (0) 2021.09.08
백트래킹  (0) 2021.09.04
DFS  (0) 2021.09.04

Recursion은 순환이라고 부르기도하고 재귀함수라고 부르기도 한다.

 

자기 자신을 호출하는 함수 func()이다.

void func()

{

    func();

}

어떤 함수가 자기 자신을 호출하면 어떻게 될까?

public static void func() {
	System.out.println("Hello")
    func();
 }

무한 루프에 빠지게 되는 것이 당연하다.

 

public static void func(int k){
	if(k <=0 )
    	return;
    else{
    	System.out.println("Hello");
        func(k-1);
        }
}

 

if문을 사용하여 탈출할 경우를 만들어주고 

호출할때 k-1을 넘겨주어 감소시켜준다면 멈출 수 있게 만들 수도 있다.

 

recursion이 항상 무한루프에 빠지는 것은 아니다.

 


 

무한 루프에 빠지지 않기 위해선?

 

적어도 하나의 다시 돌아가지 않아야 하는 경우가 존재해야한다.

이것을 Base case라고 부른다. 위의 코드의 if문이 Base case이다.

 

그렇다고 해서 종료 될 수 있을까?

 

단순히 Base case가 존재하므로 인해 종료 되는 것은 아니고

모든 경우들은 반복하다보면 base case로 수렴해야한다는 조건이다.

 

총 2가지의 조건이 만족되면 무한 루프에 빠지지 않는 recursion을 구현 할 수 있다.

 


public static int func(int n) {
	if(n==0)
    	return 0;
    else
    	return n + func(n-1);
}

1~n까지의 합을 recursion을 이용하여 쉽게 구현할 수 있다.

 

public static double gcd(int m, int n){
	if(m<n) {
    	int tmp=m;
        m=n;
        n=tmp;
    }
    if(m%n==0)
    	return n;
    else
    	return gcd(n, m%n);
}

정수론 시간에 배웠던 

최대공약수를 구하는 알고리즘인 유클리드 알고리즘이다.

 

첫번째 if문은 m이 더 큰수가 되도록 바꾸는 로직이고

두번째 if문은 m이 n으로 나누어떨어진다면 n을 리턴하고

그렇지 않다면 인자값을 n, m%n을 주며 재귀호출한다.

 

public static int gcd(int p, int q){
	if(q==0)
    	return p;
    else
    	return gcd(q,p%q);
}

이 코드는 좀더 단순한 유클리드 알고리즘이다.

'알고리즘 with 자바 > 알고리즘' 카테고리의 다른 글

Recursion의 개념과 기본 예제들 3  (0) 2021.09.08
Recursion의 개념과 기본 예제들 2  (0) 2021.09.08
백트래킹  (0) 2021.09.04
DFS  (0) 2021.09.04
BFS  (0) 2021.08.30

개념

 - 모든 경우의수를 확인해야 할때

  - for로는 확인 불가한 경우( 깊이가 달라질때 )

 

순열 : M개의 숫자 중 N개를 뽑을때 순서에 상관하여 뽑는 방식

 

3개에서 2개를 뽑는다면 for로 확인하면 된다.

 

백트래킹은? M, N이 주어졌을 경우!

재귀함수로 구현을 한다. 깊이를 마음대로 설정할 수 있게된다.

 

중요한 것은 특정 상태를 제외한다는 것이다.

 

백트래킹은 현재 상태에서 다음 상태로 나아갈 수 있는 모든 경우를 찾되, 그 중

유망하지 않다면 현재에서 더 이상 나아가지 않고 이전의 상태로 돌아가 다음 상태를 검사한다.

 

더이상 탐색할 필요가 없는 상태를 제외하는 것을 가지치기라고 한다.

 

 

 

'알고리즘 with 자바 > 알고리즘' 카테고리의 다른 글

Recursion의 개념과 기본 예제들 3  (0) 2021.09.08
Recursion의 개념과 기본 예제들 2  (0) 2021.09.08
Recursion의 개념과 기본 예제들 1  (0) 2021.09.08
DFS  (0) 2021.09.04
BFS  (0) 2021.08.30

BFS와 같이 그래프 탐색의 한 종류이다.

 

DFS : Depth-first search 깊이 우선 탐색 이라고 한다.

 

DFS는 스택으로 구현이 가능하고 재귀로도 구현이 가능하다.

 

 

DFS 동작 방식은

 

1 - 2 - 3 - 4 - 6 - 5 과 같이 동작한다.

 


재귀함수 

- 자기 자신을 다시 호출하는 함수

 

- 주의 할점

 - 재귀 함수가 종료되는 시점이 반드시 명시되어야한다.

 - 재귀함수의 깊이가 너무 깊어지면 스택 오버플로우 현상이 발생한다.

 

- DFS, 백트래킹에서 주로 사용

 

아이디어

 

- 시작점에 연결된 Vertex 찾기

 

- 연결된 Veertex를 계속해서 찾음

 

- 더이상 연결된 Vertex 없을경우 다음

 

시간 복잡도

 

- 시간복잡도란? : 알고리즘이 얼마나 오래걸리는지 나타내는것

 

-DFS : O(V+E)

'알고리즘 with 자바 > 알고리즘' 카테고리의 다른 글

Recursion의 개념과 기본 예제들 3  (0) 2021.09.08
Recursion의 개념과 기본 예제들 2  (0) 2021.09.08
Recursion의 개념과 기본 예제들 1  (0) 2021.09.08
백트래킹  (0) 2021.09.04
BFS  (0) 2021.08.30

+ Recent posts