Coder Social home page Coder Social logo

problemsolving's Introduction

ProblemSolving

This is a auto push repository for Baekjoon Online Judge created with BaekjoonHub.

problemsolving's People

Contributors

wlsgh7608 avatar

Watchers

 avatar

problemsolving's Issues

2023.09.14 - Merge Intervals

문제 - https://leetcode.com/problems/merge-intervals

문제 설명

  • 주어진 구간을 합쳐 만들어진 구간을 반환

해결 방법

  • 먼저 주어진 구간들을 시작 시간을 기준으로 오름차순으로 정렬
  • 그 후 반복문을 돌림
    • 만약 현재 만들어진 구간의 종료보다 시작이 빠르다 => 새로운 구간을 만들어야 함
    • 만약 현재 만들어진 구간의 종료보다 시작이 작거나 같다 => 구간을 합칠 수 있음
      • 종료 시간은 둘 중 큰 값으로 변경

전체 코드

class Solution {
    public int[][] merge(int[][] intervals) {
        List<int[]> list = new ArrayList<>();

        
        Comparator<int[]> myComparator = new Comparator<>(){
            @Override
            public int compare(int[] o1, int[] o2){
                // 시작 오름차순
                return o1[0]-o2[0];
            }
        };
        
        
        Arrays.sort(intervals,myComparator);
                
        int start = intervals[0][0];
        int end = intervals[0][1];
        

        
        for(int[] interval : intervals){
            int s = interval[0];
            int e = interval[1];
            
            if(s<=end){
                end = end > e? end: e;
            }else{
                list.add(new int[]{start,end});
                start = s;
                end = e;
            }
        }
        // last element
        list.add(new int[]{start,end});
        
        int size = list.size();
        int[][] result = new int[size][2];
        for(int i = 0 ; i<size;i++){
            result[i][0] = list.get(i)[0];
            result[i][1] = list.get(i)[1];
        }
        
        return result;
        
        
    }
}

실행시간

image

2023.08.12 - Find Peak Element

일반 탐색

  • 시간 복잡도 : O(N)
    public int findPeakElement(int[] nums) {
        // O(N)
        int answer = 0;
        for(int i = 1; i<nums.length-1;i++){
            int prev = nums[i-1];
            int next = nums[i+1];
            int cur = nums[i];
            if(cur>prev && cur>next){
                answer = i;
                break;
            }
        }
        return answer;
    }

이분 탐색

  • O(logN)의 시간 복잡도를 만족 시키는 알고리즘
  • 중간의 인덱스(m)의 양 옆 비교
    • 중간의 인덱스가 양 옆보다 크다면 => 리턴
    • 왼쪽의 인덱스가 크다면 => hi 값 = m-1 변경 후 이분 탐색
    • 오른쪽의 인덱스가 크다면 => lo 값 = m+1 변경 후 이분 탐색
    class Solution {
    public int findPeakElement(int[] nums) {
        // nums 길이 1000
        // O(logN)
        // 양옆보다 큰 index 찾기
        int N = nums.length;

        // element가 1개인 경우
        if(N==1){
            return 0;
        }
        
        // 맨 왼쪽이 봉우리인 경우
        if(nums[0]>nums[1]){
            return 0;
        }
        // 맨 오른쪽이 봉우리인 경우 
        if( nums[N-1]>nums[N-2]){
            return N-1;
        }
           
        
        int lo = 1;
        int hi = nums.length-2;
        
        int answer = 0;
        while(lo<=hi){
            int m = (lo+hi)/2;
            if(nums[m]>nums[m-1]&& nums[m]>nums[m+1]){
                answer = m;
                break;
            }
            else if(nums[m]<nums[m-1]){
                hi = m-1;
            }else if(nums[m]<nums[m+1]){
                lo = m+1;
            }
            
        }
        return answer;
    }
}

2024.02.17 - flatten-binary-tree-to-linked-list

문제 - 링크

문제 풀이

  • 문제 해설 1

PreOrder

  • 전위순회의 경우 루트, 왼쪽 자식, 오른쪽 자식으로 순회
    void preOrder(TreeNode cur, Stack<TreeNode> S) {
        if (cur == null) {
            return;
        }
        S.add(cur);
        preOrder(cur.left, S);
        preOrder(cur.right, S);

    }

코드

class Solution {

    void preOrder(TreeNode cur,List<TreeNode> list){
        if(cur==null){
            return;
        }
        list.add(cur);
        preOrder(cur.left,list);
        preOrder(cur.right,list);
        
    }

    public void flatten(TreeNode root) {
        List<TreeNode> list= new ArrayList<>();
        // preOrder 진행
        preOrder(root,list);
        // List (1,2,3,4,5,6) 으로 담겨 있음
        TreeNode prev = null;
        
        for(TreeNode cur : list){
            cur.left = null;
            cur.right = null;
            if(prev!=null){
                prev.right = cur;       
            }
            prev = cur;
        }

    }
}

시간, 공간 복잡도

  • 시간 복잡도 : O(N)
  • 공간 복잡도 : O(N)

실행 시간

image

2024.03.01 - Count Number of Bad Pairs

문제 - 링크

문제 풀이

  • bad pair 정의 if i < j && j - i != nums[j] - nums[i]
  • bad pair의 쌍 개수 구하기

�good pair 구하기

  • 우리는 bad pair의 개수를 바로 구하기 힘드므로 good pair을 구한 뒤 전체 - good pair 이용
        // good pair
        // i - j == nums[i] - nums[j]
        // i - nums[i] == j - nums[j]

코드

class Solution {
  public long countBadPairs(int[] nums) {
        int N = nums.length;
        int[] diff = new int[N];

        for(int i = 0 ; i< N; i++){
            diff[i] = nums[i] - i;
        }

        // good pair
        // i - j == nums[i] - nums[j]
        // i - nums[i] == j - nums[j]

        long tot = (long) N * (N - 1) / 2;
        long goodCnt = 0;
        HashMap<Integer, Integer> hm = new HashMap<>();
        for (int i = 0; i < N; i++) {
            if (hm.containsKey(diff[i])) {
                goodCnt += hm.get(diff[i]);
            }
            hm.put(diff[i], hm.getOrDefault(diff[i], 0) + 1);
        }


        return tot - goodCnt;
    }
}

시간, 공간 복잡도

  • 시간 복잡도 : O(N)
  • 공간 복잡도 : O(N)

실행 시간

image

2024.04.06 - 우박수열 정적분

문제 - 링크

문제 풀이

  • 콜라츠 함수 그래프 만들기
    image
    // 콜라츠 추측으로 우박수열 그래프 만들기
    public int[] getHeight(int k) {
        List<Integer> graph = new ArrayList<>();
        while (k > 1) {
            graph.add(k);
            if (k % 2 == 0) {
                k = k / 2;
            } else {
                k = k * 3 + 1;
            }
        }
        // 마지막에 1 추가
        graph.add(1);

        // list -> arr 변환
        return graph.stream().mapToInt(i -> i).toArray();
    }

각각의 사다리꼴 넓이 구하기

image

        // 사다리꼴 넓이 구하기
        double[] area = new double[cnt + 1];
        for (int i = 1; i <= cnt; i++) {
            area[i] = (double) (height[i - 1] + height[i]) / 2;
        }

누적합 구하기

image

        // 누적합 구하기
        double[] dp = new double[cnt + 1];
        for (int i = 1; i <= cnt; i++) {
            dp[i] = dp[i - 1] + area[i];
        }

전체 코드

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

class Solution {

    // 콜라츠 추측으로 우박수열 그래프 만들기
    public int[] getHeight(int k) {
        List<Integer> graph = new ArrayList<>();
        while (k > 1) {
            graph.add(k);
            if (k % 2 == 0) {
                k = k / 2;
            } else {
                k = k * 3 + 1;
            }
        }
        // 마지막에 1 추가
        graph.add(1);

        // list -> arr 변환
        return graph.stream().mapToInt(i -> i).toArray();
    }


    public double[] solution(int k, int[][] ranges) {
        int[] height = getHeight(k);
        // 콜라츠 횟수는 그래프 길이 -1 만큼
        int cnt = height.length - 1;

        // 사다리꼴 넓이 구하기
        double[] area = new double[cnt + 1];
        for (int i = 1; i <= cnt; i++) {
            area[i] = (double) (height[i - 1] + height[i]) / 2;
        }

        // 누적합 구하기
        double[] dp = new double[cnt + 1];
        for (int i = 1; i <= cnt; i++) {
            dp[i] = dp[i - 1] + area[i];
        }

        double[] answer = new double[ranges.length];
        for (int i = 0; i < ranges.length; i++) {
            int start = ranges[i][0];
            int end = cnt + ranges[i][1];
            // start ~ end 까지의 넓이 구하기
            if (start <= end) {
                // dp[end] = 0 ~ end 까지의 넓이
                // dp[start] = 0 ~ start 까지의 넓이
                // dp[end] - dp[start] = start ~ end 까지의 넓이
                answer[i] = dp[end] - dp[start];
            } else {
                answer[i] = -1.0;
            }


        }
        return answer;
    }

}

시간, 공간 복잡도

  • 콜라츠 함수 그래프 길이 : K, 정적분 구하는 개수 Q
  • 시간 복잡도 : O(K +Q)
  • 공간 복잡도 : O(K)

실행 시간

image

2023.09.16 - 이모티콘 할인

문제 - https://school.programmers.co.kr/learn/courses/30/lessons/150368

문제 설명

  • 카카오톡 이모티콘 할인율을 조정해서 이모티콘 플러스를 많이 구입하게 하자.

우선순위

  1. 최대한 많은 사람이 이모티콘 플러스를 구입하게 하자.
  2. 최대한 많은 매출액을 발생시키자.

사용한 알고리즘

  • 브루트 포스
  • 이모티콘의 범위가 7이므로 브루트포스로 문제를 해결해도 됨
  • 4^7= 2^14

이모티콘의 할인율 할당

  • 재귀함수를 이용하여 이모티콘의 할인율을 할당해주었다.
  • 끝까지 도달했다면 시뮬레이션을 돌려 이모티콘 플러스 구입자와 매출을 계산
    static void dfs(int idx,int[][] users,int[] emoticons){
        if(idx==N){
            // 시뮬레이션 시작
            
            int[] result = simulate(users,emoticons);
            int cnt = result[0];
            int tot = result[1];
            if(maxCnt<cnt){
                maxCnt = cnt;
                maxSale = tot;
            }else if(maxCnt==cnt){
                if(maxSale<tot){
                    maxSale = tot;
                }
            }
            return;
        }
        
        for(int i = 0 ; i<4;i++){
            // 이모티콘의 할인율 할당
            emotiPercent[idx] = salePercent[i];
            dfs(idx+1,users,emoticons);
        }
    }
    

