144. Binary Tree Preorder Traversal

 

비교적 쉬운 문제이다. 트리노드 의 트리 형태의 자료구조 가 주어지면 이걸 리스트에 담아 리턴하면 된다. 

순회의 방법은 전위순회 중위순회 후위순회가 있고 이 문제에서 요구하는 바는 전위순회 이다.

예전 easy 101 할때 풀었던 문제인데 그당시 에는 bfs 를 이용해 계층 별 탐색을 했으니 이번에는 재귀로 풀어보자.

 

class Solution {
    List<Integer> list = new ArrayList<>();
    public List<Integer> preorderTraversal(TreeNode root) {
        helper(root);
        return list;
    }
    public void helper(TreeNode root){
        if(root == null) return;
        list.add(root.val);
        helper(root.left);
        helper(root.right);
    }
}

별다를 바 없는 코드이다 현재 들어온 루트 를 기점으로 다음재귀 로 넘어갈때 마다 list 에 담아준다. 

list.add 의 위치가 한칸 아래 내려오면 중위순회이고 맨아래 들어가면 후위순회가 된다. 

LeetCode BinaryTree 설명

 

Account Login - LeetCode

Level up your coding skills and quickly land a job. This is the best place to expand your knowledge and get prepared for your next interview.

leetcode.com

 

100. Same Tree

위의 문제와 다를 바 없다 동일한 순회방법을 정해 태우면서 같은 값이 나오지 않는다면 false 를 바로바로 리턴해주면 된다.

class Solution {
    public boolean isSameTree(TreeNode p, TreeNode q) {
        return helper(p,q);
    }
    public boolean helper(TreeNode p,TreeNode q){
        if(p == null || q == null)
            return  p==q;
        if(p.val != q.val) return false;
        return helper(p.left,q.left) && helper(p.right,q.right);
    }
}

좌우 노드를 이용해서 돌렸다. if 부분 의 return 값 이 의아할텐데 하나가 null 이고 하나가 null 이 아닌경우 에 대해 해결 해주기 위해 위와 같이 작성하엿다.

여기서 중점적으로 봐야 할 부분은 여기다 null == null 은 트루가 나온다(Objects.equals(null,null)도 트루이다.) 

null 이란 특정 원시타입 혹은 객체 타입이 아닌 그냥 변수이다.

이 null 의 메모리 어드레스가 0 이라고들 말하는데 메모리 한칸을 쓰고 있는거 아니겠는가 그래서 null 값이 나오면 저 하나의 포인트 로 가기 때문에 true 값이 나온다고 이해된다.

직접 확인 해보기가 어려워서 스택오버 플로 말을 믿자..(C 나 C++ 은 0을 포인팅 한다고 한다.) 위의 이유에서 null == null 이 성립한다. 

 

1443. Minimum Time to Collect All Apples in a Tree

생각보다 시간 소모를 많이한 문제이다.(문제를 이상하게 읽어서...) 어쩃든 보면 0 에서 시작해서 모든 사과를 수거해서 0 으로 돌아오면 된다. 

사과를 가지고 올 최선의 길은 ? 항상 동일하다 같던 방향 그대로 돌아오면 된다. 단 여기서 1번 노드 는 4,5 를 1번 분기를 기준으로 다녀오면 된다. 

분기를 나누는 재귀가 마구마구 떠오른다.

솔루션 의 다른 풀이 확인을 하다보니 좋은 그림이 있어 첨부한다.

 

가장 아래에서 부터 올라가면 된다. 사과 를 가지고 위 분기로 올라갈떄는 2를 더한다 왜 ? 갔다 와야하니깐

 

코드를 보자.

class Solution {
    private List<List<Integer>> graph = new ArrayList<>();
    private void init(int n,int[][] edges){
        for (int i = 0; i < n; i++) {
            graph.add(new ArrayList());
        }
        for (int i = 0; i < edges.length; i++) {
            int from = edges[i][0],to = edges[i][1];

            graph.get(from).add(to);
            graph.get(to).add(from);
        }
    }
    public int minTime(int n, int[][] edges, List<Boolean> hasApple) {
        init(n,edges);
        boolean[] visit = new boolean[n];
        return helper(0, hasApple, visit);
    }
    public int helper(int cur,List<Boolean> hasApple,boolean[] visit){
        visit[cur]  = true;
        int move = 0;

        for(int next : graph.get(cur)){
            if(visit[next]) continue;
            move += helper(next,hasApple,visit);
        }

        if(cur != 0 && (hasApple.get(cur) || move > 0)) move +=2;
        return move;
    }
}

