Java/C/Python


문제


문제

1. [Java] 암호 해독 - cipher.charAt(i - 1)


https://school.programmers.co.kr/learn/courses/30/lessons/120892

오답

class Solution {
    public String solution(String cipher, int code) {
        StringBuilder sb = new StringBuilder();
        for (int i = code; i <= cipher.length(); code++;) {
            sb.append(cipher[i-1]); // 인덱스는 0부터 시작
        }
        return sb.toString();
    }
}


✅ for문의 증가식 오류
for (int i = code; i <= cipher.length(); code++;)


✅ 문자열 인덱스 접근 오류


정답 코드

class Solution {
    public String solution(String cipher, int code) {
        StringBuilder sb = new StringBuilder();
        for (int i = code; i <= cipher.length(); i += code) {
            sb.append(cipher.charAt(i - 1)); // 인덱스는 0부터 시작
        }
        return sb.toString();
    }
}



문제

2. [Java] 대문자와 소문자

https://school.programmers.co.kr/learn/courses/30/lessons/120893

오답

class Solution {
    public String solution(String my_string) {
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < my_string.length(); i++) {
            if(Character.isUpperCase((my_string.charAt(i)))) {
                sb.append(Character.toLowerCase(my_string.charAt(i)));
            } else {
                sb.append(Character.toUpperCase(my_string.charAt(i)));
            }
        }
        return sb.toString();
    }
}

✅ my_string.charAt(i)는 위로 빼서 char c = my_string.charAt(i); 로 만들어 주면 코드가 간단해진다.


개선한 코드

class Solution {
    public String solution(String my_string) {
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < my_string.length(); i++) {
            char c = my_string.charAt(i);
            if(Character.isUpperCase(c)) {
                sb.append(Character.toLowerCase(c));
            } else {
                sb.append(Character.toUpperCase(c));
            }
        }
        return sb.toString();
    }
}



문제

3. [Java] 영어가 싫어요 - numbers.replaceAll(a,b);

https://school.programmers.co.kr/learn/courses/30/lessons/120894

틀린 코드

class Solution {
    public long solution(String numbers) {
        String[] words = {
            "zero", "one", "two", "three", "four", "five",
            "six", "seven", "eight", "nine"
        };
        for (int i = 0; i < words.length; i++) {
            numbers = numbers.replace(words[i], String.valueOf(i));
        }
        return Integer.parseInt(numbers);
    }
}

테스트는 통과했으나 답안 제출시 런타임 에러

image.png

image.png

✅ Integer.parseInt() 사용 시 런타임 에러

개선한 코드

class Solution {
    public long solution(String numbers) {
        String[] words = {
            "zero", "one", "two", "three", "four", "five",
            "six", "seven", "eight", "nine"
        };
        for (int i = 0; i < words.length; i++) {
            numbers = numbers.replaceAll(words[i], String.valueOf(i));
        }
        return Long.parseLong(numbers); // ✅ int → long 으로 변경
    }
}



기본형 VS 래퍼클래스


✅ 기본형 (Primitive type)
👉 메모리에 값 자체를 저장함
👉 성능이 빠르고 메모리 사용이 효율적


✅ 참조형 (Reference type, Wrapper class)
👉 객체이기 때문에 null 값을 가질 수 있음
👉 ArrayList<Integer>처럼 제네릭에 사용 가능
👉 기본형은 제네릭에서 사용할 수 없음 → 참조형 필요



문제

4. [Java] 인덱스 바꾸기 - return new String(arr);

https://school.programmers.co.kr/learn/courses/30/lessons/120895

틀린 코드

class Solution {
    public String solution(String my_string, int num1, int num2) {
        String[] arr = String.toCharArray(my_string);
        char tmp = arr[num1];
        arr[num1] = arr[num2];
        arr[num2] = tmp;
        return arr.toString();
    }
}
✅ String[] arr = String.toCharArray(my_string);
✅ String.toCharArray() 용법
✅ return arr.toString();


개선한 코드

class Solution {
    public String solution(String my_string, int num1, int num2) {
        char[] arr = my_string.toCharArray();
        char tmp = arr[num1];
        arr[num1] = arr[num2];
        arr[num2] = tmp;
        return new String(arr);
    }
}



클래스 메서드 VS 인스턴스 메서드


자바에서 메서드는 크게 인스턴스 메서드와 **클래스 메서드(static 메서드)**로 나뉘는데, 이 둘은 메서드를 어떻게 호출하고 언제 사용하는지에 따라 중요한 차이를 가진다.

클래스 메서드는 static 키워드로 정의되어 있어 객체를 만들지 않아도 클래스 이름으로 바로 호출할 수 있는 메서드이다. 클래스 메서드는 객체의 고유 상태와는 무관하며, 보통 모든 인스턴스에서 공통으로 사용하는 로직을 구현할 때 사용됩니다. 대표적인 예로 Integer.parseInt("123")는 문자열을 정수로 변환하는 클래스 메서드로, Integer라는 클래스 이름만으로 바로 호출할 수 있다.

반면 인스턴스 메서드는 특정 객체가 생성되어야만 사용할 수 있는 메서드이다. 즉, new 키워드로 객체를 만든 후에야 호출이 가능하며, 주로 그 객체의 상태(멤버 변수)를 기반으로 동작한다. 예를 들어, str.length()는 문자열 객체 str의 길이를 반환하는 인스턴스 메서드인데, 이는 str이라는 객체가 존재해야만 호출할 수 있다.

인스턴스 메서드는 객체마다 상태가 다를 수 있기 때문에, 같은 메서드를 호출하더라도 객체에 따라 결과가 달라진다. 예를 들어, Person이라는 클래스에 sayHello()라는 인스턴스 메서드가 있다면, person1.sayHello()와 person2.sayHello()는 각각 다른 이름을 출력한다. 이는 각 인스턴스가 가지고 있는 name 값이 다르기 때문이다.

이처럼 인스턴스 메서드는 객체의 고유한 데이터를 다루고 조작할 때 유용하며, 클래스 메서드는 그와 무관한 공통 기능을 제공할 때 사용된다. 개발자는 기능의 목적에 따라 두 메서드 중 적절한 쪽을 선택해야 한다. 객체마다 달라지는 동작이 필요하다면 인스턴스 메서드, 객체 없이 호출해도 되는 범용적인 기능이라면 클래스 메서드를 사용하는 것이 좋다.


✅ 1. 인스턴스 메서드 (Instance Method)

정의

특징

예시

public class Person {
    String name;

    public void sayHello() {
        System.out.println("Hello, my name is " + name);
    }
}

// 사용
Person p = new Person();
p.name = "Dain";
p.sayHello();  // 출력: Hello, my name is Dain


✅ 2. 클래스 메서드 (Class Method)

정의

특징

예시

public class MathUtil {
    public static int add(int a, int b) {
        return a + b;
    }
}

// 사용
int result = MathUtil.add(3, 5);  // 객체 없이 호출 가능



문제

5. [Java] 한 번만 등장한 문자 (문자열) - sb.toString();

https://school.programmers.co.kr/learn/courses/30/lessons/120896

틀린 코드

import java.util.*;

class Solution {
    public String solution(String s) {
        int[] cnt = new int[26];
        
        for(char c : s.toCharArray()) {
            cnt[c - 'a']++;
        }
        
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 26; i++) {
            if (cnt[i] == 1) sb.append((char)i+'a');
        }
        return sb.toString(); 
    }
}

image.png


✅ sb.append((char)i + 'a');
(char)i + 'a' // => 정수 + 정수 → int로 연산됨