계산

    static int[] simulate(int[][] users, int[] emoticons){
        int cnt =0;
        int tot = 0;
        
        // 유저마다 이모티콘 구매 확인
        for(int[] user : users){
            int price = 0;
            int buyRate = user[0];
            int register = user[1];
            
            
            for(int i = 0 ; i<N;i++){
                // 유저가 정한 할인율 보다 높은 것이 있다면
                // 이모티콘 구매
                if(emotiPercent[i] >=buyRate){
                    price+= emoticons[i]*0.01*(100-emotiPercent[i]);
                }
            }
            // 유저가 이모티콘플러스를 구입하는지 체크
            if(price>=register){
                cnt++;
            }else{
                // 이모티콘 플러스 구입 X
                tot+=price;
            }
            
        }

전체 코드

import java.util.*;
class Solution {
    
    
    static int[] simulate(int[][] users, int[] emoticons){
        int cnt =0;
        int tot = 0;
        
        // 유저마다 이모티콘 구매 확인
        for(int[] user : users){
            int price = 0;
            int buyRate = user[0];
            int register = user[1];
            
            
            for(int i = 0 ; i<N;i++){
                // 유저가 정한 할인율 보다 높은 것이 있다면
                // 이모티콘 구매
                if(emotiPercent[i] >=buyRate){
                    price+= emoticons[i]*0.01*(100-emotiPercent[i]);
                }
            }
            // 유저가 이모티콘플러스를 구입하는지 체크
            if(price>=register){
                cnt++;
            }else{
                // 이모티콘 플러스 구입 X
                tot+=price;
            }
            
        }
        
        return new int[]{cnt,tot};
    }
    
    
    static void dfs(int idx,int[][] users,int[] emoticons){
        if(idx==N){
            // 시뮬레이션 시작
            
            int[] result = simulate(users,emoticons);
            int cnt = result[0];
            int tot = result[1];
            if(maxCnt<cnt){
                maxCnt = cnt;
                maxSale = tot;
            }else if(maxCnt==cnt){
                if(maxSale<tot){
                    maxSale = tot;
                }
            }
            return;
        }
        
        for(int i = 0 ; i<4;i++){
            // 이모티콘의 할인율 할당
            emotiPercent[idx] = salePercent[i];
            dfs(idx+1,users,emoticons);
        }
    }
    
    
    static int[] salePercent = {10,20,30,40};
    static int[] emotiPercent;
    static int maxCnt;
    static int maxSale;
    static int N;
    
    public int[] solution(int[][] users, int[] emoticons) {
        N= emoticons.length;
        emotiPercent = new int[N];
        
        // 이모티콘의 크기는 최대 7이다 
        // 브루트포스가 가능하다 => 4^7= 2^14
        dfs(0,users,emoticons);
        
        return new int[]{maxCnt,maxSale};
        
        
    }
}

실행 결과

image

2023.10.21 - binary-tree-zigzag-level-order-traversal

문제 - https://leetcode.com/problems/binary-tree-zigzag-level-order-traversal

문제 설명

  • 이진 트리의 값을 레벨마다 방향을 바꾸면서 출력

해결 방법

  • 현재 오른쪽 방향이면 리스트의 마지막에 추가
  • 왼쪽 방향이면 리스트의 처음에 추가

전체 코드

import java.util.*;

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */

class Solution {
    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
        List<List<Integer>> result = new ArrayList<>();
        Queue<TreeNode> Q = new ArrayDeque<>();
        boolean isRightDirect = true;
        
        // 루트노드의 값이 존재한다면 큐에 추가
        if(root!=null){
            Q.add(root);
        }
        

        while (!Q.isEmpty()) {
            List<Integer> line = new ArrayList<>();
            int size = Q.size();

            // 현재 레벨의 크기만큼 반복
            while(size-->0){
                TreeNode cur = Q.poll();
                
                //오른쪽 방향이면 마지막에 추가
                if(isRightDirect){
                    line.add(cur.val);
                }else{// 왼쪽 방향이면 처음에 추가
                    line.add(0,cur.val);
                }
                
                if(cur.left!=null){
                    Q.add(cur.left);
                }
                if(cur.right!=null){
                    Q.add(cur.right);
                }
            }
            // 레벨 반복문 끝

            /// 방향 반대로
            isRightDirect = !isRightDirect; 
            result.add(line);
        }
        return result;
    }
}

2024.01.27 - custom-sort-string

문제 - 링크

문제 풀이

  • 주어진 문자 순서를 만족하는 문자열 출력하기

주어진 순서를 만족하게 하는 Comparator 만들기

  • HashMap을 이용하여 해당 문자의 순서값 가져오기. 존재하지 않다면 최댓값 리턴
    static class MyComparator implements Comparator<Character> {
        HashMap<Character, Integer> orders;

        public MyComparator(HashMap<Character, Integer> orders) {
            this.orders = orders;
        }

        @Override
        public int compare(Character o1, Character o2) {
            // 해쉬맵에 존재하지 않는다면 최대값(26)을 리턴한다.
            int order1 = orders.getOrDefault(o1, 26);
            int order2 = orders.getOrDefault(o2, 26);

            return order1 - order2;
        }
    }
  • 순서값 저장
         HashMap<Character, Integer> hm = new HashMap<>();

        for (int i = 0; i < order.length(); i++) {
            hm.put(order.charAt(i), i);
        }

코드

class Solution {
    static class MyComparator implements Comparator<Character> {
        HashMap<Character, Integer> orders;

        public MyComparator(HashMap<Character, Integer> orders) {
            this.orders = orders;
        }

        @Override
        public int compare(Character o1, Character o2) {
            // 해쉬맵에 존재하지 않는다면 최대값(26)을 리턴한다.
            int order1 = orders.getOrDefault(o1, 26);
            int order2 = orders.getOrDefault(o2, 26);

            return order1 - order2;
        }
    }

    public String customSortString(String order, String s) {

        HashMap<Character, Integer> hm = new HashMap<>();

        for (int i = 0; i < order.length(); i++) {
            hm.put(order.charAt(i), i);
        }

        // 주어진 순서를 만족하게 해주는 Comparator
        MyComparator myComparator = new MyComparator(hm);

        Character[] charStr = new Character[s.length()];
        for (int i = 0; i < s.length(); i++) {
            charStr[i] = s.charAt(i);
        }
        // Comparator 은 Character Wrapper 클래스를 사용해야 함 
        Arrays.sort(charStr, myComparator);

        StringBuilder sb = new StringBuilder();
        for (char c : charStr) {
            sb.append(c);
        }
        return sb.toString();
    }
}

시간, 공간 복잡도

S의 길이 : N

  • 시간 복잡도 : O(NlogN) - s을 정렬하는 데 걸리는 시간
  • 공간 복잡도 : O(N)

실행 시간

image

2023.10.28 - Valid Square

문제 - https://leetcode.com/problems/valid-square

문제 설명

  • 주어진 네 좌표가 정사각형인지 파악하는 문제

문제 해설

  • 정사각형의 성질
  • 네 변의 길이가 같음, 두 대각선의 길이가 같음
  • 두 점 사이의 거리들은 오직 2가지 경우
        HashSet<Integer> hs = new HashSet<>();
        for(int i = 0; i <4;i++){
            for(int j = i+1;j<4;j++){
                int[] p = arr[i];
                int[] q = arr[j];
                
                // 두 점 사이의 거리 체크
                int dx = p[0]-q[0];
                int dy = p[1]-q[1];
                int dist = dx*dx + dy*dy;
                
                
                hs.add(dist);
            }
        }
  • 두 점 사이의 거리의 결과값에 루트를 씌우게 되어 실수가 됨 => 부동 소수점 문제가 생길 수 있음
  • 따라서 두 점 사이의 거리를 구할 때 루트를 씌우지 않음
                int dist = dx*dx + dy*dy;

전체 코드

class Solution {
    public boolean validSquare(int[] p1, int[] p2, int[] p3, int[] p4) {
        
        // 정사각형 조건
        // 네 변 길이 같음. 평행
        
        int[][] arr = new int[][]{p1,p2,p3,p4};
        
        HashSet<Integer> hs = new HashSet<>();
        for(int i = 0; i <4;i++){
            for(int j = i+1;j<4;j++){
                int[] p = arr[i];
                int[] q = arr[j];
                
                // 두 점 사이의 거리 체크
                int dx = p[0]-q[0];
                int dy = p[1]-q[1];
                int dist = dx*dx + dy*dy;
                
                
                hs.add(dist);
            }
        }
        
        if(hs.contains(0)){
            return false;
        }
        
        return hs.size()==2;

    }
}

2023.02.03 - number-of-islands

문제 - 링크

문제 풀이

  • 0 : 바다, 1: 육지인데 섬의 개수를 찾는 문제
  • 섬의 조건은 상하좌우로 이어진 육지를 섬이라고 함

섬 탐색

  • (0,0) -> (n,m)으로 탐색을 하며
  • 방문하지 않은 육지라면 탐색 시작
  • 중복 방지를 위하여 2차원 방문 배열 생성 boolean[n][m]
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                // 육지이고 방문하지 않았다면 dfs 실행
                if (grid[i][j] == '1' && !visited[i][j]) {
                    dfs(i, j, grid, visited);
                    islandCnt++;
                }
            }
        }

해당 �섬의 육지 탐색

  • 육지에 대하여 상하좌우 탐색
  • 격자를 벗어나거나, 바다이거나, 방문한 육지가 아닌 경우 재귀 호출
    // dfs 탐색
    void dfs(int x, int y, char[][] grid, boolean[][] visited) {
        visited[x][y] = true;
        // 상하 좌우에 대하여 탐색
        for (int i = 0; i < 4; i++) {
            int nx = x + dx[i];
            int ny = y + dy[i];
            // 격자에서 벗어났다면
            if (nx < 0 || ny < 0 || nx >= n || ny >= m) {
                continue;
            }
            // 바다이거나, (육지이지만)방문했다면
            if (grid[nx][ny] == '0' || visited[nx][ny]) {
                continue;
            }
            //재귀 호출
            dfs(nx, ny, grid, visited);
        }
    }

코드

class Solution {
    // dfs 탐색
    void dfs(int x, int y, char[][] grid, boolean[][] visited) {
        visited[x][y] = true;
        // 상하 좌우에 대하여 탐색
        for (int i = 0; i < 4; i++) {
            int nx = x + dx[i];
            int ny = y + dy[i];
            // 격자에서 벗어났다면
            if (nx < 0 || ny < 0 || nx >= n || ny >= m) {
                continue;
            }
            // 바다이거나, (육지이지만)방문했다면
            if (grid[nx][ny] == '0' || visited[nx][ny]) {
                continue;
            }
            //재귀 호출
            dfs(nx, ny, grid, visited);
        }
    }



    int[] dx = {-1, 1, 0, 0};
    int[] dy = {0, 0, -1, 1};
    int n, m;

    public int numIslands(char[][] grid) {
        // n = 행, m = 열
        n = grid.length;
        m = grid[0].length;
        boolean[][] visited = new boolean[n][m];
        int islandCnt = 0; //섬의 개수
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                // 육지이고 방문하지 않았다면 dfs 실행
                if (grid[i][j] == '1' && !visited[i][j]) {
                    dfs(i, j, grid, visited);
                    islandCnt++;
                }
            }
        }
        return islandCnt;

    }
}

시간, 공간 복잡도

  • 시간 복잡도 : O(N^2)
  • 공간 복잡도 : O(N^2)

실행 시간

image

2024.01.27 - 괄호 회전하기

문제 - 링크

문제 풀이

  • 괄호를 왼쪽으로 회전시키며 올바른 괄호가 몇 개인지 계산하기

괄호를 왼쪽으로 i번 회전하기

  • subString을 이용하여 index을 기준으로 왼쪽과 오른쪽을 나눔. 왼쪽에 있는 것은 회전해야 하는 문자열
            String endStr =s.substring(0, i);
            String startStr = s.substring(i);
            String str = startStr + endStr;