init 을 이용해 그래프 를 그려 주었다 예전 다익스트라 알고리즘 에서 할떄 이런 방법 으로 그래프 그렸던 기억이 있어 위와 같이 그래프를 만들었고. 중복 방문을 처리하기 위해 visit 배열을 하나 생성 했다. 다른 부분은 이상하지 않으나 저기 += 2 해주는 부분을 보자.

0 이 아니라면 왜 ? 0부터 시작하니깐 사과를 집은 시점부터 이동 거리가 0 이상이라면 계속 더해주면서 가야한다

그길은 거쳐야 하니깐 계속 2 씩 증가시켜주면서 다음으로 넘어가 준다.

'PS > LeetcCode' 카테고리의 다른 글

LeetCode Daily 문제풀기  (0) 2023.01.06
LeetCode Daily 문제풀기  (0) 2023.01.05
1월4일 문제 풀기  (2) 2023.01.04
Easy 문제 풀기 Day2(83/618)  (0) 2023.01.03
Binary Tree Maximum Path Sum  (0) 2022.12.11

1833. Maximum Ice Cream Bars

주어진 배열 이 있고 거기서 최대 아이스크림을 살만큼 사면되는 문제이다. 비교적 어제 보다 쉬운 문제이다. 왜냐하면 노트에 표기된 순서에 상관없는 구매가 가능하다는 부분이 이문제를 직관적으로 만든다.

만약 저 노트 부분이 없다면 sliding window 를 이용해 범위를 넓혀나가면서 계산해야지 싶다.

최대범위 10k 이기 떄문에 부담 없이 for loop 돌려도 된다.

정렬을 우선 해주고 o(nlogn), while을 이용해서 작은 가격의 아이스크림부터 우선적으로 챙긴다.

더보기
    class Solution {
        public int maxIceCream(int[] costs, int coins) {
            int answer = 0;
            Arrays.sort(costs);

            if(costs[0] > coins) return answer;

            int idx = 0;
            while(coins > 0 && idx < costs.length){
                if(costs[idx] <= coins){
                    answer++;
                    coins -= costs[idx++];
                }else{
                    break;
                }
            }
            return answer;
        }
    }

 

별다를 게 없는 코드이다. 다만 index 의 변수 라던지 answer의 변수를 줄일 수 있을 거 같아 여러 번 고민했다.

class Solution {
    public int maxIceCream(int[] costs, int coins) {
        Arrays.sort(costs);
        int len = costs.length;

        for(int i=0;i<len;i++){
            if(costs[i] > coins) return i;
            coins -= costs[i];
        }

        return len;
    }
}

이렇게 압축을 시켜줄수 있다. for 문안에서 리턴되는 경우라면? 배열 끝까지 못 사는 경우 

마지막에 리턴이 된다면 ? 플렉스

이거는 미디엄 레벨 이 아니라 이지 레벨로 측정되어야 하지 않는가 싶다.

 

108. Convert Sorted Array to Binary Search Tree

정렬된 배열 을 BST로 바꾸는 로직을 작성하는 문제이다. 높이가 균등한 BST 이기 때문에 한쪽으로 편향된 경우를 제외하자.

 

여기서 BST 가 생소할 수 있다. BST 란 노드 하나 기준으로 외쪽은 노드 본인보다 작고 오른쪽은 본인보다 큰 경우 이다.

즉 다시 말해 위의 답이 2가지 가 될 수 있는 이유이다. 또한 높이가 균등한 BST라고 되어 있기 때문에 왼쪽 높이가 3이라면 오른쪽 높이는 최대 4 최소 2 즉 1의 차이를 가진 트리 그래프를 그려야 한다.

** 여기서 중요한 것은 BST를 구성할 때 노드 좌 우는 본인보다 작고 커야 한다 즉 중간 값이라는 이야기다

 

바로 구현 가보자

 

더보기
class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        return helper(nums,0,nums.length-1);
    }
    public TreeNode helper(int[] arr,int left,int right){
        if(left >right) return null;
        int mid = (left+right)/2;
        TreeNode node = new TreeNode(arr[mid]);
        node.left = helper(arr,left,mid-1);
        node.right = helper(arr,mid+1,right);
        return node;
    }
}

재귀를 이용해서 좌우로 넣어주었다. mid 값 기준으로 좌우로 넣어주되 +1을 해야 하는 이유는 현재 mid 값은 검증된 값 이기 때문이다. 

추가적으로 검증을 할 이유가 없다 저렇게 안 한다면 아마 StackOverFlow를 마주하지 않을까 싶다.

 

110. Balanced Binary Tree

