13 분 소요



트리 - Trees(Part 2)


바이너리 서치 트리

바이너리 서치 트리를 BST라고 하자

BST는 다음 속성을 가진다.

  • 주어진 노드 N의 왼쪽 서브트리에 있는 모든 데이터 항목은 노드N의 데이터보다 작거나 같다.
  • 주어진 노드 N의 오른쪽 서브트리에 있는 모든 데이터 항목은 노드N의 데이터보다 크다.



bag class


BST로 구현된 백 클래스를 알아보자

template<class Item>
class bag
{
  public:
     
  private:
    binary_tree_node<Item>* root_ptr;
}


Member Funtions

BST로 구현된 백클래스의 맴버 함수를 알아보자

constructor destructor insert(entry)
erase(target) erase_one(target) += , =
size( ) count(target)  


Constructor

  • 루트 포인터를 NULL로 설정


Copy Constructor

  • 소스 트리의 복사본을 만들고 루트 포인터가 트리를 가리키도록함


Destructor

  • 트리의 모든 노드 해제


Size()

  • 트리의 노드 수를 계산
  • 1 + Size(왼쪽 서브트리) + Size(오른쪽 서브트리)


Assignment Operator

  • Self-assignment를 확인하고 그렇다면 리턴
  • 현재 트리에서 사용중인 모든 메로리를 헤제
  • RHS의 트리를 자신에게 복사



insert(entry)

  • 엔트리가 있는 새 노드를 만들고 new_ptr이 가리키도록 함
  • 만약 트리가 비어있으면 root_ptr을 new_ptr로 설정
  • 그렇지 않으면 적절한 리프노드를 찾을 때까지 바이너리 서치를 수행해 new_ptr을 왼쪽/오른쪽 하위필드의 값으로 만듬


insert(entry) : 바이너리 서치를 하는법

case1 : entry가 node_ptr->data()보다 작거나 같은 경우

  • if node_ptr->left( ) is NULL, node_ptr->set_left(new_ptr) and done

  • else node_ptr = node_ptr->left( ) ;


case2 : entry가 node_ptr->data()보다 큰 경우

  • if node_ptr->right( ) is NULL, node_ptr->set_right(new_ptr) and done

  • else node_ptr = node_ptr->right( ) ;



erase_one(target)


  • erase_one을 구현하기 위해 두개의 함수가 필요하다.
  • 아래 bst_remove()와 bst_remove_max()를 정의한다.


bst_remove(binary_tree_node*& root_ptr, const Item& target)

  • 트리에서 타겟이 있는 노드를 제거


bst_remove_max(binary_tree_node*& root_ptr, Item& removed)

  • 트리에서 가장 큰 데이터가 있는 노드를 제거



erase_one(target) : 구현


bst_remove(root_ptr, target)

  • if / target < root_ptr->data( )인 경우
  • bst_remove(root_ptr->left( ),target) 호출
  • else / target > root_ptr->data( )인경우
  • bst_remove(root_ptr->right( ),target) 호출
  • else / target == root_ptr->data( )인 경우에는 2가지 case발생


첫 번째 case : 루트에 왼쪽 자식이 없는경우

  • 루트를 삭제하고 올바른 자식을 새 루트로 만듬
  • oldroot_ptr = root_ptr ;
  • root_ptr = root_ptr->right( ) ;
  • delete oldroot_ptr ;


두 번째 case : 루트에 왼쪽 자식이 있는경우

  • 왼쪽 서브트리에서 가장 큰 데이터가 있는 노드를 제거하고
  • 가장 큰 데이터를 루트에 복사
  • bst_remove_max(root_ptr->left( ), root_ptr->data( ));


bst_remove_max(root_ptr, removed_Item)

첫 번째 case : 루트에 오른쪽 자식이 없는경우

  • 루트가 가장 큰 데이터를 가지고 있다.
  • removed_Item = root_ptr->data( ); // 루트 데이터는 최대임
  • oldroot = root_ptr ;
  • root_ptr = root_ptr->left( ) ; // 루트의 왼쪽 자식을 새 루트로 생성
  • delete oldroot ; //이전루트 삭제

