728x90
반응형

동작방식

참고사이트 설명 참조

시간복잡도

 세그먼트 트리를 이용할 경우 기존의 for문이 O(N)의 시간 복잡도보다 빠른 O(logN)의 시간 복잡도

참고사이트

https://cocoon1787.tistory.com/313
 

[자료구조] 세그먼트 트리(구간트리, Segment Tree)로 구간 내 최소값 찾기

세그먼트 트리(Segment Tree)란? 알고리즘 문제를 풀다 보면 정렬되어 있지 않은 구간 내의 합이나 최솟값들을 빠르게 찾아야 하는 경우가 많습니다. 질문이 1개일 경우 간단하게 for문을 통해서 O(N)

cocoon1787.tistory.com

 

기본코드

10 4
75
30
100
38
50
51
52
20
81
5
1 10
3 5
6 9
8 10
package segmenttree;

import java.io.*;
import java.util.StringTokenizer;

public class Rmq {

    static int[] numbers;
    static int[][] questions;
    static int[] tree;

    public static void init(int node, int start, int end) {
        if (start == end) {
            tree[node] = numbers[start];
        } else {
            init(node*2, start, (start+end)/2);
            init(node*2+1, (start+end)/2+1, end);
            tree[node] = Math.min(tree[node*2],tree[node*2+1]);
        }
    }

    public static int query(int node, int start, int end, int a, int b) {
        if (a > end || b < start) {
            return -1;
        }
        if (a <= start && end <= b) {
            return tree[node];
        }
        int left = query(node*2, start, (start+end)/2, a, b);
        int right = query(node*2+1, (start+end)/2+1, end, a, b);
        if (left == -1) {
            return right;
        } else if (right == -1){
            return left;
        } else {
            return Math.min(left, right);
        }
    }

    public static void main(String[] args) throws IOException {

        System.setIn(new FileInputStream("input\\rmq.txt"));
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringTokenizer st = new StringTokenizer(br.readLine());

        int N = Integer.parseInt(st.nextToken());
        int M = Integer.parseInt(st.nextToken());

        numbers = new int[N];
        questions = new int[M][2];
        tree = new int[N*4];

        for (int i=0; i<N; i++) {
            st = new StringTokenizer(br.readLine());
            numbers[i] = Integer.parseInt(st.nextToken());
        }

        for (int i=0; i<M; i++) {
            st = new StringTokenizer(br.readLine());
            questions[i][0] = Integer.parseInt(st.nextToken());
            questions[i][1] = Integer.parseInt(st.nextToken());
        }

        br.close();

        init(1, 0, N-1);

//        for (int i=0; i<tree.length; i++) {
//            System.out.println("tree[" + i + "] : " + tree[i]);
//        }

        for (int i=0; i<M; i++) {
            bw.write(query(1, 0, N-1, questions[i][0]-1, questions[i][1]-1)+"\n");
        }

        bw.flush();
        bw.close();
    }
}
728x90
반응형
728x90
반응형

Fenwick Tree는 Binary Indexed Tree라고도 하며, 줄여서 BIT라고 합니다.

동작방식

참고사이트에 자세한 동작 방식을 확인하여 주십시오.

시간복잡도

펜윅 트리는 대해 O(log n) 시간 복잡도를 가짐

참고사이트

https://www.acmicpc.net/blog/view/21
 

펜윅 트리 (바이너리 인덱스 트리)

블로그: 세그먼트 트리 (Segment Tree) 에서 풀어본 문제를 Fenwick Tree를 이용해서 풀어보겠습니다. Fenwick Tree는 Binary Indexed Tree라고도 하며, 줄여서 BIT라고 합니다. Fenwick Tree를 구현하려면, 어떤 수 X

www.acmicpc.net

기본코드

5 3
5 4 3 2 1
1 3
2 4
5 5
package segmenttree;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.StringTokenizer;

public class Fenwick {