주어진 TreeNode 가 균등한 BST 인지 아닌지 판별하는 문제이다. 아까 말했다시피 높이를 판별해서 true false의 값을 반환해주어야 하는데...  푸는데 시간 좀 들였다.

더보기
class Solution {
    public boolean isBalanced(TreeNode root) {
        if(root == null) return true;
        
        int left = helper(root.left,0);
        int right = helper(root.right,0);

        return Math.abs(right-left) > 1 ? false : true;
    }
    public int helper(TreeNode node,int depth){
        if(node == null) return depth;
        int left = helper(node.left,depth+1);
        int right = helper(node.right,depth+1);
        return Math.max(left,right);
    }
}

단순 트리의 깊이를 이용해 좌우측 깊이 판정을 통해 비교하는 값을 했더니만 233번인가 203번 케이스에서 걸린다.

좌우측편향 트리인 경우 true의 값을 리턴하는 문제였다.  / \ 이런 식으로 된 트리구조라면 나의 답은 true로 나오기 때문이다 왜? 좌우측 그냥 가장 깊은 트리의 높이를 반환하니깐

 

재귀 중간중간 높이 체크할 변수가 필요하다. 방법을 찾지 못해 디스커스에 가서 몇몇 해설을 읽었다.

더보기
class Solution {
    public boolean isBalanced(TreeNode root) {
        return helper(root) != -1 ;
    }
    public int helper(TreeNode node){
        if(node == null) return 0;

        int left = helper(node.left);
        int right = helper(node.right);

        if(left == -1 || right == -1) return -1;
        if(Math.abs(right-left) > 1) return -1;

        return 1 + Math.max(left,right);
    }
}

이렇게 되면 편향 트리인 경우 2번째 높이에서 1 보다 큰 차이가 있기에 두 번째 If에서 -1 로직을 타면서 false를 리턴한다.

 

오히려 Medium 난이도 인 데일리 문제보다 이 BST 문제가 더 어려웠다.
Easy 난이도인데도 이런 트리문제가 나오면 너무 약하다. 만약 라이브 코딩 을 한다면 이런 트리문제는 흐.... 트리문제 위주로 더 풀어봐야겠다.

 

'PS > LeetcCode' 카테고리의 다른 글

LeetCode 1월9 ~ 1월 11일  (2) 2023.01.11
LeetCode Daily 문제풀기  (0) 2023.01.05
1월4일 문제 풀기  (2) 2023.01.04
Easy 문제 풀기 Day2(83/618)  (0) 2023.01.03
Binary Tree Maximum Path Sum  (0) 2022.12.11

452. Minimum Number of Arrows to Burst Balloons

풍선은 Y 축에 관계없이 주어진 배열에 따라 x 축으로 길게 형성되어있다. 최소한의 화살을 쏴서 풍선을 터트려야 하는데.. 음 어떻게 풀지 감이 잘 안 온다. 뭔가 그리디 문제 같은데..

첫 번째 접근방법

좌우 차이를 이용해 가장 큰 것부터 제거하면서 그 범위를 기록해 놓는 것이다.  (x)

-> 1-5  1-2, 4-5 이렇게 주어진다면 가장 큰 1-5 가 범위가 기록되고 1-2, 4-5 모두 한 번에 1-5 라인에 소거되기에 탈락

두 번째 접근방법

좌우 차이를 이용해 가장 작은 것부터 제거하면서 그 범위를 기록해 놓는 것

-> 1-5, 1-2, 4-5 음? 뭔가 될 거 같다. 바로 PriorityQueue를 이용해 정렬 후 뽑아가면서 계산을 해보자.

더보기
class Solution {
    public int findMinArrowShots(int[][] points) {
        PriorityQueue<int[]> q
                    = new PriorityQueue<>((a,b) ->(a[1]-a[0])-(b[1]-b[0]));

        for(int[] arr : points){
            q.offer(arr);
        }

        List<int[]> list = new ArrayList<>();
        while(!q.isEmpty()){
            int[] poll = q.poll();
            boolean range = false;
            for(int[] arr : list){
                boolean a = poll[0] <= arr[0] && arr[0] <= poll[1];
                boolean b = poll[0] <= arr[1] && arr[1] <= poll[1];
                if(a || b){
                    range = true;
                    break;
                }
            }
            if(!range) list.add(poll);
        }
        return list.size();
    }
}

36 번째 테스트 케이스에서 깨진다. 왜? 3-5, 5- 7,1-4 이런 경우 3-5가 범위가 찍히고 5-7 이 찍힌다. 이러면 화살은 5번을 쏴야 하지만 1-4 또한 소거된다. 3을 기준으로 분류되기 때문에 이렇게 범위를 이용하는 것이 아닌 하나의 점을 찍어서 구현을 해야 할 것 같은데...

