9. 데이터 구조의 이 진 트 리

데이터 구조의 이 진 트 리
나무의 프로필
그 전에 우 리 는 1 차원 선형 데이터 구조의 2 단 링크, 동적 배열 과 그들의 변형 구조 스 택 과 대기 열, 그리고 그들의 조합 구조 hash 표를 접 하여 여러 가지 검색 과 저장 문 제 를 해결 했다. 그러나 선형 데이터 구 조 는 사용 에 있어 아직도 많은 문제점 이 존재 했다. 예 를 들 어 1 차원 선형 데이터 구조의 검색 효율 은 O (n) 의 시간 복잡 도 에 만 달 할 수 있다.그래서 상기 문 제 를 해결 하기 위해 우 리 는 1 차원 을 바탕 으로 확장, 도입 과 나무 구 조 를 실시 했다.
나무의 분류
트 리: n (n > = 1) 개의 유한 노드 로 차원 관 계 를 가 진 집합 입 니 다.우리 의 일상생활 에서 도 나무 구조의 모델 이 많다. 예 를 들 어 가정의 족 보 는 나무 모양 의 구조 이 고 회사 의 전체 직원 차원 도 나무 모양 의 구조 (서로 다른 부서, 조직) 이다.그것 의 저장 은 일정한 규칙 을 가지 고 있 기 때문에 (크기 순서에 따라, 모듈 에 따라) 여러 가지 트 리 구 조 를 설명 한다.
B 트 리 이 진 트 리 사전 트 리
……
이 진 트 리 의 구조
그 중에서 가장 흔히 볼 수 있 는 나무 구 조 는 바로 이 진 트 리 이다. 이 진 트 리 는 왼쪽 아이 와 오른쪽 아이 두 개의 후계 노드 가 있 는데 이것 은 우리 가 두 개의 이 진 트 리 노드 지침 을 사용 해 야 한다.이 진 트 리 를 구축 할 때 재 귀적 인 프로 그래 밍 사상 을 대량으로 사용 할 것 이다. 그 다음 에 우 리 는 이 진 트 리 의 생 성, 소각, 옮 겨 다 니 기, 그리고 각종 이 진 트 리 의 성질 에 착안 하여 (높이, 노드 개수, 균형 여부 등 특성) 이 진 트 리 의 일부 응용 을 소개 해 야 한다.
이 진 트 리 의 실현
이 진 트 리 의 생 성에 대해 우 리 는 먼저 이 진 트 리 의 노드 구 조 를 확인 해 야 한다. 데 이 터 를 저장 하 는 데이터 도 메 인 을 제외 하고 좌우 아 이 를 가리 키 는 지침 도 있어 야 한다.
//       
typedef struct Tree_node Tree_node;

struct Tree_node{
    char              data;    //    
    Tree_node  *left_child;    
    Tree_node *right_child;
};                 

typedef Tree_node *Bin_tree;   //       

이 진 트 리 의 노드 를 설계 한 후에 우 리 는 이 진 트 리 의 인 터 페 이 스 를 열거 해 야 한다. 일부 인 터 페 이 스 는 필기시험 면접 의 중점 문제 이다.
//      
//     
Bin_tree create_tree(char **str);    //     
Bin_tree create_tree_by_pre_mid(char *pre_str, char *mid_str, int length);    //           
Bin_tree create_tree_by_mid_last(char *mid_str, char *last_str, int length);    //           


void destroy_tree(Bin_tree *root);    //     
void destroy_tree_nr(Bin_tree *root);   //        
void pre_order_print(Bin_tree root);    //      
void mid_order_print(Bin_tree root);    //      
void last_order_print(Bin_tree root);    //      

void pre_order_print_nr(Bin_tree root);    //       
void mid_order_print_nr(Bin_tree root);    //       
void last_order_print_nr(Bin_tree root);    //       

void level_order_print(Bin_tree root);    //    

Bin_tree copy_binary_tree(Bin_tree root);    //      
Boolean is_binarytree_equal(Bin_tree root1, Bin_tree root2);    //       

Boolean is_full_binary_tree(Bin_tree root);   //         
Boolean is_complete_binary_tree(Bin_tree root);   //          
Boolean is_balance_binary_tree(Bin_tree root);    //          