두 번째 case : 루트에 오른쪽 자식이 있는경우

  • 가장 큰 데이터는 오른쪽 서브트리에 있음
  • bst_remove_max(root_ptr->right( ), removed_Item) ;



+=Operator

  • += 연산자를 구현하기 위해 insert_all(tree_ptr)을 정의한다.
  • insert_all(tree_ptr) 함수는 소스 트리(tree_ptr가 가리키는)의 각 데이터 항목을 트리에 삽입한다.


insert_all(tree_ptr)

template <class Item>
void bag<Item>::insert_all(const binary_tree_node<Item>* tree_ptr)
{
  if  ( tree_ptr != NULL )
  {
    insert(tree_ptr->data( )) ;
	  insert_all (tree_ptr->left( )) ;
 	  insert_all (tree_ptr->right( ) ) ;                                          
  }
}


따라서 += Operator는 다음과 같다.

template <class Item>
void bag<Item>::operator +=(const bag<Item>& addend) {
	if (this == &addend){
		binary_tree_node<item>* add_ptr = tree_copy(addend.root_ptr);
		insert_all(add_ptr);
		tree_clear(add_ptr);
  }
 	else
		insert_all(addend.root_ptr);
}




bag class 구현


1.bag.h // FILE: bag.h (part of the namespace main_savitch_10)

#ifndef BAG_H 
#define BAG_H
#include <cstdlib>     // Provides NULL and size_t
#include "bintree.h"   // Provides binary_tree_node and related functions

namespace main_savitch_10
{
    template <class Item>
    class bag
    {
    public:
        // TYPEDEFS
				typedef std::size_t size_type;
        typedef Item value_type;
        // CONSTRUCTORS and DESTRUCTOR
        bag( ) { root_ptr = NULL; }
        bag(const bag& source);
        ~bag( );
        // MODIFICATION functions
        size_type erase(const Item& target);
        bool erase_one(const Item& target);
        void insert(const Item& entry);
        void operator +=(const bag& addend);
        void operator =(const bag& source);	
        // CONSTANT functions
        size_type size( ) const;
        size_type count(const Item& target) const;
				void debug( ) const { print(root_ptr, 0); }
    private:
        binary_tree_node<Item> *root_ptr; // Root pointer of binary search tree
        void insert_all(binary_tree_node<Item>* addroot_ptr);
    };

    // NONMEMBER functions for the bag<Item> template class
    template <class Item>
    bag<Item> operator +(const bag<Item>& b1, const bag<Item>& b2);
}

#include "bag.template" // Include the implementation.
#endif


2.bag.template

// FILE: bag.template
// TEMPLATE CLASS IMPLEMENTED: bag<Item> (see bag.h for documentation)
// INVARIANT of the ADT:
//   root_ptr is the root pointer of a binary search tree that contains the
//   bag's items.

#include <cassert>
#include <cstdlib>

namespace main_savitch_10
{
  template <class Item>
	void bst_remove_max(binary_tree_node<Item>*& root_ptr, Item& removed)
	// Precondition: root_ptr is a root pointer of a non-empty binary 
	// search tree.
	// Postcondition: The largest item in the binary search tree has been
	// removed, and root_ptr now points to the root of the new (smaller) 
	// binary search tree. The reference parameter, removed, has been set 
	// to a copy of the removed item.
	{
	  binary_tree_node<Item> *oldroot_ptr;

	  assert(root_ptr != NULL);

	  if (root_ptr->right( ) != NULL)
			bst_remove_max(root_ptr->right( ), removed);
	  else
	  {
			removed = root_ptr->data( );
			oldroot_ptr = root_ptr;
			root_ptr = root_ptr->left( );
			delete oldroot_ptr;
	  }
	}

