반응형

* MST란?

Minimum Spanning Tree의 약자로, 직역하면 최소 신장 트리라고 불린다.

Spanning Tree는 모든 정점들이 연결 되어 있어야 하고 사이클을 포함해서는 안된다.

앞에 Minimum을 붙이면 각 간선의 가중치가 다른 경우 최소의 가중치로 연결하여 모든 정점을 방문하는 것을 말한다.

즉, MST에서는 간선의 가중치가 최소로 모든 정점을 방문해야하며 사이클이 생겨서는 안된다.

MST에는 크게 Kruskal(크루스칼) 알고리즘과 Prim(프림) 알고리즘이 존재한다.


* 크루스칼 알고리즘 (+Union-Find)

1. 간선을 가중치에 따라 오름차순으로 정렬한다. (Comparable 사용)

2. 가장 가중치를 가지는 간선을 첫번째 간선으로 선택한다.

3. 가중치가 작은 간선을 선택한다.

4. 선택한 간선과 이전에 선택한 간선이 사이클을 이루는지 확인한다. (Union-Find(Disjoint-Set) 사용)


Union-Find 자료구조에서 Disjoint-Set 확인 방법은 다음과 같다.

1. 초기화 : N 개의 원소가 각각의 집합에 포함되어 있도록 초기화 합니다. 

2. Union (합치기) 연산 : 두 원소 a, b 가 주어질 때, 이들이 속한 두 집합을 하나로 합칩니다. 

3. Find (찾기) 연산 : 어떤 원소 a 가 주어질 때, 이 원소가 속한 집합을 반환합니다. 

즉, disjoint를 통해 같은 집합여부를 확인할 수 있다.

이를 소스코드로 표현하면 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Disjoint 초기화
int[] disjoint = new int[n + 1];
for (int i = 0; i < n + 1; i++) {
    disjoint[i] = i;        //disjoint[i]에는 부모의 정점의 번호를 
}
 
// Disjoint Find
public static int find(int x) {
    if (disjoint[x] == x) return x;
    else return disjoint[x] = find(disjoint[x]);
}
 
//Disjoint Union
int x = find(u);        //find
int y = find(v);        //find
if (x != y) {           //union
    disjoint[x] = y;
}
cs

백준/1922 :: 네트워크 연결 문제

백준/1922 :: 네트워크 연결 풀이


* 프림 알고리즘 

1. 가중치가 낮은 간선을 선택한다.

2. 선택한 간선과 인접한 간선 중에서, 사이클을 생성하지 않는 간선 중에서 가중치가 작은 간선을 선택한다. (현재 간선의 가중치를 추가하면 다익스트라)

3. 위 과정을 반복하여 n-1개의 간선을 선택한다.

위 과정을 거치기 때문에 간선을 선택할 때마다 트리의 형태가 유지된다. (보통 우선순위큐를 이용하여 BFS로 구현한다.)

프림 알고리즘은 다익스트라(Dijkstra) 개념과 같이 이해하는게 편리하다.


cf) 프림 알고리즘 : MST를 만들 때 사용하는 알고리즘으로 BFS처럼 선마다 최소 가중치를 찾아 연결하는 방법

다익스트라 알고리즘 : 시작 정점 x에서 도착 정점 y까지 최단거리로 이동하는 방식

1
2
3
4
5
// 프림 알고리즘    => 연결된 간선들 중 최소 가중치만 구한다.
pq.add(new Edge(e.v, edge[e.v].get(i).v, edge[e.v].get(i).w));
 
// 다익스트라 알고리즘    => 현재 가중치를 계속 더해야 한다.
pq.add(new Edge(e.v, edge[e.v].get(i).v, edge[e.v].get(i).w + e.w));
cs


반응형
반응형

* 핵심

2중 for문을 돌리면서 증가수열이면 max값을 선택해가는 방식

* 문제

문제

수열 A가 주어졌을 때, 그 수열의 증가 부분 수열 중에서 합이 가장 큰 것을 구하는 프로그램을 작성하시오.

예를 들어, 수열 A = {1, 100, 2, 50, 60, 3, 5, 6, 7, 8} 인 경우에 합이 가장 큰 증가 부분 수열은 A = {1, 100, 25060, 3, 5, 6, 7, 8} 이고, 합은 113이다.

입력

첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000)이 주어진다.

둘째 줄에는 수열 A를 이루고 있는 Ai가 주어진다. (1 ≤ Ai ≤ 1,000)

출력

첫째 줄에 수열 A의 합이 가장 큰 증가 부분 수열의 합을 출력한다.