int get_binarytree_height(Bin_tree root);    //        
int get_binarytree_node_count(Bin_tree root);   //          
int get_binarytree_leaf_count(Bin_tree root);    //            
void swap_left_right(Bin_tree root);   //        
int get_binarytree_level_count(Bin_tree root, int level);    //           

Tree_node *find_value(Bin_tree root, char value);    //          
Tree_node *find_common_node(Tree_node *node1, Tree_node *node2 );   //             

Tree_node *find_parent(Bin_tree root, Tree_node *node);    //           

Boolean is_include_tree(Bin_tree root1, Bin_tree root2);    //       

이 진 트 리 의 인 터 페 이 스 를 소개 한 후에 우 리 는 각 인터페이스의 실현 에 대해 상세 하 게 소개 해 야 한다.
1. 이 진 트 리 생 성
이 진 트 리 의 생 성 방식 은 매우 다양 합 니 다. 오늘 우 리 는 문자열 의 방식 으로 이 진 트 리 를 만 드 는 것 을 소개 합 니 다. 일반 문 자 를 만 났 을 때 우 리 는 이 진 트 리 노드 를 직접 만 듭 니 다. '\#' 문 자 를 만 났 을 때 우 리 는 이 진 트 리 노드 가 존재 하지 않 는 다 고 생각 합 니 다. 그리고 이 진 트 리 의 노드 가 존재 한 후에 일반 문 자 를 만 나 왼쪽 아이 라 고 생각 합 니 다.왼쪽 아이 가 존재 할 때 일반 문 자 를 만나면 우 리 는 오른쪽 아이 라 고 생각 합 니 다. 다음은 예 를 들 어 보 겠 습 니 다.
예 를 들 어 지정 한 문자열 은: ABC\#\# DE\#\# F\#\# GH\# I\#\# J\#\#
다음 그림 과 같이 대응 하 는 이 진 트 리 입 니 다.
이 진 트 리 그림
다음은 이 진 트 리 의 생 성 과정 을 소개 합 니 다. 코드 는 다음 과 같 습 니 다.
//     
Bin_tree create_tree(char **str)    //     
{
    Bin_tree root =  NULL;

    //ABC##DE##F##GH#I##J##
    if(str != NULL && *str != NULL && **str != END){
        //                 ,            
        root = create_node();
        root->data = **str;
        ++*str;    //       
        root->left_child = create_tree(str);
        ++*str;
        root->right_child = create_tree(str);   
    }

    return root;
}

결과 문자열 을 옮 겨 다 니 며 이 진 트 리 를 만 듭 니 다.
상기 생 성 방식 을 제외 하고 예전 의 학습 과정 에서 우 리 는 접촉 한 적 이 있다. 이 진 트 리 의 옮 겨 다 니 는 방식 은 먼저 뿌리 를 옮 겨 다 니 고 중 근 서 를 옮 겨 다 니 며 후 근 서 를 옮 겨 다 니 는 세 가지 방식 을 포함한다.
만약 에 선착순 과 중 서 를 옮 겨 다 니 는 결 과 를 알려 주 었 거나 후 서 를 옮 겨 다 니 며 중 서 를 옮 겨 다 니 는 결 과 를 알려 주 었 다 면 우 리 는 이 진 트 리 의 전체적인 결 과 를 추측 할 수 있다. 이런 결론 은 정확 하 다. 중 서 를 옮 겨 다 니 는 결 과 는 각 노드 가 아이들 노드 의 순 서 를 확정 할 수 있 고 선착순 과 후 서 를 가 진 결 과 는 뿌리 결점 의 위 치 를 설명 할 수 있 기 때문이다.
다음은 옮 겨 다 니 는 결 과 를 통 해 이 진 트 리 를 만 드 는 방법 을 알려 드 리 겠 습 니 다.
//            
Bin_tree create_tree_by_pre_mid(char *pre_str,
                       char *mid_str, int length)
{
     //A B C D E F G H I J pre
     //C B E D F A H I G J mid
     Bin_tree root       =  NULL; 
     char     root_value =     0;    
     int      index      =    -1;

     //1.       value,pre    
     if(pre_str == NULL || mid_str == NULL || length <= 0){
         return root;
     }
     //2.  value      
     root_value = pre_str[0];
     root = create_node();
     root->data = root_value;
     //3.    value      ,              ,    
     //   
     index = find_index(mid_str, root_value);

     root->left_child = create_tree_by_pre_mid(pre_str + 1, mid_str, index);
     root->right_child = create_tree_by_pre_mid(pre_str + index + 1,
                                              mid_str + index + 1,
                                              length - index - 1);

     return root;
}

