28Days) 자료구조/알고리즘(8) - Algorithm With Math

nacSeo (낙서)·2022년 11월 28일
0

◉ 학습목표

1. 알고리즘 문제에서 다루는 수학을 익히고, 적재적소에 활용할 수 있는 능력을 기를 수 있다.
2. 수학적 사고를 통해 컴퓨팅 사고를 할 수 있다.
3. GCD/LCM(최대공약수, 최소공배수), 순열/조합, 멱집합에 대해 이해하고 활용할 수 있다.
  1. 순열(Permutation)과 조합(Combination)

⦿ 학습내용

☞ 순열

✔︎ 요소 n개 중에 m개를 선택하여 순서에 상관있게 뽑는 경우의 수

☞ 조합

✔︎ 순서에 상관없이 요소 n개 중에 m개를 뽑는 경우의 수

☞ ! (factorial, 팩토리얼)

✔︎ n! : n에서부터 1씩 감소하여 1까지의 모든 정수의 곱 (n보다 작거나 같은 모든 양의 정수의 곱)
✔︎ 0! = 1! = 1

☞ 문제: 카드 뽑기

✔︎ A, B, C, D, E 5장의 카드
✔︎ 5장 중 3장을 선택하여 나열할 때 경우의 수
✔︎ case1: 순서를 생각하며 3장을 선택

  • 순열
  • 5P3 = (5x4x3x2x1) / (2x1) = 60
  • 일반식: nPr = n! / (n-r)!
  • 코드
// 반복문 코드

public static ArrayList<String[]> permutationLoop() {
  // 순열 요소가 인자로 주어질 경우, 인자 그대로 사용하면 되지만, 인자가 주어지지 않고
  // 문제 안에 포함되어 있을 경우 이런 식으로 직접 적어서 사용합니다.
    String[] lookup = new String[]{"A", "B", "C", "D", "E"};
    ArrayList<String[]> result = new ArrayList<>();

    for (int i = 0; i < lookup.length; i++) {
      for (int j = 0; j < lookup.length; j++) {
        for (int k = 0; k < lookup.length; k++) {
          if (i == j || j == k || k == i) continue;
            String[] input = new String[]{lookup[i], lookup[j], lookup[k]};
            result.add(input);
        }
      }
    }
  return result;
}
  • 반복문의 개수 == 요소를 뽑는 개수
// 반복문 1개당 1개의 요소를 뽑습니다.

for (int i = 0; i < lookup.length; i++) {
  String pick1 = lookup[i];
  for (int j = 0; j < lookup.length; j++) {
    String pick2 = lookup[j];
    for (letintk = 0; k < lookup.length; k++) {
      String pick3 = lookup[k];

      if (i.equals(j) || j.equals(k) || k.equals(i)) continue;
      result.add(new String[]{pick1, pick2, pick3});
   }
  }
}
  • 중복된 요소제거

✔︎ case2: 순서를 생각하지 않고 3장을 선택

  • 조합
  • 5C3 = 5! / (3! * 2!) = 10
  • 일반식: nCr = n! / (r! * (n-r)!)
  • 코드
// 반복문 코드

public static ArrayList<String[]> combinationLoop() {
	// 조합 요소가 인자로 주어질 경우, 인자 그대로 사용하면 되지만, 인자가 주어지지 않고
	// 문제 안에 포함되어 있을 경우 이런 식으로 직접 적어서 사용합니다.
  String[] lookup = new String[]{"A", "B", "C", "D", "E"};
  ArrayList<String[]> result = new ArrayList<>();

  for(int i = 0; i < lookup.length; i++) {
    for(int j = i + 1; j < lookup.length; j++) {
      for(int k = j + 1; k < lookup.length; k++) {
        String[] input = new String[]{lookup[i], lookup[j], lookup[k]};
				result.add(input);
      }
    }
  }

  return result;
}
  • 순열과 다른 점 : 반복의 조건 (i=0, j=i+1, k=j+1)
  • 한 번 조합한 요소는 다시 조합 ❌

🚨 반복문으로 순열과 조합으로 만들 수 있지만, 분명 한계점이 존재❗️❗️❗️
✔︎ 개수가 늘어나면 반복문의 수도 늘어남

  • 비효율적이고 좋은 코드가 아님

✔︎ 뽑아야 되는 개수가 변수로 들어왔을 때 대응이 어려움

