문제

문자 데이터를 저장하는 2 개의 이진 트리 T1과 T2가 있습니다.
T2가 T1의 하위 트리인지 어떻게 알 수 있습니까? .
T1에는 수백만 개의 노드가 있으며 T2에는 수백 개의 노드가 있습니다.

도움이 되었습니까?

해결책

트래버스 T1. 전류 노드가 T2의 루트 노드와 같으면 나무 (T2 및 T1의 현재 하위 트리)를 동시에 가로 지르십시오. 현재 노드를 비교하십시오. 항상 동일하다면 T2는 T1의 하위 트리입니다.

다른 팁

전처리를 사용하는 알고리즘을 제안합니다.

1) 가능한 모든 하위 트리 뿌리의 목록을 유지하면서 T1 트리를 사전 처리합니다 (캐시 목록에는 수백만 개의 항목이 있습니다).

2) 캐시 된 루트 목록을 내림차순 데이터의 순서로 정렬하고 루트에 보관하십시오. 예를 들어 캐릭터 트리를 문자열로 구문 분석하는 등 우아한 분류 기능을 선택할 수 있습니다.

3) 이진 검색을 사용하여 필요한 하위 트리를 찾으십시오. 노드 수는 T2 노드 수와 같지 않은 노드 수 (또는 깊이가 다른) 서브 트리를 신속하게 거부 할 수 있습니다.

단계 1)과 2)는 한 번만 수행해야하며 동일한 전처리 결과를 사용하여 많은 하위 트리 후보를 테스트 할 수 있습니다.

당신의 나무가 어떤 식 으로든 분류되지 않으면 나는 무차별 검색 : 나무를 걸어 가십시오 T1 그리고 트리의 첫 번째 노드와 일치하는 노드에 도달하는지 확인하십시오. T2. 그렇지 않다면 계속 통과하십시오 T1. 그렇다면 다음 노드가 일치하는지 확인하십시오. T2, 어떤 경우에는 히트가 있습니다 : 당신의 나무 T2 실제로 하위 트리입니다 T1.

모든 단일 노드의 깊이를 알고 있다면 T1 (잎에서 노드까지) 찾고있는 하위 트리만큼 깊지 않은 노드를 건너 뛸 수 있습니다. 이것은 불필요한 비교를 많이 제거하는 데 도움이 될 수 있습니다. 그렇게 말해봐 T1 그리고 T2 균형이 잘 잡힌 나무입니다 T1 총 깊이는 20입니다 (2**20 > 1,000,000)와 나무 T2 깊이는 7입니다 (2**7 > 100). 당신은 13을 먼저 걸어야합니다 레이어T1 (8192 노드 - 또는 14 개의 레이어와 16384 개의 노드입니까?)의 약 90%를 건너 뛸 수 있습니다. T1...

그러나 만약 하위 트리 당신은 그 잎 노드를 의미합니다 T2 또한 잎 노드입니다 T1, 그러면 첫 번째 트래버스를 할 수 있습니다 T1 그리고 모든 노드의 깊이 (잎에서 노드까지의 거리)를 계산 한 다음 깊이와 같은 깊이를 가진 하위 트리 만 확인하십시오. T2.

메모리/스토리지 바운드라면 (즉, 나무를 대체 형태로 사전 관리하고 보관할 수 없음) 다른 답변이 제안한 무차별 인력 검색보다 더 잘 할 수 없을 것입니다 (Traverse P1을 찾고 있습니다. P2의 루트 일치, 트래버스 둘 다 노드가 일치하는 서브 트리의 루트에 있는지 여부를 결정하고 일치하지 않는 경우 원래 트래버스를 계속하십시오). 이 검색은 O (n * m)에서 작동합니다. 여기서 n은 p1의 크기이고 m은 p2의 크기입니다. 사용 가능한 트리 데이터에 따라 깊이 점검 및 기타 잠재적 최적화를 통해이 사람은 약간 최적화되지만 여전히 O (N * M)입니다. 특정 상황에 따라 이것은 유일한 합리적인 접근법 일 수 있습니다.

메모리/스토리지 바운드가 아니고 약간의 복잡성을 신경 쓰지 않는다면, 나는 이것이 가장 긴 공통 서브 스트링 a의 도움으로 문제 일반화 된 접미사 트리. 비슷한 문제에 대한 이것에 대한 일부 토론을 찾을 수 있습니다. 여기. 어쩌면 시간이 더 있으면 돌아와서 구현에 대한 더 많은 세부 사항을 편집 할 것입니다.

두 나무의 뿌리가 주어지고 노드가 동일한 유형이라는 점을 감안할 때 왜 T2의 루트가 T1에 충분하지 않다는 것을 확인 하는가?

