LeetCode 863 : 이진 트리의 모든 노드 거리 K

Sep 23 2020

LeetCode의 "이진 트리의 모든 노드 거리 K"에 대한 두 가지 유사한 솔루션을 게시하고 있습니다. 리뷰를 원하시면 그렇게 해주세요. 감사합니다!

문제

이진 트리 (루트 노드 포함 root), target노드 및 정수 값이 제공 K됩니다.

K대상 노드에서 거리가있는 모든 노드의 값 목록을 반환 합니다. 답변은 어떤 순서로든 반환 할 수 있습니다.

입력:

  • root = [3,5,1,6,2,0,8,null,null,7,4], target = 5,K = 2

산출:

  • [7,4,1]

설명:

  • 대상 노드에서 거리 2 (값 5)에있는 노드의 값은 7, 4, 1입니다.

  • 입력 "root"와 "target"은 실제로 TreeNode입니다.
  • 위의 입력에 대한 설명은 이러한 개체의 직렬화 일뿐입니다.

노트 :

  • 주어진 나무는 비어 있지 않습니다.
  • 트리의 각 노드에는 고유 한 값 0 <= node.val <= 500이 있습니다.
  • 대상 노드는 트리의 노드입니다.
  • 0 <= K <= 1000.

입력 "root"와 "target"은 실제로 TreeNode입니다. 위의 입력에 대한 설명은 이러한 개체의 직렬화 일뿐입니다.

해결책 1

// The following block might slightly improve the execution time;
// Can be removed;
static const auto __optimize__ = []() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);
    return 0;
}();

// Most of headers are already included;
// Can be removed;
#include <cstdint>
#include <vector>
#include <unordered_map>
#include <unordered_set>

using ValueType = int;

static const struct Solution {
        static const std::vector<ValueType> distanceK(
            TreeNode* root,
            TreeNode* target,
            const ValueType K
        ) {
            std::vector<ValueType> res;
            std::unordered_map<TreeNode*, TreeNode*> parents;
            std::unordered_set<TreeNode*> visited;


            getParent(root, parents);
            depthFirstSearch(target, K, parents, visited, res);
            return res;
        }

    private:
        static const void getParent(
            TreeNode* node,
            std::unordered_map<TreeNode*, TreeNode*>& parents
        ) {
            if (!node) {
                return;
            }

            if (node->left) {
                parents[node->left] = node;
                getParent(node->left, parents);
            }

            if (node->right) {
                parents[node->right] = node;
                getParent(node->right, parents);
            }
        }


        static const void depthFirstSearch(
            TreeNode* node,
            const ValueType K,
            std::unordered_map<TreeNode*, TreeNode*>& parents,
            std::unordered_set<TreeNode*>& visited,
            std::vector<ValueType>& res
        ) {
            if (!node) {
                return;
            }

            if (visited.count(node) > 0) {
                return;
            }

            visited.insert(node);

            if (!K) {
                res.emplace_back(node->val);
                return;
            }

            depthFirstSearch(node->left, K - 1, parents, visited, res);
            depthFirstSearch(node->right, K - 1, parents, visited, res);
            depthFirstSearch(parents[node], K - 1, parents, visited, res);
        }
};

해결 방법 2

// The following block might slightly improve the execution time;
// Can be removed;
static const auto __optimize__ = []() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);
    return 0;
}();

// Most of headers are already included;
// Can be removed;
#include <cstdint>
#include <vector>
#include <unordered_map>
#include <unordered_set>

using ValueType = int;

static const struct Solution {
        const std::vector<ValueType> distanceK(
            TreeNode* root,
            TreeNode* target,
            ValueType K
        ) {
            getParent(root);
            depthFirstSearch(target, K);
            return res;
        }

    private:
        std::vector<ValueType> res;
        std::unordered_map<TreeNode*, TreeNode*> parents;
        std::unordered_set<TreeNode*> visited;

        const void getParent(
            TreeNode* node
        ) {
            if (!node) {
                return;
            }

            if (node->left) {
                parents[node->left] = node;
                getParent(node->left);
            }

            if (node->right) {
                parents[node->right] = node;
                getParent(node->right);
            }
        }



        const void depthFirstSearch(
            TreeNode* node,
            const ValueType K
        ) {
            if (!node) {
                return;
            }

            if (visited.count(node) > 0) {
                return;
            }

            visited.insert(node);

            if (!K) {
                res.emplace_back(node->val);
                return;
            }

            depthFirstSearch(node->left, K - 1);
            depthFirstSearch(node->right, K - 1);
            depthFirstSearch(parents[node], K - 1);
        }
};

참고