∴ 순열과 조합은 재귀를 사용하여 풀이하는 경우가 多❗️

☞ 문제: 소수 찾기

☞ 문제: 일곱 난쟁이

  1. Algorithm with Math (페어프로그래밍)

⦿ 학습내용

☞ [중복순열] 가위바위보

✔︎ 문제

  • 입력받은 rounds만큼의 선택으로 가능한 모든 경우의 수를 구하는 함수를 작성

✔︎ 입력

  • int 타입의 게임 횟수 rounds

✔︎ 출력

  • ArrayList<String[]> 타입 리턴
  • ArrayList가 담고 있는 배열은 전체 경우의 수 중 한 가지 경우(총 세번의 선택)를 의미하는 배열
  • String[]rock, paper, sicssors 중 한 가지 이상을 요소로 갖는 배열

✔︎ 주의사항

  • 최종적으로 리턴되는 배열의 순서는 가중치 적용 정렬(Weighted Sort)을 따름
  • 중요도 : rock>paper>scissors

✔︎ 입출력 예시

ArrayList<String[]> output = rockPaperScissors(5);

System.out.println(output);
/*
    [
      ["rock", "rock", "rock", "rock", "rock"],
      ["rock", "rock", , "rock", "rock", "paper"],
      ["rock", "rock", , "rock", "rock", "scissors"],
      ["rock", "rock", "rock", "paper", "rock"],
      ["rock", "rock", "rock", "paper", "paper"],
      ["rock", "rock", "rock", "paper", "scissors"],
      ["rock", "rock", "rock", "scissors", "rock"],
      // ...etc ...
    ]
  */

✔︎ 도출한 답

import java.util.*;

public class Solution { 
	public ArrayList<String[]> rockPaperScissors(int rounds) {
    // TODO:
    // 중복배열은 뽑은 것은 또 뽑을 수 있음
    // 결과를 담을 ArrayList 선언
    ArrayList<String[]> outcomes = new ArrayList<>();
    return permutation(0, rounds, new String[rounds], outcomes);
	}
  // 순열 ArrayList<String[]> 메서드 선언 (현재 라운드, 끝 라운드, String[] 선택요소 담은 배열, ArrayList<String[]> 결과담을 리스트)
  public ArrayList<String[]> permutation(int curRounds, int finRounds, String[] select, ArrayList<String[]> result){
    // 현재라운드 = 끝라운드 같을 때
    if(curRounds == finRounds){
      // 새로운 배열에 선택요소 담은 배열 깊은 복사
      String[] arr = Arrays.copyOf(select, select.length);
      // 결과 담을 리스트에 새로운 배열 추가
      result.add(arr);
      // 결과담을 리스트 반환
      return result;
    }
    // 가위바위보 배열 선언
    String[] rps = new String[]{"rock", "paper", "scissors"};

    // 가위바위보 배열 순회
    for(int i=0; i<rps.length; i++){
      // 새로운 배열에 i번째 요소 담음
      String curPlay = rps[i];
      // 선택요소 담은 배열의 현재 라운드에 i번째 값 추가
      select[curRounds] = curPlay;
      // 결과 담을 리스트에 라운드를 +1해서 재귀
      result = permutation(curRounds+1, finRounds, select, result);
    }
    return result;
  }
}

☞ [순열] 새로운 치킨 소스 레시피

✔︎ 문제

1. N 가지의 재료 중에 단 M 가지만을 사용하여 조합한 모든 경우의 수 중 하나이다.
2. 재료는 0과 1로만 이루어진 숫자로 암호화가 되어 있고, 항상 1로 시작하며 복호화를 할 수 없다. (단, 0이 3개 이상인 재료는 상한 재료이기 때문에 제외한다.)
3. 재료의 순서에 따라 맛이 달라지기 때문에, 재료를 넣는 순서가 다르다면 다른 레시피이다.
  • 이 소문을 참고하여 '비밀의 승승장구 치킨 소스'가 될 수 있는 경우의 수를 모두 반환하는 함수를 작성

✔︎ 입력

  • 인자1: stuffArr
    - int타입의 재료를 담은 배열
    • 요소는 0과 1로만 이루어진 숫자이며, 항상 1로 시작
    • 요소는 중복 ❌
    • 요소의 길이는 20 이하
    • 배열의 길이는 2 이상 10 이하
    • ex) 111, 110, 1010, 10, 10110
  • 인자2: choiceNum
    - int타입의 1 이상 stuffArr 길이 이하의 자연수
    - 재료를 선택할 수 있는 수
    - ex) 2