* 소스 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import java.io.*;
import java.util.*;
 
public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        StringTokenizer stk = new StringTokenizer(br.readLine());
        int[] num = new int[n + 1];
        int[] dp = new int[n + 1];
 
        for (int i = 1; i <= n; i++) {
            num[i] = Integer.parseInt(stk.nextToken());
        }
 
        dp[1= num[1];
        int max = dp[1];
        for (int i = 2; i <= n; i++) {
            for (int j = 1; j < i; j++) {
                if (num[i] > num[j]) {      // i,j 가 증가수열이면 max값 선택
                    dp[i] = Math.max(dp[i], dp[j]);
                }
            }
            dp[i] += num[i];
            max = Math.max(max, dp[i]);
        }
        System.out.println(max);
    }
}
 
cs


반응형
반응형

* 핵심

2가지의 경우의 수를 생각해서 Top-Down 방식 사용

1. 왼쪽이 오른쪽보다 높은 경우     => 오른쪽 depth+1, dp[left][right]에 오른쪽 카드 값 추가

2. 왼쪽이 오른쪽보다 낮은 경우    => 왼쪽 depth+1 과 왼쪽 depth+1, 오른쪽 depth+1 값 중 max값 비교

최종적으로 1번값과 2번값의 max값을 dp[left][right]값으로 선택

* 문제

문제

지훈이는 최근에 혼자 하는 카드게임을 즐겨하고 있다. 게임에 사용하는 각 카드에는 양의 정수 하나가 적혀있고 같은 숫자가 적힌 카드는 여러 장 있을 수 있다. 게임방법은 우선 짝수개의 카드를 무작위로 섞은 뒤 같은 개수의 두 더미로 나누어 하나는 왼쪽에 다른 하나는 오른쪽에 둔다. 그리고 빈 통을 하나 준비한다. 

이제 각 더미의 제일 위에 있는 카드끼리 서로 비교하며 게임을 한다. 게임 규칙은 다음과 같다. 지금부터 왼쪽 더미의 제일 위 카드를 왼쪽 카드로, 오른쪽 더미의 제일 위 카드를 오른쪽 카드로 부르겠다.

  1. 언제든지 왼쪽 카드만 통에 버릴 수도 있고 왼쪽 카드와 오른쪽 카드를 둘 다 통에 버릴 수도 있다. 이때 얻는 점수는 없다.
  2. 오른쪽 카드에 적힌 수가 왼쪽 카드에 적힌 수보다 작은 경우에는 오른쪽 카드만 통에 버릴 수도 있다. 오른쪽 카드만 버리는 경우에는 오른쪽 카드에 적힌 수만큼 점수를 얻는다.
  3. (1)과 (2)의 규칙에 따라 게임을 진행하다가 어느 쪽 더미든 남은 카드가 없다면 게임이 끝나며 그때까지 얻은 점수의 합이 최종 점수가 된다. 

다음 예는 세 장 씩 두 더미의 카드를 가지고 게임을 시작하는 경우이다

카드 순서왼쪽 더미오른쪽 더미
132
224
351

이 경우, 우선 오른쪽 카드 2가 왼쪽 카드 3보다 작으므로 규칙 (1)에 따라 왼쪽 카드만 버리거나 왼쪽 카드와 오른쪽 카드를 모두 버리거나, 규칙 (2)에 따라 오른쪽 카드만 버릴 수 있다. 만약 오른쪽 카드만 버리는 것으로 선택하면, 2만큼 점수를 얻고 오른쪽 카드 2는 버린다. 이제 오른쪽 더미의 제일 위 카드는 4이고 이는 왼쪽 카드 3보다 크므로 규칙 (1)에 따라 왼쪽 카드만 버리거나 왼쪽 카드와 오른쪽 카드를 둘 다 버릴 수 있다. 만약 둘 다 버리는 것으로 선택하면, 이제 왼쪽 카드는 2가 되고 오른쪽 카드는 1이 된다. 이 경우 다시 규칙 (1)과 (2)에 따라 세 가지 중 한가지를 선택할 수 있고, 그 중 왼쪽 카드만 버리는 것으로 선택하면 이제 왼쪽 카드는 5가 되고 오른쪽 카드는 1이 된다. 이 경우에도 역시 규칙 (1)과 (2)에 따라 세 가지 중 한가지를 선택할 수 있고, 그 중 오른쪽 카드만 버리는 것으로 선택하면 1만큼 점수를 얻고 오른쪽 카드 1은 버린다. 이제 오른쪽 더미에는 남은 카드가 없으므로 규칙 (3)에 따라 게임이 끝나며 최종 점수는 2+1=3이 된다.