사실 위 방법 말고도 다양한 방법으로 구현해봤으나 다 실패했다.

하.. 멘털 갈린다. 설루션 가서 접근법을 먼저 살펴보고 오자. 

한 번에 많은 화살로 최대한 많은 풍선을 터트려야 한다. 그렇다면 가장 많은 풍선을 터트릴 방법 중 하나는 주어진 범위 최소 혹은 최대 값을 이용하면 판별하기 쉽다. 그러나 모든 가지의 경우의 수를 계산할 필요는 없다 왜? 

정렬을 이용해 오른쪽 숫자별로 기준으로 정해서 그 포인트 별로 화살을 날려주면 되기 때문이다.

balloons = [[7,10], [1,5], [3,6], [2,4], [1,4]]
정렬 이후 
balloons = [[2,4], [1,4], [1,5], [3,6], [7,10]]

하... 이 쉬운 걸 파악 못해서 1시간을 저러고 있으니 참 그렇다.

더보기
class Solution {
    public int findMinArrowShots(int[][] points) {
        PriorityQueue<int[]> q = new PriorityQueue<>(
                    (a,b) -> a[1]-b[1]
            );
            Arrays.stream(points).forEach(q::offer);

            int cnt = 0;

            while(!q.isEmpty()){
                int[] cur = q.poll();
                while(!q.isEmpty() && q.peek()[0] <= cur[1] &&  cur[1] <= q.peek()[1]){
                    q.poll();
                }
                cnt++;
            }
            return cnt;
    }
}

항상 화살은 endPoint 기준으로 날려준다는 로직을 세우고 

우선순위 큐를 활용해 정렬을 했고, 큐를 돌면서 다음 범위를 확인한 후 지워줄지 말지를 정하고 날려준다.

 

이것 외에 배열 정렬을 통해 해결하는 방법도 있다.

 

더보기
class Solution {
    public int findMinArrowShots(int[][] points) {
        Arrays.sort(points, (a,b) -> Integer.compare(a[1], b[1]));
        int cnt = 1;
        int current = points[0][1];
        for(int i=1;i<points.length;i++){
            if(current < points[i][0]){
                cnt++;
                current = points[i][1];
            }
        }
        return cnt;
    }
}

current 가 현재 화살을 소는 타깃 즉 endPoint 가 된다. 

 

Simillar Question 찍어서 비슷한 문제 더 풀어야겠다 화나서 안 되겠다.

 

56. Merge Intervals

이번에는 합쳐주는 방식이다. 양 좌우측 사이에 겹치는 오버래핑되는 수가 있다면 합쳐서 늘려주면 된다.

위에서는 pq를 이용했으니 이번에는 배열방식으로 풀어보자.

더보기
class Solution {
    public int[][] merge(int[][] intervals) {
        Arrays.sort(intervals,(a,b)->a[0]-b[0]);
            
        List<int[]> answer = new ArrayList<>();

        int min = intervals[0][0];
        int max = intervals[0][1];

        for (int i = 1; i < intervals.length; i++) {
            if(min <= intervals[i][0] && intervals[i][0] <= max){
                max = Math.max(intervals[i][1],max);
            }else{
                answer.add(new int[]{min,max});
                min = intervals[i][0];
                max = intervals[i][1];
            }
        }
        answer.add(new int[]{min,max});
        return answer.toArray(new int[answer.size()][]);
    }
}

 

 

이번에는 합치는 로직이다 보니 위에 와는 달리 작은 값이 중요하다 현재 값 사이에  정렬된 배열의 다음 왼쪽값이  포함되느냐 가 주된 로직이다.

forLoop 가 끝나고 한 번 더 추가하는데 이는 마지막까지 모든 수를 업데이트하고 나서 추가가 안 되는 경우를 위해 이렇게 따로 추가한다.

사실 틀리기 전까지 몰랐다. 그래서 추가적으로 위와 같이 작성했다.

 

435. Non-overlapping Intervals

이번에는 겹치는 부분을 제거해주는 로직을 구현하면 된다.

위와 동일하게 좌측 기준으로 정렬 을 하고 오른쪽 값을 업데이트해나가면서 오른쪽 값과 새로 들어온 왼쪽 값을 비교하면 된다.

더보기
class Solution {
    public int eraseOverlapIntervals(int[][] intervals) {
        Arrays.sort(intervals,(a,b)->Integer.compare(a[0],b[0]));

        int cur = intervals[0][1];
        int cnt = 0;

        for(int i=1;i<intervals.length;i++){
            if(cur > intervals[i][0]){
                cnt++;
                cur = Math.min(intervals[i][1],cur);
            }else{
                cur = intervals[i][1];
            }
        }
        return cnt;
    }
}

