import java.util.Scanner;
publicclassMain{
publicintDFS(int n){
if(n == 1) return1;
elsereturn n * DFS(n - 1);
}
publicstaticvoidmain(String[] args){
Main T = new Main();
System.out.println(T.DFS(5));
return ;
}
}
Note) 실행 결과 : 120
- 5! = 5 * 4 * 3 * 2 * 1의 결과인 120이 출력된다.
4. 피보나치 재귀 (메모이제이션)
java
열기
import java.util.Scanner;
publicclassMain{
staticint [] fibo;
publicintfibonacci(int n){
if(n == 1 || n == 2) return fibo[n] = 1;
elsereturn fibo[n] = fibonacci(n - 2) + fibonacci(n - 1);
}
publicstaticvoidmain(String[] args){
Main T = new Main();
int n = 10;
fibo = newint[n + 1];
T.fibonacci(n);
for(int i = 1; i <= n ; ++i){
System.out.print(fibo[i] + " ");
}
return ;
}
}
Note) 실행 결과: 1 1 2 3 5 8 13 21 34 55
- 배열과 for문으로 짜는 것이 재귀함수로 돌리는 것보다 성능이 좋다. (재귀를 돌리기에는 무겁다.)
-> 재귀는 호출될 때마다 스택에 프레임이 생기기 때문이다.
- 중소기업의 코딩테스트 질문으로 많이 나오기도 한다.
- static int 배열을 만들어 배열에 기록해두고 출력할 수 있다.
- 기록해두는 것을 "메모이제이션"이라고 한다.
- 메모이제이션을 활용하면 시간 복잡도가 확 줄어든다.
5. 이진트리순회(DFS : Depth-First Search)
Note)
- 전위 순회: 부모 -> 왼쪽 자식 -> 오른쪽 자식
- 중위 순회:왼쪽 자식 -> 부모 -> 오른쪽 자식
- 후위 순회:왼쪽 자식 ->오른쪽 자식 ->부모
(1) 전위순회
java
열기
import java.util.*;
classNode{
int data;
Node lt, rt;
publicNode(int val){
data = val;
lt = rt = null; //객체가 생성되는 순간 null 값
}
}
publicclassMain{
Node root;
publicvoidDFS(Node root){
if(root == null) return;
else{
System.out.print(root.data + " ");
DFS(root.lt);
DFS(root.rt);
}
}
publicstaticvoidmain(String[] args){
Main tree = new Main();
tree.root = new Node(1);
tree.root.lt = new Node(2);
tree.root.rt = new Node(3);
tree.root.lt.lt = new Node(4);
tree.root.lt.rt = new Node(5);
tree.root.rt.lt = new Node(6);
tree.root.rt.rt = new Node(7);
tree.DFS(tree.root);
}
}
Note) 전위순회 실행 결과 : 1 2 4 5 3 6 7
(2) 중위 순회
java
열기
import java.util.*;
classNode{
int data;
Node lt, rt;
publicNode(int val){
data = val;
lt = rt = null; //객체가 생성되는 순간 null 값
}
}
publicclassMain{
Node root;
publicvoidDFS(Node root){
if(root == null) return;
else{
DFS(root.lt);
System.out.print(root.data + " ");
DFS(root.rt);
}
}
publicstaticvoidmain(String[] args){
Main tree = new Main();
tree.root = new Node(1);
tree.root.lt = new Node(2);
tree.root.rt = new Node(3);
tree.root.lt.lt = new Node(4);
tree.root.lt.rt = new Node(5);
tree.root.rt.lt = new Node(6);
tree.root.rt.rt = new Node(7);
tree.DFS(tree.root);
}
}
Note) 중위순회 실행 결과 : 4 2 5 1 6 3 7
(3) 후위순회
java
열기
import java.util.*;
classNode{
int data;
Node lt, rt;
publicNode(int val){
data = val;
lt = rt = null;
}
}
publicclassMain{
Node root;
publicvoidDFS(Node root){
if(root == null) return;
else{
DFS(root.lt);
DFS(root.rt);
System.out.print(root.data + " ");
}
}
publicstaticvoidmain(String[] args){
Main tree = new Main();
tree.root = new Node(1);
tree.root.lt = new Node(2);
tree.root.rt = new Node(3);
tree.root.lt.lt = new Node(4);
tree.root.lt.rt = new Node(5);
tree.root.rt.lt = new Node(6);
tree.root.rt.rt = new Node(7);
tree.DFS(tree.root);
}
}
Note) 후위순회 실행 결과 : 4 5 2 6 7 3 1
6. 부분집합 구하기 (DFS)
java
열기
import java.util.*;
publicclassMain{
staticint n;
staticint[] ch;
publicvoidDFS(int L){
if(L == n + 1){
String tmp = "";
for(int i = 1; i <= n; ++i){
if(ch[i] == 1) tmp += (i + " ");
// 자동으로 string형 변환
}
if(tmp.length() > 0) System.out.println(tmp);
}else{
ch[L] = 1; // 사용한다는 의미로 '1'표시
DFS(L + 1); // 왼쪽으로 뻗는다.
ch[L] = 0;
DFS(L + 1); // 오른쪽으로 뻗는다.
}
}
publicstaticvoidmain(String[] args){
Main T = new Main();
n = 3;
ch = newint[n + 1];
T.DFS(1);
}
}
Note) 실행 결과
- 재귀함수, back tracking, BFS 코딩 테스트에 자주 나오니 꼭 해봐야한다.
????
7. 이진트리 레벨탐색(BFS : Breadth-First Search)
java
열기
import java.util.*;
classNode{
int data;
Node lt, rt;
publicNode(int val){
data = val;
lt = rt = null;
}
}
publicclassMain{
Node root;
publicvoidBFS(Node root){
Queue<Node> Q = new LinkedList<>();
Q.offer(root);
int L = 0; // level while(!Q.isEmpty()){
int len = Q.size();
System.out.print(L + " : ");
for(int i = 0 ; i < len; ++i){
Node cur = Q.poll();
System.out.print(cur.data + " ");
if(cur.lt != null) Q.offer(cur.lt);
if(cur.rt != null) Q.offer(cur.rt);
}
L++; // 레벨 증가
System.out.println();
}
}
publicstaticvoidmain(String[] args){
Main tree = new Main();
tree.root = new Node(1);
tree.root.lt = new Node(2);
tree.root.rt = new Node(3);
tree.root.lt.lt = new Node(4);
tree.root.lt.rt = new Node(5);
tree.root.rt.lt = new Node(6);
tree.root.rt.rt = new Node(7);
tree.BFS(tree.root);
}
}
Note)실행 결과
- 너비 우선 탐색 (BFS)
- 0 레벨 : 1 ( root) / 1레벨 : 2 3 (root에서 한 번에 이동 가능) / 3레벨: 4 5 6 7 (root에서 두 번 만에 간다.)
-> 즉, root로부터의 거리라고 볼 수 있다.
- 다음 문제인 송아지 찾기 이해하려면 꼭 알아야한다.
8. 송아지 찾기1(BFS - 상태 트리 탐색)
java
열기
import java.util.*;
publicclassMain{
int answer = 0;
int [] dis = {1, -1, 5}; // 전진, 후진int[] ch; //방문 여부 체크 배열 , 0이면 방문하지 않은 곳
Queue<Integer> Q = new LinkedList<>();
publicintBFS(int s, int e){
ch = newint[10001]; // 직선 좌표의 최댓값 10000
ch[s] = 1;
Q.offer(s);
int L = 0; // root node는 level이 0while(!Q.isEmpty()){
int len = Q.size(); // level에 있는 원소의 개수for(int i = 0; i < len; ++i){
int x = Q.poll();
for(int j = 0; j < 3; ++j){ //int nx = x + dis[j]; // x의 자식노드를 찾는다.if(nx == e) return L + 1;
// 자식 노드니까 1 더하기 if(1 <= nx && nx <= 10000 && ch[nx] == 0 ){
ch[nx] = 1; // 방문 체크
Q.offer(nx); // 체크 후 Q에 넣는다.
}
}
}
L++; // level 증가
}
return0;
}
publicstaticvoidmain(String[] args){
Main T = new Main();
Scanner in=new Scanner(System.in);
int s = in.nextInt(); // 현수의 위치int e = in.nextInt(); // 송아지의 위치
System.out.println(T.BFS(s, e));
return ;
}
}
Note)
- 레벨은 점프 횟수라고 볼 수 있다.
- 최초로 발견되는 것이 최단거리
- 중복되는 수는 없다.
- 3level에서 최초로 14가 발견된다.
9. Tree 말단 노드까지의 가장 짧은 경로 (DFS)
java
열기
import java.util.*;
classNode{
int data;
Node lt, rt;
publicNode(int val){
data = val;
lt = rt = null; //객체가 생성되는 순간 null 값
}
}
publicclassMain{
Node root;
publicintDFS(int L, Node root){
// root가 가리키는 말단 노드, level, 이동거리if(root.lt == null && root.rt == null) return L;
//root가 말단 노드인지 확인elsereturn Math.min(DFS(L + 1, root.lt), DFS(L + 1, root.rt));
// 말단 노드가 아닐 때
}
publicstaticvoidmain(String[] args){
Main tree = new Main();
tree.root = new Node(1);
tree.root.lt = new Node(2);
tree.root.rt = new Node(3);
tree.root.lt.lt = new Node(4);
tree.root.lt.rt = new Node(5);
System.out.println(tree.DFS(0, tree.root));
}
}
Note) 실행 결과: 1
- 경로의 길이 : 루트 -> 말단 노드까지 가는데 이동하는 횟수, (간선의 개수)
- 최단거리문제 -> BFS로 푼다.
-> DFS로 풀 수는 있지만 제약이 있다. (자식 노드가 하나만 있을 때 에러가 나기 때문이다.)
10. Tree 말단 노드까지의 가장 짧은 경로 (BFS)
java
열기
import java.util.*;
classNode{
int data;
Node lt, rt;
publicNode(int val){
data = val;
lt = rt = null; //객체가 생성되는 순간 null 값
}
}
publicclassMain{
Node root;
publicintBFS(int L, Node root){
Queue<Node> Q = new LinkedList<>();
Q.offer(root);
int L = 0;
while(!Q.isEmpty()){
int len = 0;
for(int i = 0; i < len; ++i){
Node cur = Q.poll();
if(cur.lt == null && cur.rt == null )return L;
if(cur.lt != null) Q.offer(cur.lt); // 왼쪽 자식이 있을 때 넣어준다. if(cur.rt != null) Q.add(cur.rt); //add offer 상관없음
}
++L;
}
return0;
}
publicstaticvoidmain(String[] args){
Main tree = new Main();
tree.root = new Node(1);
tree.root.lt = new Node(2);
tree.root.rt = new Node(3);
tree.root.lt.lt = new Node(4);
tree.root.lt.rt = new Node(5);
System.out.println(tree.BFS(0, tree.root));
}
}
Note) 실행 결과 : 1
11. 그래프와 인접행렬
- G(V, E) : G:그래프, V: vertax, E: edge
- 인접행렬: 연결된 노드에 대해 1로 표시, 연결되있지 않으면 0으로 표시한다.
1) 무방향 그래프
- 방향 개념이 없다. - graph[a][b]와 graph[b][a] 에 모두 1 표시한다.
2) 방향 그래프
- 이동하는 방향이 정해져 있다.
- 행 -> 열로 해석한다.
-
3) 가중지 방향 그래프
- 간선에 가중치 까지 있다. (ex) 비용)
- graph[a][b] = 3 : a -> b로 가는데 가중치는 3이다.
12. 경로 탐색 (DFS) - 방향그래프가 주어지면 1번 -> N번 정점으로 가는 모든 경우의 수 출력
- 1번 노드 -> 5번까지 가는 경로
- 12345
- 125
- 13425
- 1345
- 1425
- 145
java
열기
import java.util.*;
publicclassMain{
staticint n, m , answer = 0;
staticint[][] graph;
staticint[] ch; // 방문 여부 체크publicvoidDFS(int v){
if (v == n) answer++;
else{ // 가능한 곳으로 계속 뻗어 나간다.for(int i = 1; i <= n ; ++i){
if(graph[v][i] == 1 && ch[i] == 0){
ch[i] = 1;
DFS(i);
ch[i] = 0; // 취소 (체크를 풀어준다.)
}
}
}
}
publicstaticvoidmain(String[] args){
Main T = new Main();
Scanner in=new Scanner(System.in);
n = in.nextInt();
m = in.nextInt();
graph = newint[n+1][n+1]; // 인덱스 1 ~ n까지 쓰려고
ch = newint[n+1];
for(int i = 0; i < m; ++i){
int a = in.nextInt();
int b = in.nextInt();
graph[a][b] = 1; // 방향 그래프
}
ch[1] = 1; // 1번 노드 출발점 표시
T.DFS(1); //
System.out.println(answer);
}
}
Note) 실행 결과
- 경로에서 방문 했던 곳은 방문하지 않는다.
- 노드 개수만큼 for문이 돈다.
- back할 때는 체크를 하나씩 풀어준다.
13. 경로탐색 (인접리스트, ArrayList)
- 1번 노드 -> 5번까지 가는 경로
- 12345
- 125
- 13425
- 1345
- 1425
- 145
// 오류
5 9 1 2 1 3 1 4 2 1 2 3 2 5 3 4 4 2 4 5
java
열기
package first;
import java.util.*;
publicclassMain3{
staticint n, m, answer=0;
static ArrayList<ArrayList<Integer>> graph;
staticint[] ch;
publicvoidDFS(int v){
if(v==n) answer++;
else{
for(int nv : graph.get(v)){
if(ch[nv]==0){
ch[nv]=1;
DFS(nv);
ch[nv]=0;
}
}
}
}
publicstaticvoidmain(String[] args){
Main T = new Main();
Scanner kb = new Scanner(System.in);
n=kb.nextInt();
m=kb.nextInt();
graph = new ArrayList<ArrayList<Integer>>();
for(int i=0; i<=n; i++){
graph.add(new ArrayList<Integer>());
}
ch=newint[n+1];
for(int i=0; i<m; i++){
int a=kb.nextInt();
int b=kb.nextInt();
graph.get(a).add(b);
}
ch[1]=1;
T.DFS(1);
System.out.println(answer);
}
}
Note)
- 정점의 개수가 많으면 ArrayList<>를 이용한다.
14. 그래프 최단거리(BFS) - 그래프에서 1번 정점에서 각 정점으로 가는 최소 이동 간건 수를 출력
java
열기
import java.util.*;
classMain{
staticint n, m, answer=0;
static ArrayList<ArrayList<Integer>> graph;
staticint[] ch, dis;
publicvoidBFS(int v){
ch[v]=1; // 1번에서 시작
dis[v]=0; // 출발 지점: 0
Queue<Integer> queue=new LinkedList<>();
queue.offer(v);
while(!queue.isEmpty()){
int cv=queue.poll(); //cv: current vertax (현재 지점)for(int nv : graph.get(cv)){
if(ch[nv]==0){ // 방문 안 했으면
ch[nv]=1;
queue.offer(nv);
dis[nv]=dis[cv]+1;
}
}
}
}
publicstaticvoidmain(String[] args){
Main T = new Main();
Scanner kb = new Scanner(System.in);
n=kb.nextInt(); // 정점 개수
m=kb.nextInt(); // 간선 개수
graph=new ArrayList<ArrayList<Integer>>();
for(int i=0; i<=n; i++){
graph.add(new ArrayList<Integer>());
}
ch=newint[n+1];
dis=newint[n+1];
for(int i=0; i<m; i++){ // 인접 list 만든다.int a=kb.nextInt();
int b=kb.nextInt();
graph.get(a).add(b);
}
T.BFS(1); // 1번 부터 시작한다.for(int i=2; i<=n; i++){ // 2번 부터 출력
System.out.println(i+" : "+dis[i]);
}
}
}