✔︎ 출력

  • ArrayList<Integer[]> 타입을 반환
    - stuffArr가 [1, 10, 11000, 1111]이고, choiceNum은 2라면 사용 가능한 재료는 [1, 10, 1111]
    - 조합할 수 있는 경우의 수는 6가지

✔︎ 주의사항

  • 만약, 주어진 재료 모두 사용할 수 없다면 null을 반환
  • 만약, 사용할 수 있는 재료가 choiceNum보다 작다면 null을 반환
  • 조합 및 요소는 작은 숫자 -> 큰 숫자로 정렬
    - 예시로 [1, 10, 11000, 1111]이 요소로 들어왔다면, 0이 세 개인 11000을 제외하고 [1, 10, 1111] 순서가 되어야 하며,
    [ [1, 10], [1, 1111], [10, 1], [10, 1111], [1111, 1], [1111, 10] ]을 반환

✔︎ 입출력 예시

ArrayList<Integer[]> output1 = newChickenRecipe(new int[]{1, 10, 1100, 1111}, 2);
System.out.println(output1);
/*
  [
    [1, 10], [1, 1100], [1, 1111],
    [10, 1], [10, 1100], [10, 1111],
    [1100, 1], [1100, 10], [1100, 1111],
    [1111, 1], [1111, 10], [1111, 1100]
  ];
*/

ArrayList<Integer[]> output2 = newChickenRecipe(new int[]{10000, 10, 1}, 3);
System.out.println(output2); // []

ArrayList<Integer[]> output3 = newChickenRecipe(new int{11, 1, 10, 1111111111, 10000}, 4);
System.out.println(output3);
/* 
  [
    [1, 10, 11, 1111111111],
    [1, 10, 1111111111, 11],
    [1, 11, 10, 1111111111],
    [1, 11, 1111111111, 10],
    [1, 1111111111, 10, 11],
    [1, 1111111111, 11, 10],
    [10, 1, 11, 1111111111],
    [10, 1, 1111111111, 11],
    [10, 11, 1, 1111111111],
    [10, 11, 1111111111, 1],
    [10, 1111111111, 1, 11],
    [10, 1111111111, 11, 1],
    [11, 1, 10, 1111111111],
    [11, 1, 1111111111, 10],
    [11, 10, 1, 1111111111],
    [11, 10, 1111111111, 1],
    [11, 1111111111, 1, 10],
    [11, 1111111111, 10, 1],
    [1111111111, 1, 10, 11],
    [1111111111, 1, 11, 10],
    [1111111111, 10, 1, 11],
    [1111111111, 10, 11, 1],
    [1111111111, 11, 1, 10],
    [1111111111, 11, 10, 1],
  ]
*/

✔︎ 도출한 답

import java.util.*;

public class Solution { 
	public ArrayList<Integer[]> newChickenRecipe(int[] stuffArr, int choiceNum) {
    //사용 가능한 재료만 넣을 변수를 선언합니다
    ArrayList<Integer> freshArr = new ArrayList<>();    

    //stuffArr를 순회하며 사용 가능한 재료만 freshArr리스트에 추가합니다
    for(int i = 0; i < stuffArr.length; i++) {
      //stuffArr[i]를 String타입으로 변환한 이후
      String str = String.valueOf(stuffArr[i]);
      //해당 값을 char타입의 배열로 바꾼 이후, 0이 들어간 갯수만큼 element 배열에 추가합니다.
      int[] element = str.chars().filter(c -> c == '0').toArray();
      //element 배열의 숫자가 2 이하인 경우('0'이 2번 이하인 재료의 경우)
      if(element.length <= 2) {       
        //freshArr에 해당 재료를 넣어줍니다.
        freshArr.add(stuffArr[i]);
      }
    }
    //재료가 들어간 List를 오름차순으로 정렬합니다.
    Collections.sort(freshArr); 

    //사용할 수 있는 재료가 없거나, 재료의 양보다 사용해야 할 갯수가 더 많은 경우 null을 반환합니다.
    if (freshArr.size() == 0 || freshArr.size() < choiceNum) return null;

    //결과를 담을 리스트를 선언합니다.
    ArrayList<Integer[]> result = new ArrayList<>();

    //해당 재료의 사용 여부를 확인할 배열을 선언합니다.
    boolean[] visited = new boolean[freshArr.size()];

    //순열 메소드를 사용하여 모든 경우의 수를 구하고, 해당 자료를 반환합니다.
    return permutation(choiceNum, new Integer[]{}, result, freshArr, visited, 0);
  }