어으 사실 이 문제도 여러 번 틀렸다. 접근방법은 비슷했으나. 어떻게 다음으로 걸러 주는지 분기를 나누는 부분에서 버벅 거렸다..

 

결론 그리디 문제 너무 싫다.

'PS > LeetcCode' 카테고리의 다른 글

LeetCode 1월9 ~ 1월 11일  (2) 2023.01.11
LeetCode Daily 문제풀기  (0) 2023.01.06
1월4일 문제 풀기  (2) 2023.01.04
Easy 문제 풀기 Day2(83/618)  (0) 2023.01.03
Binary Tree Maximum Path Sum  (0) 2022.12.11

2244. Minimum Rounds to Complete All Tasks

0번 부터 시작하는 인덱스가 주어지고 한번의 테스크는 2,3 개 만 이루어진다. 동일한 레벨 만 동시에 작업할수 있다.

최소 라운드 를 구해라 만약 없다면 -1 을 리턴 하라는 문제이다.

 

최대 갯수가 10000 개 밖에 되지않는다.

 

Map 을 이용해 저 숫자들 을 분류하고, bfs 를 이용해 최소 라운드 를 찾아갈 생각을 했다.

 

더보기
class Solution {
    public int minimumRounds(int[] tasks) {
        Map<Integer,Integer> map = new HashMap();
            for(int a:tasks){
                Integer b = map.getOrDefault(a,0);
                map.put(a,b+1);
            }

            int round = 0;
            for(Map.Entry<Integer,Integer> entry : map.entrySet()){
                Integer value = entry.getValue();
                if(value < 2) return -1;
                int calculate = calculate(value);
                if(calculate < 0) return -1;
                round += calculate;
            }

            return round;
    }

    private int calculate(Integer value) {
            int round =0;
            boolean found = false;

            Queue<Integer> q = new ArrayDeque<>();
            Set<Integer> set = new HashSet();
            q.add(0);

            while(!q.isEmpty()){
                int size = q.size();
                for(int i =0;i<size;i++){
                    int poll = q.poll();

                    int a = poll + 2;
                    if( a == value) return round+1;
                    int b = poll + 3;
                    if( b == value) return round+1;

                    if(a < value && set.add(a)) q.offer(a);
                    if(b < value && set.add(b)) q.offer(b);
                }
                round++;
            }

            return found ? round : -1;
        }
}

이렇게 작성했더니 무려 126ms 나 나와버렸더 무진장 느리다 ...  아무래도 저 bfs 부분이 문제인거 같은데 나름 최적화 가 되어있다고 생각하는데 이렇게 느린걸 보면 다른 규칙이 있는것 같다.

1번   2,3
2번  4,5,6
3번 7,8,9
4번 10,11,12

연산 을 한번 두번 .... 했을때 중복 값을 제외하고 나올 경우의 수를 작성해 보면 위와 같다. 파란색으로 마킹된 부분을 살펴보자. 

모두 3의 배수이다. 2,3 을 이용한다면 모든수 를 작성할수 있다 단 1 인 경우를 제외한다면 이다. caculate 함수 부분을 변경하자.

    private int calculate(Integer value) {
        if(value%3 == 0) return value/3;
        return (value/3)+1;
    }

좋다 이렇게 변경하니 속도가 개선 되었으나 여전히 36ms 로 상위 19% 이다. 많이 느리다. 어떤 부분을 놓친걸까...

 

더보기
class Solution {
    public int minimumRounds(int[] tasks) {
        Arrays.sort(tasks);
        int round = 0;
        int cnt = 1;
        if(tasks.length < 2) return -1;

        for(int i=1;i<tasks.length;i++){
            if(tasks[i-1] != tasks[i]){
                if(cnt < 2) return -1;
                round += calculate(cnt);
                cnt =1;
            }else{
                cnt++;
            }
        }
        
        if(cnt < 2) return -1;
        else round += calculate(cnt);

        return round;
    }

    private int calculate(Integer value) {
        if(value%3 == 0) return value/3;
        return (value/3)+1;
    }
}

 

로직을 변경해 배열을 정렬하고 for loop 으로 로직 변경 => 10ms  98.8% 까지 나왔다.

 

음 ? 다른사람의 풀이도 별반 다르지 않다... 다 이런 2가지 방식으로 풀었다.

 

83. Remove Duplicates from Sorted List

