| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
- 서버
- 연관관계
- 백엔드공부
- 핵심가이드
- actuator
- 제로베이스
- 스프링부트
- queue
- spring
- 자료구조
- JWT
- SpringSecurity
- SpringBoot
- error
- 백엔드스쿨
- LinkedList
- spring boot
- 백준
- 백엔드
- Java
- JPA
- array
- TestCode
- 개발자
- ORM
- restTemplate
- 프로그래머스
- 컴퓨터 공학
- QueryDSL
- 컴퓨터 구조
- Today
- Total
be_better
[백준] 24174번 - 힙 정렬 2 본문
목차
💎 문제 링크
24174번: 알고리즘 수업 - 힙 정렬 2
2 5 1 4 3(heapify(A, 2, 5)) -> 2 3 1 4 5(heapify(A, 1, 5)) -> 1 3 2 4 5(A[1] <-> A[5]) -> 5 3 2 4 1(heapify(A, 1, 4)) -> 2 3 5 4 1(A[1] <-> A[4]) -> 4 3 5 2 1(heapify(A, 1, 3)) -> 3 4 5 2 1(A[1] <-> A[3]) -> 5 4 3 2 1(heapify(A,
www.acmicpc.net
📝 문제

heap_sort(A[1..n]) { # A[1..n]을 정렬한다.
build_min_heap(A, n);
for i <- n downto 2 {
A[1] <-> A[i]; # 원소 교환
heapify(A, 1, i - 1);
}
}
build_min_heap(A[], n) {
for i <- ⌊n / 2⌋ downto 1
heapify(A, i, n)
}
# A[k]를 루트로 하는 트리를 최소 힙 성질을 만족하도록 수정한다.
# A[k]의 두 자식을 루트로 하는 서브 트리는 최소 힙 성질을 만족하고 있다.
# n은 배열 A의 전체 크기이며 최대 인덱스를 나타낸다.
heapify(A[], k, n) {
left <- 2k; right <- 2k + 1;
if (right ≤ n) then {
if (A[left] < A[right]) then smaller <- left;
else smaller <- right;
}
else if (left ≤ n) then smaller <- left;
else return;
# 최소 힙 성질을 만족하지 못하는 경우 재귀적으로 수정한다.
if (A[smaller] < A[k]) then {
A[k] <-> A[smaller];
heapify(A, smaller, n);
}
}
✍🏻 입력 & 출력

🤔 풀기 전에 생각하기
슈도 코드가 제시되어 있기 때문에 그걸 자바 코드로 바꿔서 작성하기만 하면된다.
관건은 위치를 바꾸는 동작을 할 때 count를 증가 시키다가 count가 K와 같아질 때 return을 해야 한다는 것이다.
더이상 바꿀 노드가 없을 때 까지만 재귀함수가 동작하기 때문에 바꿀 노드가 없다면 함수를 알아서 빠져 나올 것이다.
그럼에도 count값이 K에 도달하지 못한 것이라면 두 번째 예제와 같은 상황이기 때문에 -1을 출력해야 한다.
✨ 풀이
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
public static int count = 0;
public static int k = 0;
public static boolean flag = false;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int n = Integer.parseInt(st.nextToken()); // n 입력
k = Integer.parseInt(st.nextToken()); // k 입력
int[] A = new int[n + 1]; // root는 0으로 채워서 비워놔야 한다.
st = new StringTokenizer(br.readLine());
for (int i = 1; i < A.length; i++) {
A[i] = Integer.parseInt(st.nextToken()); // A배열 채우기
}
heapSort(A, n); // 함수를 호출해도 더 이상 바꿀 게 없다면 그냥 빠져나옴
if (!flag) {
/*
* heapSort 함수를 빠져나왔다는 것은
* heap의 정렬이 모두 끝난것을 의미
* 정렬이 완료 되었어도 k보다 작다면
* -1을 출력
*/
System.out.println(-1);
}
}
public static void heapSort(int[] A, int n) {
buildMinHeap(A, n);
for (int i = n; i > 1; i--) {
swap(A, 1, i);
heapify(A, 1, i - 1);
}
}
public static void swap(int[] A, int a, int b) {
count++; // swap 할때 count 증가
int temp = A[a];
A[a] = A[b];
A[b] = temp;
if (count == k) { // count하다가 k수 만큼 교환을 했다면
StringBuilder sb = new StringBuilder();
for (int i = 1; i < A.length; i++) {
sb.append(A[i]);
sb.append(" ");
}
System.out.println(sb);
flag = true; // 원하는 횟수만큼 교환했거나 정렬을 마쳐서 더이상 교환할 필요가 없기 때문에 true로 바꿔줌
}
}
public static void buildMinHeap(int[] A, int n) {
for (int i = n / 2; i > 0; i--) {
heapify(A, i, n);
}
}
public static void heapify(int[] A, int k, int n) {
int left = 2 * k;
int right = 2 * k + 1;
int smaller = -1;
if (right <= n) {
smaller = A[left] < A[right] ? left : right;
} else if (left <= n) {
smaller = left;
} else {
return;
}
if (A[smaller] < A[k]) {
swap(A, k, smaller);
heapify(A, smaller, n);
}
}
}
🪲 시간초과
처음 정답 제출을 할 때 Scanner와 출력문은 String에 '+'연산으로 문자열을 이어붙여 출력문을 만들었다.
시간 초과가 뜬 것을 보고 백준 문제풀이를 할 때 Scanner사용에 주의해야 한다고 얼핏 본 기억이 있다.
입력은 공백과 '\n'으로 구분하여 입력해야 하기 때문에
Scanner 대신 BufferedReader 를 쓰고 StringTokenizer을 이용해 숫자 하나씩 받는 코드로 바꿨다.
출력문은 문자열을 '+'연산으로 이어붙여 출력하는 것이 아닌
StringBuilder를 사용해 append하였다. (StringBuffer 도 가능)
원래코드
package baekjoon.n24174;
import java.util.Scanner;
public class Main {
public static int count = 0;
public static int k = 0;
public static boolean flag = false;
public static void main(String[] args) throws IOException {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt(); // n 입력
k = scanner.nextInt(); // k 입력
int[] A = new int[n + 1]; // root는 0으로 채워서 비워놔야 한다.
for (int i = 1; i < A.length; i++) {
A[i] = scanner.nextInt(); // A배열 채우기
}
heapSort(A, n); // 함수를 호출해도 더 이상 바꿀 게 없다면 그냥 빠져나옴
if (!flag) {
/*
* heapSort 함수를 빠져나왔다는 것은
* heap의 정렬이 모두 끝난것을 의미
* 정렬이 완료 되었어도 k보다 작다면
* -1을 출력
*/
System.out.println(-1);
}
}
public static void heapSort(int[] A, int n) {
buildMinHeap(A, n);
for (int i = n; i > 1; i--) {
swap(A, 1, i);
heapify(A, 1, i - 1);
}
}
public static void swap(int[] A, int a, int b) {
count++; // swap 할때 count 증가
int temp = A[a];
A[a] = A[b];
A[b] = temp;
if (count == k) { // count하다가 k수 만큼 교환을 했다면
for (int i = 1; i < A.length; i++) {
System.out.print(A[i] + " ");
}
flag = true; // 원하는 횟수만큼 교환했거나 정렬을 마쳐서 더이상 교환할 필요가 없기 때문에 true로 바꿔줌
}
}
public static void buildMinHeap(int[] A, int n) {
for (int i = n / 2; i > 0; i--) {
heapify(A, i, n);
}
}
public static void heapify(int[] A, int k, int n) {
int left = 2 * k;
int right = 2 * k + 1;
int smaller = -1;
if (right <= n) {
smaller = A[left] < A[right] ? left : right;
} else if (left <= n) {
smaller = left;
} else {
return;
}
if (A[smaller] < A[k]) {
swap(A, k, smaller);
heapify(A, smaller, n);
}
}
}
'코딩테스트 > 백준' 카테고리의 다른 글
| [백준] 10807번 - 개수 세기 (0) | 2023.08.15 |
|---|---|
| [백준] 2830번 - 행성 X3 (0) | 2023.08.15 |
| [백준] 1158번 - 요세푸스 문제 (0) | 2023.08.11 |
| [백준] 10818번 - 최소, 최대 (0) | 2023.08.09 |
| [백준] 1021번 - 회전하는 큐 (0) | 2023.08.08 |