hyelie
hyelie
Hyeil Jeong
       
글쓰기    관리    수식입력
  • 전체보기 (495)
    • PS (283)
      • Algorithm (28)
      • PS Log (244)
      • Contest (6)
      • Tips (5)
    • Development (52)
      • Java (14)
      • Spring (23)
      • SQL (2)
      • Node.js (2)
      • Socket.io (3)
      • Study (4)
      • Utils (4)
    • DevOps (36)
      • Git (5)
      • Docker (4)
      • Kubernetes (2)
      • GCP (3)
      • Environment Set Up (8)
      • Tutorial (12)
      • Figma (2)
    • CS (74)
      • OOP (7)
      • OS (24)
      • DB (2)
      • Network (24)
      • Architecture (0)
      • Security (2)
      • Software Design (0)
      • Parallel Computing (15)
    • Project (15)
      • Project N2T (5)
      • Project ASG (0)
      • Project Meerkat (1)
      • Model Checking (7)
      • Ideas (2)
    • 내가 하고싶은 것! (34)
      • Plan (16)
      • Software Maestro (10)
      • 취준 (8)
hELLO · Designed By 정상우.
hyelie

hyelie

PS/PS Log

23.03.16. 풀었던 문제들

Leetcode 106. Construct Binary Tree from Inorder and Postorder Traversal

 어떤 binary tree의 inorder와 postorder 순회 결과가 주어졌을 때 tree를 구성하는 문제이다. 이 때 inorder와 postorder의 모든 element는 unique하다.

 

첫 접근 : time O($n^{2}$), space O(n)

  일단 postorder의 제일 마지막 index가 root인 것(rootidx라 하자), 그리고 inorder에서 rootidx 왼쪽의 것은 왼쪽 subtree, 오른쪽의 것은 오른쪽 subtree임을 알 수 있다. 따라서 각 탐색에서 왼쪽/오른쪽을 나누어야 한다는 것은 쉬운 직관으로 알 수 있다.

 그럼 다음 탐색에서는 postorder의 어떤 index가 root가 될까?

 

 만약 탐색 범위를 알 수 있다면 어떨까? inorder에서 [시작 범위 ~ rootidx-1]는 왼쪽 subtree, [roodix + 1 ~ 끝 범위]가 오른쪽 subtree이므로 이 두 범위의 길이를 알 수 있다. 그러면 이 길이로 postorder의 왼쪽 subtree와 오른쪽 subtree또한 나눌 수 있게 된다.

 왼쪽 subtree 길이를 leftlen, 오른쪽 subtree 길이를 rightlen이라 하면 postorder의 [시작 범위 ~ 시작범위 + leftlen - 1]은 왼쪽 subtree, [시작 범위 ~ 끝 범위 -1]은 오른쪽 subtree이다.

 따라서 각 탐색에서 inorder와 postorder의 left, right subtree를 나눌 수 있게 되었다. 정리하면 다음과 같고 이를 구현하면 된다.

  • postorder 탐색 끝 index에 있는 element가 inorder의 root이다.
  • inorder 탐색 시작 index, 끝 index를 이용해 leftlen, rightlen을 계산한다.
  • leftlen, rightlen을 이용해 inorder와 postorder의 다음 탐색 범위를 계산한다.
  • left subtree, right subtree를 recurse한다.
// Runtime 51 ms Beats 16.18%
// Memory 26.1 MB Beats 78.64%

class Solution {
public:
    vector<int> inorder, postorder;
    TreeNode* build(int inStart, int inEnd, int postStart, int postEnd){
        // 탐색 종료 조건
        if(inStart > inEnd || postStart > postEnd) return NULL;

        // postorder의 마지막 index의 element가 root이므로 inorder에서 그 값을 찾는다.
        int inRoot = -1;
        for(int i = inStart; i<=inEnd; i++){
            if(postorder[postEnd] == inorder[i]){
                inRoot = i;
                break;
            }
        }

        if(inRoot == -1) return NULL;

        TreeNode* curroot = new TreeNode(postorder[postEnd]);
        // leftlen, rightlen을 계산한다.
        int leftlen = inRoot - inStart;
        int rightlen = inEnd - inRoot;

        // left, right subtree로 recurse한다.
        curroot->left = build(inStart, inRoot-1, postStart, postStart + leftlen - 1);
        curroot->right = build(inRoot+1, inEnd, postStart + leftlen, postEnd - 1);

        return curroot;
    }
    TreeNode* buildTree(vector<int>& _inorder, vector<int>& _postorder) {
        inorder = _inorder; postorder = _postorder;
        return build(0, _inorder.size()-1, 0, _postorder.size()-1); // *** 실수 : size()를 넣었다.
    }
};

 

시간복잡도

 이렇게 풀 경우 inorder를 순회하는 과정에서 worst O(n)의 시간이 걸린다. 따라서 T(n) = 2T(n/2) + O(n)이므로 worst O($n^{2}$)이다.

 

공간복잡도

 stack은 O(h)만큼 탐색하게 된다. worst case로 1자로 길게 늘어진 tree의 경우 h == n이므로 O(n)이다. 추가 공간으로 inorder, postorder의 공간을 그대로 사용하므로 O(n)이다. 따라서 O(n)

 

 

두번째 접근 : time, space O(n)

 inorder를 순회하는 과정이 너무 길다. inorder의 모든 element가 unique이므로 이를 map을 사용해 O(logn), 또는 unordered map을 사용해 O(1)로 접근할 수 있다.

// Runtime 8 ms Beats 97.85%
// Memory 26.6 MB Beats 25.72%

class Solution {
public:
    vector<int> inorder, postorder;
    unordered_map<int, int> inorder_idx;
    TreeNode* build(int inStart, int inEnd, int postStart, int postEnd){
        if(inStart > inEnd || postStart > postEnd) return NULL;

        int inRoot = inorder_idx[postorder[postEnd]]; // 개선 : unordered_map으로 index get

        if(inRoot == -1) return NULL;

        TreeNode* curroot = new TreeNode(postorder[postEnd]);
        int leftlen = inRoot - inStart;
        int rightlen = inEnd - inRoot;

        curroot->left = build(inStart, inRoot-1, postStart, postStart + leftlen - 1);
        curroot->right = build(inRoot+1, inEnd, postStart + leftlen, postEnd - 1);

        return curroot;
    }
    TreeNode* buildTree(vector<int>& _inorder, vector<int>& _postorder) {
        inorder = _inorder; postorder = _postorder;
        // 개선 : map initialize
        for(int i = 0; i<inorder.size(); i++){
            inorder_idx[inorder[i]] = i;
        }
        return build(0, _inorder.size()-1, 0, _postorder.size()-1);
    }
};

 

시간복잡도

 이렇게 풀 경우 inorder를 순회하는 과정에서 worst O(1)의 시간이 걸린다. 따라서 T(n) = 2T(n/2) + O(1)이므로 worst O(n)이다.

 

공간복잡도

 공간복잡도는 위와 동일하다.

 

 

 

 

 

 

 

 

저작자표시 (새창열림)

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

23.03.18. 풀었던 문제들  (0) 2023.03.18
23.03.17. 풀었던 문제들  (0) 2023.03.17
23.03.15. 풀었던 문제들  (1) 2023.03.15
23.03.14. 풀었던 문제들  (0) 2023.03.14
23.03.13. 풀었던 문제들  (0) 2023.03.13
    hyelie
    hyelie

    티스토리툴바