연결 노드상 에서 중복된 값을 지워주는 로직을 구현하면 된다. 재귀 를 노드 의 마지막 까지 이동을 하고

현재 헤드 기준으로 헤드 의 다음을 계속 업데이트 해주면서 날려주는 로직을 재귀 안에 구성하면 될것같다.

 

더보기
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        helper(head);
        return head;
    }
    public void helper(ListNode head){
        if(head == null || head.next == null) return;
        while(head.next != null && head.val == head.next.val){
            ListNode next = head.next;
            if(next == null){
                head.next = null;
                break;
            }
            head.next = next.next;
        }
        helper(head.next);
    }
}

null 체크 를 제대로 하지않아서 nullPointer exception 을 여러번 맞았다 ㅎㅎㅎ... while 을 next 의 null 까지 돌리지 않는다면 

1,1,1,1,1,1,1 케이스 에서 1,1 로 반환되는 불상사가 발생한다. 주의 하자

Solution 중에 가독성 좋은 코드가 있어서 가져와 봤다.

더보기
public class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        ListNode list = head;
         
         while(list != null) {
        	 if (list.next == null) {
        		 break;
        	 }
        	 if (list.val == list.next.val) {
        		 list.next = list.next.next;
        	 } else {
        		 list = list.next;
        	 }
         }
         
         return head;
    }
}

while 을 이용해서 loop 를 돌리는게 확실히 재귀보다 직관적 이다.

 

1619. Mean of Array After Removing Some Elements

정렬하고 5% 계산해서 평균 구해주면 되는 Easy 난이도 에 맞는 문제이다.

더보기
class Solution {
    public double trimMean(int[] arr) {
        Arrays.sort(arr);
        int cut = (int) ((double) arr.length * 0.05);
        double sum = 0;
        for(int i = cut;i<arr.length-cut;i++){
            sum+=arr[i];
        }
        return sum/(arr.length-(cut*2));
    }
}

 

깔끔한 코드 20 개 의 배수가 주어진다는 문제의 요구사항에 따라 미리 20 을 나눠서 계산하는 모습이 인상적이다.

    public double trimMean(int[] arr) {
        int n = arr.length;
        double sum = 0d;
        Arrays.sort(arr);
        for (int i = n / 20; i < n - n / 20; ++i) {
            sum += arr[i];
        }
        return sum / (n * 9 / 10);
    }

 

'PS > LeetcCode' 카테고리의 다른 글

LeetCode Daily 문제풀기  (0) 2023.01.06
LeetCode Daily 문제풀기  (0) 2023.01.05
Easy 문제 풀기 Day2(83/618)  (0) 2023.01.03
Binary Tree Maximum Path Sum  (0) 2022.12.11
1339. Maximum Product of Splitted Binary Tree  (0) 2022.12.10

작년 나름 릿코드 열심히 들어가서 문제를 풀었다고 생각했는데 올해 처음 들어가니 100 일 짜리 뱃지 를 받았다 ㅎㅎ

내년 에는 200 일 짜리 뱃지 를 받아보도록 노력해보자.

 

944. Delete Columns to Make Sorted

주어진 문자 를 나열하고 세로로 읽었을때 사전 순이 되지 않는 경우를 판별하는 문제이다. 모든 문자열의 길이가 똑같다는 전제조건 이 있어서 이지 난이도라고 생각된다.

 

더보기
class Solution {
    public int minDeletionSize(String[] strs) {
        int answer = 0;
        int len = strs[0].length();
        for(int i=0;i<len;i++){
            for(int j=1;j<strs.length;j++){
                char a = strs[j].charAt(i);
                char b = strs[j-1].charAt(i);
                if( a < b){
                    answer++;
                    break;
                }
            }
        }
        return answer;
    }
}

배열의 최대 크기 1000, 문자의 최대길이 100 => 2중 포문 돌려도 문제없다고 생각해서 바로 작성했다. 

최초 작성시 에 i, j 를 이상하게 지정을 해버리는 바람에 indexOutOfRange 에러가 났다. 언제쯤 이런 잔 실수를 없앨수 있는지...

그래도 제출하니 바로 통과 가 되긴한다.

 

대부분 의 solution code 들도 이중포문 을 이용해서 작성 되어있다.

 

 

520. Detect Capital

 

주어지는 문제 의 조건 에 맞춰서 구현만 해주면 된다. 

더보기
class Solution {
    public boolean detectCapitalUse(String word) {
        char first = word.charAt(word.length()-1);
        if('A' <= first && first <= 'Z'){
            for(char c : word.toCharArray()){
                if(!('A' <= c && c <= 'Z')) return false;
            }
        }else{
            for(int i=word.length()-1;i>=0;i--){
                char c = word.charAt(i);
                if(!('a' <= c && c <= 'z') && (i!= 0)) return false;
            }
        }
        return true;
    }
}