    static int N;
    static int M;
    static int[] numbers;
    static int[][] questions;
    static int[] tree;

    public static void update(int x, int val) {
        for (int i=x; i<=N; i+=i&-i) {
            tree[i] += val;
        }
    }

    public static int query(int x) {
        int ans = 0;
        for (int i=x; i>0; i-=i&-i) {
            ans += tree[i];
        }
        return ans;
    }

    public static void main(String[] args) throws IOException {

        System.setIn(new FileInputStream("input\\rsq.txt"));
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringTokenizer st = new StringTokenizer(br.readLine());

        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());

        numbers = new int[N+1];
        questions = new int[M][2];
        tree = new int[N+1];

        st = new StringTokenizer(br.readLine());
        for (int i=1; i<=N; i++) {
            numbers[i] = Integer.parseInt(st.nextToken());
        }

        for (int i=0; i<M; i++) {
            st = new StringTokenizer(br.readLine());
            questions[i][0] = Integer.parseInt(st.nextToken());
            questions[i][1] = Integer.parseInt(st.nextToken());
        }

        br.close();

        for (int i=1; i<=N; i++) {
            update(i, numbers[i]);
        }

        for (int i=0; i<tree.length; i++) {
            System.out.println("tree[" + i + "] : " + tree[i]);
        }

        for (int i=0; i<M; i++) {
            bw.write((query(questions[i][1]) - query(questions[i][0]-1))+"\n");
        }

        bw.flush();
        bw.close();
    }
}

 

728x90
반응형
728x90
반응형

 

 

동작방식

맹목적 탐색방법의 하나로 탐색트리의 최근에 첨가된 노드를 선택하고, 이 노드에 적용 가능한 동작자 중 하나를 적용하여 트리에 다음 수준(level)의 한 개의 자식노드를 첨가하며, 첨가된 자식 노드가 목표노드일 때까지 앞의 자식 노드의 첨가 과정을 반복해 가는 방식입니다.

  • 장점
    • 단지 현 경로상의 노드들만을 기억하면 되므로 저장공간의 수요가 비교적 적다.
    • 목표노드가 깊은 단계에 있을 경우 해를 빨리 구할 수 있다.
  • 단점
    • 해가 없는 경로에 깊이 빠질 가능성이 있다. 따라서 실제의 경우 미리 지정한 임의의 깊이까지만 탐색하고 목표노드를 발견하지 못하면 다음의 경로를 따라 탐색하는 방법이 유용할 수 있다.
    • 얻어진 해가 최단 경로가 된다는 보장이 없다. 이는 목표에 이르는 경로가 다수인 문제에 대해 깊이우선 탐색은 해에 다다르면 탐색을 끝내버리므로, 이때 얻어진 해는 최적이 아닐 수 있다는 의미이다.

시간복잡도

- 인접리스트 : O(|V| + |E|) (정점의 개수:V, 간선의 개수:E)

- 인접행렬 : O(V^2) (정점의 개수:V)

참고사이트

https://ko.wikipedia.org/wiki/%EA%B9%8A%EC%9D%B4_%EC%9A%B0%EC%84%A0_%ED%83%90%EC%83%89
 

깊이 우선 탐색 - 위키백과, 우리 모두의 백과사전

깊이 우선 탐색의 애니메이션 예시 깊이 우선 탐색( - 優先探索, 영어: depth-first search, DFS)은 맹목적 탐색방법의 하나로 탐색트리의 최근에 첨가된 노드를 선택하고, 이 노드에 적용 가능한 동작

ko.wikipedia.org

기본코드

입력값

7
8
1 2
1 3
2 4
2 5
3 7
4 6
5 6
6 7  

코드

package study.graph;


import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Stack;
import java.util.StringTokenizer;