다음은 LeetCode의 템플릿입니다.

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> distanceK(TreeNode* root, TreeNode* target, int K) {
        
    }
};
  • 863. 이진 트리의 모든 노드 거리 K-문제

  • 863. 이진 트리의 모든 노드 거리 K-토론

답변

4 G.Sliepen Sep 23 2020 at 22:32

약 __optimize__

이중 밑줄로 시작하는 식별자 는 예약되어 있습니다. 또한 왜 이것이 일반 함수가 아닌 람다로 작성됩니까? 또한 표준 I / O를 읽고 쓰는 것도 아니므로이 기능은 아무 효과가 없습니다.

클래스 또는 네임 스페이스 외부에서 형식 별칭을 만들지 마십시오.

using ValueType = int매우 일반적인 이름이며 동일한 작업을 수행하는 다른 코드와 충돌 할 수 있으므로 전역 네임 스페이스에서 선언하지 마십시오 . 이 경우에는 이것을 내부에 선언하십시오 struct Solution.

static const 구조체 정의에 영향을주지 않습니다.

한정자 static이며 const의 정의에 영향을주지 않습니다 struct. 다음과 같이 구조체를 정의하고 해당 유형의 변수를 한 번에 선언 할 수 있기 때문에 C ++ 문법에서 허용됩니다.

static const struct foo {
    ...
} bar;

foo baz;

상기에서, bar이다 static const, 그러나 baz아니다.

const 비 포인터 / 참조 반환 값에는 영향을주지 않습니다.

마찬가지로, const반환되는 포인터 나 참조가 아니면 함수의 반환 값에 영향을주지 않습니다. const값을 항상 비 const변수 에 복사 할 수 있기 때문에 이치에 맞지 않습니다 . 또한 무엇 const void을 의미합니까?

정적 static함수 와 비 멤버 함수

두 가지 변형은 static상태가 클래스 멤버 변수로 유지되는 비 멤버 함수 를 사용하는지 아니면 static스택에 할당되고 다른 멤버 함수에 대한 포인터로 전달되는 상태가있는 멤버 함수 를 사용하는지에 따라 다릅니다 . 둘 다 유효한 접근 방식이지만 실제로 중요하지 않다는 사실은 struct Solution자체 사용이 무의미하다는 것을 알려야합니다 . 실제 응용 프로그램에서는 distanceK()클래스의 구성원이 아닌 함수가 있습니다 . 나는 LeetCode class가 최소한의 변경으로 Java 문제를 C ++에 복사하고 붙여 넣었 기 때문에 당신에게 제공한다고 믿습니다 . 그리고 Java는 함수가 클래스 외부에서 정의되는 것을 허용하지 않습니다.

최적화가 활성화되면 컴파일러는 두 경우 모두 매우 유사한 어셈블리를 생성 할 것입니다.

를 사용하여 std::bitset방문한 노드를 추적 하십시오.

문제는 최대 501 개의 고유 노드 만 있다는 것입니다. 즉, std::bitset를 사용하여 방문한 노드를 추적 할 수 있습니다 . bitset은 64 바이트 만 사용하므로 .NET TreeNode *을 유지하는 다른 모든 오버 헤드는 고사하고 단일에 대해 8 바이트와 비교 합니다 std::unordered_set<TreeNode *>.

많은 메모리 사용을 피하십시오

그렇지 않으면 매우 합리적으로 보이는 알고리즘의 문제는 모든 노드의 부모를 계산해야한다는 것입니다. 그 TreeNode자체 로는 저장할 수 없기 때문에 이제는 unordered_map<TreeNode *, TreeNode *>입력만큼 많은 공간을 차지하는를 유지해야합니다 .

깊이 우선 검색을 수행하면 DFS 함수를 재귀 적으로 호출 할 때 재귀하는 노드의 부모를 알고 있으므로 다음과 같이 함수에 전달할 수 있습니다.

void DFS(Node *node, Node *parent) {
    if (!node)
        return;

    // do something with node and/or parent

    DFS(node->left, node);
    DFS(node->right, node);
}

문제는 트리의 루트가 아닌 대상 노드에서 DFS를 시작하려고하므로 대상의 부모를 알지 못한다는 것입니다. 그러나 어쨌든 루트에서 시작하도록 알고리즘을 수정 한 다음 대상에 도달하기 위해 얼마나 멀리 내려야했는지 추적 할 수 있습니다. 일단 목표에 도달하면 평소처럼 아래로 재귀하지만, 완료되면 위쪽으로 돌아와서 어떻게 든 목표를 만났다는 신호를 보내야하며, 다른 방법으로 거리 K에서 노드를 찾습니다. 이것은 트리의 일부를 두 번 방문해야한다는 것을 의미 할 수 있지만 이미 현재 알고리즘에서 그렇게하고 있습니다.