중간 순서 와 뒷 순 서 를 통 해 이 진 트 리 생 성:
//              
Bin_tree create_tree_by_ml(char *mstr, char *lstr, int length)
{
    Bin_tree root = NULL;
    int index = -1;
    int root_value = 0;

    if(mstr == NULL || lstr == NULL || length <= 0){ 
        return root;
    }

    root_value = lstr[length - 1];   //        
    root = buy_node(); 
    root->data = root_value;

    index = find_char_index(mstr, root_value); 
    root->left_child = create_tree_by_ml(mstr, lstr, index);
    root->right_child = create_tree_by_ml(mstr + index + 1,
                            lstr + index, length - index - 1);

    return root;
}

2. 이 진 트 리 소각
이 진 트 리 의 생 성 작업 을 완 성 했 습 니 다. 이 어 우 리 는 이 진 트 리 의 소각 을 소개 합 니 다. 많은 자료 에서 재 귀적 인 소각 방식 과 이 진 트 리 의 왼쪽 아 이 를 먼저 소각 한 다음 에 이 진 트 리 의 오른쪽 아 이 를 소각 하고 마지막 으로 현재 이 진 트 리 노드 를 소각 합 니 다.이런 방법 은 노드 주소 의 분실 을 방지 하기 위해 서 이다. 만약 에 현재 이 진 트 리 노드 를 먼저 없 애 면 아이의 주 소 를 좌우 로 잃 어 버 리 고 메모리 가 유출 되 기 때문에 재 귀 하 는 것 이 좋 은 해결 방안 이다.코드 예 시 는 다음 과 같다.
//        
//     
void destroy_tree(Bin_tree *root)
{
    //      ,    
    if(root == NULL || *root == NULL){
        return ;
    }

    destroy(*root);
    *root = NULL;
}

static void destroy(Bin_tree root)
{
    if(root != NULL){
        destroy(root->left_child);    //     
        destroy(root->right_child);    //     
        free(root);
    }
}

다시 한 번 문 제 를 생각 하 자 면 우리 가 재 귀적 인 방식 을 사용 하 는 것 은 이 진 트 리 좌우 아이의 주 소 를 기록 하기 위 한 것 일 뿐이다. 만약 에 대열 을 통 해 각 이 진 트 리 노드 의 좌우 아이의 주 소 를 기록 하면 교체 하 는 방식 으로 이 진 트 리 를 없 앨 수 있 기 때문에 층 차 를 옮 겨 다 니 는 사상 을 이용 하여 순환 작업 으로 이 진 트 리 를 없 앨 수 있다. 함수 의 재 귀적 호출 을 줄 이기 때문이다.그래서 효율 을 크게 높 일 수 있 습 니 다.
* 그 중에서 사 용 된 queue (대기 열) 데이터 구 조 는 이전의 선형 데이터 구조 에서 소개 되 었 다.
코드 예 시 는 다음 과 같다.
//             

void destroy_tree_nr(Bin_tree *root)   //        
{
    //  :
    // 
    //
    // A
    // / \
    // B C
    //
    // 1.   n     ,   n + 1           
    //
    Queue *queue = init_queue();
    Tree_node *p_node = NULL;

    if(root == NULL || *root == NULL){
        return ;    
    }

    p_node = *root;
    *root = NULL;
    in(queue, p_node);

    while(!is_queue_empty(queue)){
         get_queue_front(queue, (void **)&p_node);
         out(queue);

         //   p_node           ,   p_node
         if(p_node->left_child != NULL){
             in(queue, p_node->left_child);
         }

         if(p_node->right_child != NULL){
             in(queue, p_node->right_child);
         }

         free(p_node);
    }

    destroy_queue(&queue);
}