올바른 괄호 확인하기

  • for문을 돌며 (),{},[]을 공백으로 치환한다.
  • for문을 다 돌고, 만약 주어진 문자열이 공백이라면 => 올바른 괄호문이다.
            for(int j= 0 ;j <size;j++){
                str = str.replace("{}","");
                str = str.replace("[]","");
                str = str.replace("()","");
            }   

코드

class Solution {
    public int solution(String s) {
        // 길이 1000
        // 회전 1000
        
        
        
        int size = s.length();
        int cnt = 0;
        
        for(int i = 0 ; i<size;i++){
            //회전 시킨다.
            String endStr =s.substring(0, i);
            String startStr = s.substring(i);
            String str = startStr + endStr;
            // System.out.println(str);
            
            for(int j= 0 ;j <size;j++){
                str = str.replace("{}","");
                str = str.replace("[]","");
                str = str.replace("()","");
            }            
            if(str.length()==0){
                cnt++;
            }
        }

        return cnt;
        
    }
}

시간, 공간 복잡도

  • 시간 복잡도 : O(N^3)
  • 공간 복잡도 : O(N)

실행 시간

image

2023.09.09 - Word Break

문제 - https://leetcode.com/problems/word-break/

문제 설명

  • 주어진 s 문자열을 wordDict을 이용해서 만들 수 있냐 없냐를 판단하는 문제

첫 번째 시도

  • DFS을 시도
class Solution {
    
    static boolean dfs(int idx ,String s){
        if(idx == s.length()){
            return true;
        }
        for(int i = idx+1;i<=s.length();i++){
            String subStr = s.substring(idx,i);
            if(hs.contains(subStr)){
                if(dfs(i,s)){
                    return true;
                };
            }
        }
        
        
        return false;
    }
    static HashSet<String> hs;
    public boolean wordBreak(String s, List<String> wordDict) {
        hs = new HashSet<>();
        for(String word : wordDict){
            hs.add(word);
        }
        return dfs(0,s);
    }
}
  • 시간 초과 발생

두 번째 시도

  • 단어들을 가지고 앞에서 부터 차근차근 만들어보기
class Solution {
    
    
    public boolean wordBreak(String s, List<String> wordDict) {
        
        HashSet<String> answer = new HashSet<>();
        HashSet<String> hs = new HashSet<>();
        
        for(String word : wordDict){
            hs.add(word);
            if(word.length()==s.length()){
                answer.add(word);
            }
        }
        
        while(hs.size()!=0){
            HashSet<String> newHs = new HashSet<>();
            for(String word: wordDict){
                for(String str : hs){
                    String word1 = word+str;
                    String word2 = str+word;
                    int size = word1.length();
                    if(size<s.length()){
                        if(s.substring(0,size).equals(word1)){
                            newHs.add(word1);
                        }else if(s.substring(0,size).equals(word2)){
                            newHs.add(word2);
                        }
                    }else if(size==s.length()){
                        answer.add(word1);
                        answer.add(word2);
                    }
                }
            } 
            hs = newHs;
        }
        if(answer.contains(s)){
            return true;
        }else{
            return false;
        }
    }
}

실행 결과

image

  • 실행 시간 & 메모리 모두 처참

약간의 최적화

  • 반복되는 subString 연산 제거
class Solution {
    
    
    public boolean wordBreak(String s, List<String> wordDict) {
        
        HashSet<String> hs = new HashSet<>();
        
        for(String word : wordDict){
            hs.add(word);
            if(s.equals(word)){
                return true;
            }
        }
        
        String[] sArr = new String[s.length()];
        for(int i = 1; i<s.length();i++){
            sArr[i] = s.substring(0,i);
        }
        
        while(hs.size()!=0){
            HashSet<String> newHs = new HashSet<>();
            for(String word: wordDict){
                for(String str : hs){
                    String word1 = word+str;
                    String word2 = str+word;
                    int size = word1.length();
                    if(size<s.length()){
                        if(sArr[size].equals(word1)){
                            newHs.add(word1);
                        }else if(sArr[size].equals(word2)){
                            newHs.add(word2);
                        }
                    }else if(size==s.length()){
                        if(s.equals(word1)){
                            return true;
                        }
                        if(s.equals(word2)){
                            return true;
                        }
                        
                    }
                }
            } 
            hs = newHs;
        }
        return false;
    }
}

실행 결과

image

  • 그래도 처참하다.

2023.02.03 - simplify-path

문제 - 링크

문제 풀이

  • 주어진 경로를 압축하여 단순화한 경로를 리턴하는 문제

경로 압축하기

  • .은 현재 디렉토리를 의미하고, ..은 이전 디렉토리를 의미함
            String cur = st.nextToken();
            //현재 path 값이 . 이라면 현재 디렉토리이므로 아무 작업 할 필요 없음
            if (cur.equals(".")) {
                continue;
            }
            // ..이라면 이전 디렉토리으로 이동
            if (cur.equals("..")) {
                if (!deque.isEmpty()) {
                    deque.pollLast();
                }
            //그 외는 경로에 추가
            } else {
                deque.addLast(cur);
            }

코드

import java.util.*;

class Solution {

    //경로 압축
    public String simplifyPath(String path) {
        // "/"을 기준으로 나눔
        StringTokenizer st = new StringTokenizer(path, "/");
        Deque<String> deque = new ArrayDeque<>();

        while (st.hasMoreTokens()) {
            String cur = st.nextToken();
            //현재 path 값이 . 이라면 현재 디렉토리이므로 아무 작업 할 필요 없음
            if (cur.equals(".")) {
                continue;
            }
            // ..이라면 이전 디렉토리으로 이동
            if (cur.equals("..")) {
                if (!deque.isEmpty()) {
                    deque.pollLast();
                }
            //그 외는 경로에 추가
            } else {
                deque.addLast(cur);
            }
        }
        StringBuilder sb = new StringBuilder();
        while (!deque.isEmpty()) {
            sb.append("/").append(deque.pollFirst());
        }
        // 경로가 공백인 경우는 루트 경로 리턴
        return !sb.isEmpty() ? sb.toString() : "/";

    }
}

시간, 공간 복잡도

K : /으로 나눈 문자의 개수

  • 시간 복잡도 : O(N + K)
  • 공간 복잡도 : O(N + K)

실행 시간

image

2024.01.06 - Minimum Path Sum

문제 - 링크

문제 풀이

  • 가장 작은 숫자들의 합으로 N,N에 도착하기

문제 풀이 로직

  • 현재 내가 위치한 곳에 도착하기 위해서는 위쪽에서 오는 경우 or 왼쪽에서 오는 경우 2가지
  • 따라서 두 경우 중 작은 값과 현재 내 값을 더한 값이 작은 값이 됨

문제 풀이 로직 1

  • 코드를 간단하게 작성하기 위하여 배열의 크기를 1씩 키움
  • NxN -> (N+1)x(N+1) 배열 만들기
        int[][] dp = new int[N+1][M+1];
        
        for(int i = 1; i<=N;i++){
            for(int j = 1; j<=M;j++){
                dp[i][j] = grid[i-1][j-1];
            }
        }

문제 풀이 로직 2

  • 가장 작은 합 구하기
  • dp은 현재까지 더한 숫자들의 합 중 작은 값을 저장하는 배열
                int up = dp[i-1][j];
                int left = dp[i][j-1];
                int minValue = Math.min(up,left);
                dp[i][j] += minValue;

코드

class Solution {
    private static int INF = 40_001;
    
    public int minPathSum(int[][] grid) {
        int N = grid.length;
        int M = grid[0].length;
        
        int[][] dp = new int[N+1][M+1];
        
        for(int i = 1; i<=N;i++){
            for(int j = 1; j<=M;j++){
                dp[i][j] = grid[i-1][j-1];
            }
        }
        for(int i = 2; i<=N;i++){
            dp[i][0] = INF;
        }
        for(int j = 2 ; j<=M;j++){
            dp[0][j] = INF;
        }
        
        /* init grid
        //  0   0  INF INF
        //  0   1   3   1
        // INF  1   5   1
        // INF  4   2   1
        */
        
        
        for(int i = 1; i <=N;i++){
            for(int j =1 ; j<=M;j++){
                int up = dp[i-1][j];
                int left = dp[i][j-1];
                int minValue = Math.min(up,left);
                dp[i][j] += minValue;
            }
        }

        return dp[N][M];
        
    }
}

시간, 공간 복잡도

  • 시간 복잡도 : O(N^2)
  • 공간 복잡도 : O(N^2)

실행 시간

2024.01.06 - lowest-common-ancestor-of-a-binary-search-tree

문제 - 링크

문제 풀이

  • 트리에서 가장 작은 공통 부모 구하기

문제 풀이 로직 1

  • BST은 다음과 같은 특성을 가지고 있음

    • 왼쪽 자식들은 부모의 값보다 작음
    • 오른쪽 자식들은 부모의 값보다 큼
  • 따라서, 루트에서부터 내려가서 현재 노드의 값(val)과 찾으려는 p와 q의 값을 비교

    • val < p,q ? => 오른쪽 자식으로 이동
    • val > p,q ? => 왼쪽 자식으로 이동
    • 그 외 => 현재 노드가 가장 작은 부모가 됨
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */

class Solution {
    
    
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        TreeNode cur = root;
        int pValue = p.val;
        int qValue = q.val;
        
        while(cur!=null){
            int curValue = cur.val;
            // p ,q 모두 현재 노드의 값보다 큰 경우 오른쪽 자식으로 이동
            if(pValue > curValue && qValue > curValue){
                cur = cur.right;
            }// p,q 모두 현재 노드의 값 보다 작은 경우 왼쪽 자식으로 이동
            else if(pValue < curValue && qValue < curValue){
                cur = cur.left;
            }// 그렇지 않은 경우 현재 노드가 LCA이다.
            else{
                break;
            }
        }
        return cur;
        
    }
}

코드

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */

class Solution {
    
    
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        TreeNode cur = root;
        int pValue = p.val;
        int qValue = q.val;
        
        while(cur!=null){
            int curValue = cur.val;
            // p ,q 모두 현재 노드의 값보다 큰 경우 오른쪽 자식으로 이동
            if(pValue > curValue && qValue > curValue){
                cur = cur.right;
            }// p,q 모두 현재 노드의 값 보다 작은 경우 왼쪽 자식으로 이동
            else if(pValue < curValue && qValue < curValue){
                cur = cur.left;
            }// 그렇지 않은 경우 현재 노드가 LCA이다.
            else{
                break;
            }
        }
        return cur;
        
    }
}

시간, 공간 복잡도

  • 시간복잡도 : O(logN) - 이진트리의 높이 = logN
  • 공간복잡도 : O(N)

실행 시간

image

2023.10.28 - course-schedule

문제 - https://leetcode.com/problems/course-schedule

문제 설명

  • 선 이수과목이 있는 과목이 있음
  • [q,p] : q을 듣기 위해서는 p을 들어야 함
  • 주어진 과목에서 모든 과목을 들을 수 있는지 체크

풀이

  • 해당 과목을 듣기 위하여 선 이수과목이 몇개 있는지 확인하는 배열