→ i + 'a' 전체를 char로 캐스팅해야 문자로 제대로 변환된다.

sb.append((char)(i + 'a'));


개선한 코드

import java.util.*;

class Solution {
    public String solution(String s) {
        int[] cnt = new int[26];
        
        for(char c : s.toCharArray()) {
            cnt[c - 'a']++;
        }
        
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 26; i++) {
            if (cnt[i] == 1) sb.append((char)(i + 'a'));
        }
        return sb.toString(); 
    }
}




문제

6. [Java] 약수구하기 - list.toArray(new String[0]);

https://school.programmers.co.kr/learn/courses/30/lessons/120897

틀린 코드

import java.util.*;

class Solution {
    public int[] solution(int n) {
        ArrayList<Integer> list = new ArrayList<>();
        for (int i = 0; i <= n; i++) {
            if(n%i==0) list.append(i);
        }
        int[] answer = list.toArray(new String[0]);
        return answer;
    }
}

image.png

✅ for (int i = 0; i <= n; i++)
✅ list.add(i)
✅ int[] answer = list.toArray(new String[0]);


개선한 코드

import java.util.*;

class Solution {
    public int[] solution(int n) {
        ArrayList<Integer> list = new ArrayList<>();
        for (int i = 1; i <= n; i++) {
            if(n%i==0) list.add(i);
        }
        int[] answer = new int[list.size()];
        for(int i = 0; i < list.size(); i++) {
            answer[i] = list.get(i);
        }
        return answer;
    }
}


Stream 사용 버전

import java.util.*;
import java.util.stream.*;

class Solution {
    public int[] solution(int n) {
        return IntStream.rangeClosed(1, n) // 1부터 n까지 숫자 생성
                .filter(i -> n % i == 0)   // 약수만 필터링
                .toArray();                // int[]로 반환
    }
}




List → Array 형변환

List 타입

변환 방법

List<String>

list.toArray(new String[0])

List<Integer>

list.toArray(new Integer[0])

List<Integer>int[]

반복문으로 수동 변환 필요


✅ 1. List → Array (참조형, 예: Integer, String 등)
List<String> list = Arrays.asList("a", "b", "c");
String[] arr = list.toArray(new String[0]);
List<Integer> list = Arrays.asList(1, 2, 3);
Integer[] arr = list.toArray(new Integer[0]);


✅ 2. List → Array (기본형, 예: int)
List<Integer> list = Arrays.asList(1, 2, 3);
int[] arr = new int[list.size()];

for (int i = 0; i < list.size(); i++) {
    arr[i] = list.get(i);  // 언박싱 자동 수행
}




Array → List 형변환


변환 방법

String[]

Arrays.asList(arr)

Integer[]

Arrays.asList(arr)

int[]

Arrays.stream(arr).boxed().collect(Collectors.toList())


✅ 1. 참조형 배열 → List (예: String[], Integer[] 등)
String[] arr = {"a", "b", "c"};
List<String> list = Arrays.asList(arr);
List<String> modifiableList = new ArrayList<>(Arrays.asList(arr));


✅ 2. 기본형 배열 → List (예: int[], double[] 등)
int[] arr = {1, 2, 3, 4};

List<Integer> list = Arrays.stream(arr)  // IntStream 생성
                           .boxed()      // int → Integer (박싱)
                           .collect(Collectors.toList());



문제

7. [Java] 가장 큰 수 찾기- return new int[]{max, idx};

https://school.programmers.co.kr/learn/courses/30/lessons/120899

틀린 코드

import java.util.*;

class Solution {
    public int[] solution(int[] array) {
        Arrays.sort(array);
        int[] answer = new int[2];
        answer[0] = array[array.length-1];
        answer[1] = array.length-1;
        return answer;
    }
}

image.png

✅ index 계산 잘못됨


개선한 코드

import java.util.*;

class Solution {
    public int[] solution(int[] array) {
        int max = array[0];
        int idx = 0;
        
        for(int i = 1; i < array.length; i++) {
            if (array[i] > max) {
                max = array[i];
                idx = i;
            }
        }
        return new int[]{max, idx};
    }
}


배열 리터럴을 반환할 때 쓰는 코드

int형 두 개짜리 배열을 직접 생성하고, 그 배열을 메서드의 반환값으로 사용하는 코드이다.

return new int[]{max, idx};

아래와 같이 배열을 만들고 반환하는 코드인데 한 줄로 줄인 것이다.

int[] result = new int[]{max, idx};
return result;

Java에서 return은 값을 반환하기만 하면 되기 때문에, 굳이 int[] result = ...처럼 중간에 변수에 담을 필요가 없다.


배열 리터럴은 선언 없이 쓰면 컴파일 오류가 난다.

int[] a;
a = {1, 2}; // ❌ 컴파일 오류

→ 대신 이렇게 써야 한다.

int[] a = new int[]{1, 2};



문제

8. [Java] 문자열 계산하기 - Character.isDigit(c)

https://school.programmers.co.kr/learn/courses/30/lessons/120902

틀린 코드 (1차 시도)

class Solution {
    public int solution(String my_string) {
        char[] arr = my_string.toCharArray();
        int sum = 0;
        for (char c : arr ) {
            if (c != '+' && c != '-' && c !=' ') {
                sum+= Integer.parseInt(c);
            }
        }
        return sum;
    }
}

image.png

✅ Integer.parseInt()


String → int VS char → int

1. String을 int로 변환할 때는 Integer.parseInt()

String str = "123";

// 문자열 → int
int num = Integer.parseInt(str);       // 123

2. char을 숫자로 변환할 때는 char 타입 변수 - '0'

char c = '7';

// 문자 → 숫자 (정수값)
int digit = c - '0';   // '7' - '0' = 7


틀린 코드 (2차 시도)

class Solution {
    public int solution(String my_string) {
        char[] arr = my_string.toCharArray();
        int sum = 0;
        for (char c : arr ) {
            if (c != '+' && c != '-' && c !=' ') {
                sum+= c - '0';
            }
        }
        return sum;
    }
}

image.png

내가 쓴 코드는 모든 숫자를 "한 자리 숫자"로 처리한다. 즉, "12"가 있으면 1 + 2 = 3으로 계산한다.

String my_string = "a12b3";
→ arr = ['a', '1', '2', 'b', '3']
→ 결과: 1 + 2 + 3 = 6 (❗ 실제 12 + 3이 아님)

문제에서 원하는 것은 만약 "12 + 3" → 15로 계산하는 것이다.

String num 변수를 밖으로 빼고, Character.isDigit() 으로 c가 0~9사이 숫자형 문자열이면 num에 저장하는 형식으로 코드를 짜 주었다.


틀린 코드 (3차 시도)

class Solution {
    public int solution(String my_string) {
        char[] arr = my_string.toCharArray();
        int sum = 0;
        String num = "";
        for (char c : arr ) {
            if (Character.isDigit(c)) {
                num += c;
            } else {
                if (!num.isEmpty()) {
                    sum += Integer.parseInt(num);
                    num = ""; 
                }
            }
        }
        if (!num.isEmpty()) {
            sum += Integer.parseInt(num);
        }
        return sum;
    }
}

image.png

덧셈만 하는게 아니라 뺄셈도 있었다. 연산자(+, -)에 따라 연산을 모두 처리하는 코드를 짜야 한다.

  1. 문자열을 " "(공백) 기준으로 나눠서
  2. 처음 값은 result에 저장
  3. 이후에는 연산자(+ 또는 -)와 숫자가 번갈아 등장
  4. 연산자에 따라 +면 더하고, -면 빼야 함