3. 이 진 트 리 의 재 귀 방식 (재 귀)
이 진 트 리 의 재 귀 방식 은 매우 간단 합 니 다. 우 리 는 먼저 뿌리 순 서 를 예 로 들 면 이 진 트 리 의 데이터 내용 을 인쇄 한 다음 에 이 진 트 리 의 왼쪽 아이 부분 을 인쇄 한 다음 에 이 진 트 리 의 오른쪽 아이 부분 (모두 인쇄 함수 자 체 를 호출 하고 들 어 오 는 매개 변수 만 다 릅 니 다) 을 인쇄 합 니 다. 코드 예 는 다음 과 같 습 니 다.
void pre_order_print(Bin_tree root)    //      
{
    if(root != NULL){
         printf("%c ", root->data);
         pre_order_print(root->left_child);
         pre_order_print(root->right_child);
    }
}

void mid_order_print(Bin_tree root)    //      
{
    if(root != NULL){
         mid_order_print(root->left_child);
         printf("%c ", root->data);
         mid_order_print(root->right_child);
    }
}

void last_order_print(Bin_tree root)    //      
{
    if(root != NULL){
         last_order_print(root->left_child);
         last_order_print(root->right_child);
         printf("%c ", root->data);
    }
}

재 귀적 으로 옮 겨 다 니 는 방식 을 제외 하고 우 리 는 이 진 트 리 를 재 귀적 으로 옮 겨 다 닐 수 있다. 이것 은 스 택 을 통 해 옮 겨 다 니 는 노드 정 보 를 기록 하여 역 추적 효 과 를 얻 을 수 있다.코드 예 시 는 다음 과 같다.
//           

void pre_order_print_nr(Bin_tree root)    //       
{
    Stack *stack = init_stack();    //            
    Tree_node *p_node = NULL;

    if(root == NULL){
        return ;
    }

    p_node = root;
    push(stack, p_node);

    while(!is_stack_empty(stack)){
        get_top(stack, (void **)&p_node);
        printf("%c ", p_node->data);
        pop(stack);

        if(p_node->right_child != NULL){
            push(stack, p_node->right_child);
        }

        if(p_node->left_child != NULL){
            push(stack, p_node->left_child);
        }
    }

    destroy_stack(&stack);
     //
    // A
    // / \
    // B G 
    // / \ / \
    // C D H J
    // / \ \
    // E F I
    //
    //     :
    //
    //    ,              、   
    //
    //
    //
    // 
    // D
    // G
    //
    //
    // A B C
}

void mid_order_print_nr(Bin_tree root)    //       
{
    Stack *stack = NULL;
    Tree_node *p_node = NULL;

    if(root == NULL){
        return ;
    }

    stack = init_stack();
    p_node = root;

    while(!is_stack_empty(stack) || p_node != NULL){
        while(p_node != NULL){  //              
            push(stack, p_node);
            p_node = p_node->left_child;
        }

        get_top(stack, (void **)&p_node);
        printf("%c ", p_node->data);
        pop(stack);

        p_node = p_node->right_child;
    }
    destroy_stack(&stack);

    // A
    // / \
    // B G 
    // / \ / \
    // C D H J
    // / \ \
    // E F I
    //
    // CBEDFAHIGJ
    //
    //   :
    //
    // 1.       
    // 2.        ,         
    // 3.                     
}

void last_order_print_non(Bin_tree root)    //    (   )
{
    Tree_node *p_node = NULL;
    Tree_node **temp = NULL; 
    Tree_node *prev = NULL;
    Stack *stack = init_stack();

    if(root == NULL){ 
        return ;
    }

    p_node = root;
    push(stack, p_node);

    while(!is_stack_empty(stack)){
        get_top(stack, (void **)&p_node);
        if((p_node->left_child == NULL && p_node->right_child == NULL)
        || (prev != NULL && (p_node->left_child == prev || p_node->right_child == prev))){
        //            ,              
        //    ,        ,              
            printf("%c ", p_node->data);
            pop(stack);
            prev = p_node;
        }else{    //          ,              
            if(p_node->right_child != NULL){
                push(stack, p_node->right_child);
            }
            if(p_node->left_child != NULL){
                push(stack, p_node->left_child);
            }
        }
    }
    printf("
"
); destroy_stack(&stack); }

