본문 바로가기

코딩테스트

[코딩테스트] 백준 - N번째 큰 수

N번째 큰 수

 

문제

N×N의 표에 수 N2개 채워져 있다. 채워진 수에는 한 가지 특징이 있는데, 모든 수는 자신의 한 칸 위에 있는 수보다 크다는 것이다. N=5일 때의 예를 보자.

 

 

이러한 표가 주어졌을 때, N번째 큰 수를 찾는 프로그램을 작성하시오. 표에 채워진 수는 모두 다르다.

입력

첫째 줄에 N(1 ≤ N ≤ 1,500)이 주어진다. 다음 N개의 줄에는 각 줄마다 N개의 수가 주어진다. 표에 적힌 수는 -10억보다 크거나 같고, 10억보다 작거나 같은 정수이다.

출력

첫째 줄에 N번째 큰 수를 출력한다.

 

 

-> 배열에서 정렬로 풀기

 

처음에는 쉽게 배열로 넣어서 정렬을 하면 되겠다고 생각을 하였다.

 

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

class Main {
	public static void main(String[] args) throws Exception{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer stringTokenizer;

        int number = Integer.parseInt(br.readLine());
        int[] arr = new int[number * number];
        int index = 0;

        for(int i = 0 ; i < number; i++) {
            stringTokenizer = new StringTokenizer(br.readLine());
            for(int j=0; j < number; j++) {
                arr[index++] = Integer.parseInt(stringTokenizer.nextToken());
            }
        }

        Arrays.sort(arr);
        System.out.println(arr[number*number - number]);
    }
}

 

 

결과적으로 시간은 1648ms 시간이 나왔다.

여기서 더 시간을 줄여볼 수 없을 까? 라는 생각을 하였다.

 

 

-> 우선 순위 큐 사용하여 풀기

 

PriortyQueue로 풀어봤다.

 

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));
        StringTokenizer st;

        PriorityQueue<Integer> pq = new PriorityQueue<>((o1, o2) -> o2-o1);

        int N = Integer.parseInt(br.readLine());

        for(int i=0;i<N;i++) {
            st = new StringTokenizer(br.readLine());
            for(int j=0;j<N;j++) {
                pq.add(Integer.parseInt(st.nextToken()));
            }
        }

        for(int i=0;i<N-1;i++) pq.poll();

        System.out.println(pq.poll());
    }
}

 

 

 

 

여기서 더 속도를 줄일 수 있을 까? 라고 생각하면서 검색을 해봤는데, peek로 더큰지 계산 후, 넣을 지 말지를 계산하는 방법이 있었다.

 

-> peek 사용 

 

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));
        StringTokenizer st;

        int n = Integer.parseInt(br.readLine());
        PriorityQueue<Integer> pq = new PriorityQueue<Integer>();

        st = new StringTokenizer(br.readLine());
        for(int i=0; i<n; i++) {
            int temp = Integer.parseInt(st.nextToken());
            pq.offer(temp);
        }

        for(int i=1; i<n; i++) {
            st = new StringTokenizer(br.readLine());
            for(int j=0; j<n; j++) {
                int temp = Integer.parseInt(st.nextToken());

                if(pq.peek() < temp) {
                    pq.poll();
                    pq.offer(temp);
                }
            }
        }

        System.out.println(pq.poll());
    }

}

 

 

 

peek()를 사용한 방법이 별로 시간의 변화를 많이 시켜주지는 않는것같았다.

 

 

-> QuickSelct 알고리즘 사용하기

 

우선 순위 큐를 사용하기 않고 QuickSelect 알고리즘을 사용하여 문제 풀기

 

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));
        StringTokenizer stringTokenizer;

        int number = Integer.parseInt(br.readLine());
        int[] arr = new int[number * number];
        int index = 0;

        for(int i = 0 ; i < number; i++) {
            stringTokenizer = new StringTokenizer(br.readLine());
            for(int j=0; j < number; j++) {
                arr[index++] = Integer.parseInt(stringTokenizer.nextToken());
            }
        }

        System.out.println(quickSelect(arr, 0, arr.length-1, number-1));
    }

    private static int quickSelect(final int[] NUMS, int start, int end, int k) {
        if (start <= end) {
            int pivot = partition(NUMS, start, end);

            if (pivot == k) {
                return NUMS[k];
            }

            else if (pivot > k) {
                return quickSelect(NUMS, start, pivot - 1, k);
            }

            else {
                return quickSelect(NUMS, pivot + 1, end, k);
            }
        }

        return Integer.MIN_VALUE;
    }

    private static int partition(final int[] NUMS, int start, int end) {
        int pivot = start + new Random().nextInt(end - start + 1);

        swap(NUMS, end, pivot);

        for (int i = start; i < end; i++) {
            if (NUMS[i] > NUMS[end]) {
                swap(NUMS, i, start);
                start++;
            }
        }

        swap(NUMS, start, end);

        return start;
    }

    private static void swap(final int[] nums, final int aIdx, final int bIdx) {
        int tmp = nums[aIdx];
        nums[aIdx] = nums[bIdx];
        nums[bIdx] = tmp;
    }

}

 

 

시간적으로 제일 빠르게 해결이 가능하다.