public class DFS {
	static int[] used;
	static int[][] map;
	static int N, M;
	public static void main(String[] args) throws IOException{
		
		System.setIn(new FileInputStream("input\\dfs.txt"));
		
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
		
		N = Integer.parseInt(br.readLine());
		M = Integer.parseInt(br.readLine());
		
		used = new int[N+1];
		map = new int[N+1][N+1]; // 인접행렬을 이용하기 위한 2차원 배열 생성
		
		StringTokenizer st = null;
		for(int m=0; m<M; m++) {
			st = new  StringTokenizer(br.readLine());
			int a = Integer.parseInt(st.nextToken());
			int b = Integer.parseInt(st.nextToken());
			
			map[a][b] = 1;
			map[b][a] = 1; //양방향이기 때문에..
		}
		
		System.out.print("그래프 DFS 방문 순서 : " );
		dfs(1);
		
		bw.close();
		br.close();
	}
	
	static void dfs(int point) {
		Stack<Integer> st = new Stack<Integer>();
		st.add(point);
		used[point] = 1; // 방문표시
		System.out.print( point + " ");
		
		while(!st.isEmpty()) {
			st.pop();
			for(int i=1; i<=N; i++) {
				if(map[point][i]==1 && used[i] == 0) {
					// 다음 노드와 연결되어 있고 아직 방문하지 않았으면 st에 넣고, 재귀실행
					st.add(i);
					used[i] = 1;
					dfs(i);
				}
			}
		}
		
	}
}
728x90
반응형
728x90
반응형

 

동작방식

하나로 시작 정점을 방문한 후 시작 정점에 인접한 모든 정점들을 우선 방문하는 방법입니다. 더 이상 방문하지 않은 정점이 없을 때까지 방문하지 않은 모든 정점들에 대해서도 너비 우선 검색을 적용합니다.

 

장점

  • 출발노드에서 목표노드까지의 최단 길이 경로를 보장한다.

단점

  • 경로가 매우 길 경우에는 탐색 가지가 급격히 증가함에 따라 보다 많은 기억 공간을 필요로 하게 된다.
  • 해가 존재하지 않는다면 유한 그래프(finite graph)의 경우에는 모든 그래프를 탐색한 후에 실패로 끝난다.
  • 무한 그래프(infinite graph)의 경우에는 결코 해를 찾지도 못하고, 끝내지도 못한다.

 

시간복잡도

DFS 시간복잡도와 동일함

- 인접리스트 : O(|V| + |E|) (정점의 개수:V, 간선의 개수:E)

- 인접행렬 : O(V^2) (정점의 개수:V)

참고사이트

https://ko.wikipedia.org/wiki/%EB%84%88%EB%B9%84_%EC%9A%B0%EC%84%A0_%ED%83%90%EC%83%89
 

너비 우선 탐색 - 위키백과, 우리 모두의 백과사전

 

ko.wikipedia.org

기본코드

입력값

7
8
1 2
1 3
2 4
2 5
3 7
4 6
5 6
6 7  

코드

package study.graph;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;

public class BFS {

	static int[] used;
	static int[][] map;
	static int N, M;
	public static void main(String[] args) throws IOException{
		
		System.setIn(new FileInputStream("input\\bfs.txt"));
		
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
		
		N = Integer.parseInt(br.readLine());
		M = Integer.parseInt(br.readLine());
		
		used = new int[N+1];
		map = new int[N+1][N+1]; // 인접행렬을 이용하기 위한 2차원 배열 생성
		
		StringTokenizer st = null;
		for(int m=0; m<M; m++) {
			st = new  StringTokenizer(br.readLine());
			int a = Integer.parseInt(st.nextToken());
			int b = Integer.parseInt(st.nextToken());
			
			map[a][b] = 1;
			map[b][a] = 1; //양방향이기 때문에..
		}
		
		System.out.print("그래프 BFS 방문 순서 : " );
		bfs(1);
		
		bw.close();
		br.close();
	}
	