  public ArrayList<Integer[]> permutation(int choiceNum, Integer[] bucket, ArrayList<Integer[]> result, ArrayList<Integer> freshArr, boolean[] visited, int depth) {
    //사용한 재료의 숫자가 choiceNum에 도달한다면(재귀의 종료)
    if(depth == choiceNum) {
      //result에 재료가 저장된 bucket 배열을 넣어준 이후
      result.add(bucket);
      //해당 result를 반환합니다.
      return result;
    }

    //사용할 수 있는 신선한 재료의 수만큼 반복합니다.
    for(int i = 0; i < freshArr.size(); i++) {
      //해당 재료를 사용하지 않았다면
      if(!visited[i]) {
        //해당 재료의 사용 여부를 체크하고
        visited[i] = true;
        //bucket에 사용한 재료를 넣어줍니다. (새로운 배열 선언)
        Integer[] concatArray = Arrays.copyOf(bucket, bucket.length + 1);
        concatArray[concatArray.length - 1] = freshArr.get(i);

        //다시 재귀를 사용합니다.
        result = permutation(choiceNum, concatArray, result, freshArr, visited, depth + 1);
        //한번 재귀를 순회한 이후, 반복문을 다시 시작하기 위해 첫 시작재료의 사용여부를 false로 변경합니다.
        visited[i] = false;
      }
    }
    return result;
  }
}

☞ [조합] 블랙잭은 지겨워

✔︎ 문제

1. 숫자로 이루어진 여러 장의 카드를 받음
2. 3장씩 카드를 고르고, 3장에 적힌 숫자들의 합이 소수인지 확인
3. 받아든 카드로 만들 수 있는 소수의 개수가 많은 사람이 이기게 됨
  • 예시
    • [1, 2, 3, 4]라는 카드를 받았을 때 만들 수 있는 숫자는 6, 7, 8, 9이고, 소수는 7 하나이기 때문에 소수의 개수는 1개
    • [2, 3, 4, 8, 13]라는 카드를 받았을 때 만들 수 있는 숫자는 9, 13, 18, 14, 19, 23, 15, 20, 24, 25이고, 소수는 13, 19, 23 총 3개
  • 여러 장의 카드 중 세 장씩 조합해 소수가 되는 경우의 수를 리턴하는 함수를 완성해라

✔︎ 입력

  • 인자1
    • cards: int 타입을 요소로 가지는 배열
    • 3개 이상 50개 이하의 카드가 숫자로 들어있는 배열
    • 2 < cards.length < 51

✔︎ 출력

  • int 타입을 리턴

✔︎ 주의사항

  • cards에는 중복된 숫자의 카드는 들어있지 않음
  • 각 카드에 적힌 수는 1이상 1000이하의 자연수

✔︎ 입출력 예시

int output = boringBlackjack(new int[]{1, 2, 3, 4});
System.out.println(output); // 1

int output = boringBlackjack(new int[]{2, 3, 4, 8, 13});
System.out.println(output); // 3

✔︎ 도출한 답

import java.util.*;

public class Solution { 
	public int boringBlackjack(int[] cards) { // 1,2,3,4
    // TODO:
    // 소수가 되는 경우의 수를 선언
    int count = 0;
    // 고르는 카드 수만큼 반복문 생성
    for(int i=0; i<cards.length-2; i++){ // i=1
    // for(int i=0; i<cards.length; i++){ // 어차피 끝까지 돌아도 아무것도 안하고 나와서 상관없음
      for(int j=i+1; j<cards.length-1; j++){ // j=2
      // for(int j=i+1; j<cards.length; j++){ // 어차피 끝까지 돌아도 아무것도 안하고 나와서 상관없음
        for(int k=j+1; k<cards.length; k++){ // k=3
          // 3장의 합을 나타내는 변수 선언
          int sum = cards[i]+cards[j]+cards[k]; // 1+2+3=6 1+2+4=7(count) 1+3+4=8 2+3+4=9
          // 변수가 소수 판별에서 참일 경우 count 추가
          if(isPrime(sum)) count++; // count = 1
        }
      }
    }
    return count;
	} 
  // 소수 판별 boolean타입 선언
  public boolean isPrime(int num){ // num = 9
    // i = 2부터 비교해 나누어 떨어지면 소수 x
    for(int i=2; i<=Math.sqrt(num); i++){ // i=2; i<=3; i++;
      if(num % i == 0) return false; // 9%2 == 1, 9%3 == 0
    }
    return true;
  }
}