틀린 코드 (4차 시도)

class Solution {
    public int solution(String my_string) {
        String[] splited = my_string.split(" ");
        int answer = Integer.parseInt(splited[0]);
        
        for(int i = 1; i < splited.length; i += 2) {
            String op = splited[i];
            int num = Integer.parseInt(splited[i + 1]);
            
            if (op == '+')) {
                answer += num;
            } else if (op == '-') {
                answer -= num;
            }
        }
        return answer;
    }
}
✅ if(op == '+')


개선한 코드

class Solution {
    public int solution(String my_string) {
        String[] splited = my_string.split(" ");
        int answer = Integer.parseInt(splited[0]);
        
        for(int i = 1; i < splited.length; i += 2) {
            String op = splited[i];
            int num = Integer.parseInt(splited[i + 1]);
            
            if (op.equals("+")) {
                answer += num;
            } else if (op.equals("-")) {
                answer -= num;
            }
        }
        return answer;
    }
}



문제

9. [Java] 배열의 유사도(배열, Set) - set.contains(str)

https://school.programmers.co.kr/learn/courses/30/lessons/120903

Set 을 활용한 코드

import java.util.*;

class Solution {
    public int solution(String[] s1, String[] s2) {
        Set<String> set = new HashSet<>(Arrays.asList(s1));
        int cnt = 0;
        for (String str : s2) {
            if (set.contains(str)) cnt++;
        }
        return cnt; 
    }
}


✅ Set<String> set = new HashSet<>(Arrays.asList(s1));


이중 for문(반복문)을 사용한 코드

class Solution {
    public int solution(String[] s1, String[] s2) {
        int cnt = 0;
        for (int i = 0; i < s1.length; i++) {
            for (int j = 0; j < s2.length; j++) {
                if (s1[i].equals(s2[j])) {
                    cnt++;
                    break;
                }
            }
        }
        return cnt;
    }
}


문제

10. [Java] 숫자 찾기 - String.indexOf(), String.valueOf()

https://school.programmers.co.kr/learn/courses/30/lessons/120904

틀린 코드

class Solution {
    public int solution(int num, int k) {
        String numToString = Integer.toString(num);
        char[] arr = numToString.toCharArray();
        char kToChar = (char) k; 
        int idx = -1;
        for(int i = 0; i < arr.length; i++ ) {
            if (arr[i] == kToChar) {
                return i+1;
            }
        }
        return idx;
    }
}
✅ char kToChar = (char) (k + '0');

개선한 코드

class Solution {
    public int solution(int num, int k) {
        String numToString = Integer.toString(num);
        char[] arr = numToString.toCharArray();
        char kToChar = (char) (k + '0');
        int idx = -1;
        for(int i = 0; i < arr.length; i++ ) {
            if (arr[i] == kToChar) {
                return i+1;
            }
        }
        return idx;
    }
}


더 간단한 방법 (String API 활용)

public int solution(int num, int k) {
    String s = String.valueOf(num);
    int idx = s.indexOf(String.valueOf(k));
    return idx == -1 ? -1 : idx + 1;
}


✅ String.indexOf()

문자열 내에서 특정 문자나 문자열이 처음 등장하는 위치의 인덱스를 알려주는 매우 유용한 메서드이다.

📌 기본 사용법

String s = "29183";
int idx = s.indexOf("1");   // 결과: 2

📌 자바 공식 문법

int indexOf(int ch)           // 문자 또는 문자 코드 (ex: 'a', 97)
int indexOf(String str)       // 문자열


✅ 삼항연산자

자바의 삼항 연산자(ternary operator)는 if-else를 한 줄로 간단하게 쓸 수 있게 해주는 조건 연산자이다.

📌 기본 사용법

조건식 ? 참일 때 값 : 거짓일 때 값;

📌 예제

int age = 20;
String result = (age >= 18) ? "성인" : "미성년자";
System.out.println(result);  // 출력: 성인



문제

11. [Java] 삼각형의 완성조건(2): case2는 왜 longer - shorter인가?

1. 문제 요약


2. 정수 범위 공식

코드

import java.util.*;
 
class Solution {
    public int solution(int[] sides) {
        Arrays.sort(sides); // 배열 오름차순 정렬
        int shorter = sides[0];
        int longer = sides[1];
        
        // case1: x가 가장 긴 변일 때 -> x < a + b
        int case1 = longer + shorter - 1; 
        
        // case2: x가 짧거나 중간인 경우 -> x > max - min
        int case2 = longer - shorter;
        
        return case1 - case2;
    }
}

3. longer - shorter + 1이 아닌 이유


4. 예시


요약



문제

12. [Java] 옹알이(1): break vs continue

1. 문제 요약


2. 전체 코드

import java.util.*;

class Solution {
    public int solution(String[] babbling) {
        String[] canSay = {"aya", "ye", "woo", "ma"};
        int count = 0;

        for (String word : babbling) {
            String temp = word;
            boolean isValid = true;

            for (String say : canSay) {
                // 중복된 발음이 있는지 확인
                if (temp.contains(say + say)) {
                    isValid = false;
                    break;
                }
            }

            if (!isValid) continue;

            // 발음 가능 단어 제거
            for (String say : canSay) {
                temp = temp.replace(say, " ");
            }

            // 모두 제거되었는지 확인 (공백만 남은 경우 trim()으로 제거)
            if (temp.trim().isEmpty()) {
                count++;
            }
        }

        return count;
    }
}


3. 같은 단어 중복인지 확인하는 코드

if (temp.contains(say + say)) {
    isValid = false;
    break;
}

4. continue

for (String word : babbling) {
    // 어떤 조건으로 유효한 발음인지 검사
    if (!isValid) continue;

    // 여기 있는 코드는 건너뜀!
}

5. break

for (String say : canSay) {
    if (temp.contains(say + say)) {
        isValid = false;
        break; // ✅ 이 for 루프를 빠져나옴!
    }
}

여기서 break;는 바로 canSay 배열을 순회하는 for문을 탈출한다.

따라서 그 다음 실행되는 건 → continue; 실행됨 → 이 단어는 skip하고 다음 단어로 넘어가는 로직이 실행된다.

if (!isValid) continue;

6. continue VS break

for each word in babbling:
    for each say in canSay:
        if word contains say+say:
            isValid = false
            break ← 여기서 안쪽 for문 탈출
    if (!isValid) continue ← 이 줄로 와서 현재 word 스킵



주제별 정리


주제별 정리

Java Coding Test Cheatsheet


0. Frequently Used Libraries

import java.util.*;      // Data structures
import java.io.*;        // Fast I/O
import java.math.*;      // BigInteger, BigDecimal

1. Variable & Array Declaration

String[] arr1 = new String[5];
int[] arr2 = {1, 2, 3};
int N = 3;
int[] arr3 = new int[N];

2. Arrays Utility

Arrays.sort(arr);                                 // Ascending
Arrays.sort(arr, Collections.reverseOrder());     // Descending
Arrays.sort(arr, 0, 4);                           // Partial sort
Arrays.binarySearch(arr, 2);                      // Binary search
List<String> list = Arrays.asList(arr1);          // Convert to List
int[] tmp = Arrays.copyOfRange(arr, 0, 3);        // Slice

3. length / length() / size()

arr.length        // Arrays
str.length()      // String
list.size()       // Collections

4. String Handling

str.split(" ");
str.substring(0, 5);
str.charAt(i);
str.toUpperCase();
str.toLowerCase();
String[] letters = str.split("");
String newStr = str.substring(0,4) + "X" + str.substring(5);