int[] preqNum = new int[N];
  • 어떤 과목을 들었을 때 필수과목 리스트 생성
  // 이수과목 p -> q  (q을 들으라면 p을 들어야 함)
  for(int[] preq : prerequisites){
      int q = preq[0];
      int p = preq[1];
      preqList[p].add(q);
      preqNum[q]++;
  }
  • 들을 수 있는 과목을 큐에 넣음
// 내가 들을 수 있는 과목
Queue<Integer> Q = new ArrayDeque<>();
for(int i = 0 ;i<N;i++){
    if(preqNum[i]==0){
        Q.add(i);
    }
}
while(!Q.isEmpty()){
    int cur = Q.poll();
    for(int next: preqList[cur]){
        if(--preqNum[next]==0){
            Q.add(next);
        }
    }
}

전체 코드

class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        int N = numCourses;
        // 이후과목?
        List<Integer>[] preqList = new ArrayList[N];
        int[] preqNum = new int[N];
        
        
        for(int i = 0; i <N;i++){
            preqList[i] = new ArrayList<>();
        }
        
        // 이수과목 p -> q  (q을 들으라면 p을 들어야 함)
        for(int[] preq : prerequisites){
            int q = preq[0];
            int p = preq[1];
            preqList[p].add(q);
            preqNum[q]++;
        }
        
        // 내가 들을 수 있는 과목
        Queue<Integer> Q = new ArrayDeque<>();
        for(int i = 0 ;i<N;i++){
            if(preqNum[i]==0){
                Q.add(i);
            }
        }
        
        while(!Q.isEmpty()){
            int cur = Q.poll();
            for(int next: preqList[cur]){
                if(--preqNum[next]==0){
                    Q.add(next);
                }
            }
        }
        
        // 들을 수 없는 과목이 존재한다면
        for(int i = 0 ; i<N;i++){
            if(preqNum[i]>0){
                return false;
            }
        }
        return true;
        
        
    }
}

실행 결과

image
image

2023.09.07 - 혼자서 하는 틱택토

문제 - https://school.programmers.co.kr/learn/courses/30/lessons/160585

문제 설명

  • 주어진 틱택토가 유효한지 아닌지를 판단하는 문제

문제 해설

  • 먼저 X와 O의 개수를 파악
  • X의 개수가 O의 개수보다 많으면 안됨
  • O의 개수가 X의 개수보다 2이상 많으면 안됨( O를 연속으로 2번 둔 것)
  • X의 개수가 O의 개수가 같은 경우 ( 현재 X까지 진행한 경우 )
    • 주어진 틱택토에서 O 빙고가 있으면 안됨
  • O의 개수가 X의 개수보다 많은 경우 (현재 O까지 진행한 경우)
    • 주어진 틱택토에서 X 빙고가 있으면 안됨

전체 코드

class Solution {
    
    
    static boolean isBingo(char c ){
        // row check
        for(int i = 0; i <3;i++){
            boolean flag = true;
            for(int j = 0 ;j<3;j++){
                if(G[i][j] != c){
                    flag =false;
                }
            }
            if(flag){
                return true;
            }
            
        }
        
        // col check
        for(int i = 0 ; i<3;i++){
            boolean flag = true;
            for(int j= 0 ; j <3;j++){
                if(G[j][i]!=c){
                    flag = false;
                }
            }
            if(flag){
                return true;
            }
        }
        // diag check
        boolean isTrue = true;
        for(int i = 0 ; i<3;i++){
            if(G[i][i]!= c){
                isTrue = false;
            }
        }
        if(isTrue){
            return true;
        }
        
        isTrue = true;
        for(int i = 0 ; i<3;i++){
            if(G[i][2-i]!=c){
                isTrue = false;
            }
        }
        if(isTrue){
            return true;
        }
        return false;
        
    }
    
    static char[][] G;
    
    public int solution(String[] board) {
        int oCnt = 0;
        int xCnt = 0;
        G = new char[3][3];
        for(int i = 0 ; i<3;i++){
            for(int j = 0 ; j<3;j++){
                G[i][j] = board[i].charAt(j);
                if(G[i][j]=='O'){
                    oCnt++;
                }else if(G[i][j] =='X'){
                    xCnt++;
                }
            }
        }

        // X의 개수가 더 많거나
        // O의 개수가 X의 개수+1 보다 많은 경우
        if(xCnt>oCnt || oCnt>xCnt+1){
            return 0;
        }
        // 현재 o까지 했을 때
        if(oCnt>xCnt){
            // X 빙고 있으면 안됨
            if(isBingo('X')){
                return 0;
            }
        }
        // 현재 x까지 했을 때
        else if (xCnt == oCnt){
            // O 빙고 있으면 안됨
            if(isBingo('O')){
                return 0;
            }

        }
        
        return 1;
    }
}

실행 결과

image

2024.03.07 - Integer break

문제 - 링크

문제 풀이

  • 2개 이상의 자연수의 합으로 만드는 N에 대하여 각각의 자연수의 곱의 최댓값 구하기

i을 만드는 데 가장 큰 최댓값 구하기

  • i보다 작은 자연수 j에 대하여 d[i]의 값은 d[j] * d[i-j]로 만들어 짐
  • 각각에 대하여 최댓값 구하기
  • 여러 자연수의 곱보다 단일 숫자의 값이 더 클 수도 있음
            for (int j = 1; j < i; j++) {
                dp[i] = Math.max(dp[i], dp[j] * dp[i - j]);
            }
            if (i < n) {
                dp[i] = Math.max(dp[i], i);
            }

코드

class Solution {
    public int integerBreak(int n) {
        int[] dp = new int[n + 1];

        dp[1] = 1;
        for (int i = 1; i <= n; i++) {
            // dp[1]*dp[1]
            for (int j =1;j<i;j++){
                dp[i] = Math.max(dp[i], dp[j] * dp[i - j]);
            }
            if(i<n){
                dp[i] = Math.max(dp[i], i);
            }
        }

        return dp[n];
    }
}

시간, 공간 복잡도

  • 시간 복잡도 : O(N*2)
  • 공간 복잡도 : O(N)

실행 시간

image

2023.12.02 - minimum-number-of-operations-to-move-all-balls-to-each-box

문제 - minimum-number-of-operations-to-move-all-balls-to-each-box

문제 링크

문제 해설

import java.util.*;

class Solution {
    static class Ball{
        int idx;
        int cnt;
        boolean isRight;
        
        public Ball(int idx , int cnt, boolean isRight){
            this.idx = idx;
            this.cnt = cnt;
            this.isRight = isRight;
        }
    }
    
    public int[] minOperations(String boxes) {
        // 박스 길이 최대 2000
        int N = boxes.length();
        int[] arr = new int[N];
        int[] tot = new int[N];
        Queue<Ball> Q = new ArrayDeque<>();
        for(int i = 0 ; i < N ; i++){
            if(boxes.charAt(i)-'0' == 1){
                if(i>0)
                Q.add(new Ball(i-1,1,false));
                if(i<N-1)
                Q.add(new Ball(i+1,1,true));
            }
        }
        
        while(!Q.isEmpty()){
            Ball cur = Q.poll();
            tot[cur.idx]+=cur.cnt;
            
            if(cur.isRight && cur.idx<N-1){
                Q.add(new Ball(cur.idx+1,cur.cnt+1, true));
            }
            if(!cur.isRight && cur.idx>0){
                Q.add(new Ball(cur.idx-1,cur.cnt+1,false));
            }
        }
        return tot;
        
        
    }
}

2023.09.07 - Triangle

문제 - https://leetcode.com/problems/triangle/

문제 설명

  • 위에서 부터 내려오는 값을 더하였을 때 최소가 되는 값을 찾기

사용 알고리즘

  • dp

  • dp 배열을 j번째에 도착할 때 최소가 되는 값이라고 할 때

  • dp[i][j] = Math.min(dp[i-1][j], dp[i-1][j-1]) + list.get(j) 으로 계산 가능

  • 메모리를 최소화 하기 위하여 이를 1차원 배열로 사용 가능

  • 0 -> j 로 탐색 하는 것이 아니라 j-1 -> 0으로 반대로 탐색

    for(int i = 0 ; i<N;i++){
        List list = triangle.get(i);
        for(int j = list.size()-1 ; j>=0;j--){
            dp[j+1] = Math.min(dp[j+1],dp[j]) +(int) list.get(j);
        }
    }

전체 코드

import java.util.List;

class Solution {
    public int minimumTotal(List<List<Integer>> triangle) {
        int N = triangle.size();
        int[] dp = new int[N+1];

        for(int i = 0 ; i<N;i++){
            List list = triangle.get(i);
            for(int j = list.size()-1 ; j>=0;j--){
                dp[j+1] = Math.min(dp[j+1],dp[j]) +(int) list.get(j);
            }
        }

        int minValue = Integer.MAX_VALUE;
        for(int i = 1; i<=N;i++){
            minValue = Math.min(minValue,dp[i]);
        }
        return minValue;

    }
}

시간 복잡도

image

메모리

image

  • dp 배열을 1차원으로 사용했음에도 불구하고 메모리 공간을 많이 차지하였다..

2024.03.01 - smallest-number-in-infinite-set

문제 - 링크

문제 풀이

  • 자연수가 들어 있는 집합에서 가장 작은 값을 출력하는 연산과 어떤 숫자를 집합에 추가하는 연산 수행

문제 풀이 로직 1

  • 집합은 중복된 값이 존재하면 안되므로 HashSet을 이용하여 중복을 체크하였음
  • 주어진 상황에서 가장 작은 값을 출력하는 가장 적합한 자료구조인 PriorityQueue 사용

가장 작은값 출력

    public int popSmallest() {
        int result = PQ.poll();
        hs.remove(result);
        
        return result;
        
    }

숫자 추가

    public void addBack(int num) {
        if(!hs.contains(num)){
            hs.add(num);
            PQ.add(num);
        }
    }

코드

class SmallestInfiniteSet {
    
    PriorityQueue<Integer> PQ ;
    HashSet<Integer> hs;

    public SmallestInfiniteSet() {
        PQ = new PriorityQueue<>();
        hs = new HashSet<>();
        for(int i = 1; i<=1000;i++){
            PQ.add(i);
            hs.add(i);
        }
    }
    
    public int popSmallest() {
        int result = PQ.poll();
        hs.remove(result);
        
        return result;
        
    }
    
    public void addBack(int num) {
        if(!hs.contains(num)){
            hs.add(num);
            PQ.add(num);
        }
    }
}

/**
 * Your SmallestInfiniteSet object will be instantiated and called as such:
 * SmallestInfiniteSet obj = new SmallestInfiniteSet();
 * int param_1 = obj.popSmallest();
 * obj.addBack(num);
 */

시간, 공간 복잡도

  • 시간 복잡도 : O(nlogn)
  • 공간 복잡도 : O(n)

실행 시간

image

2023.08.05 - 수식 최대화

링크 - https://school.programmers.co.kr/learn/courses/30/lessons/67257

연산자 +, -, *의 우선순위를 정하여 수식 결과의 절댓값을 최대화 하는 문제

* > + > - 
100-200*300-500+20
= 100-(200*300)-500+20
= 100-60000-(500+20)
= (100-60000)-520
= (-59900-520)
= -60420