☞ [GCD] 빼빼로 데이

✔︎ 문제

  • 한 회사의 팀장은 출근길에 아몬드 빼빼로 M개와 누드 빼빼로 N개를 구매
  • 자신보다 먼저 출근해있는 직원들에게 빼빼로를 공평하게 전부 나누어 주어야 함
  • 먼저 출근해있는 직원이 몇 명인지는 모름
  • 예시
    • 아몬드 4개, 누드 8개를 구매했다면 다음과 같은 3가지 방법이 있음
    • 출근한 직원이 1명이라면 아몬드 4개, 누드 8개
    • 출근한 직원이 2명이라면 아몬드 2개, 누드 4개
    • 출근한 직원이 3명이라면 남기지 않고 공평하게 나누어 줄 수 없음
    • 출근한 직원이 4명이라면 아몬드 1개, 누드 2개
  • 직원 수에 따라 빼빼로를 나누어주는 방법을 구하는 솔루션을 제공하라

✔︎ 입력

  • 인자1
    • int 타입의 양의 정수 (1 ≦ M ≦ 1,000,000,000)
  • 인자2
    • int 타입의 양의 정수 (1 ≦ N ≦ 1,000,000,000)

✔︎ 출력

  • ArrayList<Integer[]>타입의 output을 리턴
  • output[i]은 다음과 같은 순서를 가진 길이 3의 배열
    • [빼빼로를 받게 되는 직원의 수, 나누어 주는 아몬드 빼빼로의 수, 나누어 주는 누드 빼빼로의 수]
  • outputoutput[i][0], 즉 빼빼로를 받게 되는 직원의 수를 기준으로 오름차순으로 정렬

✔︎ 입출력 예시

int M = 4;
int N = 8;
ArrayList<Integer[]> output = divideChocolateStick(M, N);
System.out.println(output);
// [[1, 4, 8], [2, 2, 4], [4, 1, 2]]

✔︎ 도출한 답

import java.util.*;

public class Solution { 
	public ArrayList<Integer[]> divideChocolateStick(int M, int N) {
    // TODO:
    // 최대공약수 -> 유클리드 호제법 사용
    // 직원수, 아몬드 빼빼로수=M, 누드 빼빼로수=N
    ArrayList<Integer[]> output = new ArrayList<>();
    Integer[] arr = new Integer[3];
    int member = 1;
    while(output.size()!=3){
      int temp = gcd(M, member);
      int temp2 = gcd(N, member);
      if(temp == -1 || temp2 == -1) continue;
      arr = new Integer[] {member, M/temp, N/temp2};
      output.add(arr);
    }
    return output;
	} 
  public int gcd(int a, int b) {
      int r = a % b;
      if(r==0) {
        return a;
      }
      else {
        return gcd
      }
  }
  /**
     * 유클리드 호제법 구현 메서드
     * @param bn : 큰 숫자
     * @param sn : 작은 숫자
     * @return
     * 큰 숫자를 작은숫자로 나눈 값이 0이면 작은숫자 리턴, 아니면 재귀형태로 자신을 호출
     */
    public int eucd(int bn, int sn) {
        // 큰숫자를 작은숫자로 나눈 나머지를 계산
        int r = bn % sn;
        // 나머지가 0이면 작은숫자가 최대공약수이므로 작은숫자 리턴
        if (r == 0) {
            return sn;
        } else {
            // 나머지가 0 이상이면 재귀형태로 호출
            // 이때 파라미터는 작은숫자와 나머지를 넣어줌
            return eucd(sn, r);
        }
    }
}

☞ [멱집합] 집밥이 그리워

✔︎ 문제

  • 오랜만에 집으로 돌아와 집밥을 먹는데, 무엇부터 먹어야할 지 고민
  • 최대한 많은 방법으로 먹고 싶음
  • 밥은 한 가지이며 반찬은 다수일 때, 밥과 함께 먹을 수 있는 반찬의 모든 경우의 수를 배열에 담아 리턴하라

✔︎ 입력

  • 인자1: sideDishes
    • String 타입의 영문으로 된 반찬이 나열되어 있는 배열