5. HashMap

Map<String, Integer> map = new HashMap<>();
map.put("key", 1);
map.get("key");
map.containsKey("key");
map.getOrDefault("key", 0);
for (String k : map.keySet()) map.get(k);

6. ArrayList

List<String> list = new ArrayList<>();
list.add("a");
list.set(0, "b");
list.remove("b");
list.contains("a");
list.indexOf("a");

7. Queue (LinkedList)

Queue<Integer> q = new LinkedList<>();
q.offer(1);
q.poll();
q.peek();
q.clear();
q.isEmpty();

8. PriorityQueue

PriorityQueue<Integer> pq = new PriorityQueue<>();
PriorityQueue<Integer> maxPq = new PriorityQueue<>(Collections.reverseOrder());

9. Math

Math.max(a, b);
Math.min(a, b);
Math.abs(x);
Math.ceil(x);
Math.floor(x);
Math.round(x);
String.format("%.2f", d);  // Round to 2 decimal
Math.pow(a, b);
Math.sqrt(x);

10. HashSet

Set<String> set = new HashSet<>();
set.add("a");
set.remove("a");
set.contains("a");

11. Stack

Stack<Integer> stack = new Stack<>();
stack.push(1);
stack.pop();
stack.peek();

12. Deque (ArrayDeque)

Deque<Integer> dq = new ArrayDeque<>();
dq.addFirst(1);
dq.addLast(2);
dq.pollFirst();
dq.pollLast();

13. TreeSet

TreeSet<Integer> ts = new TreeSet<>();
ts.add(5);
ts.first();
ts.last();
ts.lower(5);
ts.higher(5);

14. TreeMap

TreeMap<String, Integer> tm = new TreeMap<>();
tm.put("apple", 3);
tm.firstKey();
tm.lastKey();

15. Fast I/O

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int a = Integer.parseInt(st.nextToken());
String[] parts = br.readLine().split(" ");

BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
bw.write("Hello\n");
bw.flush();
bw.close();

16. DFS

void dfs(int node) {
    visited[node] = true;
    for (int next : graph.get(node)) {
        if (!visited[next]) dfs(next);
    }
}

17. BFS

Queue<Integer> q = new LinkedList<>();
q.offer(start);
visited[start] = true;
while (!q.isEmpty()) {
    int cur = q.poll();
    for (int next : graph.get(cur)) {
        if (!visited[next]) {
            q.offer(next);
            visited[next] = true;
        }
    }
}

18. Sorting

list.sort(Comparator.naturalOrder());
list.sort(Comparator.reverseOrder());
people.sort(Comparator.comparingInt(p -> p.age));

19. DP (Fibonacci)

int[] dp = new int[N+1];
dp[0] = 0; dp[1] = 1;
for (int i = 2; i <= N; i++) dp[i] = dp[i-1] + dp[i-2];

20. Union-Find

int find(int x) {
    if (x != parent[x]) parent[x] = find(parent[x]);
    return parent[x];
}
void union(int a, int b) {
    a = find(a); b = find(b);
    if (a != b) parent[b] = a;
}

21. Bitmask

int bit = 0;
bit |= (1 << 3);
bit &= ~(1 << 3);
boolean on = (bit & (1 << 3)) != 0;
Integer.bitCount(bit);

22. Greedy

int[] coins = {500, 100, 50, 10};
int count = 0;
for (int coin : coins) {
    count += target / coin;
    target %= coin;
}

23. Prefix Sum

int[] prefix = new int[N+1];
for (int i = 0; i < N; i++) prefix[i+1] = prefix[i] + arr[i];

24. Sliding Window

int sum = 0;
for (int i = 0; i < k; i++) sum += arr[i];
int max = sum;
for (int i = k; i < N; i++) {
    sum += arr[i] - arr[i-k];
    max = Math.max(max, sum);
}

25. Sieve of Eratosthenes

boolean[] isPrime = new boolean[N+1];
Arrays.fill(isPrime, true);
for (int i = 2; i*i <= N; i++) {
    if (isPrime[i]) {
        for (int j = i*i; j <= N; j += i) isPrime[j] = false;
    }
}

26. Dijkstra

PriorityQueue<int[]> pq = new PriorityQueue<>(Comparator.comparingInt(a -> a[1]));
int[] dist = new int[N+1];
Arrays.fill(dist, INF);
dist[start] = 0;
pq.offer(new int[]{start, 0});

27. Tree / LCA

void dfs(int node, int par, int d) {
    parent[node] = par;
    depth[node] = d;
    for (int next : tree.get(node)) {
        if (next != par) dfs(next, node, d+1);
    }
}

28. Coding Test Patterns

29. Backtracking (Permutation, Combination)

void permute(List<Integer> list, boolean[] used) {
    if (list.size() == N) {
        System.out.println(list);
        return;
    }
    for (int i = 0; i < N; i++) {
        if (!used[i]) {
            used[i] = true;
            list.add(arr[i]);
            permute(list, used);
            list.remove(list.size() - 1);
            used[i] = false;
        }
    }
}

30. Binary Search / Parametric Search

int left = 1, right = maxVal;
while (left <= right) {
    int mid = (left + right) / 2;
    if (condition(mid)) {
        answer = mid;
        right = mid - 1;
    } else {
        left = mid + 1;
    }
}

31. Recursion Optimization

32. String Algorithms (KMP)

int[] makeTable(String pattern) {
    int[] table = new int[pattern.length()];
    int j = 0;
    for (int i = 1; i < pattern.length(); i++) {
        while (j > 0 && pattern.charAt(i) != pattern.charAt(j)) j = table[j-1];
        if (pattern.charAt(i) == pattern.charAt(j)) table[i] = ++j;
    }
    return table;
}

33. Permutations / Combinations / Bitmask

// Bitmask Combination
for (int mask = 0; mask < (1 << N); mask++) {
    for (int i = 0; i < N; i++) {
        if ((mask & (1 << i)) != 0) {
            // element i is selected
        }
    }
}

34. BigInteger / BigDecimal

BigInteger a = new BigInteger("12345678901234567890");
BigInteger b = new BigInteger("98765432109876543210");
BigInteger sum = a.add(b);
BigDecimal dec = new BigDecimal("1234.5678");
BigDecimal result = dec.setScale(2, RoundingMode.HALF_UP);

출처 : chatGPT


주제별 정리

Java 배열 관련 주요 메서드 정리


1. 배열 비교: Arrays.equals()

Arrays.equals(array1, array2);

설명

내용

비교 대상

두 배열의 크기와 요소 값을 모두 비교

반환값

true(같음), false (다름)

주의

== 또는.equals()는 배열 주소 비교이므로 사용 ❌

🔍 예시:

int[] a = {1, 2, 3};
int[] b = {1, 2, 3};
System.out.println(Arrays.equals(a, b)); // ✅ true
System.out.println(a == b);              // ❌ false (주소 비교)

2. 배열 복사: Arrays.copyOf()

Arrays.copyOf(원본배열, 복사할길이);

원본 배열의 앞에서부터 지정한 길이만큼 복사하여 새 배열을 생성한다.
반환값은새로운 배열이고, 원본과 별개의 객체이다.

🔍 예시:

int[] original = {1, 2, 3, 4};
int[] copied = Arrays.copyOf(original, 2);  // [1, 2]

✨ 확장 사용: 길이 초과 시 0으로 채움

int[] extended = Arrays.copyOf(original, 6);  // [1, 2, 3, 4, 0, 0]

3. 부분 복사: Arrays.copyOfRange()