  template <class Item>
	bool bst_remove(binary_tree_node<Item>*& root_ptr, const Item& target)
	// Precondition: root_ptr is a root pointer of a binary search tree 
	// or may be NULL for an empty tree).
	// Postcondition: If target was in the tree, then one copy of target
	// has been removed, and root_ptr now points to the root of the new 
	// (smaller) binary search tree. In this case the function returns true.
	// If target was not in the tree, then the tree is unchanged (and the
	// function returns false).
	{
	  binary_tree_node<Item> *oldroot_ptr;
	    
	  if (root_ptr == NULL)
	  {   // Empty tree
			return false;
	  }

	  if (target < root_ptr->data( ))
	  {   // Continue looking in the left subtree
			return bst_remove(root_ptr->left( ), target);
	  }

	  if (target > root_ptr->data( ))
	  {   // Continue looking in the right subtree
			return bst_remove(root_ptr->right( ), target);
	  }
	    
	  if (root_ptr->left( ) == NULL)
	  {   // Target was found and there is no left subtree, so we can
				// remove this node, making the right child be the new root.
			oldroot_ptr = root_ptr;
			root_ptr = root_ptr->right( );
			delete oldroot_ptr;
			return true;
	  }
	    
	  // If code reaches this point, then we must remove the target from
	  // the current node. We'll actually replace this target with the
	  // maximum item in our left subtree.
	  bst_remove_max(root_ptr->left( ), root_ptr->data( ));
	  return true;
	}

  template <class Item>
	typename bag<Item>::size_type bst_remove_all
	(binary_tree_node<Item>*& root_ptr, const Item& target)
	// Precondition: root_ptr is a root pointer of a binary search tree 
	// or may be NULL for an empty tree).
	// Postcondition: All copies of target have been removed from the tree
	// has been removed, and root_ptr now points to the root of the new 
	// (smaller) binary search tree. The return value tells how many copies
	// of the target were removed.
	{
	    binary_tree_node<Item> *oldroot_ptr;
	    
	  if (root_ptr == NULL)
	  {   // Empty tree
			return 0;
	  }

   if (target < root_ptr->data( ))
   {   // Continue looking in the left subtree
			return bst_remove_all(root_ptr->left( ), target);
	  }

	  if (target > root_ptr->data( ))
	  {   // Continue looking in the right subtree
			return bst_remove_all(root_ptr->right( ), target);
	  }
	    
   if (root_ptr->left( ) == NULL)
   {   // Target was found and there is no left subtree, so we can
			// remove this node, making the right child be the new root.
			oldroot_ptr = root_ptr;
			root_ptr = root_ptr->right( );
			delete oldroot_ptr;
			return 1;
	  }
	    
	  // If code reaches this point, then we must remove the target from
	  // the current node. We'll actually replace this target with the
	  // maximum item in our left subtree. We then continue
	  // searching for more copies of the target to remove.
	  // This continued search must start at the current root (since
	  // the maximum element that we moved up from our left subtree
	  // might also be a copy of the target).
	  bst_remove_max(root_ptr->left( ), root_ptr->data( ));
	  return 1 + bst_remove_all(root_ptr, target);
	}

  template <class Item>
	bag<Item>::bag(const bag<Item>& source)
	// Library facilities used: bintree.h
	{
	    root_ptr = tree_copy(source.root_ptr);
	}

  template <class Item>
	bag<Item>::~bag( )
	// Header file used: bintree.h
	{
	  tree_clear(root_ptr);
	}

  template <class Item>
	typename bag<Item>::size_type bag<Item>::size( ) const
	// Header file used: bintree.h
	{
	  return tree_size(root_ptr);
	}

  template <class Item>
	void bag<Item>::insert(const Item& entry)
	// Header file used: bintree.h
	{
	  binary_tree_node<Item> *cursor = root_ptr;
	  bool done = false;
	
	  if (root_ptr == NULL)
	  {
			root_ptr = new binary_tree_node<Item>(entry);
			return;
    }
	
    do
    {
		if (cursor->data( ) >= entry)
		{   // Go left
	    if (cursor->left( ) == NULL)
	    {
				cursor->set_left( new binary_tree_node<Item>(entry) );
				done = true;
		  }
		  else
				cursor = cursor->left( );
		}
		else
		{   // Go right
		  if (cursor->right( ) == NULL)
		  {
				cursor->set_right( new binary_tree_node<Item>(entry) );
				done = true;
		  }
		  else
				cursor = cursor->right( );
		}
	  }   while (!done);
	}