	static void bfs(int st) {
		Queue<Integer> q = new LinkedList<Integer>();
		q.add(st);
		used[st] = 1; //방문표시
		
		while(!q.isEmpty()) {
			int x = q.poll();
			System.out.print( x + " ");
			
			for(int i=1; i<=N; i++) {
				if(map[x][i] == 1 && used[i] == 0) {
					// 다음 노드와 연결되어 있고 아직 방문하지 않았으면 q에 넣음
					q.add(i);
					used[i] = 1;//방문 표시
				}
			}
		}
	}
}
728x90
반응형
728x90
반응형

PriorityQueue 는 우선순위가 가장 높은것을 빼는 규칙입니다.

 

동작방식

  1. 자료를 넣는다.
  2. 자료 삭제시 가장 우선순위 높은걸 삭제,
  3. 자료 읽을때 우선순위 가장 높은것을 읽는다.

시간복잡도

자료를 넣고 빼는 속도 모두 : logN

기본코드

PriorityQueue의 기본코드 입니다.

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Collections;
import java.util.PriorityQueue;


public class PriorityQueue_example_0 {

	/**
	 * Priority Queue - Heap 으로 가장 많이 구현되어있다.
	 *  - 먼저넣은 것중 우선순위가 높은 것을 뺀다.
	 *  - 자료를 넣고 빼는 속도 모두 : logN
	 *  - max, min 값 찾기에 많이 쓰임
	 */
	static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

	
    public static void main(String[] args) throws IOException {

        // min heap - 가장 위(0)가 가장 작은값 
        PriorityQueue<Integer> q = new PriorityQueue<Integer>();
        
        q.add(5);
        q.add(3);
        q.add(7);
        q.add(4);
        q.add(5);
        
        System.out.println(q.poll());
        System.out.println(q.poll());
        System.out.println(q.poll());
        System.out.println(q.poll());
        System.out.println(q.poll());
        System.out.println(q.poll());
        
        System.out.println("=======================");
        // max heap - 가장 위(0)가 가장 큰값 
        PriorityQueue<Integer> qr = new PriorityQueue<Integer>(Collections.reverseOrder());
        
        qr.add(5);
        qr.add(3);
        qr.add(7);
        qr.add(4);
        qr.add(5);
        
        // 빼지않고, 가장 우선순위 
        
        System.out.println(qr.poll()); // 가장 큰 하나를 제거하고 출력
        System.out.println(qr.poll()); // 가장 큰 하나를 제거하고 출력
        System.out.println(qr.poll()); // 가장 큰 하나를 제거하고 출력
        System.out.println(qr.poll()); // 가장 큰 하나를 제거하고 출력
        System.out.println(qr.poll()); // 가장 큰 하나를 제거하고 출력
        System.out.println(qr.poll()); // 가장 큰 하나를 제거하고 출력
        // --> 힙소트
        
		br.close();
		bw.close();

    }
}

결과는 다음과 같습니다.

3
4
5
5
7
null
=======================
7
5
5
4
3
null

 

아래는 class를 PriorityQueue에 넣어 우선순위를 조절해주어 실행해보는 코드입니다.

import java.io.IOException;
import java.util.PriorityQueue;

class Nodes implements Comparable<Nodes> {
    int a;
    int b;

    Nodes(int a, int b) {
        this.a = a;
        this.b = b;
    }

    @Override
    public int compareTo(Nodes tar) {
        if (tar.a > this.a) return 1;
        if (tar.a < this.a) return -1;
        
        if(tar.b > this.b) return 1;
        if(tar.b < this.b) return -1;
        return 0;

    }

}

public class PriorityQueue_example_1 {
    public static void main(String[] args) throws IOException {

        PriorityQueue<Nodes> q = new PriorityQueue<Nodes>();

        q.add(new Nodes(1, 2));
        q.add(new Nodes(5, 1));
        q.add(new Nodes(5, 3));
        q.add(new Nodes(1, 2));
        q.add(new Nodes(4, 6));

        System.out.println(q.peek().a + " " + q.peek().b);
        q.poll();
        System.out.println(q.peek().a + " " + q.peek().b);
        q.poll();
        System.out.println(q.peek().a + " " + q.peek().b);
        q.poll();
        System.out.println(q.peek().a + " " + q.peek().b);
        q.poll();
        System.out.println(q.peek().a + " " + q.peek().b);
        q.poll();
    }
}