문자열을 숫자 리스트와, 연산자 리스트로 분리하기

  • StringTokenizer(str, del, true)이용
  • 첫 번째 인자 : 분리할 문자열
  • 두 번째 인자 : 분리자(문자) - 정규표현식이 아니라 단순한 char 문자만 가능
  • 세 번째 인자 : 분리자도 토큰 포함 여부
        String del = "+-*";
        StringTokenizer st = new StringTokenizer(expression, del, true);

        nums = new ArrayList<>();
        ops = new ArrayList<>();
        while (st.hasMoreTokens()) {
            String next = st.nextToken();
            if (next.length() == 1 && del.contains(next)) {
                ops.add(next.charAt(0));
            } else {
                long n = Long.parseLong(next);
                nums.add(n);
            }
        }

연산자 우선순위 정하기

  • 순열을 통해 결정
    /* 연산 우선순위 순열 */
    static void perm(int depth) {
        if (depth == 3) {
            long result = solve();
            maxVal = Math.max(maxVal, result);
            return;
        }
        for (int i = 0; i < 3; i++) {
            if (!visited[i]) {
                visited[i] = true;
                opOrder[depth] = opArr[i];
                perm(depth + 1);
                visited[i] = false;
            }
        }
    }

피연산자 2개와 연산자를 이용한 계산

    static long calc(long a, long b, char op) {
        switch (op) {
            case '+':
                return a + b;
            case '-':
                return a - b;
            default:// 연산자가 *
                return a * b;
        }
    }

연산하기

  • 연산 뒤, newNums 리스트에 두 피연산자 인덱스 제거, 결과값 추가
  • 연산자도 사용했으므로 제거
  • 인덱스 -1 (리스트 크기가 1 줄었으므로)
        // 연산자 우선순위가 높은 것부터 반복문 돌아서 실행
        for (char op : opOrder) {
            for (int i = 0; i < newOps.size(); i++) {
                if (newOps.get(i) == op) {
                    long n1 = newNums.get(i);
                    long n2 = newNums.get(i + 1);
                    long result = calc(n1, n2, op);

                    newNums.remove(i + 1);
                    newNums.remove(i);
                    newNums.add(i, result);

                    newOps.remove(i);
                    i--;
                }
            }
        }

단계별 결과값

우선순위 : [*, +, -]
ops : [-, *, -, +] ,nums : [100, 200, 300, 500, 20]
ops : [-, -, +] ,nums : [100, 60000, 500, 20]
ops : [-, -] ,nums : [100, 60000, 520]
ops : [-] ,nums : [-59900, 520]
result :-60420

전체 코드

import java.util.*;

class Solution {
    static long calc(long a, long b, char op) {
        switch (op) {
            case '+':
                return a + b;
            case '-':
                return a - b;
            default:// 연산자가 *
                return a * b;
        }
    }

    static long solve() {
        List<Long> newNums = new ArrayList<>(nums);
        List<Character> newOps = new ArrayList<>(ops);

        System.out.println("우선순위 : " + Arrays.toString(opOrder));
        // 연산자 우선순위가 높은 것부터 반복문 돌아서 실행
        for (char op : opOrder) {

            for (int i = 0; i < newOps.size(); i++) {
                if (newOps.get(i) == op) {
                    System.out.print("ops : " + newOps);
                    System.out.println(" ,nums : " + newNums);
                    long n1 = newNums.get(i);
                    long n2 = newNums.get(i + 1);
                    long result = calc(n1, n2, op);

                    newNums.remove(i + 1);
                    newNums.remove(i);
                    newNums.add(i, result);

                    newOps.remove(i);
                    i--;
                }

            }
        }
        long result = Math.abs(newNums.get(0));
        System.out.println("result :" + newNums.get(0));
        return result;
    }

    /* 연산 우선순위 순열 */
    static void perm(int depth) {
        if (depth == 3) {
            long result = solve();
            maxVal = Math.max(maxVal, result);
            return;
        }
        for (int i = 0; i < 3; i++) {
            if (!visited[i]) {
                visited[i] = true;
                opOrder[depth] = opArr[i];
                perm(depth + 1);
                visited[i] = false;
            }
        }
    }


    static char[] opOrder = new char[3];
    static boolean[] visited = new boolean[3];
    static char[] opArr = {'+', '-', '*'};
    static long maxVal = 0;

    static List<Long> nums;
    static List<Character> ops;

    public long solution(String expression) {
        String del = "+-*";
        StringTokenizer st = new StringTokenizer(expression, del, true);

        nums = new ArrayList<>();
        ops = new ArrayList<>();
        while (st.hasMoreTokens()) {
            String next = st.nextToken();
            if (next.length() == 1 && del.contains(next)) {
                ops.add(next.charAt(0));
            } else {
                long n = Long.parseLong(next);
                nums.add(n);
            }
        }
        perm(0);

        return maxVal;
    }
}

2023.09.02 - Delete Node in a Linked List

문제 - https://leetcode.com/problems/delete-node-in-a-linked-list

문제 설명

  • 링크드리스트에서 주어진 노드 삭제

문제 풀이

  • 해당 노드를 가리키는 노드가 주어지지 않기 때문에 직접적으로 해당 노드를 삭제하는 것은 불가능
  • 주어진 노드의 값을 변환하자
    • val = 노드.next.val
    • next = 노드.next.next

주어진 노드

image

변경

image

코드

class Solution {
    public void deleteNode(ListNode node) {
        
        // 해당 노드를 삭제하는 것이 아니라
        // 다음 노드의 값들을 가져옴
        ListNode next = node.next;
        node.val = next.val;
        node.next = next.next;
   
        
    }
    
    
}

2023.10.21 - minimum-time-difference

문제 - https://leetcode.com/problems/minimum-time-difference/

문제 설명

  • 주어진 시각의 차이 중 가장 짧은 차이를 출력

해결 방법

  • 먼저 주어진 시각을 정렬
  • 이후 인접한 두 시각의 차이 계산
  • 마지막으로 0번째 인덱스와 제일 마지막 인덱스의 시각 차이 계산(이 때, 0번째 인덱스 시각에 24시간 더함)

전체 코드

import java.util.*;
class Solution {
    
    static class Time implements Comparable<Time>{
        int h;
        int m;
        
        
        public Time(int h, int m){
            this.h = h;
            this.m = m;
        }
        
        static int diff(Time a,Time b){
            int aMinute = 60*a.h+a.m;
            int bMinute = 60*b.h+b.m;
            
            return Math.abs(aMinute-bMinute);
        }
        
        public int compareTo(Time o){
            if(this.h ==o.h){
                return this.m - o.m;
            }
            return this.h - o.h;
        }
        
        
    }
    
    
    
    public int findMinDifference(List<String> timePoints) {
        
        int N  = timePoints.size();
        Time[] times = new Time[N];
        
        int p = 0;
        for(String time : timePoints){
            StringTokenizer st = new StringTokenizer(time,":");
            int h = Integer.parseInt(st.nextToken());
            int m = Integer.parseInt(st.nextToken());
    
            times[p++] = new Time(h,m);
            
        }
        Arrays.sort(times);
        int minDiff = 24*60;
        for(int i=1;i<N;i++){
            Time left = times[i-1];
            Time right = times[i];
            int diff = Time.diff(left,right);
            minDiff = Math.min(minDiff,diff);
        }
        Time firstPlus24 = new Time(times[0].h+24, times[0].m);
        minDiff = Math.min(minDiff,Time.diff(firstPlus24,times[N-1]));
        
        
        
        return minDiff;
    }
}

2023.09.23 - 두 큐 합 같게 만들기

문제 - https://school.programmers.co.kr/learn/courses/30/lessons/118667

문제 설명

  • 두 큐의 합을 같게 만드는 횟수를 리턴해주면 됨

최대 연산 횟수

Q1 = [3,3,3,3]
Q2 = [3,3,21,3]

  • Q2의 마지막 3을 제외한 원소들을 Q1으로 옮긴 뒤( O(Q2)) , Q1에서 21을 제외한 원소들 Q2로 옮김 - ( O(Q1) + O(Q2))
  • Q1 + 2*Q2의 연산
import java.util.*;
class Solution {
    
    static int solve(int[] queue1,int[] queue2){
        Queue<Integer> Q1 = new ArrayDeque<>();
        Queue<Integer> Q2 = new ArrayDeque<>();
        int cntMax = (queue1.length + queue2.length)*2;
        long q1Tot = 0;
        long tot = 0;
        
        int q1Max = 0;
        int q2Max = 0;
        for(int n : queue1){
            Q1.add(n);
            q1Tot+=n;
            tot+=n;
            
            q1Max = Math.max(q1Max,n);
        }
        for(int n : queue2){
            Q2.add(n);
            tot+=n;
            q2Max = Math.max(q2Max,n);
        }
        // 두 큐의 합을 같게 만들 수 없는 경우
        
        long mid = tot/2;
        if(tot%2==1 || q1Max>mid || q2Max>mid ){
            return -1;
        }
        
        int cnt = 0;
        
        while(Q1.size()>0 && cnt<=cntMax){
            if(q1Tot == mid){
                return cnt;
            }
            else if(q1Tot > tot/2){
                int n = Q1.poll();
                Q2.add(n);
                q1Tot-=n;
                cnt++;
            }else{
                int n = Q2.poll();
                Q1.add(n);
                q1Tot+=n;
                cnt++;
            }
        }
        return -1;
    }
    
    
    public int solution(int[] queue1, int[] queue2) {
        int result  = solve(queue1,queue2);
        
        return result;
    }
}

2024.03.01 - 멀리 뛰기

문제 - 링크

문제 풀이

  • 1칸 또는 2칸 멀리뛰기를 할 수 있는 효진이가 N칸 도착했을 때 가능한 경우의 수 출력하기

가능한 경우

  • i칸 도착했을 때 가능한 경우의 수는
    • i-1칸에서 1칸 점프
    • i-2칸에서 2칸 점프
      따라서 점화식은 다음과 같다.
  dp[i] = (dp[i-2] +dp[i-1])

코드

class Solution {
    public long solution(int n) {
        int[] dp  = new int[n+1];
        dp[1] = 1;
        dp[0] = 1;
        for(int i =2;i<n+1;i++){
            dp[i] = (dp[i-2] +dp[i-1])%1234567;
        }
        
        return dp[n];
    }
}

시간, 공간 복잡도

  • 시간 복잡도 : O(N)
  • 공간 복잡도 : O(N)

실행 시간

image

2024.04.27 - 최고의 집합

문제 - 링크

문제 풀이

자연수 n 개로 이루어진 중복 집합(multi set, 편의상 이후에는 "집합"으로 통칭) 중에 다음 두 조건을 만족하는 집합을 최고의 집합이라고 합니다.
각 원소의 합이 S가 되는 수의 집합
위 조건을 만족하면서 각 원소의 곱 이 최대가 되는 집합

  • 최고의 집합 구하기

최고의 집합 구하기

  • 최고의 집합이 되기 위해서는 집합의 각각의 원소가 골고루 분배되어야 함

코드

class Solution {
    public int[] solution(int n, int s) {
        // n개의 자연수로 s를 만들 수 없는 경우
        if (n > s) {
            return new int[]{-1};
        }

        // 각 원소가 골고루 분배되어야 함

        int[] answer = new int[n];

        int div = s / n;
        int remainder = s % n;

        // 오름차순으로 정렬되어야 하므로 오른쪽부터 시작
        for (int i = n - 1; i >= 0; i--) {
            answer[i] = div;
            if (remainder-- > 0) {
                answer[i] += 1;
            }
        }

        return answer;
    }
}