나는 "트리 t가 주어진다"는 T의 루트와 노드의 데이터 유형에 대한 포인터가 주어진다고 가정합니다.

문안 인사.

내 아이디어가 올바른지 확실하지 않습니다. 그럼에도 불구하고, 당신의 페르얼을 위해.

  1. 트리 1 & 트리 2에서 우편 주문을 수행하고 P1과 P2라고 부릅니다.
  2. P1 & P2를 비교하십시오. 그들이 다르면. 나무는 하위 트리가 아닙니다. 출구.
  3. 그것들이 동일하거나 p1이 p2에 포함 된 경우. 어느 것이 서브 트리인지 결정할 수 있습니다.

나는 우리가 무차별 인 힘으로 가야한다고 생각하지만, T1에서 T2의 뿌리를 찾은 후에 왜 나무와 일치해야합니까? 나무가 동일한 지 찾지 않아도되는 것과 동일하지 않습니다. (그런 다음 나무 전체를 비교해야합니다)

당신은 값이 아닌 나무 T1과 T2, 포인터가 주어집니다.

노드 T2 (포인터 인) 인 경우 T1 트리에 존재합니다.

그런 다음 트리 T2는 T1의 하위 트리입니다.


편집하다:

T2가 실제로 객체에 의한 다른 트리라고 말하면, T1과 동일한 T1에 하위 트리가있는 경우 알 수 있어야합니다.

그러면 이것은 작동하지 않습니다.

그리고 우리는 이미 여기에서 논의 된 솔루션을 찾는 것 외에는 다른 옵션이 없습니다.

우리는 T1을 부모 트리로, T2를 T1의 하위 트리 일 수있는 트리로한다고 가정 해 봅시다. 다음을 수행하십시오. 가정은 T1이고 T2는 균형 요소가없는 이진 트리입니다.

1) T1에서 T2의 루트를 검색하십시오. 찾을 수없는 경우 T2는 하위 트리가 아닙니다. BT에서 요소를 검색하는 데 시간이 걸립니다.

2) 요소가 발견되면 T2의 노드 루트 요소로부터 T1의 선주문 트래버스를 찾으십시오. 이것은 O (n) 시간이 걸립니다. T2의 선주문 횡단도 사용하십시오. O (n) 시간이 걸립니다. 선주문 순서의 결과는 스택에 저장 될 수 있습니다. 스택의 삽입은 O (1) 만 필요합니다.

3) 두 스택의 크기가 같지 않으면 T2는 하위 트리가 아닙니다.

4) 각 스택에서 하나의 요소를 팝하고 평등을 점검하십시오. 불일치가 발생하면 T2는 하위 트리가 아닙니다.

5) 모든 elments와 일치하는 T2가 하위 트리 인 경우.

나는 당신의 나무가 있다고 가정합니다 불변의 나무 그래서 당신은 하위 트리를 바꾸지 않습니다 (당신은하지 않습니다. set-car! 계획에서), 그러나 당신은 단지 잎이나 기존 나무에서 새로운 나무를 건설하고 있습니다.

그런 다음 조언 할 것입니다 모든 노드에 보관하십시오 (또는 하위 트리) 해시 코드 그 노드의. C 용어에서는 나무를 선언합니다

 struct tree_st {
   const unsigned hash;
   const bool isleaf;
   union {
     const char*leafstring; // when isleaf is true
     struct { // when isleaf is false
        const struct tree_st* left;
        const struct tree_st* right;
     };
   };
 };

그런 다음 시공 시간에 해시를 계산하고 평등을위한 노드를 비교할 때 먼저 해시를 평등과 비교합니다. 대부분의 경우 해시 코드는 다릅니다 (그리고 콘텐츠 비교를 귀찮게하지 않을 것입니다).

가능한 잎 건설 기능은 다음과 같습니다.

struct tree_st* make_leaf (const char*string) {
   assert (string != NULL);
   struct tree_st* t = malloc(sizeof(struct tree_st));
   if (!t) { perror("malloc"); exit(EXIT_FAILURE); };
   t->hash = hash_of_string(string);
   t->isleaf = true;
   t->leafstring = string;
   return t;
}

해시 코드를 계산하는 기능은 다음과 같습니다

unsigned tree_hash(const struct tree_st *t) {
  return (t==NULL)?0:t->hash;
}

두 하위 트리에서 노드를 구성하는 함수 sleft & sright ~이다