결과는 아래와 같습니다.

5 3
5 1
4 6
1 2
1 2

compareTo 함수를 오버라이드하여 class 내부적으로 순서를 조절하여 우선순위를 만들어 첫번째 숫자가 큰순, 두번째 수가 큰순으로 우선순위를 부여하여 큐에서 빼낸 결과입니다.

728x90
반응형
728x90
반응형

이번에는 슬라이딩 윈도우 알고리즘과 DAT 자료구조를 이용한 간단한 문제를 풀어보겠습니다.

 

문제

입력된 문자열(알파벳)에서 길이가 M개의 구간에서 가장 많이 등장하는 알파벳을 출력하라

문자열 : "AAAABBBBAABKKKABKKKKDKAAA"
M: 6

코드

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;


public class SlidingWindow_DAT {

	
	/**
	 * 슬라이등 윈도우 + DAT
	 */
	static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));


    public static void main(String[] args) throws IOException {        

        String str = "AAAABBBBAABKKKABKKKKDKAAA";
        int n = str.length();
        int m = 6;

        int[] dat = new int[100];

        //초기세팅 6개 DAT
        char maxCh = ' ';
        int maxCnt = 0;

        for (int i = 0; i<m; i++) {
            dat[str.charAt(i)]++;
            if (dat[str.charAt(i)] > maxCnt) {
                maxCnt = dat[str.charAt(i)];
                maxCh = str.charAt(i);
            }
        }

        //슬라이딩 윈도우
        for (int i = 0; i<n - m; i++) {            
            //다음 준비
            dat[str.charAt(i + m)]++;            
            dat[str.charAt(i)]--;    

            if (dat[str.charAt(i + m)] > maxCnt) {
                maxCnt = dat[str.charAt(i + m)];
                maxCh = str.charAt(i + m);
            }
        }

        System.out.println(maxCh);

        bw.close();
        br.close();
    }    

}

풀이

dat 배열에 길이가 M인 구간에서 나타나는 알파벳을 카운트하여 줍니다. 문자열 AAAABBBBAABKKKABKKKKDKAAA에서 처음 AAAABB 를 확인하면 dat 배열 dat['A']는 4, dat['B'] 는 2가 들어갑니다. 그리고 다음으로 이동합니다. 슬라이딩 윈도우 알고리즘으로 길이가 6인 구간을 기준으로 뒤문자는 카운트를 더해주고, 앞문자는 카운트에서 빼줍니다. 작업이 완려되면 dat배열에 문자번째와 max변수를 비교하여 max변수를 갱신합니다. 이러한 방식으로 M구간에서 가장 많이 나오는 알파벳을 알아낼 수 있습니다.

728x90
반응형
728x90
반응형

슬라이딩 윈도우 알고리즘은 배열 요소의 일정 범위 값을 비교할때 사용하면 유용한 알고리즘 입니다.

동작방식

일정 정수로 이루어진 배열 int[] arr= {3,1,5,3,4,1,5,7,5,1,8} 가 있다면, 길이가 5인 배열의 합계가 가장 큰 것은 무엇인가? 라는 문제에 사용될 수 있습니다. 일반적으로 아래와 같이 0번째 부터 4번째 (길이가 5이므로) 합을 구하고 저장합니다. 그리고 1번째 부터 5번째까지의 합을 구하고 저장후 처음에 구했던 값과 비교를 하여 큰것을 가지고 있습니다. 이렇게 반복을 하면 됩니다. 하지만 이 방법은 매번 for 루프로 모든 배열의 요소를 지정된 길이만큼 순회하며 합계를 구해 최대값을 구해야하므로 비효율적 입니다.