이 진 트 리 의 층 차 를 옮 겨 다 니 기: 이 진 트 리 의 한 층 노드 를 옮 겨 다 니 며 모든 높이 를 옮 겨 다 닐 때 까지.관건 은 현재 노드 를 옮 겨 다 닌 후 반드시 좌우 아 이 를 순서대로 옮 겨 다 니 는 대기 열 에 추가 하 는 것 이다.코드 예 시 는 다음 과 같다.
void level_print(Bin_tree root)    //    
{
    Tree_node *p_node = NULL;
    // A B G C D H E F
    // 1.               ,          ,   
    //             ,      ,     
    if(root == NULL){
        return ;
    }

    p_node = root;
    Queue *queue = init_queue();

    in(queue, p_node);
    while(!is_queue_empty(queue)){
        get_queue_front(queue, (void **)&p_node);
        out(queue);
        //sleep(1);
        printf("%c ", p_node->data);
        //   p_node       
        if(p_node->left_child != NULL){
            in(queue, p_node->left_child);
        }
        if(p_node->right_child != NULL){
            in(queue, p_node->right_child);
        }
    }
    destroy_queue(&queue);
}

4. 이 진 트 리 의 관련 속성 (높이, 노드 개수, 잎 노드 트 리)
두 갈래 나무의 높이 얻 기:
int get_binarytree_height(Bin_tree root)   //    
{
    //1.      :
    //
    // max(     ,     ) + 1;
    if(root == NULL){
       return 0;
    }

    return 1 + max(get_binarytree_height(root->left_child),
                   get_binarytree_height(root->right_child));
}

이 진 트 리 의 노드 트 리 를 가 져 옵 니 다.
int get_binarytree_node_count(Bin_tree root)   //    
{
    //         = 1 +       +      
    if(root == NULL){
        return 0;
    }

    return 1 + get_binarytree_node_count(root->left_child)
             + get_binarytree_node_count(root->right_child);
}

이 진 트 리 잎 사 귀 노드 개 나무 얻 기
int get_binarytree_leaf_count(Bin_tree root)   //      
{
    //            
    if(root == NULL){
        return 0;
    }else if(root->left_child == NULL && root->right_child == NULL){
        //      ,  1
        return 1;
    }else{ 
        //                    
        return get_binarytree_leaf_count(root->left_child)
             + get_binarytree_leaf_count(root->right_child);
    }
}

5. 이 진 트 리 의 상태 판단 (동일 여부, 균형 여부, 완전, 만 이 진 트 리)
(1) 이 진 트 리 가 같은 지 판단
이 진 트 리 의 성질 에 따라 두 개의 이 진 트 리 가 같 으 면 현재 노드 데이터 내용 이 같 고 왼쪽 아이 와 오른쪽 아이의 데이터 내용 도 같 아야 하 며 이 진 트 리 의 구 조 는 같 아야 한다.코드 는 다음 과 같다.
Boolean is_binary_equal(Bin_tree root1, Bin_tree root2)    //         
{
    //     :
    //
    //1.        ,     
    //
    //2.
    // (1)         ;
    // (2)        ;
    // (3)       ;
    // (4)       。
    if(root1 == NULL && root2 == NULL){ 
        return TRUE;
    }else if((root1 != NULL && root2 != NULL)
          && (root1->data == root2->data)
          && is_binary_equal(root1->left_child, root2->left_child)
          && is_binary_equal(root1->right_child, root2->right_child)){
        return TRUE;
    }else{
        return FALSE;
    }
}

(2) 이 진 트 리 가 가득 찼 는 지 판단 하기
만 이 진 트 리 는 잎 노드 (좌우 아이 가 없 음) 를 제외 하고 다른 이 진 트 리 노드 는 반드시 좌우 아 이 를 동시에 가 져 야 한 다 는 것 을 의미한다.우 리 는 이 진 트 리 의 높이 h 와 이 진 트 리 의 노드 개 트 리 count 를 계산 할 수 있 습 니 다. 만약 에 이들 이 만족 하면 (2 ^ h - 1 = = count) 이 진 트 리 가 이 진 트 리 가 가득 하 다 는 것 을 설명 합 니 다.코드 예 시 는 다음 과 같다.
Boolean is_full_binary_tree(Bin_tree root)   //         
{

    //     height    count    
    //
    //
   //    2^height - 1 == count
    int height = 0;
    int count = 0;

    if(root == NULL){ 
        return FALSE;
    }

    height = get_binarytree_height(root);
    count = get_binarytree_node_count(root);

    return (powl(2, height) - 1) == count; 
}