  template <class Item>
	typename bag<Item>::size_type bag<Item>::count(const Item& target) const
	{
	  size_type answer = 0;
	  binary_tree_node<Item> *cursor = root_ptr;

	  while (cursor != NULL)
	  {
			if (cursor->data( ) < target)
	  		cursor = cursor->right( );
			else
			{
	  		if (cursor->data( ) == target) answer++;
	  		cursor = cursor->left( );
			}
	  }
	  return answer;
	}

  template <class Item>
	typename bag<Item>::size_type bag<Item>::erase(const Item& target)
	{
	  return bst_remove_all(root_ptr, target);
	}

  template <class Item>
	bool bag<Item>::erase_one(const Item& target)
	{
	  return bst_remove(root_ptr, target);
	}

  template <class Item>
	void bag<Item>::operator =(const bag<Item>& source)
    // Header file used: bintree.h
    {
	    if (root_ptr == source.root_ptr)
	      return;
	    tree_clear(root_ptr);
	    root_ptr = tree_copy(source.root_ptr);
		}

  template <class Item>
	void bag<Item>::operator +=(const bag<Item>& addend)
    {
	    if (root_ptr == addend.root_ptr)
	    {
			bag<Item> copy = addend;
			insert_all(copy.root_ptr);
	    }
	    else
			insert_all(addend.root_ptr);
	}

  template <class Item>
	bag<Item> operator +(const bag<Item>& b1, const bag<Item>& b2)
	{
	  bag<Item> answer = b1;
	  answer += b2;
	  return answer;
	}

  template <class Item>
	void bag<Item>::insert_all(binary_tree_node<Item>* addroot_ptr)
    // Precondition: addroot_ptr is the root pointer of a binary search tree that
    // is separate for the binary search tree of the bag that activated this
    // method.
    // Postcondition: All the items from the addroot_ptr's binary search tree
    // have been added to the binary search tree of the bag that activated this
    // method.
	{
		if (addroot_ptr != NULL)
	  {
		insert(addroot_ptr->data( ));
		insert_all(addroot_ptr->left( ));
		insert_all(addroot_ptr->right( ));
	  }
	}
}


3.bintree.h

#ifndef BINTREE_H
#define BINTREE_H
#include <cstdlib>  // Provides NULL and size_t

namespace main_savitch_10
{

  template <class Item>
  class binary_tree_node
  {
  public:
	// TYPEDEF
	typedef Item value_type;
	// CONSTRUCTOR
	binary_tree_node(
    const Item& init_data = Item( ),
    binary_tree_node* init_left = NULL,
    binary_tree_node* init_right = NULL
	)
	{ 
    data_field = init_data; 
    left_field = init_left; 
    right_field = init_right;
	}
	// MODIFICATION MEMBER FUNCTIONS
	Item& data( ) { return data_field; }
	binary_tree_node*& left( ) { return left_field; }
	binary_tree_node*& right( ) { return right_field; }
	void set_data(const Item& new_data) { data_field = new_data; }
	void set_left(binary_tree_node* new_left) { left_field = new_left; }
	void set_right(binary_tree_node* new_right) { right_field = new_right; }
	// CONST MEMBER FUNCTIONS
	const Item& data( ) const { return data_field; }
	const binary_tree_node* left( ) const { return left_field; }
	const binary_tree_node* right( ) const { return right_field; }
	bool is_leaf( ) const 
	    { return (left_field == NULL) && (right_field == NULL); }
  private:
	Item data_field;
	binary_tree_node *left_field;
	binary_tree_node *right_field;
  };

    // NON-MEMBER FUNCTIONS for the binary_tree_node<Item>:
    template <class Process, class BTNode>
    void inorder(Process f, BTNode* node_ptr); 