[3,1,5,3,4],1,5,7,5,1,8 ==> 합: 16, 최대값: 16
3,[1,5,3,4,1],5,7,5,1,8 ==> 합: 14, 최대값: 16
3,1,[5,3,4,1,5],7,5,1,8 ==> 합: 18, 최대값: 18 
...
==> 비효율적

이것을 간단히 생각해보면 처음에 해당 길이만큼 합계를 구하는 것은 어쩔수 없습니다. 하지만, 그 이후부터 매번 돌지 않고 인덱스 1~5까지의 합은 처음에 길했던 0~4까지의 합계에 0번째 배열의 값을 빼고, 5번째 배열의 값을 더한 값과 같습니다. 바로 이 부분에서 처음 구했던 0~4까지의 합계를 재사용하며 다음값을 구할 수 있는데, 이것이 슬라이딩 윈도우 알고리즘의 핵심입니다.

참고사이트

https://blog.fakecoding.com/archives/algorithm-slidingwindow/
 

[알고리즘] 슬라이딩 윈도우 알고리즘

슬라이딩 윈도우(Sliding Window) 알고리즘은 배열이나 리스트의 요소의 일정 범위의 값을 비교할때 사용하면 유용한 알고리즘이다. 예를들어 정수로 이루어진 배열 [2, 4, 7, 10, 8, 4, 5, 6, 7, 1] 에서 길

blog.fakecoding.com

기본코드

위 설명의 예제를 구현한 코드입니다.

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class SlidingWindow_example {
    static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));    

    static int[] arr= {3,1,5,3,4,1,5,7,5,1,8};
    static int n;

    static int getFive(int index)
    {
        int sum= 0;
        for (int i = 0; i<5; i++) {
            sum += arr[i + index];
        }
        return sum;
    }

    public static void main(String[] args) throws IOException {        

        n = arr.length;
        int max = -9999;

        int sum = getFive(0);

        for (int i = 0; i <= n - 5; i++) {
            if (sum > max) max = sum;

            // 마지막순간에는 다음 것을 준비할 필요 없음
            if (i == n - 5) break;

            // 다음 것을 준비
            sum += arr[i + 5];
            sum -= arr[i];
        }

        System.out.println(max);

        bw.close();
        br.close();
    }        
}

결과는 26이 나옵니다.

728x90
반응형
728x90
반응형

DAT는 자료구조입니다. 값을 배열의 index를 활용하는 것이 포인트 입니다. 

 

동작방식

배열을 사용하여 레코드를 해당 키에 매핑할 수 있는 데이터 구조입니다. 직접 주소 테이블에서 레코드는 키 값을 인덱스로 직접 사용하여 배치됩니다. 빠른 검색, 삽입 및 삭제 작업을 용이하게 합니다.

출처: https://www.geeksforgeeks.org/direct-address-table/

최대값에 1을 더한 크기의 배열을 만든 다음(0 기반 인덱스로 가정) 값을 인덱스로 사용합니다. 예를 들어 다음 다이어그램에서 키 21은 인덱스로 직접 사용됩니다.

시간복잡도

탐색,삽입,삭제 연산을 모두 O(1)

참고사이트

https://www.geeksforgeeks.org/direct-address-table/
 

Direct Address Table - GeeksforGeeks

A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.

www.geeksforgeeks.org

기본코드

아래는 글을 입력받아 가장 많이 나온 문자를 찾아내는 간단한 코드입니다.

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;


public class DAT_example {
	
	/**
	 * DTA : Direct Address Table 자료구조 
	 * ==> 값을 index로 활용하는 것인 포인트
	 */
	
	static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