(3) 이 진 트 리 가 완전 이 진 트 리 인지 판단
완전 이 진 트 리 는 만 이 진 트 리 와 헷 갈 리 기 쉬 운 개념 이 고 완전 이 진 트 리 는 비슷 한 만 이 진 트 리 이다. 그 는 특정한 데이터 구조 (더미) 로 전환 할 수 있다. 다음은 이 진 트 리 가 완전 이 진 트 리 라 는 것 을 어떻게 판단 하 는 지 소개 한다.코드 는 다음 과 같다.
Boolean is_complete_binary_tree(Bin_tree root)    //          
{
    //         ,                          ,  
    //               ,            
    Queue *queue = init_queue();
    Tree_node *p_node = NULL;
    Boolean find_last = FALSE;   //                        

    if(root == NULL){
        return FALSE;
    }

    in(queue, root);
    while(!is_queue_empty(queue)){
        get_queue_front(queue, (void **)&p_node);
        out(queue);
        if(find_last == FALSE){
            if(p_node->left_child != NULL && p_node->right_child != NULL){
                //           
                in(queue, p_node->left_child);
                in(queue, p_node->right_child);
            }else if(p_node->left_child != NULL && p_node->right_child == NULL){
                //          ,     ,             
                find_last = TRUE;
                in(queue, p_node->left_child);
            }else if(p_node->left_child == NULL && p_node->right_child != NULL){
                //          ,       
                return FALSE;
            }else{
                //             
                find_last = TRUE;
            }
        }else{
            //                 ,            
            if(p_node->left_child != NULL || p_node->right_child != NULL){
                return FALSE;
            }
        }
    }

    destroy_queue(&queue);
    return TRUE;
}

(4) 이 진 트 리 가 밸 런 스 이 진 트 리 인지 판단
균형 이 진 트 리 는 특수 한 요구 입 니 다. 즉, 이 진 트 리 의 좌우 아이들 의 높이 차 이 는 1 보다 크 면 안 됩 니 다. 일반적인 상황 에서 이런 엄격 한 요 구 는 이 진 트 리 를 찾 는 데 사 용 됩 니 다. 이 진 트 리 의 성질 은 다음 과 같 습 니 다. 요 소 를 찾 는 데 사 용 됩 니 다. 현재 노드 보다 작은 요 소 를 찾 으 면 현재 노드 의 왼쪽 트 리 부분 에서 찾 습 니 다. 현재 노드 보다 큰 요 소 를 찾 으 면현재 노드 의 오른쪽 하위 트 리 부분 에서 찾 습 니 다. 현재 노드 데이터 필드 의 요소 와 같 으 면 이미 찾 았 다 고 생각 합 니 다. 현재 노드 가 비어 있 으 면 전체 이 진 트 리 에서 찾 지 못 했다 고 생각 합 니 다.코드 예 시 는 다음 과 같다.
Boolean is_balance_binary_tree(Bin_tree root)   //          
{
    int height_left = 0;
    int height_right = 0;
    Boolean left = FALSE;
    Boolean right = FALSE;

    //          :
    //
    if(root == NULL){
        return TRUE;
    }

    height_left = get_binarytree_height(root->left_child);
    height_right = get_binarytree_height(root->right_child);

    left = is_balance_binary_tree(root->left_child);
    right = is_balance_binary_tree(root->right_child);

    //                 ,     
    //             1
    if(left == TRUE && right == TRUE 
    && abs(height_left - height_right) <= 1){
        return TRUE;
    }else{
        return FALSE;
    }
}

6. 이 진 트 리 기타 조작
(1) 이 진 트 리 의 미 러 를 얻 으 려 면 현재 이 진 트 리 노드 의 좌우 아이 주 소 를 교환 하고 좌우 아 이 를 재 귀적 으로 처리 해 야 합 니 다 (좌우 아 이 를 교환). 마지막 으로 원 이 진 트 리 를 미 러 대칭 적 으로 처리 해 야 합 니 다.코드 는 다음 과 같다.
 void swap_left_right(Bin_tree root)   //      
{
    if(root == NULL || (root->left_child == NULL 
                     && root->right_child == NULL)){
        return ;
    }

    //           ,             
    swap(&(root->left_child), &(root->right_child), sizeof(root->left_child));
    swap_left_right(root->left_child);
    swap_left_right(root->right_child);  
}