Arrays.copyOfRange(배열, 시작인덱스, 끝인덱스);

시작 인덱스부터 끝 인덱스 전까지 복사한다.

반환값은 새로운 배열 (구간 복사)이다.

한 마디로 배열의 슬라이싱을 구현한다.

🔍 예시:

int[] sub = Arrays.copyOfRange(original, 1, 3);  // [2, 3]

4. 배열 정렬: Arrays.sort()

Arrays.sort(배열);

배열을 오름차순 정렬할 때 쓴다. (in-place 정렬, 반환값 없음 void)

void타입을 반환하기 때문에 새로운 변수에 담을 수 없다.

정렬 기준(숫자: 오름차순, 문자열: 유니코드 기준)

🔍 예시:

int[] arr = {5, 1, 3};
Arrays.sort(arr);  // arr → [1, 3, 5]

5. 배열 출력: Arrays.toString()

Arrays.toString(배열);

배열의 내용을 문자열로 출력한다. (디버깅용)

🔍 예시:

System.out.println(Arrays.toString(arr)); // [1, 3, 5]

6. 배열 채우기: Arrays.fill()

Arrays.fill(배열, 값);

모든 요소를 동일한 값으로 초기화한다.

🔍 예시:

int[] arr = new int[5];
Arrays.fill(arr, 7); // arr → [7, 7, 7, 7, 7]

7. 배열 이진 탐색: Arrays.binarySearch()

Arrays.binarySearch(정렬된배열, 찾을값);

정렬된 배열에서 이진 탐색을 수행한다.
반환값은 인덱스이다. (없으면 음수)

🔍 예시:

int[] sorted = {1, 2, 3, 4};
int idx = Arrays.binarySearch(sorted, 3); // 2

✅ 요약

기능

메서드

배열 내용 비교

Arrays.equals()

전체 복사

Arrays.copyOf()

부분 복사

Arrays.copyOfRange()

배열 정렬

Arrays.sort()

배열 출력

Arrays.toString()

배열 초기화

Arrays.fill()

이진 탐색

Arrays.binarySearch


✅ 코테에서 자주 쓰는 조합

상황

추천 조합

두 문자열이 같은 문자로 이루어졌는지 확인

toCharArray()Arrays.sort()Arrays.equals()

원본 배열 유지하며 정렬

Arrays.copyOf() + Arrays.sort()

배열 슬라이스

Arrays.copyOfRange()




주제별 정리

Java 표현식(Expression)


1. 표현식이란?

**표현식(Expression)**이란, 값을 생성하는 코드 조각이다.
변수, 상수, 연산 결과, 객체 생성 등 결과값이 나오는 모든 코드가 표현식이다


2. 예시로 보는 표현식

코드

결과

설명

3 + 4

7

정수 덧셈

"Hi" + " there"

"Hi there"

문자열 연결

new String("abc")

"abc"

객체 생성

arr.length

배열 길이

배열의 길이 참조

x > 5

true/false

비교 연산


3. 표현식과 문장(statement)의 차이

// 표현식
3 + 4               // 값: 7
"Hello" + "World"   // 값: "HelloWorld"
x > 5               // 값: true 또는 false

// 문장
int a = 3 + 4;               // 변수 선언 (할당)
System.out.println("Hi");   // 메서드 호출
if (x > 5) { ... }           // 조건문

4. 표현식이 쓰이는 위치

위치

예시

return 문

return x + y;

변수 대입

int z = x * 2;

조건문 조건

if (a > b)

메서드 인자

System.out.println("Hi");


5. 요약



주제별 정리

Java 이스케이프 시퀀스 (Escape Sequences)


1. 이스케이프 시퀀스란?

이스케이프 시퀀스는 일반 문자처럼 출력할 수 없는 특수한 동작(예: 줄바꿈, 탭 등)을
문자열 안에서 표현할 수 있도록 해주는 백슬래시 \로 시작하는 문자 조합이다.


2. 주요 이스케이프 시퀀스 표

이스케이프

의미

설명 예시

\n

줄바꿈 (newline)

줄을 바꿔줌

\t

탭 (tab)

띄어쓰기 4~8칸 (탭 위치에 따라 다름)

\"