	public static void main(String[] args) throws IOException  {
		
		// 입력
		String input = br.readLine();
		int[] arr = new int[200];
		int max = 0;
		String maxChar = "";
		for(int i=0; i<input.length(); i++){
			arr[input.charAt(i)] += 1;
			if(arr[input.charAt(i)]  > max){
				max = arr[input.charAt(i)] ;
				maxChar = input.charAt(i)+"";
			}
		}
		
		// 풀이
		System.out.println(maxChar);
		
		br.close();
		bw.close();
	}

}

결과

AMDKIDD   <== 입력
D              <== 결과
728x90
반응형
728x90
반응형

알고리즘 문제를 풀다보면 정렬을 해야하는 경우가 많이 발생합니다. 기본적으로 제공되는 Arrays.sort() 함수가 있지만 이것만으로 문제를 풀기에는 한계가 있습니다. 특히 다중차순을 요하는 문제는 직접 구현하는 것이 빠르고 이해가 쉽습니다.

 

기본적인 정렬 코드

아래는 오름차순, 내림차순, 다중차순의 기본 정렬 구현 코드입니다.

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Arrays;
import java.util.Comparator;


class Node implements Comparable<Node>{
	int a;
	int b;
	
	public Node(int a, int b){
		this.a = a;
		this.b = b;
	}

	@Override
	public int compareTo(Node tar) {
		
		// tar 기준으로 tar vs this 중 tar 우선순위가 더 높으면 1
		// tar 기준으로 우선순위가 동등하면 0
		// tar 기준으로 tar vs this 중 tar 우선순위가 더 낮으면 -1
		
		if(tar.a > this.a) return 1;
		if(tar.a < this.a) return -1;
		
		// 첫 수가 우선쉰위가 도등할때 아래 조건을 따른다.
		if (tar.b < this.b) return 1;
		if (tar.b > this.b) return -1;
		
		return 0;
	}
}

public class Sort_ex01 {

	// 정렬
	// 1. 오름차순 
	// 2. 내림차순 
	// 3. 다중차순

	static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

	
    public static void main(String[] args) throws IOException {

        Integer[] arr = {5,4,5,4,4,5};
        
        //1. 오름차순
        Arrays.sort(arr);
        
        //2. 내림차순
        Arrays.sort(arr, Comparator.reverseOrder());
        
        //3. 다중차순
        Node[] arrNode = new Node[6];
        arrNode[0] = new Node(5,8);
        arrNode[1] = new Node(4,2);
        arrNode[2] = new Node(5,1);
        arrNode[3] = new Node(4,9);
        arrNode[4] = new Node(4,7);
        arrNode[5] = new Node(5,3);
        
        // 정렬조건
        // 1) 첫번째 수가 큰수 우선
        // 2) 두번째 수가 작은 수 우선        
        Arrays.sort(arrNode);
        
        int a=1;

		br.close();
		bw.close();

    }
}

오름차순, 내림차순은 Arrays.sort() 함수를 이용하여 구현이 간단합니다. 하지만 다중차순의 경우 정렬 조건이 1) 첫번째 수가 큰수, 2) 두번째 수가 작은 수 우선이라고 한다면 위 코드와 같이 class 를 만들어 Comparable 을 이용하여 구현하여 줍니다. 정렬 우선순위는 compareTo() 함수를 오버라이드 하여 구현하는데, tar 기준으로 tar vs this 중 tar 우선순위가 더 높으면 1,  tar 기준으로 우선순위가 동등하면 0,  tar 기준으로 tar vs this 중 tar 우선순위가 더 낮으면 -1 이 중요합니다. 

이러한 방법을 통하여 "숫자와 문자를 다음과 같은 우선순위 조건으로 맞추어 정렬 후 출력" 하는 문제도 해결이 가능합니다. (ex, 1)짝수 우선 2)숫자 오름차순 3)문자 오름차순)

 

정렬은 알고리즘에서 응용을 많이 하는 것으로 방법을 꼭 숙지하여야 합니다.

728x90
반응형
728x90
반응형