그냥 문제 에서 주어진 그대로 구현했다. 

이 방법 외에는 regex 가 있다. 

class Solution {
    public boolean detectCapitalUse(String word) {
        return word.matches("[A-Z]*|[A-Z]?[a-z]*");
    }
}

대문자 이거나 혹은 대문자가 있거나 없거나 그리고 나머지 소문자 로 채우면 된다.

이것 외에 괜찮다고 생각하는 코드가 있어서 들고왔다.

더보기
public boolean detectCapitalUse(String word) {
        if (word.length() < 2) return true;
        if (word.toUpperCase().equals(word)) return true;
        if (word.substring(1).toLowerCase().equals(word.substring(1))) return true;
        return false;
}

반대로 true 가 되는 조건만 을 subString 과 toUpperCase 를 이용해 깔끔하게 코드 를 구사했다. 

그러나 toUpperCase 의 작동하는 방식을 보면 내부적으로 forLoop 를 돌려서 내부로직 수행후 String 을 리턴해준다.

하물며 subString 은 어떤가 String 은 불변 이다 따라서 클래스 내부 의 value 바이트 코드 배열을 arrayCopy 를 수행해 새로운 스트링을 만들어 뱉어준다. 즉 for 를 한번더 돈다는 의미이다. eqaul 또한 하나씩 비교하기 위해 forLoop 을 돈다 

이렇게 수행 로직 만 보고 생각한다면 느릴수 밖에없다. 다만 이해하기 쉽다. 

실제 업무에 투입되어 작성한코드 라면 사실 나는 이 코드 처럼 작성하고 싶다. 성능상의 문제가 있는 것 이 아니라면 코드 자체를 이렇게 두고 유지할것이다. 왜 ? 읽기 쉬운 코드가 최고니깐.

 

290. Word Pattern

주어진 패턴에 맞는 스트링 값을 찾아 반환 해주면 되는 문제이다.

 

더보기
class Solution {
    public boolean wordPattern(String pattern, String s) {
        Map<Character,String> map = new HashMap();
        Set<String> set = new HashSet();

        int len = pattern.length();
        String[] arr = s.split(" ");

        if(len != arr.length) return false;
        for(int i=0;i<len;i++){
            char p = pattern.charAt(i);
            if(!map.containsKey(p) && set.add(arr[i])){
                map.put(p,arr[i]);
            }
            if(!map.getOrDefault(p,"ㄱ").equals(arr[i])) return false;
        }

        return true;
    }
}

쫌 과하다 싶을 만큼 자료구조 를 선언해서 사용했다. Set 없이 그냥 map.containsValue 를 사용해도 됬었다. 다만 containsValue 를 사용해서 forLoop 를 두번돌게 하고 싶지 않았다. 

Map 을 두번써서 좌우로 맵핑 한 사람도 있고, 다양한 풀이방법 이 있다.

본인이 선호하는 방법으로 작성하면 된다고 생각한다. 

 

이렇게 3문제를 풀어도 2,3 번 같은경우는 2~3번씩 제출을 더 했다. 로직 의 오류, 예외케이스 핸들링 생략 등이 그 이유이다.

언제쯤 풀어야 이런 이지 난이도 를 한번에 클리어 할수 있을까.... 

'PS > LeetcCode' 카테고리의 다른 글

LeetCode Daily 문제풀기  (0) 2023.01.05
1월4일 문제 풀기  (2) 2023.01.04
Binary Tree Maximum Path Sum  (0) 2022.12.11
1339. Maximum Product of Splitted Binary Tree  (0) 2022.12.10
Easy 문제 풀기 Day1(79/610)  (0) 2022.12.03

트리 내에 존재하는 엣지 를 타고 가장 합이 높은 부분을 구하는 문제이다. 어제와 유사한 문제라고 생각해서 비슷하게 접근했으나 머리가 대차게 깨져버렸다.

어제 와 유사한 코드로 접근하였으나 - 값의 여부와 한가지 패스 안에서는 길을 타고 다시 거슬러 올라오면 안 된다는 부분에서 신경이 쓰여 preorder를 사용했다. 중복을 처리해줄 방법이 없어 생각보다 애를 많이 먹었다... 

if를 이용해서 틀릴 때마다 처리를 해주었으나. 99개 테스트 케이스에서 56 이 한계였다. preOrder, postOrder 전부 실패했다. prefix를 이용하기 위해 리스트 넘기는 방법, max를 이용해 현재 비교대상을  3번 비교하는 방법 전부 실패했다.