큰따옴표 (")

문자열 안에서 큰따옴표 출력 가능

\'

작은따옴표 (')

작은따옴표를 문자 또는 문자열 안에서 사용

\\

백슬래시 ()

역슬래시 자체 출력

\r

캐리지 리턴 (CR)

커서를 맨 앞으로 이동 (거의 사용 안 함)

\b

백스페이스

이전 문자 삭제 (거의 사용 안 함)

\f

폼 피드 (Form feed)

페이지 넘김 (프린터 제어용, 현재는 거의 사용 안 함)


3. 사용 예시 코드

public class EscapeExample {
    public static void main(String[] args) {
        System.out.println("이름\t나이\t성별");
        System.out.println("철수\t20\t남");
        System.out.println("그는 말했다: \"안녕하세요\"");
        System.out.println("C:\\Program Files\\Java");
    }
}

출력 결과:

이름	나이	성별
철수	20	남
그는 말했다: "안녕하세요"
C:\Program Files\Java

4. 사용예시

System.out.println("Hello\nWorld");  // 줄바꿈
char quote = '\'';                   // 작은따옴표 문자


주제별 정리

Java 고급 출력 포맷 (이스케이프 시퀀스, printf, format)


1. 이스케이프 시퀀스란?

**이스케이프 시퀀스(Escape Sequence)**는 문자열 내에서 특수한 동작(줄바꿈, 탭, 따옴표 등)을 표현하기 위한 문자 조합이다.
Java에서는 \ 백슬래시로 시작한다.


1.1 주요 이스케이프 시퀀스

이스케이프

의미

예시 출력

\n

줄바꿈

줄을 바꿈

\t

띄어쓰기(보통 4~8칸)

\"

큰따옴표 출력

"그는 말했다: \"안녕\""

\'

작은따옴표 출력

'I\'m fine'

\\

백슬래시 출력

"C:\\Users\\Dain"

\r

캐리지 리턴

커서를 맨 앞으로 이동 (거의 안 씀)

\b

백스페이스

글자 하나 삭제 (거의 안 씀)

\f

폼 피드

페이지 넘김 (거의 안 씀)


1.2 사용 예시

System.out.println("이름\t나이");
System.out.println("영희\t25");
System.out.println("그는 말했다: \"안녕하세요\"");
System.out.println("C:\\Program Files\\Java");

출력 결과:

이름    나이
영희    25
그는 말했다: "안녕하세요"
C:\Program Files\Java

2. 문자열 포맷 서식 문자

Java에서 문자열을 정해진 형식으로 출력하고 싶을 때 System.out.printf() 또는 String.format()을 사용한다.


2.1 기본 형식

System.out.printf("형식", 값1, 값2...);
String result = String.format("형식", 값1, 값2...);

2.2 주요 서식 문자

서식 문자

설명

예시 출력

%s

문자열

"이름: %s" → 이름 출력

%d

정수 (10진수)

"나이: %d"

%f

실수 (소수점 가능)

"점수: %.2f"

%c

문자

"성별: %c"

%n

줄바꿈 (OS별 호환)

"다음 줄%n"

%%

퍼센트(%) 출력

"100%% 완료"


2.3 자리수/정렬 설정

형식

설명

%5d

전체 5자리, 오른쪽 정렬

%-5d

전체 5자리, 왼쪽 정렬

%05d

앞자리를 0으로 채움 (총 5자리)

%.2f

소수점 둘째자리까지 출력

%6.2f

전체 6칸 중 소수점 2자리 포함


2.4 사용 예시

String name = "영희";
int age = 25;
double score = 93.756;

System.out.printf("이름: %s, 나이: %d%n", name, age);
System.out.printf("점수: %.2f%n", score);
System.out.printf("진행률: %d%%%n", 100);

출력 결과:

이름: 영희, 나이: 25
점수: 93.76
진행률: 100%

3. 요약 비교표

항목

예시

설명

이스케이프 문자

\n, \t, \\

문자열 내부 제어

서식 문자 (printf)

%d, %.2f, %s

형식을 지정하여 출력


4. 콘솔 색상 출력 (ANSI 컬러)

Java 자체는 콘솔 색상 기능을 제공하지 않지만, 터미널에서 ANSI 코드를 사용하면 출력 색을 변경할 수 있다.

public class ColorExample {
    public static final String RED = "\u001B[31m";
    public static final String RESET = "\u001B[0m";

    public static void main(String[] args) {
        System.out.println(RED + "이 문장은 빨간색입니다." + RESET);
    }
}

색상

ANSI 코드

빨강

\u001B[31m

초록

\u001B[32m

노랑

\u001B[33m

파랑

\u001B[34m

리셋

\u001B[0m


5. 날짜 및 시간 포맷 출력

Java에서는 java.time.LocalDateTimeDateTimeFormatter를 사용해 날짜/시간을 원하는 형식으로 출력할 수 있다.


5.1 DateTimeFormatter 기본 사용

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

LocalDateTime now = LocalDateTime.now();
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println("현재 시간: " + now.format(fmt));

5.2 주요 포맷 패턴

패턴

의미

예시

yyyy

연도

2025

MM

05

dd

22

HH

시간 (24시간제)

14

hh

시간 (12시간제)

02

mm

07

ss

59

a

오전/오후

AM

/

PM

E

요일

Wed


5.3 사용자 지정 포맷 예시

DateTimeFormatter customFmt = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일 (E) a hh:mm");
String formatted = now.format(customFmt);
System.out.println("형식화된 시간: " + formatted);

6. 로깅용 포맷 출력

Java에는 기본 로그 시스템인 java.util.logging.Logger가 있다.


6.1 Logger 기본 사용

import java.util.logging.*;

Logger logger = Logger.getLogger("MyLogger");

logger.info("정보 메시지");
logger.warning("경고 메시지");
logger.severe("에러 메시지");

6.2 포맷 문자열과 함께 쓰기

String name = "다인";
int age = 25;
logger.info(String.format("사용자 이름: %s, 나이: %d", name, age));

6.3 로그 레벨 종류

레벨

설명

SEVERE

심각한 오류

WARNING

경고

INFO

일반 정보

CONFIG

설정 관련 정보

FINE

디버깅 수준의 정보


6.4 출력 형식 커스터마이징



주제별 정리

Java 정규식(Regular Expression)


1. 개요

정규표현식(Regular Expression)은 특정 문자열을 패턴에 맞게 검색, 대체, 검증하는 데 사용되는 강력한 도구이다. 자바에서는 java.util.regex 패키지를 통해 정규표현식을 사용할 수 있다.

1.1. 정규표현식 구성 요소

정규표현식은 기본적인 문법과 특수 문자를 조합하여 만들어지며, 자주 사용되는 몇 가지 주요 구성 요소가 있다.

1.2. 자주 사용되는 패턴

2. 자바에서 정규표현식 사용하기

2.1. Pattern 클래스

Pattern 클래스는 정규표현식의 패턴을 컴파일하는 데 사용된다. 이 클래스는 정규표현식 패턴을 매칭하고 검색하는 방법을 제공한다.

2.1.1. Pattern 컴파일

Pattern pattern = Pattern.compile("a*b");

위의 코드에서 "a*b"는 'a'가 0번 이상 반복되고 뒤에 'b'가 오는 패턴을 의미한다.

2.1.2. Matcher 클래스 사용

Matcher 클래스는 Pattern 객체를 사용하여 문자열과의 매칭을 처리한다.

Pattern pattern = Pattern.compile("a*b");
Matcher matcher = pattern.matcher("aaab");
boolean matches = matcher.matches(); // true

2.2. PatternMatcher의 주요 메서드

3. 정규표현식 활용 예시

3.1. 이메일 주소 검증

이메일 주소가 유효한지 검사하는 정규표현식 예시

Pattern pattern = Pattern.compile("^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$");
Matcher matcher = pattern.matcher("example@domain.com");
boolean isValid = matcher.matches(); // true

3.2. 전화번호 형식 검증

전화번호 형식이 맞는지 검사하는 정규표현식 예시

Pattern pattern = Pattern.compile("^\\d{3}-\\d{3,4}-\\d{4}$");
Matcher matcher = pattern.matcher("010-1234-5678");
boolean isValid = matcher.matches(); // true


주제별 정리

Java Arrays.sort() 정리


1. 개요


2. 기본 문법

Arrays.sort(배열);

매개변수

설명

배열

정렬 대상 (예: int[], char[], String[])


3. 반환값이 void인 이유

항목

설명

제자리 정렬

Arrays.sort()는 원본 배열 자체를 수정함

새로운 배열 반환 안 함

정렬된 배열을 새로 만들어 반환하지 않음

메모리 절약

불필요한 배열 복사 없이 빠른 정렬 수행 가능

할당 X

int[] sorted = Arrays.sort(arr); ❌ 오류 발생


4. 예시

int[] numbers = {3, 1, 4};
Arrays.sort(numbers);  // numbers 자체가 정렬됨

System.out.println(Arrays.toString(numbers)); // [1, 3, 4]

5. 흔한 실수 예시 ❌

int[] sorted = Arrays.sort(numbers); // ❌ 컴파일 에러: void를 int[]에 대입할 수 없음

6. 대안: 정렬된 배열을 새로 만들고 싶다면?

int[] original = {3, 1, 2};
int[] copy = Arrays.copyOf(original, original.length);
Arrays.sort(copy);

7. 관련 메서드

메서드

설명

Arrays.sort(arr)

기본 오름차순 정렬

Arrays.sort(arr, Comparator)

사용자 정의 기준 정렬 (객체 배열)

Collections.sort(list)

리스트 정렬 (반환값 없음)



[C언어] for루프의 실행 순서 (정보처리기사 25년 1회 실기)


10. 다음은 C언어에 대한 문제이다. 아래 코드를 확인하여 알맞는 출력값을 작성하시오.

#include <stdio.h>
char Data[5] = {'B', 'A', 'D', 'E'};
char c;
 
int main(){
    int i, temp, temp2;
 
    c = 'C';
    printf("%d\n", Data[3]-Data[1]);
 
    for(i=0;i<5;++i){
        if(Data[i]>c)
            break;
    }
 
    temp = Data[i];
    Data[i] = c;
    i++;
 
    for(;i<5;++i){
        temp2 = Data[i];
        Data[i] = temp;
        temp = temp2;
    }
 
    for(i=0;i<5;i++){
        printf("%c", Data[i]);
    }
}

정답 :

4
BACDE


1. ASCII 숫자로 변환할 때

2. for 루프의 실행 순서

char Data[5] = {'B', 'A', 'D', 'E'};  // 인덱스 0~3만 값 있음
char c = 'C';

for(i=0;i<5;++i){
    if(Data[i]>c)
        break;
}

break한 시점에서의 i 값은 뭐야?

++i는 다음 루프 시작 시에만 영향을 주므로, break 시점의 i는 여전히 2인 것이다.

++i든 i++든 증가 자체는 루프의 끝에서 일어나기 때문에 결과는 똑같다.



[Java] 부모의 생성자와 Override (정보처리기사 25년 1회 실기)


13. 다음은 Java 코드에 대한 문제이다. 아래 코드를 확인하여 알맞는 출력값을 작성하시오.

public class Main {
    public static void main(String[] args) {
        new Child();
        System.out.println(Parent.total);
    }
}
 
 
class Parent {
    static int total = 0;
    int v = 1;
 
    public Parent() {
        total += (++v);
        show();    
    }
 
    public void show() {
        total += total;
    }
}
 
 
class Child extends Parent {
    int v = 10;
 
    public Child() {
        v += 2;
        total += v++;
        show();
    }
 
    @Override
    public void show() {
        total += total * 2;
    }
}

정답: 54


Parent클래스의 생성자 Parent()에서 show()호출하면 왜 Child 클래스의 show() 가 실행돼?

자식의 인자 없는 생성자를 호출하면 묵시적으로 부모의 인자없는 생성자가 먼저 호출된다. 이 때 부모의 Parent() 생성자에서 show() 메서드를 실행할 때 가까운 부모의 show() 메서드가 실행된다고 오해하기 쉬운데 @Override된 자식의 생성자가 실행된다.

Java에서 생성자 내에서 this.show()를 호출할 때, 실제로 호출되는 메서드는 "현재 객체 타입 기준의 오버라이드된 메서드"이다. main 영역에서 이미 객체는 Child 타입으로 생성되었기 때문에 super() 안에서 호출되는 show()는 Child의 @Override show()가 실행되는 것이다.

이유는 Java의 "가상 메서드 호출(virtual method call)" 원칙 때문이다. Java에서는 모든 인스턴스 메서드 호출은 '실제 객체 타입' 기준으로 동작한다. 즉, Parent 생성자 안에서 this.show()를 호출하면, this는 Child 객체이므로 Child 클래스에서 @Override한 show()가 실행된다는 것이 이 원칙이다.

이건 단순히 시험 문제라서 그렇지 실제 개발에서 생성자에서 오버라이딩된 메서드를 호출하는 건 권장되지 않는다. 그 이유는자식 필드가 아직 초기화되지 않은 상태에서 메서드가 호출될 수 있기 때문이다. 예를 들면 Child의 v는 아직 10으로 초기화되지 않은 상태일 수 있다.


[Java] 재귀함수 (정보처리기사 25년 1회 실기)


16. 다음은 Java 코드에 대한 문제이다. 아래 코드를 확인하여 알맞는 출력값을 작성하시오.

public class Main {
 
    public static void main(String[] args) {
        int[] data = {3, 5, 8, 12, 17};
        System.out.println(func(data, 0, data.length - 1));
    }
 
    static int func(int[] a, int st, int end) {
        if (st >= end) return 0;
        int mid = (st + end) / 2;
        return a[mid] + Math.max(func(a, st, mid), func(a, mid + 1, end));
    } 
 
}
public class Main {
 
    public static void main(String[] args) {
        int[] data = {3, 5, 8, 12, 17};
        System.out.println(func(data, 0, data.length - 1));
    }
 
    static int func(int[] a, int st, int end) {
        if (st >= end) return 0;
        int mid = (st + end) / 2;
        return a[mid] + Math.max(func(a, st, mid), func(a, mid + 1, end));
    } 
 
}

정답: 20


재귀트리

func(0,4)
│
├── mid=2 → a[2]=8
│
├── func(0,2)
│   ├── mid=1 → a[1]=5
│   │
│   ├── func(0,1)
│   │   ├── mid=0 → a[0]=3
│   │   ├── func(0,0) = 0
│   │   └── func(1,1) = 0
│   │   └── result: 3 + max(0,0) = 3
│   │
│   ├── func(2,2) = 0
│   └── result: 5 + max(3,0) = 8
│
├── func(3,4)
│   ├── mid=3 → a[3]=12
│   ├── func(3,3) = 0
│   ├── func(4,4) = 0
│   └── result: 12 + max(0,0) = 12
│
└── 최종 result: 8 + max(8,12) = 8 + 12 = 20

문제를 복잡하게도 냈네...

변수도 mid가 다시 start 자리나 end 자리 매개변수로 들어가서 변수명도 헷갈리고 복잡하다

a는 반복적으로 들어가는 배열이므로 2,3번째 인자만으로 정리해서 반드시 재귀트리 그리면서 풀기



[Python] __init__(클래스 생성자) & 이진트리 (정보처리기사 25년 1회 실기)


17. 다음은 파이썬에 대한 문제이다. 아래 코드를 확인하여 알맞는 출력값을 작성하시오.

class Node:
    def __init__(self, value):
        self.value = value
        self.children = []
 
def tree(li):
    nodes = [Node(i) for i in li]
    for i in range(1, len(li)):
        nodes[(i - 1) // 2].children.append(nodes[i])
    return nodes[0]
 
def calc(node, level=0):
    if node is None:
        return 0
    return (node.value if level % 2 == 1 else 0) + sum(calc(n, level + 1) for n in node.children)
 
li = [3, 5, 8, 12, 15, 18, 21]
 
root = tree(li)
 
print(calc(root))

정답: 13


왜 i는 1부터 시작할까?

li = [3, 5, 8, 12, 15, 18, 21]일 때 i = 0이면 nodes[(0 - 1) // 2] = nodes[-1]이 되기 때문에 오류가 나거나 엉뚱한 위치를 참조한다. nodes[0]은 루트 노드(root)로 사용되기 때문에, 부모가 없다. 그래서 연결할 필요도, 연결할 수 도 없다.


18, 21은 레벨 2인데, 왜 부모 인덱스가 2야? 부모 인덱스랑 레벨은 다른 개념이야?

부모 인덱스와 레벨은 완전히 다른 개념이다.

용어

인덱스(index)

배열에서 요소의 위치 (0부터 시작)

레벨(level)

트리에서 루트로부터 몇 층 아래인지 나타내는 깊이 (0부터 시작)


if node is None: 이 무슨뜻이야?

트리에서 node는 일반적으로 어떤 노드를 가리킨다. 그런데 재귀 호출을 하다 보면, 어떤 노드에는 자식이 없을 수도 있다.

3
   / \
  5   8
 / \
12 15

위의 노드 트리 구조에서 12와 15는 자식이 없다. 그러면 node.children은 빈 리스트이고, 그 아래는 없으니까 None인 경우가 생길 수 있다. 그럴 때 이 줄이 작동한다. 만약 node가 없으면 0을 리턴하고 있으면 node.value를 사용하라는 뜻이다.

if node is None:
    return 0  # 아무것도 없으니 계산할 것도 없음


1. 클래스 Node

class Node:
    def __init__(self, value):
        self.value = value       # 이 노드가 가지는 값
        self.children = []       # 이 노드의 자식 노드들 (리스트)

[예시]

n = Node(3)
print(n.value)      # 3
print(n.children)   # []


2. 트리 생성 함수 tree()

def tree(li):
    nodes = [Node(i) for i in li]
    for i in range(1, len(li)):
        nodes[(i - 1) // 2].children.append(nodes[i])
    return nodes[0]
예시: li = [3, 5, 8, 12, 15, 18, 21]
index : value
0     : 3    → root
1     : 5    → 3의 왼쪽 자식
2     : 8    → 3의 오른쪽 자식
3     : 12   → 5의 왼쪽 자식
4     : 15   → 5의 오른쪽 자식
5     : 18   → 8의 왼쪽 자식
6     : 21   → 8의 오른쪽 자식

3. 계산 함수 calc()

def calc(node, level=0):
    if node is None:
        return 0
    return (node.value if level % 2 == 1 else 0) + sum(calc(n, level + 1) for n in node.children)

[Java] 상속: 생성자 호출 순서


생성자의 묵시적 호출, 매개변수 문제

class A {
    A() {
        System.out.printf("%d", 10); // sys → System, printf → OK
    }
}

class B extends A {
    B(int a) {
        System.out.printf("%d", a); // Sys → System
    }
}

class C extends B {
    C(int a) {
        super(a / 10); // B의 생성자 호출
        System.out.printf("%d", a); // sys → System
    }
}

public class Test {
    public static void main(String[] args) {
        A b = new C(1000);
    }
}


A b = new C(1000);

순서별 호출 흐름

  1. new C(1000) 실행 → C 생성자 호출
  2. C 생성자에서 super(a / 10)super(1000 / 10)super(100) → B 생성자 호출
  3. B 생성자는 또 super()가 생략되어 있음 → A 생성자 호출

실제 호출 순서


최종 출력 결과

101001000

각 숫자는 System.out.printf("%d", ...)에 의해 줄바꿈 없이 출력되기 때문에 이렇게 나온다.



A b = new C(1000); 관련

업캐스팅 (Upcasting)

A b = new C(1000);

이 구문은 부모 타입(A)의 변수 b에 **자식 클래스(C)**의 객체를 대입하는 것이다.


왜 이렇게 쓰는가?

A b = new C(1000);  // ✅ 업캐스팅

예시

class A {
    void greet() { System.out.println("Hello from A"); }
}

class C extends A {
    void sayBye() { System.out.println("Bye from C"); }
}

public class Main {
    public static void main(String[] args) {
        A obj = new C(); // 업캐스팅
        obj.greet();     // ✅ 가능
        // obj.sayBye(); // ❌ 컴파일 에러 (A 타입엔 없음)
    }
}

[Java] 상속/생성자에서 this()의 의미


문제1

class Super {
    Super() {
        System.out.print('A');
    }
    Super(char x) {
        System.out.print(x);
    }
}

class Sub extends Super {
    Sub() {
        super();             // 호출되면 'A' 출력
        System.out.print('B');
    }
    Sub(char x) {
        this();              // Sub() 생성자 호출 → 'A' + 'B' 출력
        System.out.print(x); // 이후 'D' 출력
    }
}

class Test {
    public static void main(String[] args) {
        Super s1 = new Super('C'); // Super(char) 생성자 → 출력: C
        Super s2 = new Sub('D');   // Sub(char) → this() → super() → A + B + D
    }
}
// 최종출력 CABD

자바에서 this()는 같은 클래스 내부의 다른 생성자를 호출할 때 사용된다.

정확히 말하면 생성자 안에서 this();를 쓰면 그 클래스(Sub)의 '매개변수가 없는 생성자'를 호출한다.

현재 실행 중이던 생성자는 잠시 멈추고, 호출된 생성자의 실행이 먼저 진행된다.

즉, 제어권(control)이 this()가 호출한 다른 생성자로 넘어가는 것이다.

Sub(char x) {
    this();          // 여기서 Sub() 생성자를 먼저 호출
    System.out.print(x);  // 그 다음에 'D' 출력
}
  1. 이 때 this()는 같은 클래스 안에서만 쓸 수 있다.
  2. 또한 this()는 항상 현재 클래스의 다른 생성자를 호출한다.
  3. 매개변수가 맞아야 호출할 수 있다. (아래는 예시)
this();       // → Sub()
this('X');    // → Sub(char x)
this(1, 2);   // → Sub(int a, int b)


문제2

class Parent {
    int x = 100;      // Parent 클래스의 인스턴스 변수 x

    Parent() {
        this(500);    // Parent(int x) 호출
    }

    Parent(int x) {
        this.x = x;   // Parent의 x에 값 저장
    }

    int getX() {
        return this.x;  // Parent의 x 반환
    }
}

class Child extends Parent {
    int x = 2000;     // Child 클래스의 인스턴스 변수 x

    Child() {
        this(5000);   // Child(int x) 호출
    }

    Child(int x) {
        this.x = x;   // Child의 x에 값 저장
    }

    public static void main(String[] args) {
        Child obj = new Child();
        System.out.println(obj.getX()); // Parent의 getX() 호출
    }
}
// 최종출력 500
  1. Child() 호출
  2. 내부에서 this(5000)Child(int x) 호출
  3. 자바가 자동으로 super() 삽입 → Parent() 호출
  4. Parent() 안에서 this(500)Parent(int x) 호출
  5. Parentx = 500 설정
  6. 이후 Child(int x) 실행 → Childx = 5000 설정
  7. obj.getX()Parentx = 500 반환

this(5000)은 같은 클래스인 Child의 생성자 중, int를 매개로 받는 생성자를 호출한다.

Child() {
    this(5000);   // 같은 클래스의 생성자 Child(int x) 호출
}

여기서 설정되는 건 Child 클래스의 x 필드 (int x = 2000) 이고, this.x = x;에 의해 Child의 x는 5000으로 바​뀐다.


📌 그럼 Parent의 생성자는 언제 호출되는가?

항상 서브클래스 생성자가 호출되기 전에 슈퍼클래스 생성자가 먼저 호출되어야 한다.

그래서 Child(int x)가 실행되기 전에, 자동으로 super()가 먼저 붙는다.

즉, 자바가 내부적으로 다음처럼 처리한다.

Child(int x) {
    super();       // 자동으로 삽입됨!
    this.x = x;    // Child의 x = 5000
}

그리고 Parent()는 다음처럼 생겼다.

Parent() {
    this(500);    // Parent(int x) 호출
}
Parent(int x) {
    this.x = x;   // Parent의 x = 500
}


📌 obj.getX()는 왜 500이 나오는가?

getX()는 Parent 클래스에 정의되어 있다.

int getX() {
    return this.x;
}

하지만 중요한 건 getX()는 Parent의 x를 반환한다는 뜻이 아니라, "지금 참조 중인 인스턴스의 x 필드 중, Parent 클래스에서 접근 가능한 걸 반환한다"는 의미​이다.

→ 여기서 this는 Child 인스턴스를 가리키지만, getX()는 Parent 클래스 안에 있어서 Parent의 x를 가리킨다.


📌 super()는 부모 클래스의 '기본 생성자'를 호출한다.

Child(int x)는 이렇게 생겼다.

Child(int x) {
    this.x = x;
}

그런데 이 생성자의 첫 줄에 명시적으로 super(...)가 없으면, 자바는 자동으로 super();를 삽입한다. 내부적으로는 이렇게 되는 것과 같다.

Child(int x) {
    super();      // 자동으로 삽입됨
    this.x = x;   // Child의 x 필드
}

→ 이 super()Parent()를 호출한다. 왜냐하면 Parent 클래스에 기본 생성자 Parent()가 존재하기 때문이다.


📌 Parent()에서의 this(500)

Parent() {
    this(500);    // 같은 클래스의 다른 생성자 호출
}

이 this(500)은 마찬가지로 같은 Parent()클래스 내에 있는 매개변수가 있는 생성자를 호출한다.  

Parent(int x) {
    this.x = x;
}

그래서 결국 아래와 같은 흐름으로 코드가 동작한다.

super();             // → Parent()
  → this(500);       // → Parent(int x)
     → this.x = 500; // → Parent의 x 필드에 500 저장