//문제
자연수 N이 입력되면 재귀함수를 이용하여 1부터 N까지를 출력하는 프로그램을 작성하세요.
//입력
3
//출력
1 2 3
1.그냥 for문을 이용한 풀이 -> 생략
2.재귀 : 스택 프레임을 이용한 풀이
public class ex1_2 {
public static void solution(int n){
if(n==0) return;
else{
solution(n-1);
System.out.print(n + " ");
//재귀함수는 스택프레임을 사용한다.
//호출이 끝나면 전 함수로 복귀한다.
//빽 트래킹
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
solution(n);
}
}
풀이
1.재귀 함수를 만들어서 계속 호출한다.
2.이때 함수는 스택구조를 가진다.
//문제
10진수->2진수
//입력
11
//출력
1011
public class ex2 {
public static void DFS(int n){
if(n==0) return;
else{
DFS(n/2);
System.out.print(n%2);
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
DFS(n);
}
}
풀이
재귀를 이용한다. 2의 몫을 호출하고 호출이 끝나면 순차적으로 2로 나눈 나머지를 출력한다.
//문제
재귀를 이용한 팩토리얼
//입력
5
//출력
120
public class ex3 {
public static int factorial(int n){
if(n<=1) return n;
else return n * factorial(n-1);
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
System.out.println(factorial(n));
}
}
풀이
재귀 사용
//문제
//입력
//출력
1.기본 재귀 풀이
public class ex4 {
public static int fibonacci(int n){
if(n==1||n==2) return 1;
else {
int a = fibonacci(n-1) + fibonacci(n-2);
return a;
}
}
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
for(int i=1; i<=n; i++){
System.out.print(fibonacci(i) + " ");
}
}
}
풀이
기본적인 재귀를 이용한 피보나치 수열을 구하는 방식이다.
2.메모이제이션 + 스택 프레임 (중요)
public class ex4_2 {
static int[] fibo;
public static int fibonacci(int n){
//핵심 //fibo(배열)의 값은 0으로 초기화 되어있는 것을 이용
//해당 n값이 구해져있으면 그거 가져다 쓴다는 뜻.
if(fibo[n]>0) return fibo[n];
if(n==1||n==2) return fibo[n]=1;
else return fibo[n] = fibonacci(n-1) + fibonacci(n-2);
}
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
fibo=new int[n+1];
fibonacci(n);
for(int i=1; i<=n; i++){
System.out.print(fibo[i]+" ");
}
}
}
풀이
1.배열을 새로하나 만들어서 배열에 값을 기록해 그 배열의 값을 출력하는 방식 -> 시간이 좀 더 빨라짐
2.모든 배열은 선언되면 0으로 초기화되기 때문에 if(fibo[n]>0) return fibo[n]; 을 이용해서 이미 구해져있는 부분은 끌어다 사용 -> 완전 빨라짐
//문제
아래 그림과 같은 이진트리를 전위,중위,후위 순회를 연습해보세요.
//출력
전위순회 출력 : 1 2 4 5 3 6 7
중위순회 출력 : 4 2 5 1 6 3 7
후위순회 출력 : 4 5 2 6 7 3 1
class Node{
int data;
Node lt,rt; //node객체의 주소를 저장하는 변수
public Node(int val){
data=val;
lt=rt=null;
}
}
public class ex5 {
Node root;
public void DFS(Node root){
if(root==null) return;
else{
System.out.println(root.data + " "); -> 전위
DFS(root.lt);
System.out.println(root.data + " "); -> 중위
DFS(root.rt);
System.out.println(root.data + " "); -> 후위
}
}
public static void main(String[] args) {
ex5 tree = new ex5();
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);
}
}
풀이
1.기본베이스는 스택프레임을 활용한 재귀함수이다.
2.Node클래스를 만든다.
3.Node클래스에 그림과 같이 값을 삽입한다.
4.DFS()를 계속 호출해서 root가 없으면 빽트래킹을 시키고 아니라면 계속 재귀함수를 호출한다.
[ 주요기능 ]
스택 프레임의 구조를 잘 생각해서 출력하면 된다.
//문제
자연수 N이 주어지면 1부터 N까지의 원소를 갖는 집합의 부분집합을 모두 출력
//입력
3
//출력
1 2 3
1 2
1 3
1
2 3
2
3
class ex6 {
static int n;
static int[] ch;
public void DFS(int L){
if(L==n+1){
String tmp="";
for(int i=1; i<=n; i++){
if(ch[i]==1) tmp+=(i+" ");
}
if(tmp.length()>0) System.out.println(tmp);
}
else{
ch[L]=1;
DFS(L+1);
ch[L]=0;
DFS(L+1);
}
}
public static void main(String[] args){
ex6 T = new ex6();
n=3;
ch=new int[n+1];
T.DFS(1);
}
}
풀이
1.DFS()와 값을 저장할 ch[]배열을 만든다.
2.DFS()를 호출시켜서 입력되는 숫자 n의 크기를 넘을 때 빽트래킹을 하면서 체킹 해뒀던 ch[]의 값을 출력한다.
//문제
아래 그림과 같은 이진트리를 레벨탐색 연습하세요.
//출력
0 : 1
1 : 2 3
2 : 4 5 6 7
class Node2 {
int data;
Node2 lt,rt; //node객체의 주소를 저장하는 변수
public Node2(int val){
data=val;
lt=rt=null;
}
}
public class ex7 {
Node2 root;
public void BFS(Node2 root) {
Queue<Node2> Q = new LinkedList<>();
Q.offer(root);
int L=0;
while(!Q.isEmpty()){
int len=Q.size();
System.out.print(L + " : ");
for(int i=0; i<len; i++){
Node2 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();
}
}
public static void main(String[] args) {
ex7 tree = new ex7();
tree.root = new Node2(1);
tree.root.lt = new Node2(2);
tree.root.rt = new Node2(3);
tree.root.lt.lt = new Node2(4);
tree.root.lt.rt = new Node2(5);
tree.root.rt.lt = new Node2(6);
tree.root.rt.rt = new Node2(7);
tree.BFS(tree.root);
}
}
풀이
1.순회 문제와 같은 구조로 Node 클래스를 만들고 값을 삽입해서 만든다.
2.BFS()를 만들어 호출될 때마다 Q에 있는 값을 Q.poll()을 해주면서 그 Q.poll()되어서 나오는 값들의 lt,rt가 있으면 그것들을 Q.offer()해준다.
[ 주요 기능 ]
1.BFS는 기본적으로 Queue의 구조를 가진다.
//문제
현수는 스카이 콩콩을 타고 가는데 한 번의 점프로 앞으로 1, 뒤로 1, 앞으로 5를 이동할
수 있다. 최소 몇 번의 점프로 현수가 송아지의 위치까지 갈 수 있는지 구하라
//입력
5 14
//출력
3
public class ex8 {
public static int BFS(int s,int e){
int[] dis={1,-1,5};
int[] ch = new int[10001];
Queue<Integer> Q = new LinkedList<>();
ch[s] = 1;
Q.offer(s);
int L=0;
while(!Q.isEmpty()){
int len = Q.size();
for(int i=0; i<len; i++){
int x = Q.poll();
for(int j=0; j<3; j++){
int nx = x + dis[j];
if(nx==e) return L+1;
if(nx>=1 && nx<=10000 && ch[nx]==0){
ch[nx]= 1;
Q.offer(nx);
}
}
}
L++;
}
return L;
}
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int s = sc.nextInt();
int e = sc.nextInt();
System.out.println(BFS(s,e));
}
}
풀이
1.몇 번의 점프로 -> 최단거리 -> BFS
2.점프를 담은 배열 dis[], 중복 방지를 위해 값을 체크 할 ch[],길이를 리턴 할 L을 선언한다.
3.BFS원리인 Queue방식대로 돌리고 Q.poll할때 마다 자식노드를 넣는다. 이때 자식노드는 Q.poll()+dis[j]가 된다.
//문제
말단 노드까지의 가장 짧은 경로를 구하라. DFS 방식사용.
class Node3 {
int data;
Node3 lt,rt; //node객체의 주소를 저장하는 변수
public Node3(int val){
data=val;
lt=rt=null;
}
}
class ex9 {
static Node3 root;
public static int DFS(int L, Node3 root){
if(root.lt==null && root.rt==null) return L;
else return Math.min(DFS(L+1,root.lt),DFS(L+1,root.rt));
}
public static void main(String[] args) {
root=new Node3(1);
root.lt=new Node3(2);
root.rt=new Node3(3);
root.lt.lt=new Node3(4);
root.lt.rt=new Node3(5);
System.out.println(DFS(0,root));
}
}
풀이
1.Node클래스를 만든다. 그림 처럼 값을 삽입한다. 호출.
2.재귀가 돌면서 DFS방식으로 탐색한다.자식노드인 lt,rt로 한칸씩 갈 수록 L도 +1씩 증가한다. 자식노드가 둘 다 없으면서 더 작은 L값을 가지는
노드의 L값을 리턴한다.
//문제
말단 노드까지의 가장 짧은 경로를 구하라. DFS 방식사용.
class Node4 {
int data;
Node4 lt,rt; //node객체의 주소를 저장하는 변수
public Node4(int val){
data=val;
lt=rt=null;
}
}
public class ex10 {
static Node4 root;
public static int BFS(Node4 root) {
Queue<Node4> Q = new LinkedList<>();
Q.offer(root);
int L=0;
while(!Q.isEmpty()){
int len=Q.size();
for(int i=0; i<len; i++){
Node4 poll= Q.poll();
if(poll.lt ==null && poll.rt ==null) return L;
if(poll.lt!=null) Q.offer(poll.lt);
if(poll.rt!=null) Q.offer(poll.rt);
}
L++;
}
return L;
}
public static void main(String[] args) {
root = new Node4(1);
root.lt = new Node4(2);
root.rt = new Node4(3);
root.lt.lt = new Node4(4);
root.lt.rt = new Node4(5);
System.out.print(BFS(root));
}
}
풀이
1.Q.poll()하면서 그 논드의 자식이 있으면 Q.offer()로 담는게 원래 방식.
2.자식이 둘다 없으면 현재 L(레벨)값을 리턴.
G(V,E)가 주어지면 이 E(간선)의 방향을 체크하는 graph[][]에서 무방향(양방향)을 체킹할 수 있다.
1.무방향(양방향) 그래프
graph[a][b]=1;
graph[b][a]=1;
2.방향 그래프
graph[a][b]=1;
3.가중치 방향 그래프
graph[a][b]=c;
//문제
방향그래프가 주어지면 1번 정점에서 N번 정점으로 가는 모든 경로의 가지 수를 출력하는 프로그램 작성
//입력
5 9
1 2
1 3
1 4
2 1
2 3
2 5
3 4
4 2
4 5
//출력
6
public class ex12 {
static int n=0,m=0,answer=0;
static int[][] graph;
static int[] ch;
public static void DFS(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;
}
}
}
}
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
n=sc.nextInt();
m=sc.nextInt();
graph=new int[n+1][n+1];
ch=new int[n+1];
for(int i=0; i<m; i++){
int a= sc.nextInt();
int b= sc.nextInt();
graph[a][b]=1;
}
ch[1]=1;
DFS(1);
System.out.println(answer);
}
}
풀이
1.정답을 체킹할 answer, 입력받는 정점의수 n, 간선의수 e를 전역으로 static선언 그리고 방향을 체크할 graph[][]를 선언. 마지막으로 방문했던 노드를 체크하기 위한 ch[]을 선언한다.
2.graph[][],ch[]를 선언하고 입력받는 m만큼 a,b를 입력받아 graph[a][b]=1을 체크한다.
3.노드1번은 항상 시작하기 때문에 ch[1]=1로 해둔다.
4.DFS()함수에서 if문을 v==n으로 해서 n에 도달하면 answer++를 한다.
5.else문에서 for문으로 1~n만큼 다 돌려서 graph[v][i]가 1이면서 ch[i]==0인 곳을 방문하게 만들고, ch[]를 1로 체크하고 DFS(i)로 더 깊이 들어간다. 다시 나오게 되면 ch[i]=0; 으로 만들어 준다.
//12번과 같은 문제
public class ex13 {
static int n=0,m=0,answer=0;
static ArrayList<ArrayList<Integer>> graph;
static int[] ch;
public static void DFS(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;
}
}
}
}
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
n=sc.nextInt();
m=sc.nextInt();
graph=new ArrayList<ArrayList<Integer>>();
for(int i=0; i<=n; i++){
graph.add(new ArrayList<Integer>());
}
ch=new int[n+1];
for(int i=0; i<m; i++){
int a= sc.nextInt();
int b= sc.nextInt();
graph.get(a).add(b);
}
ch[1]=1;
DFS(1);
System.out.println(answer);
}
}
풀이
1.전체적인 풀이는 위와 비슷하다. 인접리스트를 이용해서 경로를 탐색하는 방식은 정점이 1000이상 , 10000이상 많을 때 사용한다.
2.graph[a][b]==1 인지 볼필요가 없다.입력받는 노드에대한 각 간선을 graph.get(a).add(b)를 해주면 된다.
3.DFS()함수에서 forEach문으로 돌면서 각각의 노드를 graph.get(v)해서 가져온다음에 해당하는 방향으로 n에 도달하면 answer++;
//문제
1번 정점에서 각 정점으로 가는 최소 이동 간선수를 출력하세요.
//입력
6 9
1 3
1 4
2 1
2 5
3 4
4 5
4 6
6 2
6 5
//출력
2 : 3
3 : 1
4 : 1
5 : 2
6 : 2
public class ex14 {
static int n, m, answer=0;
static ArrayList<ArrayList<Integer>> graph;
static int[] ch, dis;
public static void BFS(int v){
ch[v]=1;
dis[v]=0;
Queue<Integer> Q=new LinkedList<>();
Q.offer(v);
while(!Q.isEmpty()){
int x=Q.poll();
for(int nx : graph.get(x)){
if(ch[nx]==0){
ch[nx]=1;
Q.offer(nx);
dis[nx]=dis[x]+1;
}
}
}
}
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
n=sc.nextInt();
m=sc.nextInt();
graph=new ArrayList<ArrayList<Integer>>();
for(int i=0; i<=n; i++){
graph.add(new ArrayList<Integer>());
}
ch=new int[n+1];
dis=new int[n+1];
for(int i=0; i<m; i++){
int a=sc.nextInt();
int b=sc.nextInt();
graph.get(a).add(b);
}
BFS(1);
for(int i=2; i<=n; i++){
System.out.println(i+" : "+dis[i]);
}
}
}
풀이
1. 인접배열이아닌 인접리스트로 풀어야 한다. 입력값 크기에 따른 런타임에러 방지.
2. graph=new ArrayList<ArrayList>(); 로 인접리스트 그래프를 만든다.
3. 방문했던 정점인지 아닌지 체크용 ch[], 거리를 담을 dis[]선언
4. 간선 방향을 그림처럼 입력 후 , 그래프에 해당 정점에 간선을 추가.
5. BFS 1번 정점부터 시작