✔︎ 출력

  • ArrayList<String[]> 타입을 리턴해야 함
  • 밥과 함께 먹을 수 있는 반찬의 모든 경우의 수가 담긴 배열을 포함한 ArrayList

✔︎ 주의사항

  • 반찬은 영문으로 작성되어 있음
  • 반찬은 중복 ❌
  • 반찬을 먹지 않는 것도 포함 (출력되는 2차원 배열에는 빈 배열을 포함)
  • 반찬은 3개 이상 99개 이하
  • 출력되는 배열은 전부 오름차순 정렬

✔︎ 입출력 예시

ArrayList<String[]> output = missHouseMeal(new String[]{"eggroll", "kimchi", "fishSoup"});
System.out.println(output);
/*
[ [],
  [eggroll, fishSoup, kimchi],
  [eggroll, fishSoup],
  [eggroll, kimchi],
  [eggroll],
  [fishSoup, kimchi],
  [fishSoup],
  [kimchi],
] 
*/

✔︎ 도출한 답

import java.util.*;

public class Solution { 
	public ArrayList<String[]> missHouseMeal(String[] sideDishes) {
    //search 함수에서 사용할 빈 스택을 선언합니다.
    Stack<String> stack = new Stack<>();

    //결과를 담을 ArrayList를 선언합니다.
    ArrayList<String[]> result = new ArrayList<>();

    //재료들을 오름차순으로 정렬합니다.
    Arrays.sort(sideDishes);

    // 빈 스택과 0 번째 인덱스, 정렬된 재료로 구성된 배열, 결과를 담을 List를 인자로 받는 재귀 함수를 실행합니다.
    result = search(stack, 0, sideDishes, result);

    // 결과를 오름차순 순서로 정렬합니다.
    result.sort((o1, o2) -> Arrays.toString(o1).compareTo(Arrays.toString(o2)));
    //결과를 반환합니다.
    return result;
  }

  // 모든 조합을 검사하는 재귀 함수를 작성합니다.
  public ArrayList<String[]> search(Stack<String> stack, int idx, String[] sideDishes, ArrayList<String[]> result) {
    // 재귀 함수이기 때문에 탈출 조건을 만듭니다.
    if (idx >= sideDishes.length) {
      // 만약, idx와 sideDishes의 길이가 같거나 크다면(마지막까지 검토한 경우) 스택을 배열로 변환한 후, 해당 스택을 result에 넣어줍니다.
      String[] arr = stack.toArray(new String[0]);
      result.add(arr);
      return result;
    } else {
      // idx가 부분집합에 포함된 경우
      stack.push(sideDishes[idx]);
      search(stack, idx + 1, sideDishes, result);

      // idx가 부분집합에 포함되지 않은 경우
      stack.pop();
      search(stack, idx + 1, sideDishes, result);
    }
    return result;
  }
}

◉ 느낀 점

☞ 기초적인 수학적 지식을 활용하여 알고리즘 문제를 푸는 연습을 했다. 혼자서 했다면 정말 시간도 오래 걸리고 머리가 터졌었겠지만, 페어 프로그래밍을 통해 그나마 선방했다 🥲 서로 어떻게 알고리즘을 적용시켜 문제를 해결할지 아이디어를 공유하고 실수들을 잡아주며 시간을 꽤나 단축시킨 느낌이 들었다. 물론 30분 이상 고민하며 막힌 부분에 대해서는 크루님께 질문도 드려가며 문제를 해결하기도 했다 😅 나중에 코딩 테스트를 준비할 때 얼마나 힘들지 미리 경험해보는 느낌이다.. 알고리즘 파트가 현업에서 필요한 개발을 위해 필수적인 부분은 아니지만, 컴퓨팅적 사고를 익히며 보다 성장할 수 있는 요소라고 생각하며 크게 부담감을 갖진 않지만, 몰두하며 해결해나갔던 것 같다.
오늘로써 자료구조 및 알고리즘 파트를 마무리 짓고 내일부터는 네트워크 파트로 넘어간다. 정말 많이 어렵고 머리가 하얘질만큼 고생했던 파트이기도 했기에 교육을 마무리 짓고도 반드시 복습이 필요한 파트인 것 같다.

◉ 내일의 키워드

・ 웹 애플리케이션 작동 원리
profile
백엔드 개발자 김창하입니다 🙇‍♂️

0개의 댓글