시간, 공간 복잡도

  • 시간 복잡도 : O(N)
  • 공간 복잡도 : O(N)

실행 시간

image

2023.12.15 - Target Sum

문제 - https://leetcode.com/problems/target-sum/submissions

문제 해설

  • 주어진 배열의 값을 더하기 빼기만을 이용하여 타겟을 만들 수 있는 횟수 구하기

1차 풀이

  • dfs로 브루트포스 돌리기
class Solution {
    
    public int dfs(int idx ,int sum,int[] nums,int target){
        //마지막에 도달했을 때
        if(idx==N){
            //총합이 target 이라면
            if(sum==target){
                return 1;
            }
            return 0;
        }
        return dfs(idx+1,sum+nums[idx],nums,target) + dfs(idx+1,sum-nums[idx],nums,target);
        
    }
    static int N;
    static int target;
    static int[] nums;
    
    public int findTargetSumWays(int[] nums, int target) {
        N = nums.length;
        //sum 범위 -20000 ~ 20000
        
      
        return dfs(0,0,nums,target);
        
    }
}

결과

image

2023.08.26 - Count Primes

문제 - https://leetcode.com/problems/count-primes/

문제 풀이

  • n보다 작은 소수의 개수 체크

소수 개수 찾는 알고리즘

  • 에라토스테네스의 체

코드

class Solution {
    public int countPrimes(int n) {
        // n보다 작은 소수 찾기
        
        boolean[] isPrime = new boolean[n+1];
        Arrays.fill(isPrime,true);
        
        // 에라토스체네스의 해
        // 소수가 아닌 것 false로 변경
        for(int i = 2; i <=n;i++){
            for(int j =2;i*j<=n;j++){
                int num = i*j;
                isPrime[num] = false;
            }
        }
        
        // 개수 체크
        int cnt = 0;
        for(int i = 2; i<n;i++){
            if(isPrime[i]){
                cnt++;
            }
        }
        return cnt;
        
    }
}

2024.01.02 - 연속 펄스 부분 수열의 합

문제 - 링크

문제 풀이

  • 펄스 수열이란 [1,-1,1,-1...]이 번갈아 나오는 수열
  • 연속 펄스 부분 수열의 합 중 가장 큰 값 리턴

문제 풀이 로직 1

  • 부분 수열과 펄스 수열을 곱한 수열 계산
// mul  : 1 -1  1 -1  1 -1  1 -1 
//  *
// seq  : 2  3 -6  1  3 -1  2  4
//  ||
// calc : 2 -3 -6 -1  3  1  2 -4
  int[] calc = new int[N]; 
  
  // (1,-1)을 교차하여 곱한 값
  int p = 1; 
  for(int i = 0 ;i <N;i++){
      calc[i] = sequence[i]*p;
      p*=-1;
  }

문제 풀이 로직 2

  • 구간 배열의 최대 합 계산하기
  • 펄스 수열은 [1,-1,1,-1 ..] 이런 경우와 [-1,1,-1,1, ....] 이런 경우 2가지가 존재함
  • 따라서 구간 배열의 합에서 양수 값 중 큰 값 or 음수 값 중 작은 값 중 절댓값이 큰 값
    • 또한 구간 배열
    long plus = 0; // 구간 배열의 합중 가장 큰 양수
    long minus = 0;// 구간 배열의 합중 가장 큰 음수
    long result = 0;
    
    for(int  i = 0 ; i<N;i++){
        int cur = calc[i];
        
        plus +=cur;
        minus +=cur;
        
        
        // 구간 배열의 합 < cur 인 경우 기존 구간을 사용하지 않아도 됨
        plus = Math.max(plus,cur);
        minus = Math.min(minus,cur);
        
        long maxAbs = Math.max(plus,Math.abs(minus));
        result = Math.max(result,maxAbs); 
    }

코드

class Solution {
    public long solution(int[] sequence) {
        //연속 펄스 부분 수열의 합 가장 큰 것 리턴하기
        
        int N = sequence.length;
        // MAX = 500_000 * 100_000 => 500억?  
        
        // mul  : 1 -1  1 -1  1 -1  1 -1 
        //  *
        // seq  : 2  3 -6  1  3 -1  2  4
        //  ||
        // calc : 2 -3 -6 -1  3  1  2 -4
        
        int[] calc = new int[N]; 
        
        // (1,-1)을 교차하여 곱한 값
        int p = 1; 
        for(int i = 0 ;i <N;i++){
            calc[i] = sequence[i]*p;
            p*=-1;
        }
        
        long plus = 0; // 구간 배열의 합중 가장 큰 양수
        long minus = 0;// 구간 배열의 합중 가장 큰 음수
        long result = 0;
        
        for(int  i = 0 ; i<N;i++){
            int cur = calc[i];
            
            plus +=cur;
            minus +=cur;
            
            
            // 구간 배열의 합 < cur 인 경우 기존 구간을 사용하지 않아도 됨
            plus = Math.max(plus,cur);
            minus = Math.min(minus,cur);
            
            long maxAbs = Math.max(plus,Math.abs(minus));
            result = Math.max(result,maxAbs); 
        }
        
        return result;
    }
    
}

시간, 공간 복잡도

  • 시간 복잡도 : O(N)
  • 공간 복잡도 : O(N)

실행 시간

image

2024.03.09 - 도넛과 막대 그래프

문제 - 링크

문제 풀이

  • 도넛, 막대, 8자 그래프들의 개수와 시작 지점 구하기

시작 지점 판별

  • 시작 지점은 무작위로 하나 주워지고 다른 그래프들을 가리키고 있음
  • 따라서 시작 지점으로 들어오는 간선은 0개, 시작 지점에서 나가는 간선은 2개 이상임
int start = -1;

for(int i = 0; i<N;i++){
    if(in[i] == 0 && out[i]>=2){
        start = i;
    }
}

그래프 모양 판별

  • 도넛 모양
    image

  • 막대 모양
    image

  • 8자 모양
    image

  • dfs을 이용하여 그래프를 탐색한다.

  • 만약, 해당 정점에서 나가는 간선의 개수가 2개 이상이면 해당 정점이 있는 그래프는 8자 그래프이다.

  • 또한, 시작 지점으로 돌아 오는 경우 이 그래프는 도넛 모양 그래프이다.

    void dfs(int v,int start){
        if(adjList[v].size()>=2){
            isEight = true;
            return;
        }

        for(int next : adjList[v]){
            // 그래프 탐색시 시작 지점으로 돌아 온다면 도넛모양
            if(next == start){
                isDoughnut = true;
                return;
            }
            dfs(next,start);
        }

    }

코드

import java.util.ArrayList;
import java.util.List;

class Solution {
    int[] in;
    int[] out;
    List<Integer>[] adjList;

    boolean isEight;
    boolean isDoughnut;

    static final int DOUGHNUT_IDX = 1;
    static final int STICK_IDX = 2;

    static final int EIGHT_IDX = 3;


    void init(int[][] edges){

        int N = -1;
        for(int[] edge : edges){
            N = Math.max(N,edge[0]);
            N = Math.max(N,edge[1]);
        }
        in = new int[N+1];
        out = new int[N+1];
        adjList = new ArrayList[N+1];
        for(int i = 0; i<=N;i++){
            adjList[i] = new ArrayList<>();
        }
        for (int[] edge : edges) {
            int start = edge[0];
            int end = edge[1];
            // start -> end
            in[end]++;
            out[start]++;

            adjList[start].add(end);
        }

    }


    void dfs(int v,int start){
        if(adjList[v].size()>=2){
            isEight = true;
            return;
        }

        for(int next : adjList[v]){
            // 그래프 탐색시 시작 지점으로 돌아 온다면 도넛모양
            if(next == start){
                isDoughnut = true;
                return;
            }
            dfs(next,start);
        }

    }



    public int[] solution(int[][] edges) {
        int N = -1;
        for(int[] edge : edges){
            N = Math.max(N,edge[0]);
            N = Math.max(N,edge[1]);
        }

        init(edges);


        int start = -1;

        for(int i = 0; i<N;i++){
            if(in[i] == 0 && out[i]>=2){
                start = i;
            }
        }


        int[] answer = new int[4];

        answer[0] = start;

        for(int next: adjList[start]){
            // 그래프 탐색
            dfs(next,next);
            if(isEight){
                answer[EIGHT_IDX] += 1;
            }else if(isDoughnut){
                answer[DOUGHNUT_IDX] += 1;
            }else{
                answer[STICK_IDX] += 1;
            }
        }


        return answer;
    }

}

시간, 공간 복잡도

  • 시간 복잡도 : O(E)
  • 공간 복잡도 : O(E)

실행 시간

image

2023.12.16 - 합승 택시 요금

문제 - https://school.programmers.co.kr/learn/courses/30/lessons/72413

문제 해설

  • 무지, 어피치가 출발지(s)에서 택시를 타고 특정 지점(i)까지 같이 타고 감 (dp[s][i])
  • 특정 지점(i)에서 무지 집까지의 가격 (dp[i][a])
  • 특정 지점(i)에서 어피치 집까지의 가격(dp[i][b])

알고리즘

  • 플로이드 워셜 : 모든 정점에서 다른 정점까지의 최소 거리를 구할 때 사용
        for(int k = 1; k<=n ; k++){
            for(int i = 1; i<=n ; i++){
                for(int j = 1 ; j<=n ; j++){
                    dp[i][j] = Math.min(dp[i][j],dp[i][k]+dp[k][j]);
                }
            }
        }

전체 코드

import java.util.*;
class Solution {
    public int solution(int n, int s, int a, int b, int[][] fares) {
        // s -> m
        // m -> a
        // m -> b 경로 계산
        int[][]dp = new int[n+1][n+1];
        int MAX_COST = 100_000*200;
        for(int[] row : dp){
            Arrays.fill(row,MAX_COST);
        }
        for(int i = 1; i<=n;i++){
            dp[i][i] = 0;
        }
        
        for(int[] fare : fares){
            int v1 = fare[0];
            int v2 = fare[1];
            int cost = fare[2];
            
            dp[v1][v2] = cost;
            dp[v2][v1] = cost;
        }
        
        
        for(int k = 1; k<=n ; k++){
            for(int i = 1; i<=n ; i++){
                for(int j = 1 ; j<=n ; j++){
                    dp[i][j] = Math.min(dp[i][j],dp[i][k]+dp[k][j]);
                }
            }
        }
        // for(int[] row : dp){
        //     System.out.println(Arrays.toString(row));
        // }
        int minCost = MAX_COST*2;
        
        for(int i = 1;i<=n;i++){
            int cost = dp[s][i] + dp[i][a] + dp[i][b];
            minCost = Math.min(minCost,cost);
        }
        
        return minCost;
    }
}

결과

image

2024.03.16 - 스킬트리

문제 - 링크

문제 풀이

  • 주어진 스킬 트리 중 선행스킬 조건에 만족하는 개수 구하기

선행 스킬 순서 저장

        // ex) "CBD" => C:1, B:2, D:3
        for (int i = 0; i < skill.length(); i++) {
            int idx = skill.charAt(i) - 'A';
            skillOrder[idx] = i + 1;
        }

코드

class Solution {