    template <class Process, class BTNode>
    void preorder(Process f, BTNode* node_ptr);

    template <class Process, class BTNode>
    void postorder(Process f, BTNode* node_ptr); 

    template <class Item, class SizeType>
    void print(binary_tree_node<Item>* node_ptr, SizeType depth);

    template <class Item>
    void tree_clear(binary_tree_node<Item>*& root_ptr);

    template <class Item>
    binary_tree_node<Item>* tree_copy(const binary_tree_node<Item>* root_ptr);

    template <class Item>
    std::size_t tree_size(const binary_tree_node<Item>* node_ptr);
}

#include "bintree.template"
#endif


4.bintree.template

// FILE: bintree.template
// IMPLEMENTS: The binary_tree node class (see bintree.h for documentation). 
#include <cassert>    // Provides assert
#include <cstdlib>   // Provides NULL, std::size_t
#include <iomanip>    // Provides std::setw
#include <iostream>   // Provides std::cout

namespace main_savitch_10
{
    template <class Process, class BTNode>
    void inorder(Process f, BTNode* node_ptr)
    // Library facilities used: cstdlib
    {
        if (node_ptr != NULL)
        {
            inorder(f, node_ptr->left( ));
            f( node_ptr->data( ) );
            inorder(f, node_ptr->right( ));
        }
    }

    template <class Process, class BTNode>
    void postorder(Process f, BTNode* node_ptr)
    // Library facilities used: cstdlib
    {
        if (node_ptr != NULL)
        {
            postorder(f, node_ptr->left( ));
            postorder(f, node_ptr->right( ));
            f(node_ptr->data( ));
        }
    }

    template <class Process, class BTNode>
    void preorder(Process f, BTNode* node_ptr)
    // Library facilities used: cstdlib
    {
        if (node_ptr != NULL)
        {
            f( node_ptr->data( ) );
            preorder(f, node_ptr->left( ));
            preorder(f, node_ptr->right( ));
        }
    }

    template <class Item, class SizeType>
    void print(binary_tree_node<Item>* node_ptr, SizeType depth)
    // Library facilities used: iomanip, iostream, stdlib
    {
        if (node_ptr != NULL)
        {
            print(node_ptr->right( ), depth+1);
            std::cout << std::setw(4*depth) << ""; // Indent 4*depth spaces.
            std::cout << node_ptr->data( ) << std::endl;
            print(node_ptr->left( ),  depth+1);
        }
    }    
	
    template <class Item>
    void tree_clear(binary_tree_node<Item>*& root_ptr)
    // Library facilities used: cstdlib
    {
	binary_tree_node<Item>* child;
	if (root_ptr != NULL)
	{
	    child = root_ptr->left( );
	    tree_clear( child );
	    child = root_ptr->right( );
	    tree_clear( child );
	    delete root_ptr;
	    root_ptr = NULL;
	}
    }

    template <class Item>
    binary_tree_node<Item>* tree_copy(const binary_tree_node<Item>* root_ptr)
    // Library facilities used: cstdlib
    {
	binary_tree_node<Item> *l_ptr;
	binary_tree_node<Item> *r_ptr;

	if (root_ptr == NULL)
	    return NULL;
	else
	{
	    l_ptr = tree_copy( root_ptr->left( ) );
	    r_ptr = tree_copy( root_ptr->right( ) );
	    return
		new binary_tree_node<Item>( root_ptr->data( ), l_ptr, r_ptr);
	}
    }

    template <class Item>
    size_t tree_size(const binary_tree_node<Item>* node_ptr)
    // Library facilities used: cstdlib
    {
        if (node_ptr == NULL)
            return 0;
        else 
            return 
	    1 + tree_size(node_ptr->left( )) + tree_size(node_ptr->right( ));
    }    
}


5.bagtest.cpp

// FILE: bagtest.cxx
// Written by Michael Main (main@colorado.edu) - Nov 8, 2000
// An interactive test program for the new Bag class, implemented with
// a binary search tree.