(2) 이 진 트 리 의 중간 값 이 value 인 노드 찾기
일반적인 이 진 트 리 라면, 우 리 는 나무 전 체 를 옮 겨 다 녀 야 한다.이것 은 이 진 트 리 의 노드 갯 수 에 의존 할 것 입 니 다. 현재 노드 의 데이터 값 이 조건 에 부합 되 는 지 먼저 판단 할 수 있 습 니 다. 만약 에 부합 되 지 않 으 면 좌우 서브 트 리 부분 을 선후 로 판단 할 수 있 습 니 다.코드 는 다음 과 같 습 니 다:
Tree_node *find_value(Bin_tree root, char value)    //      value   
{
    Tree_node *p_node = NULL;

    if(root == NULL || root->data == value){
        return root;
    }

    p_node = find_value(root->left_child, value);
    if(p_node == NULL){
        p_node = find_value(root->right_child, value);
    }

    return p_node;
}

(3) 지정 한 노드 의 부모 노드 찾기
Tree_node *find_parent(Bin_tree root, Tree_node *node)   //          
{
    Tree_node *p_node = NULL;

    if(root == NULL || node == NULL
     || root->left_child == node || root->right_child == node){
        //                      ,      
        return root;
    }

    //                     ,           ,
    //              。            ,   NULL
    p_node = find_parent(root->left_child, node);
    if(p_node == NULL){ 
        p_node = find_parent(root->right_child, node);
    }

    return p_node;
}

(4) 두 노드 가 포함 관 계 를 가 지 는 지 판단 하려 면 먼저 두 개의 이 진 트 리 에 포 함 된 첫 번 째 노드 (뿌리 노드) 의 관 계 를 판단 해 야 한다. 만약 에 tree 2 의 뿌리 노드 가 tree 1 과 존재 한다 면 tree 2 의 다른 부분 을 판단 할 수 있다. tree 2 의 모든 노드 가 tree 1 에 존재 할 때 우 리 는 tree 2 가 tree 1 에 포함 되 어 있다 고 생각한다. 코드 예 는 다음 과 같다.
Boolean has_sub_tree(Bin_tree root1, Bin_tree root2)   //       
{
    Boolean ok = FALSE;

    if(root1 != NULL && root2 != NULL){
        if(root1->data == root2->data){
            //            ,              
            ok = does_tree1_has_tree2(root1, root2);
        }

        if(ok == FALSE){
            ok = has_sub_tree(root1->left_child, root2);
        }

        if(ok == FALSE){ 
            ok =  has_sub_tree(root1->right_child, root2);
        }
    } 
    return ok;
}


(5) 두 개의 나무 노드 를 찾 은 최근 의 공공 조상
이 문제 에 대해 우 리 는 두 가지 상황 에 대해 특별한 처 리 를 해 야 한다. 1. 이 두 노드 자체 가 직접적인 계승 관 계 를 가 질 때 (즉, 하 나 는 다른 노드 의 부모) 우 리 는 상부 에 그들의 조상 을 계속 찾 아야 한다.
2. 만약 에 이들 사이 에 계승 관계 가 없다 면 이 두 노드 의 위 치 를 판단 해 야 한다.
코드 예 시 는 다음 과 같다.
Tree_node *find_common_node(Bin_tree root, Tree_node *node1,
                            Tree_node *node2 )   //             
{
    // A
    // / \
    // B G 
    // / \ / \
    // C D H J
    // / \ \
    // E F I
    //
    // 1.    node1 node2     ,              
    //
    // find_value
    // find_parent
    //
    int height1 = get_binarytree_height(node1);
    int height2 = get_binarytree_height(node2);

    //      
    if(height1 > height2){   
        if(find_value(node1, node2->data) != NULL){
            return find_parent(root, node1);         
        }
    }else if(height1 < height2){
        if(find_value(node2, node1->data) != NULL){
            return find_parent(root, node2);
        }
    }

    //       
    return find_common(root, node1, node2);
}