두 더미의 카드가 주어졌을 때, 게임을 통해 얻을 수 있는 최종 점수의 최댓값을 출력하는 프로그램을 작성하시오. 위 예에서 최종 점수의 최댓값은 7이다.

입력

첫 줄에는 한 더미의 카드의 개수를 나타내는 자연수 N(1 ≤ N ≤ 2,000)이 주어진다. 다음 줄에는 왼쪽 더미의 카드에 적힌 정수 A(1 ≤ A ≤ 2,000)가 카드 순서대로 N개 주어진다. 그 다음 줄에는 오른쪽 더미의 카드에 적힌 정수 B(1 ≤ B ≤ 2,000)가 카드 순서대로 N개 주어진다. 각 더미에는 같은 숫자를 가진 카드가 두 개 이상 있을 수 있다.

출력

얻을 수 있는 최종 점수의 최댓값을 출력한다.

* 소스 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import java.io.*;
import java.util.*;
 
public class Main {
    public static int n;
    public static int[][] card, dp;
 
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        n = Integer.parseInt(br.readLine());
        card = new int[n][2];
        dp = new int[n][n];     // [leftDepth][rightDepth]
        for (int i = 0; i < n; i++) {
            Arrays.fill(dp[i], -1);
        }
        for (int i = 0; i < 2; i++) {
            StringTokenizer stk = new StringTokenizer(br.readLine());
            for (int j = 0; j < n; j++) {
                card[j][i] = Integer.parseInt(stk.nextToken());
            }
        }
        System.out.println(TopDown(00));
    }
 
    public static int TopDown(int leftDepth, int rightDepth) {
        if (leftDepth == n || rightDepth == n) return 0;                        //종료 조건
        if (dp[leftDepth][rightDepth] != -1return dp[leftDepth][rightDepth];  //메모이제이션
        else {
            dp[leftDepth][rightDepth] = 0;
            if (card[leftDepth][0> card[rightDepth][1])           //왼쪽이 오른쪽보다 큰 경우
                dp[leftDepth][rightDepth] = TopDown(leftDepth, rightDepth + 1+ card[rightDepth][1];
            dp[leftDepth][rightDepth] = Math.max(TopDown(leftDepth + 1, rightDepth + 1),
                    Math.max(TopDown(leftDepth + 1, rightDepth), dp[leftDepth][rightDepth]));
        }
        return dp[leftDepth][rightDepth];
    }
}
cs


반응형
반응형

* OOP(객체지향 프로그래밍)이란?

프로그래밍에서 필요한 데이터를 추상화시켜 상태와 행위를 가진 객체를 만들고 그 객체들 간의 유기적인 상호작용을 통해 로직을 구성하는 프로그래밍 방법

장점         - 코드 재사용이 용이하다.

- 유지보수가 쉽다.

단점         - 처리속도가 상대적으로 느리다.

- 설계시에 많은 시간과 노력이 필요하다.


* 클래스와 인스턴스(객체)

클래스 : 객체의 속성(Attribute)과 행위(Behavior)를 변수와 메소드로 정의한 것

객체 : 클래스에 정의된 내용을 바탕으로 실제 메모리에 할당된 데이터


* OOP(객체지향 프로그래밍) 4가지 특징(캡상추다)

1. 캡슐화

객체의 멤버변수는 private로 설정하여 외부에서 접근을 거부하고, Getter/Setter를 public으로 선언하여 객체의 속성에 접근하도록 사용한다.

그 이유는 객체의 무결성을 보장하기 위함인데, 객체 필드에 직접적인 접근을 거부하고 잘못된 입력에 대해 Getter/Setter는 사전에 처리 및 제한할 수 있기 때문이다.

2. 상속

클래스의 멤버와 함수를 다른 클래스에 물려주거나, 물려 받는 것을 말하며 이를 통해 재사용성이 높아진다.

3. 추상화

중요한 정보만을 표현함으로써 공통의 속성이나 기능을 묶어 이를 하나의 클래스로 다루는 것

4. 다형성

오버로딩과 오버라이딩을 통해 다른 클래스의 객체가 같은 메시지를 받았을 때 각자의 방식으로 동작하는 능력

오버로딩(Overloadding)    : 상위 클래스의 이름과 return값이 같지만, 매개변수를 다른 메소드를 만들어 가독성을 높이는 방법

오버라이딩(Overriding)     : 상위 클래스에 존재하는 메소드를 하위 클래스에서 용도에 맞게 재정의 하여 재사용성을 높이는 방법


* Call by Value vs Call by Reference

# Call by Value (값에 의한 호출)        << Java

함수가 호출될 때, 메모리 공간 안에서는 함수를 위한 별도의 임시 공간이 생성된다.

함수 호출시 인자로 전달되는 변수의 값을 복사하여 함수의 인자로 전달한다.

복사된 인자는 함수 안에서 지역적으로 사용되는 local value의 특성을 가진다.

따라서 함수 안에서 인자의 값이 변경되어도, 외부의 변수의 값은 변경되지 않는다.

# Call by Reference (참조에 의한 호출)

함수가 호출될 때, 메모리 공간 안에서는 함수를 위한 별도의 임시 공간이 생성된다.

함수 호출시 인자로 전달되는 변수의 레퍼런스를 전달한다. (해당 변수를 가리킨다.)

따라서 함수 안에서 인자의 값이 변경되면, 인자로 전달된 변수의 값도 함께 변경된다.

cf) Java는 자료형은(Integer, Long, String, ...) Call by Value이다. 하지만 배열, 클래스는 Call by Reference로 생각하는 것이 편하다.


* StringBuffer vs StringBuilder

StringBuffer    : Multi-Thread 환경에서 동기화가 가능하기 때문에 Thread-Safe하다.

StringBuilder   : 동기화를 지원하지 않기 때문에 멀티쓰레드환경에서는 적합하지 않지만 Single-Thread 환경에서 빠르다.


* Annotation

어노테이션이란 본래 주석이란 뜻으로, 인터페이스를 기반으로 한 문법으로 주석처럼 코드에 달아 클래스에 특별한 의미를 부여하거나 기능을 주입할 수 있다.

컴파일러에게 코드 문법 에러를 체크하도록 정보를 제공 및 실행 시(런타임 시) 특정 기능을 실행하도록 정보를 제공한다.

어노테이션은 컴파일러에게 이 소스코드를 어떻게 처리해야 되는 것인지 표시를 해준다. 

예를들어 내장 어노테이션인 @Override 경우 해당 메소드가 부모클래스를 오버라이딩 한 메소드라고 컴파일러에게 미리 일러주는 것이다. 

따라서 컴파일러는 런타임 이전에 이 메소드가 문제없이 오버라이딩 되었는지 검사한다.


* Generic

제네릭 타입을 이용해서 컴파일 과정에서 타입 체크를 할 수 있다.

제네릭은 클래스와 인터페이스, 메소드를 정의할 때 타입 파라미터로 사용한다.

1. 컴파일할 때 타입을 체크해서 에러를 사전에 잡을 수 있다.

2. 컴파일러가 타입캐스팅을 해주기 때문에 개발자가 편리하다.

3. 타입만 다르고 코드의 내용이 대부분 일치할 때, 코드의 재사용성이 좋아진다.

반응형

'∙Java' 카테고리의 다른 글

[JAVA] int, char, String 형변환 정리  (1) 2019.04.30
[Java] if-else 줄이는 코딩방법  (0) 2019.01.17
[Java] ==와 equals()의 차이  (0) 2019.01.10
[Java] Abstract Class와 Interface  (0) 2019.01.10
반응형

Java를 사용하다 보면 언제 equals를 사용해야 하고, 언제 ==를 사용해야 하는 지 헷갈리는 경우가 있다.

예시를 통해 차이점을 알아보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.io.*;
import java.util.*;
 
public class Main {
    public static void main(String[] args) throws Exception {
        String a = "aaa";
        String b = a;
        String c = new String("aaa");
        String d = new String("aaa");
        System.out.println(System.identityHashCode(a));         //1627674070
        System.out.println(System.identityHashCode(b));         //1627674070
        System.out.println(System.identityHashCode(c));         //1360875712
        System.out.println(System.identityHashCode(d));         //1625635731
        System.out.println("========================");
        System.out.println("a == b : " + (a == b));             //True
        System.out.println("a == c : " + (a == c));             //False
        System.out.println("c == d : " + (c == d));             //False
        System.out.println("a.equals(b) : " + a.equals(b));     //True
        System.out.println("a.equals(c) : " + a.equals(c));     //True
        System.out.println("c.equals(d) : " + b.equals(d));     //True
    }
}
cs

위 코드를 실행한 결과값을 통해 다음과 같은 결론을 얻을 수 있다.

1. String a = "aaa" 와 String b = a는 같은 주소를 가르친다.

2. String c = new String("aaa")와 String d = new String("aaa")는 서로 다른 주소를 가르킨다.

3. == 연산은 같은 주소값인지를 비교한다. 즉, 안에 데이터는 비교하지 않는다.

4. equals 함수는 내부의 값을 비교한다.


여기서 4번 equals 함수 코드를 보면 구체적인 작동 방식을 알 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//String.java
public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}
cs