#include <cctype>     // Provides toupper
#include <iostream>   // Provides cout, cin
#include <cstdlib>    // Provides EXIT_SUCCESS, size_t
#include "bag.h"     // Provides the bag<double> class (with Item as a double)
using namespace std;
using namespace main_savitch_10;


// PROTOTYPES for the functions used in this test program.
void print_menu( );
// Postcondition: A menu of choices for this program has been written to cout.

char get_user_command( );
// Postcondition: The user has been prompted to enter a one character command.
// A line of input (with at least one character) has been read, and the first
// character of the input line is returned.

void display_bags(const bag<double>& b1, const bag<double>& b2);
// Postcondition: The function has tested whether the numbers 0..9 are in
// the two bags, and printed the results to standard output.

bag<double> copybag(bag<double> b);
// Postcondition: The return value is a copy of b.

double get_number( );
// Postcondition: The user has been prompted to enter a double number. The
// number has been read, echoed to the screen, and returned by the function.

int main( )
{
    bag<double> b1, b2;  // Bags that we'll perform tests on
    char choice; // A command character entered by the user

    cout << "I have initialized two empty bags of doubles." << endl;

    do
    {
        print_menu( );
        choice = get_user_command( );
        switch (choice)
        {
	    case 'A': b1 = b2;
		      break;
	    case 'a': b2 = b1;
		      break;
            case 'C': b1 = copybag(b2);
                      break;
            case 'c': b2 = copybag(b1);
                      break;
            case 'S':
            case 's': cout << "The bags' sizes are " << b1.size( );
                      cout << " and " << b2.size( ) << endl;
                      break;
            case 'I': b1.insert(get_number( ));
                      break;
            case 'i': b2.insert(get_number( ));
                      break;
            case 'O': b1.erase_one(get_number( ));
                      break;
            case 'o': b2.erase_one(get_number( ));
                      break;
            case 'E': b1.erase(get_number( ));
                      break;
            case 'e': b2.erase(get_number( ));
                      break;
            case 'D':
            case 'd': display_bags(b1, b2);
                      break;
	    case 'q':
	    case 'Q': cout << "Ridicule is the best test of truth." << endl;
                      break;
            default: cout << choice << " is invalid. Sorry." << endl;
        }
    }
    while ((toupper(choice) != 'Q'));

    return EXIT_SUCCESS;

}

void print_menu( )
// Library facilties used: iostream.h
{
    cout << "The following choices are available with 2 bags: " << endl;
    cout << " A  Use the assignment operator to make b1 equal to b2" << endl;
    cout << " a  Use the assignment operator to make b2 equal to b1" << endl;
    cout << " C  Use the copy constructor to make b1 equal to b2" << endl;
    cout << " c  Use the copy constructor to make b2 equal to b1" << endl;
    cout << " I  Insert an item into b1" << endl;
    cout << " i  Insert an item into b2" << endl;
    cout << " E  Erase item from b1" << endl;
    cout << " e  Erase item from b2" << endl;
    cout << " O  Erase one item from b1" << endl;
    cout << " o  Erase one item from b2" << endl;
    cout << " D  Display a count of items 0-9 in b1 and b2" << endl;
    cout << " S  Print the result from the size( ) functions" << endl;
    cout << " Q  Quit this test program" << endl;
}

char get_user_command( )
// Library facilties used: iostream.h
{
    char command;

    cout << "Enter choice: ";
    cin >> command; 
   
    return command;
}

void display_bags(const bag<double>& b1, const bag<double>& b2)
// Library facilties used: iostream.h
{
    int i;

    for (i = 0; i < 10; i++)
    {
        cout << i << " in b1? " << b1.count(i);
        cout << "            " << i << " in b2? " << b2.count(i) << endl;
    }
}

bag<double> copybag(bag<double> b)
{
    return b;
}

double get_number( )
// Library facilties used: iostream.h
{
    int result;

    cout << "Please enter a double number for the bag: ";
    cin  >> result;
    cout << result << " has been read." << endl;
    return result;
}

댓글남기기