    public int solution(String skill, String[] skill_trees) {
        int answer = 0;
        int[] skillOrder = new int[26];


        // 선행스킬 순서를 저장
        // ex) "CBD" => C:1, B:2, D:3
        for (int i = 0; i < skill.length(); i++) {
            int idx = skill.charAt(i) - 'A';
            skillOrder[idx] = i + 1;
        }

        fail:
        for (String skill_tree : skill_trees) {
            int skillDepth = 1;
            int skillLen = skill_tree.length();
            for (int i = 0; i < skillLen; i++) {
                int idx = skill_tree.charAt(i) - 'A';

                // 선행조건에 없는 스킬은 무시
                // 선행조건에 있는 스킬이 순서에 맞지 않으면 실패
                if (skillOrder[idx] == skillDepth) {
                    skillDepth++;
                } else if (skillOrder[idx] > skillDepth) {
                    // 선행조건에 있는 스킬이 순서에 맞지 않으면 실패(다음 스킬트리로 continue)
                    continue fail;
                }
            }
            // 주어진 스킬트리가 선행조건에 만족하면  성공
            answer++;
        }
        return answer;
    }

}

시간, 공간 복잡도

  • 스킬 트리 개수 : N
  • 시간 복잡도 : O(N)
  • 공간 복잡도 : O(N)

실행 시간

image

2023.08.05 - Sort List

[링크] - https://leetcode.com/problems/sort-list/

1. Node의 값을 Selection Sort을 해보자. (실패)

다음과 같은 selection sort을 이용한 solution => 시간초과 O (N^2)

class Solution {
    
    
    public ListNode sortList(ListNode head) {  
        if(head==null){
            return null;
        }
        ListNode cur = head;
        int minValue = head.val;
        ListNode minNode = head;
        
        // find min Node
        while(cur.next!=null){
            ListNode next = cur.next;
            if(next.val< minValue){
                minValue = next.val;
                minNode = next;
            }
            cur = next;
        }
        // swapping 
        int tmp = head.val;
        head.val = minNode.val;
        minNode.val = tmp;
        sortList(head.next);
        
        return head;
        
    }
}

2. 링크드 리스트의 값들을 모두 가져온 뒤 정렬

  • Collections의 sort은 내부적으로 O(NlogN)의 시간복잡도를 가지고 있다.
  • list의 get()연산은 O(N)이므로 정렬된 값들을 FIFO의 Queue을 사용함
class Solution {
    
    
    public ListNode sortList(ListNode head) {  
        ListNode cur = head;
        
        
        List<Integer> list = new ArrayList<>();
        
        while(cur!=null){
            list.add(cur.val);
            cur = cur.next;
        }
        Collections.sort(list);
        Queue<Integer> Q = new ArrayDeque<>();
        for(int n : list){
            Q.add(n);
        }
        
        int p = 0;
        cur = head;
        while(cur!=null){
            cur.val = Q.poll();
            cur = cur.next;
        }
        
        
        return head;
        
    }
}

2023.08.26 - JadenCase 문자열 만들기

[문제] - https://school.programmers.co.kr/learn/courses/30/lessons/12951

문제 풀이

  • 단어의 첫 번째 => 대문자로 변경 (Character.toUpperCase()) 이용
  • 그 외 => 소문자로 변경(Characgter.toLowerCase()) 이용

StringTokenizer 이용

import java.util.*;

class Solution {
    public String solution(String s) {
        String answer = "";
        StringTokenizer st = new StringTokenizer(s);
        StringBuilder sb = new StringBuilder();
        
        while(st.hasMoreTokens()){
            String word = st.nextToken();
            System.out.println("word = "+word);
            int len = word.length();
            
            // 첫 번째 문자 대문자로
            char firstChar = word.charAt(0);
            sb.append(Character.toUpperCase(firstChar));
            
            // 나머지 문자 소문자로
            for(int i =1 ; i<len;i++){
                char c = word.charAt(i);
                sb.append(Character.toLowerCase(c));
                
            }
            if(st.hasMoreTokens()){
                sb.append(" ");
            }
        }
        
        return sb.toString();
    }
}
  • StringTokenzer은 연속된 공백을 체크해주지 못함

charAt()을 이용

import java.util.*;

class Solution {
    public String solution(String s) {
        StringBuilder sb = new StringBuilder();
        
        boolean isFirst = true;
        int len = s.length();
        
        for(int i=0; i<len;i++){
            char c = s.charAt(i);
            
            // 단어의 첫 문자 체크
            if(isFirst){
                sb.append(Character.toUpperCase(c));
            }else{
                sb.append(Character.toLowerCase(c));
            }
            // 현재 공백인지 아닌지 체크
            if(c==' '){
                isFirst = true;
            }else{
                isFirst = false;
            }
        }
        
        return sb.toString();
    }
}

2024.03.15 - path-with-minimum-effort

문제 - 링크

문제 풀이

  • (0,0) -> (N-1,M-1)로 이동할 때 최소의 노력으로 이동하기
  • 여기서 최소의 노력이란, 경로상 연속된 칸들의 차이 중 가장 큰 차이 값을 의미

간단한 DFS을 통하여 계산(실패)

  • DFS을 돌리며 현재보다 최소의 노력일 때만 진행하도록 수행
            int nextHeight = heights[nx][ny];
            int diffHeight =Math.abs(nextHeight - currentHeight);
            int maxEffort = Math.max(effort, diffHeight);
            // 현재보다 노력이 큰 경우 더이상 진행하지 않음
            if(maxEffort>=minEffort[nx][ny])           {
                continue;
            }
            dfs(nx, ny, maxEffort, heights);

DFS 코드

    void dfs(int x,int y, int effort,int[][] heights){

        minEffort[x][y] = Math.min(minEffort[x][y],effort);
        if(x == N-1 && y== M-1){
            return;
        }

        int currentHeight = heights[x][y];

        for(int i = 0; i<4;i++){
            int nx = x + dx[i];
            int ny = y + dy[i];

            if(nx<0|| ny<0 || nx>=N|| ny>=M ) {
                continue;
            }
            int nextHeight = heights[nx][ny];
            int diffHeight =Math.abs(nextHeight - currentHeight);
            int maxEffort = Math.max(effort, diffHeight);
            // 현재보다 노력이 큰 경우 더이상 진행하지 않음
            if(maxEffort>=minEffort[nx][ny]){
                continue;
            }
            dfs(nx, ny, maxEffort, heights);
        }
    }

�실패

  • 시간초과 발생

시간 초과 발생 원인

  • 특정 노드의 값에 노력의 값이 내림차순으로 진행되는 경우 불필요한 연산을 계속 수행하는 문제가 있음
  • 노력의 값이 최소인 것부터 연산을 수행하자(Dijkstra)

2023.12.02 - 하노이탑

문제

문제 설명

  • 주어진 하노이탑을 1번 원판 -> 3번 원판으로 옮길 때 최소로 옮기는 경우 구하기

문제 해설

  • 재귀 함수를 이용하여 문제 해결
  • n번째까지 원판을 a -> c로 옮기기 위해서는
    • n-1번째까지의 원판을 a->b로 옮기고
    • n번째 원판을 a->c로 옮기고
    • n-1번째까지의 원판을 b->c로 옮겨야 함
import java.util.*;
class Solution {
    static List<int[]> routes;
    static void move(int start,int end,int other, int size){
        if(size==0){
            return;
        }
        move(start,other,end,size-1);
        routes.add(new int[]{start,end});
        move(other,end,start,size-1);
    }
    public int[][] solution(int n) {
        routes = new ArrayList<>();
        move(1,3,2,n);
        int N = routes.size();
        int[][] answer = new int[N][2];
        for(int i = 0; i < N; i++){
            answer[i] = routes.get(i);
        }
        return answer;
    }
}

2024.03.16 - palindrome-partitioning

문제 - 링크

문제 풀이

  • �팰린드룸으로 만들 수 있는 파티셔닝 구하기

�팰린드룸 확인

  • i 인덱스부터 j 인덱스로 이루어진 문자열이 팰린드룸인지 확인하기
    • 문자 단 하나는 팰린드룸임
    • 양 끝이 동일하고 차이가 1이라면 팰린드룸 , ex) aa
    • 차이가 2 이상이라면 양 끝을 없앤 (i+1~j-1)으로 이루어진 문자열이 팰린드룸인지 확인
        isPalindrome = new boolean[N][N];
        for(int i = 0 ; i<N;i++){
            isPalindrome[i][i] = true;
        }

        for(int i = N-1;i>=0;i--){
            for(int j = i+1;j<N;j++){
                // i~j가 팰린드룸인지 확인
                // i와 j가 같은 문자라면
                if (s.charAt(i) ==s.charAt(j)){
                    // i와 j의 차이가 1이라면 팰린드룸 , ex) aa
                    if(j-i==1) {
                        isPalindrome[i][j] = true;
                    }else{
                        // i~j가 팰린드룸이라면 i+1~j-1도 팰린드룸인지 확인
                        isPalindrome[i][j] = isPalindrome[i+1][j-1];
                    }
                }
            }
        }

문자열 쪼개기

  • 주어진 팰린드룸으로 이루어진 문자열로 쪼개야 함
    // s~e까지의 문자열을 팰린드룸으로 나누는 함수
    private void rec(int s, int e, String str, List<String> route){
        if(s==e){
            result.add(route);
            return;
        }

        //  문자열을 다음과 같이 쪼갤 것임
        //  [s, s+1, .... i] , i+1, ... e-1
        // [s, s+1, .... i]가 팰린드룸이라면 다음 재귀 호출
        for(int i = s;i<e;i++){
            // s~i가 팰린드룸이라면  다음 재귀 호출
            if(isPalindrome[s][i]){
                List<String> newRoute = new ArrayList<>(route);
                String substring = str.substring(s, i+1);
                newRoute.add(substring);
                // 다음 제귀 호출
                rec(i + 1, e, str, newRoute);
            }
        }
    }

코드

class Solution {
    
    
    public int solution(String skill, String[] skill_trees) {
        int answer = 0;
        int[] skillOrder = new int[26];


        // 스킬트리 순서를 저장
        // ex) "CBD" => C:1, B:2, D:3
        for (int i = 0; i < skill.length(); i++) {
            int idx = skill.charAt(i) - 'A';
            skillOrder[idx] = i + 1;
        }

        fail:
        for (String skill_tree : skill_trees) {
            int skillDepth = 1;
            int skillLen = skill_tree.length();
            for (int i = 0; i < skillLen; i++) {
                int idx = skill_tree.charAt(i) - 'A';

                // 선행조건에 없는 스킬은 무시
                // 선행조건에 있는 스킬이 순서에 맞지 않으면 실패
                if (skillOrder[idx] == skillDepth) {
                    skillDepth++;
                } else if (skillOrder[idx] > skillDepth) {
                    continue fail;
                }
            }
            // 주어진 스킬트리가 선행조건에 만족하면  성공
            answer++;
        }
        return answer;
    }
}

시간, 공간 복잡도

  • 시간 복잡도 : O(N^2)
  • 공간 복잡도 : O (N^2)

실행 시간

image

2023.12.31 - 순위

문제 - 링크

문제 풀이

  • 주어진 경기의 승패를 통하여 정확하게 순위를 매길 수 있는 선수의 수를 구하는 문제