Tree_node *find_parent(Bin_tree root,
                       Tree_node *node)    //           
{
    Tree_node *p_node = NULL;

    if(root == NULL || root->left_child == node 
                    || root->right_child == node){
        //root     root         
        return root;
    }
    p_node = find_parent(root->left_child, node); 
    if(p_node == NULL){ 
        p_node = find_parent(root->right_child, node);
    }
    return p_node;
}

static Tree_node *find_common(Bin_tree root,
                              Tree_node *node1, Tree_node *node2){
    //       :
    //
    //   node1 node2   root      , root       ;
    //
    //     ( ),  root  ( )           ,    
    //
    // node1  node2       
    if(find_value(root->left_child, node1->data)){
        if(find_value(root->right_child, node2->data)){
            return root;
        }else{ 
            return find_common(root->left_child, node1, node2);
        }
    }else{
        if(find_value(root->left_child, node2->data)){ 
            return root;
        }else{ 
            return find_common(root->right_child, node1, node2);
        }
    }
}

상기 에서 우 리 는 이 진 트 리 에 관 한 많은 조작 을 한 다음 에 이런 조작 을 테스트 했다. 테스트 코드 는 다음 과 같다.
//         
#include <stdio.h>
#include "binary_tree.h"

int main(int argc, char **argv)
{
    Bin_tree root = NULL;
    Bin_tree root1 = NULL;
    Bin_tree root2 = NULL;
    Tree_node *find = NULL;
    char *str = "ABC##DE##F##GH#I##J##";
    char *pre  = "ABCDEFGHIJ";
    char *mid  = "CBEDFAHIGJ";
    char *last = "CEFDBIHJGA";
    Boolean equal = FALSE;


    root = create_tree(&str);    //      
    root1 = create_tree_by_pre_mid(pre, mid, strlen(pre));

    printf("pre order:
"
); pre_order_print(root); printf("
"
); printf("mid order:
"
); mid_order_print(root); printf("
"
); printf("last order:
"
); last_order_print(root); printf("
"
); printf("last order:
"
); last_order_print(root1); printf("
"
); printf("level order:
"
); level_order_print(root1); printf("
"
); printf("pre order:
"
); pre_order_print_nr(root); printf("
"
); printf("mid order:
"
); mid_order_print_nr(root); printf("
"
); root2 = copy_binary_tree(root1); // if((equal = is_binarytree_equal(root1, root2)) == TRUE){ printf("two trees are equal!
"
); } printf("mid order:
"
); mid_order_print_nr(root2); printf("
"
); printf("the height of root2:%d
"
, get_binarytree_height(root2)); printf("the count of root2:%d
"
, get_binarytree_node_count(root2)); printf("the leaves count of root2:%d
"
, get_binarytree_leaf_count(root2)); // swap_left_right(root1); printf("pre order:
"
); pre_order_print_nr(root1); printf("
"
); // if(is_full_binary_tree(root1) == TRUE){ printf("root1 is full!
"
); }else{ printf("root1 is not full!
"
); } // n printf("the %d level count is:%d
"
, 3, get_binarytree_level_count(root, 3)); if((find = find_value(root, 'H')) == NULL){ printf("H is not found!
"
); }else{ printf("%c is found!
"
, find->data); } Tree_node *find1 = find_value(root, 'B'); Tree_node *find2 = find_value(root, 'I'); // Tree_node *parent = find_common_node(root, find1, find2); if(parent != NULL){ printf("(%c and %c) parent is:%c
"
, find1->data, find2->data, parent->data); }else{ printf("not have baba!
"
); } destroy_tree_nr(&root); destroy_tree_nr(&root1); destroy_tree_nr(&root2); return 0; }

작은 매듭
이 진 트 리 는 매우 신기 한 데이터 구조 로 선형 데이터 구조의 검색 과 삽입 효율 의 병목 을 해결 하기 때문에 우 리 는 그의 구조 와 응용 을 깊이 이해 해 야 한다. 다음 장 에서 우 리 는 더 많은 이 진 트 리 유형 (이 진 트 리, 이 진 트 리 등) 과 다 진 트 리 (B 트 리) 를 소개 할 것 이다.기대 해 주세요.

좋은 웹페이지 즐겨찾기