플러드 필 알고리즘은 다차원 배열의 어떤 카과 연결된 영역을 찾는 알고리즘 입니다. 그림 프로그램에서 연결된 비슷한 색을 가지는 영역에 "채우기" 도구에 사용되며, 바둑이나 지뢰 찾기 같은 게임에서 어떤 비어 있는 칸을 표시 할 지를 결정할 때에도 사용됩니다.

 

동작과정

  1. 시작하는 칸을 큐에 넣고 방문했다는 표시를 한다.
  2. 큐에 원소를 꺼내어 그 칸에 인접한 칸(ex. 상,하,좌,우)에 대하여 3과 같이 동작한다.
  3. 인접한 칸에 대하여 방문여부를 판단하여 처음 방문했다면 방문 표시를 남기고 큐에 넣는다.
  4. 큐의 모든 원소가 빌 때까지 2의 행동을 반복한다.

시간복잡도

모든 칸이 큐에 1번 들어가는 것이 보장되므로 칸이 N개일 때 시간복잡도는 O(N) 입니다.

참고사이트

https://choijiuen.tistory.com/99
 

플러드 필(Flood Fill) - BFS , DFS

다차원 배열에서 어떤 칸과 연결된 영역을 찾는 알고리즘 그림판에서 색 채우기 명령으로 동일한 색을 전부 바꿔버리는 것과 동일하다. 플러드 필 문제는 BFS/DFS 알고리즘으로 해결할 수 있다. BF

choijiuen.tistory.com

https://ko.wikipedia.org/wiki/%ED%94%8C%EB%9F%AC%EB%93%9C_%ED%95%84
 

플러드 필 - 위키백과, 우리 모두의 백과사전

4방향 재귀적 플러드 필 플러드 필(영어: flood fill) 혹은 시드 필(영어: seed fill)은 다차원 배열의 어떤 칸과 연결된 영역을 찾는 알고리즘이다. 이 알고리즘은 그림 프로그램에서 연결된 비슷한 색

ko.wikipedia.org

기본코드

2차원 배열 (4*4) 에서 2,1 에서 시작하여 모든 배열을 도는데 걸리는 cnt를 알아내는 예제입니다.

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.LinkedList;
import java.util.Queue;

class PNode {
    int y, x;
    PNode(int a, int b)
    {
        y = a;
        x = b;
    }
}

class Main {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

    static int [][]map = new int[4][4];
    static Queue<PNode> q = new LinkedList<PNode>();

    public static void main(String[] args) throws IOException {        

        map[2][1] = 1;
        q.add(new PNode(2, 1));

        int[][] direct = {{-1, 0}, {1, 0}, {0, 1}, {0, -1}};
        int cnt = 0;

        while(!q.isEmpty()) {
            PNode now = q.poll();

            //now에서 다음 퍼지는 길 모두 등록
            for (int t = 0; t<4; t++ ) {
                int ny = now.y + direct[t][0];
                int nx = now.x + direct[t][1];
                if (ny < 0 || nx < 0 || ny >= 4 || nx >= 4) continue;
                if (map[ny][nx] != 0) continue;

                map[ny][nx] = map[now.y][now.x]+ 1;
                if (map[ny][nx] > cnt) cnt = map[ny][nx];

                q.add(new PNode(ny, nx));
            }
        }

        bw.write(cnt + "\n");

        br.close();
        bw.close();
    }
}

실행하여 map 의 변화를 알아보면 처음 map은 (2,1) 에서 시작하기 때문에 아래와 같다.

[0, 0, 0, 0]
[0, 0, 0, 0]
[0, 1, 0, 0]
[0, 0, 0, 0]

전부 완료되었을때 map 은 아래와 같이 변경된다.

[4, 3, 4, 5]
[3, 2, 3, 4]
[2, 1, 2, 3]
[3, 2, 3, 4]

위와 같은 결과로 모든 배열이 채워지는데 마지막 숫자인 5가 결과로 나타난다.

728x90
반응형

+ Recent posts