뭔가 잡힐듯 말듯한데 자꾸 실패해서 분하다...

 

약 1시간 정도 풀다가 실패해서 discuss를 보고 오니 내가 구해야 할 명확한 목적을 알고 나니 풀이가 쉬워졌다.

 

머리 깨진 풀이

class Solution {
    int max;
    public int maxPathSum(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        max = Integer.MIN_VALUE;
        helper(root, list);
        int total = list.get(list.size()-1);
        for (int i = 0; i < list.size()-1; i++) {
            max = Math.max(Math.max(total,total-list.get(i)),max);
        }
        return max;
    }

    public void helper(TreeNode node, List<Integer> list){
        if(node == null) return;
        helper(node.left, list);
        int previous = list.size() == 0 ? 0 : list.get(list.size()-1);;
        list.add(previous+node.val);
        max = Math.max(max,node.val);
        helper(node.right,list);
    }
}

트리 순회를 위해 helper를 선언해 중위 순회를 선택했다. 이유는 후위 순회를 하게 된다면 리프부터 타고 올라가야 하는데 이 기준점을 정해주기 가 생각보다 어려웠다. prefix를 사용해야 한다는 압박감 때문 인지. 후위 순회를 하게 되니 오류가 많이 생겼다. 

디스커스에서 읽고 바로 풀이 방법을 바꾼 힌트 를 보자.

우리가 구해야 할 4가지 경우의 수가 있다.

1. 왼쪽 패스 + 현재 값

2. 오른쪽 패스 + 현재 값

3. 왼쪽 패스 + 오른쪽 패스 + 현재 값

4. 현재 값 자체로 위 값보다 큰 경우

이 4 가지를 명확히 하고 갔어야 했는데 이게 안되니 null 이 여러 번 들어간 깊이 있는 트리인 경우 오답이 나게 되는 것이다. ㅠ

 

위 4가지 힌트를 얻어서 작성한 풀이법이다. 

class Solution{
	int max = Integer.MIN_VALUE;
    
	public int maxPathSum(TreeNode root) {
        helper(root);
        return max;
    }
    
    public int helper(TreeNode node){
    	if(node == null) return Integer.MIN_VALUE;
        int left = helper(node.left);
        int right = helper(node.right);
        
        max = Math.max(max,Math.max(left,right));
        max = Math.max(max,node.val);
        max = Math.max(max,node.val+Math.max(left,0)+Math.max(right,0));
        return node.val + Math.max(0,Math.max(left,right));
    }
}

max 값을 비교할 Integer를 선언해주고 , 현재 값이 - 값이라면 무조건 0으로 초기화시켜주었다. 

left 패스, right 패스를 각각 구하고 현 max와 패스의 합 +현재 값

그리고 현재 값에 패스 값 중 높은 값을 다음으로 넘겨주는 재귀를 작성하였다. 

 

class Solution{
	int value = Integer.MIN_VALUE;

    public int maxPathSum(TreeNode root) {
        int temp = process(root);
        return Math.max(temp, value);
    }

    public int process(TreeNode head){
        if(head == null){
            return 0;
        }
        int leftTree = Math.max(0, process(head.left));
        int rightTree = Math.max(0, process(head.right));
        value = Math.max(value, leftTree + rightTree + head.val);
        return Math.max(leftTree, rightTree) + head.val;
    }
}

이 사람도 보면 참 깔끔하게 작성한 거 같다. 왼쪽 패스, 오른쪽 패스를 나누고 현재 값의 맥스를 왼쪽 오른쪽의 합과 현재 값을 이용하고 다음 재귀로 왼쪽 오른쪽 중 큰 값을 현재 값에 더해 넘겨준다.

 

maxValue = Math.max(maxValue, left + right + node.val);

작성할 때 난감 했던 부분이다. maxVaule를 현재 와 왼쪽 오른쪽 그리고 노드의 값을 합친 값으로 업데이트를 하는 것이 아닌가.

문제의 포인트를 잡지 못해서 이상한 풀이법으로 나가 결국 맞지 못한 문제였다.

'PS > LeetcCode' 카테고리의 다른 글

1월4일 문제 풀기  (2) 2023.01.04
Easy 문제 풀기 Day2(83/618)  (0) 2023.01.03
1339. Maximum Product of Splitted Binary Tree  (0) 2022.12.10
Easy 문제 풀기 Day1(79/610)  (0) 2022.12.03
BFS 문제들을 풀어보자 1탄  (0) 2022.08.25

+ Recent posts