String에 구현된 equals 함수를 보면 length를 구해 while문으로 각 char 값을 비교하는 것을 확인할 수 있다.

반응형
반응형

* Abstract Class와 Interface의 공통점

추상클래스와 인터페이스는 선언만 있고 구현 내용이 없는 클래스다.

인터페이스와 추상클래스를 가지고 새로운 인스턴스(객체)를 생성할 수 없기 때문에 상속을 통해 자식 클래스에서만 객체를 생성할 수 있다.


* Abstract Class(추상 클래스)

extends 키워드를 통해 추상 클래스를 상속받는다.

추상클래스는 추상메서드(abstract method)가 하나라도 존재하는 클래스를 말한다. 

일부는 구현된 메소드도 있을 수 있고, 일부는 abstract method로 구현이 되어있지 않은 메소드도 있을 수 있다. 

즉, 구현된 메소드가 있을 수 있기 때문에 만들어야할 여러 클래스들의 공통점을 찾아 추상화시켜서 사용한다.


* Interface

implements 키워드를 통해 인터페이스를 상속받는다.

인터페이스는 쉽게 말하면 껍데기라고 말할 수 있고, 설계도 또는 명세라고 생각하면 된다.

모든 메소드가 추상 메소드이기 때문에 인터페이스를 상속받는 자식 클래스는 인터페이스의 모든 메소드를 필수적으로 구현해야 한다.