선수 사이의 승패 업데이트

  • 주어진 경기를 통하여 선수 사이의 승패를 알 수 있는 경기를 업데이트 한다.
        for (int k = 1; k <= n; k++) {
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= n; j++) {
                    // i가 k을 이기고
                    // k가 j을 이긴다면?
                    // => i > k > j
                    // => i가 j을 이긴다.
                    if (isWin[i][k] && isWin[k][j]) {
                        isWin[i][j] = true;
                    }
                }
            }
        }

이길 수 있는지 체크

  • j을 1~n까지 for문을 돌려 i가 j을 이길 수 있거나, j가 i을 이길 수 있는 지 체크
  • i와 j의 승패를 알 수 없다면 다음 반복문 호출
  • continue loop; : label이 loop부분을 continue 합니다.
        loop:
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                // i가 j을 이길 수 있거나
                // j가 i을 이길 수 있거나 
                if (!isWin[i][j] && !isWin[j][i]) {
                    continue loop;
                }
            }
            // 1부터 j까지 모두 승패를 알 수 있다면 canCnt++;
            canCnt++;
        }

코드

import java.util.*;

class Solution {

    private static final int INF = 100_000;

    public int solution(int n, int[][] results) {


        // 1-based (1~n)
        // 이길 수 있는지 없는지 체크하는 배열
        boolean[][] isWin = new boolean[n + 1][n + 1];

        for (boolean[] row : isWin) {
            Arrays.fill(row, false);
        }

        for (int i = 1; i <= n; i++) {
            isWin[i][i] = true;
        }


        for (int[] result : results) {
            int winIdx = result[0];
            int loseIdx = result[1];

            // winIdx는 loseIdx을 이길 수 있음
            isWin[winIdx][loseIdx] = true;
        }

        for (int k = 1; k <= n; k++) {
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= n; j++) {
                    // i가 k을 이기고
                    // k가 j을 이긴다면?
                    // => i > k > j
                    // => i가 j을 이긴다.
                    if (isWin[i][k] && isWin[k][j]) {
                        isWin[i][j] = true;
                    }
                }
            }
        }


        int canCnt = 0;

        loop:
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                // i가 j을 이길 수 있거나
                // j가 i을 이길 수 있거나 
                if (!isWin[i][j] && !isWin[j][i]) {
                    continue loop;
                }
            }
            // 1부터 j까지 모두 승패를 알 수 있다면 canCnt++;
            canCnt++;
        }

        return canCnt;
    }
}

실행 시간

image

2024.05.03 - House Robber

문제 - 링크

내가 현재 할 수 있는 경우의 수

  • 이번에 내가 터는 경우(한 칸 건너뛰고 최대의 금액을 얻는 경우)
  • 이번에 내가 털지 않는 경우(이전의 최대 금액)�
dp[i] = Math.max(dp[i - 1], dp[i - 2] + newNums[i]);

아래에서 부터 채워나가기

for (int i = 2; i <= N; i++) {
    dp[i] = Math.max(dp[i - 1], dp[i - 2] + newNums[i]);
}

코드

class Solution {
    public int rob(int[] nums) {
        int N = nums.length;

        // 0-base => 1-base
        int[] newNums = new int[N + 1];
        int[] dp = new int[N + 1];
        for (int i = 1; i <= N; i++) {
            newNums[i] = nums[i - 1];
        }

        dp[1] = newNums[1];

        for (int i = 2; i <= N; i++) {
            dp[i] = Math.max(dp[i - 1], dp[i - 2] + newNums[i]);
        }

        return dp[N];


    }
}

시간, 공간 복잡도

  • 시간 복잡도 : O(N)
  • 공간 복잡도 : O(N)

실행 시간

2024.04.26 - Restore IP Addresses

문제 - 링크

문제 풀이

  • 주어진 문자열을 이용하여 가능한 ip주소를 가진 리스트 반환하기

주어진 문자열을 자르면서 ip주소를 완성할 것임

  • ip는 4개의 정수와 . 으로 이루어짐
  • 주어진 문자열을 1~3개씩 잘라서 4개의 정수로 이루어지는 지 확인!

ip 주소 자르기

image
  • 문자열에서 앞에서부터 1,2,3개씩 잘라서 유효한 ip주소인지 확인(255이하, 0xx로 시작하지 않는 ip)

적합한 ip인지 확인

    boolean isValidIp(String s, int len) {
        // len의 길이로 자를 수 없는 경우
        if (s.length() < len) {
            return false;
        }
        String currentString = s.substring(0, len);
        // 0으로 시작하는 경우
        if (currentString.length() > 1 && currentString.charAt(0) == '0') {
            return false;
        }
        int currentInt = Integer.parseInt(currentString);
        // 255보다 큰 경우
        if (currentInt > 255) {
            return false;
        }

        return true;
    }

코드

class Solution {
    List<String> list = new ArrayList<>();


    boolean isValidIp(String s, int len) {
        // len의 길이로 자를 수 없는 경우
        if (s.length() < len) {
            return false;
        }
        String currentString = s.substring(0, len);
        // 0으로 시작하는 경우
        if (currentString.length() > 1 && currentString.charAt(0) == '0') {
            return false;
        }
        int currentInt = Integer.parseInt(currentString);
        // 255보다 큰 경우
        if (currentInt > 255) {
            return false;
        }

        return true;
    }

    // xxx.xxx.xxx.xxx 형태로 변환
    String getIpAddress(String[] ip) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 4; i++) {
            sb.append(ip[i]);
            if (i != 3) {
                sb.append(".");
            }
        }
        return sb.toString();
    }

    void dfs(int depth, String s, String[] ip) {
        if (depth == 4) {
            // 아직 남아있는 문자열이 있다면 완성하지 못한 것임
            if (!s.isEmpty()) {
                return;
            }
            String ipAddress = getIpAddress(ip);
            list.add(ipAddress);
            return;
        }
        // 앞에서부터 1~3개 짤라봄
        for (int i = 1; i <= 3; i++) {
            if (!isValidIp(s, i)) {
                continue;
            }
            ip[depth] = s.substring(0, i);
            String remainString = s.substring(i);
            dfs(depth + 1, remainString, ip);
        }
    }

    public List<String> restoreIpAddresses(String s) {
        String[] ip = new String[4];
        dfs(0, s, ip);
        return list;
    }

}

실행 시간

image

2023.09.02 - 리코쳇 로봇

문제 - https://school.programmers.co.kr/learn/courses/30/lessons/169199

문제 풀이

  • 격자보드에서 최소한의 이동으로 G에 도달
  • BFS이용한 풀이

로봇이 멈출 수 있을 때 까지 이동

                int nx = cur.x ;
                int ny = cur.y ;
                while(true){
                    
                    if(isStop(nx,ny,i)){
                        if(!visited[nx][ny]){
                            visited[nx][ny] = true;
                            Q.add(new Node(nx,ny,cur.cnt+1));
                        }
                        break;
                    }else{
                        nx += dx[i];
                        ny += dy[i];   
                    }

                }

로봇이 멈출 수 있는가?

  • 다음 이동 시 격자에서 벗어나거나 장애물 만날 시 true 리턴
    // 멈출 수 있는지 여부
    static boolean isStop(int x, int y,int d){
        int nx = x+dx[d];
        int ny = y+dy[d];
        if(nx==-1 || nx== N || ny==-1 || ny==M|| G[nx][ny]=='D'){
            return true;
        }
        
        return false;
       

코드

import java.util.*;

class Solution {
    static class Node{
        int x;
        int y;
        int cnt;
        
        public Node(int x, int y,int cnt){
            this.x = x;
            this.y = y;
            this.cnt =cnt;
        }
    }
    
    // 멈출 수 있는지 여부
    static boolean isStop(int x, int y,int d){
        int nx = x+dx[d];
        int ny = y+dy[d];
        if(nx==-1 || nx== N || ny==-1 || ny==M|| G[nx][ny]=='D'){
            return true;
        }
        
        return false;
        
    }
    
    
    static int N;
    static int M;
    static char[][] G;
    static boolean[][] visited;
    static int[] dx = {-1,1,0,0};
    static int[] dy= {0,0,-1,1};
    
    public int solution(String[] board) {
        int answer = 0;
        N = board.length;
        M = board[0].length();
        
        visited= new boolean[N][M];
        Queue<Node> Q = new ArrayDeque<>();
        G = new char[N][M];
        Node end= null;
        
        for(int i =0 ; i<N;i++){
            for(int j = 0 ; j<M;j++){
                G[i][j] = board[i].charAt(j);
                if(G[i][j] == 'R'){
                    Q.add(new Node(i,j,0));
                    visited[i][j] = true;
                }else if (G[i][j]=='G'){
                    end = new Node(i,j,0);
                }
            }
        }
        
        while(!Q.isEmpty()){
            Node cur = Q.poll();
            //골에 도달 
            if(cur.x==end.x && cur.y==end.y){
                return cur.cnt;
            }
            
            for(int i = 0 ; i<4;i++){
                int nx = cur.x ;
                int ny = cur.y ;
                while(true){
                    
                    if(isStop(nx,ny,i)){
                        if(!visited[nx][ny]){
                            visited[nx][ny] = true;
                            Q.add(new Node(nx,ny,cur.cnt+1));
                        }
                        break;
                    }else{
                        nx += dx[i];
                        ny += dy[i];   
                    }

                }
            }
        }
        
        
        
        return -1;
    }
}

실행 시간

image

2023.02.17 - generate-parentheses

문제 - 링크

문제 풀이

  • N 개의 괄호 쌍으로 만들 수 있는 경우의 수

문제 풀이 로직 1

  • N개의 괄호 쌍으로 만들 수 있는 경우
  • N-1의 괄호 쌍 양 옆에 () 감싸기
  • 1개의 괄호 쌍과 N-1개의 괄호 쌍 합치기
  • 2개의 괄호 쌍과 N-2개 괄호 쌍 합치기
  • ....
  • N-1개의 괄호 쌍과 1개의 괄호 쌍 합치기
        for (int i = 2; i <= n; i++) {
            for (int j = 1; j < i; j++) {
                for (String a : parenthesis[j]) {
                    for (String b : parenthesis[i - j]) {
                        parenthesis[i].add(a + b);
                    }
                }
            }
            for (String a : parenthesis[i - 1]) {
                parenthesis[i].add("(" + a + ")");
            }

        }

코드

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

class Solution {

    public List<String> generateParenthesis(int n) {
        // n개의 괄호 리스트 출력하기

        // 1 : ()
        // 2 : ()(), (())
        // 3 : ((())), (()()), (())(), ()(()), ()()()

        HashSet<String>[] parenthesis = new HashSet[n + 1];
        for (int i = 0; i <= n; i++) {
            parenthesis[i] = new HashSet<>();
        }
        parenthesis[1].add("()");
        for (int i = 2; i <= n; i++) {
            for (int j = 1; j < i; j++) {
                for (String a : parenthesis[j]) {
                    for (String b : parenthesis[i - j]) {
                        parenthesis[i].add(a + b);
                    }
                }
            }
            for (String a : parenthesis[i - 1]) {
                parenthesis[i].add("(" + a + ")");
            }

        }

        return new ArrayList<>(parenthesis[n]);

    }
}

시간, 공간 복잡도

  • 시간 복잡도 :
  • 공간 복잡도 :

실행 시간

image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.