struct tree_st*make_node (const struct tree_st* sleft,
                          const struct tree_st* sright) {
   struct tree_st* t = malloc(sizeof(struct tree_st));
   if (!t) { perror("malloc"); exit(EXIT_FAILURE); };
   /// some hashing composition, e.g.
   unsigned h = (tree_hash(sleft)*313) ^ (tree_hash(sright)*617);
   t->hash = h;
   t->left = sleft;
   t->right = sright;
   return t;
 }

비교 함수 (두 나무의 tx & ty) 해시 코드가 다르면 비교가 다릅니다.

bool equal_tree (const struct tree_st* tx, const struct tree_st* ty) {
  if (tx==ty) return true;
  if (tree_hash(tx) != tree_hash(ty)) return false;
  if (!tx || !ty) return false;
  if (tx->isleaf != ty->isleaf) return false;
  if (tx->isleaf) return !strcmp(tx->leafstring, ty->leafstring);
  else return equal_tree(tx->left, ty->left) 
              && equal_tree(tx->right, ty->right); 

}

대부분의 경우 tree_hash(tx) != tree_hash(ty) 테스트가 성공하고 다시 반복 할 필요가 없습니다.

에 대해 읽다 해시 컨소싱.

일단 효율적인 해시 기반을 가지고 있다면 equal_tree 기능 다른 답변 (또는 핸드북)에 언급 된 기술을 사용할 수 있습니다.

일반적인 방법 중 하나는 트리에 대한 is_equal () 메소드를 작성하고 다음을 수행하는 것입니다.

bool contains_subtree(TNode*other) {
    // optimization
    if(nchildren < other->nchildren) return false;
    if(height < other->height) return false;

    // go for real check
    return is_equal(other) || (left != NULL && left->contains_subtree(other)) || (right != NULL && right->contains_subtree(other));
}

트리에 해시 코드를 사용하여 is_equal ()을 최적화 할 수 있습니다. 나무의 높이 또는 어린이 수 또는 값의 범위를 해시 코드로 사용하여 간단하게 수행 할 수 있습니다.

bool is_equal(TNode*other) {
    if(x != other->x) return false;
    if(height != other->height) return false;
    if(nchildren != other->nchildren) return false;
    if(hashcode() != other->hashcode()) return false;
    // do other checking for example check if the children are equal ..
}

트리가 링크 된 목록과 유사하면 O (n) 시간이 걸립니다. 우리는 또한 비교할 아이들을 선택하면서 휴리스틱을 사용할 수 있습니다.

bool contains_subtree(TNode*other) {
    // optimization
    if(nchildren < other->nchildren) return false;
    if(height < other->height) return false;

    // go for real check
    if(is_equal(other)) return true;
    if(left == NULL || right == NULL) {
          return (left != NULL && left->contains_subtree(other)) || (right != NULL && right->contains_subtree(other));
    }
    if(left->nchildren < right->nchildren) { // find in smaller child tree first
          return (left->contains_subtree(other)) || right->contains_subtree(other);
    } else {
          return (right->contains_subtree(other)) || left->contains_subtree(other);
    }
}

또 다른 방법은 두 트리를 문자열로 직렬화하고 두 번째 문자열 (T2에서 직렬화)이 첫 번째 문자열의 하위 스트링 (T1에서 직렬화)인지 찾는 것입니다.

다음 코드는 선주문에서 직렬화됩니다.

   void serialize(ostream&strm) {
            strm << x << '(';
            if(left)
                    left->serialize(strm);
            strm << ',';
            if(right)
                    right->serialize(strm);
            strm << ')';
    }

예를 들어 최적화 된 알고리즘을 사용할 수 있습니다. Knuth – Morris – Pratt 알고리즘 하위 스트링의 존재를 찾기 위해 (아마도 O (n) 시간에)를 찾으려면 결국 나무가 다른 사람의 하위 트리인지 확인하십시오.

다시 문자열은 Burrows (wheeler_transform)로 효율적으로 압축 될 수 있습니다. 그리고 가능합니다 bzgrep 압축 데이터에서 하위 스트링을 검색합니다.

또 다른 방법은 나무의 하위 나무를 키와 어린이의 수에 따라 분류하는 것입니다.

bool compare(TNode*other) {
    if(height != other->height)
        return height < other->height;

    return nchildren < other->nchildren;
}

O (n^2) 서브 트리가있을 것입니다. 숫자를 줄이기 위해 높이를 기준으로 일부 범위를 사용할 수 있습니다. 예를 들어, 우리는 높이 1000 ~ 1500의 하위 트리에만 관심을 가질 수 있습니다.

정렬 된 데이터가 생성되면 이진 검색을 수행하고 O (LG N) 시간에 서브 세트인지 확인할 수 있습니다 (정렬 된 데이터에 중복이 없음을 고려할 때).

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top