반응형
반응형

* Stack 영역

프로그램 실행 과정에서 메소드 실행 시 임시로 할당되고 해당 메소드가 끝나면 바로 소멸되는 것들이 저장된다.

Heap 영역에 생성된 Object 타입의 데이터의 참조값이 할당된다.


* Heap 영역

Java에서 new 명령을 통해 생성된 인스턴스 변수가 놓인다.

모든 Object 타입(Integer, String, ArrayList, ...)은 heap 영역에 생성된다.

GC에 의해 지위지지 않는 이상 Heap 영역에 계속 남아있다.


* 예제

1
2
3
4
5
6
public class Main {
    public static void main(String[] args) {
        int port = 4000;
        String host = "localhost";
    }
}
cs

위와 같은 코드가 있다고 가정했을 때, 해당하는 Stack 영역과 Heap 영역은 다음과 같다.


Stack 영역에는 Heap 영역에 생성된 Object 타입의 데이터 참조값이 할당된다.

만약, 여기서 host += :8080 이라는 코드를 추가하면, localhost에 값이 추가되는 것이 아니라 localhost:8080 라는 새로운 String Object가 할당되어 참조하게 된다.

즉, localhost는 unreachable 객체가 되어 GC에 의해 메모리에서 제거된다.


참조 : https://yaboong.github.io/java/2018/05/26/java-memory-management/

반응형
반응형

Java를 처음 시작하는 사람도 Class를 만들면 public static void main 를 쉽게 접할 수 있다.

왜 Java를 시작할 때 public static void main 이여야만 할까?


이러한 형식인 이유는 

하나하나 뜯어보면서 확인해보자.


public    : 모든 클래스에 접근이 가능한 접근 제어자

 >> private, default, protected의 경우 다른 클래스에서 Main을 사용하지 못하기 때문

static    : 프로그램 시작과 동시에 static으로 선언된 것들은 메모리에 호출되는데, 이렇게 호출된 static은 프로그램이 종료되는 시점까지 유지된다.

>> Main 함수의 경우 Java에서의 프로그램의 시작과 끝이기 때문에 Static으로 선언되어야 한다.

>> Singletone 

void      : 프로그램 자체가 종료가 되는 시점에서 어떤 특정 값이 반환되어도 아무 의미가 없다.


즉, 모든 클래스들이 접근 가능하여야 하고, 시작되기 전 메모리에 올려져 있어야 하며, return 값에는 의미가 없기 때문에 public static void main를 사용한다.

반응형

'∙Java' 카테고리의 다른 글

[Java] ==와 equals()의 차이  (0) 2019.01.10
[Java] Abstract Class와 Interface  (0) 2019.01.10
[Java] Stack 영역과 Heap 영역  (0) 2019.01.10
[Java] JVM의 개념과 작동 방식  (0) 2019.01.10