[데이터 구조] Binarytree | Binary_search_tree | avl_tree
//Record.h
#include
using namespace std;
class Record {
public:
Record(int x = 0, int y = 0);
int the_key() const;
private:
int key;
int other;
};
bool operator == (const Record &x, const Record &y);
bool operator > (const Record &x, const Record &y);
bool operator < (const Record &x, const Record &y);
ostream & operator << (ostream &output, Record &x);
이 Record 는 나무의 내용 을 포장 하여 하나의 유형 으로 삼 아 나중에 나무의 내용 을 수정 하 는 데 편리 하 다.
//Record.cpp
#include "Record.h"
Record::Record(int x, int y) {
key = x;
other = y;
}
int Record::the_key() const {
return key;
}
bool operator == (const Record &x, const Record &y)
{
return x.the_key() == y.the_key();
}
bool operator > (const Record &x, const Record &y)
{
return x.the_key() > y.the_key();
}
bool operator < (const Record &x, const Record &y)
{
return x.the_key() < y.the_key();
}
ostream & operator << (ostream &output, Record &x)
{
output << x.the_key();
output << " ";
return output;
}
//Binary_tree.h
enum Error_code { success, not_present, duplicate_error };
enum Balance_factor { left_higher, equal_height, right_higher };
#include"Record.h"
template
struct Binary_node {
Entry data;
Binary_node *left_child;
Binary_node *right_child;
Binary_node();
Binary_node(const Entry &x);
//Inherit used:
virtual void set_balance(Balance_factor b); // ,
virtual Balance_factor get_balance() const; // avl_node
// left_child right_child avl_tree
};
template
inline Binary_node::Binary_node()
{
left_child = nullptr;
right_child = nullptr;
}
template
inline Binary_node::Binary_node(const Entry & x)
{
data = x;
left_child = nullptr;
right_child = nullptr;
}
template
inline void Binary_node::set_balance(Balance_factor b)
{
}
template
inline Balance_factor Binary_node::get_balance() const
{
return equal_height;
}
template
class Binary_tree
{
public:
//Basic function:
Binary_tree();
bool empty();
void preorder(void(*visit)(Entry &));
void inorder(void(*visit)(Entry &));
void postorder(void(*visit)(Entry &));
int size() ;
void clear();
int height();
void insert(const Entry &item);
Binary_node *Get_root();
//Safety functions:
Binary_tree(const Binary_tree &original);
Binary_tree &operator=(const Binary_tree &original);
~Binary_tree();
protected:
Binary_node *root;
int count;
void recursive_preorder(Binary_node *sub_root, void(*visit)(Entry &));
void recursive_inorder(Binary_node *sub_root, void(*visit)(Entry &));
void recursive_postorder(Binary_node *sub_root,void (*visit)(Entry &));
int recursive_height(Binary_node *sub_root);
int recursive_size(Binary_node *sub_root);
void recursive_clear(Binary_node *sub_root);
void recursive_insert(Binary_node *sub_root,Binary_node *node);
Binary_node* creat(Binary_node *sub_root);
};
//Binary_tree.cpp
#include"Binary_tree.h"
#include
#include
using namespace std;
template
Binary_tree::Binary_tree()
{
root = NULL;
count = 0;
}
template
void Binary_tree::recursive_preorder(Binary_node* sub_root, void(*visit)(Entry &))
{
if (sub_root) {
(*visit)(sub_root->data);
recursive_preorder(sub_root->left_child, visit);
recursive_preorder(sub_root->right_child, visit);
}
}
template
void Binary_tree::recursive_inorder(Binary_node* sub_root, void(*visit)(Entry &))
{
if (sub_root) {
recursive_inorder(sub_root->left_child,visit);
(*visit)(sub_root->data);
recursive_inorder(sub_root->right_child, visit);
}
}
template
void Binary_tree::recursive_postorder(Binary_node* sub_root, void(*visit)(Entry &))
{
if (sub_root) {
recursive_postorder(sub_root->left_child, visit);
recursive_postorder(sub_root->right_child, visit);
(*visit)(sub_root->data);
}
}
template
int Binary_tree::recursive_height(Binary_node* sub_root)
{
if (!sub_root)return 0;
int left = recursive_height(sub_root->left_child);
int right = recursive_height(sub_root->right_child);
if (left > right) return (left + 1);
else return (right + 1);
}
template
int Binary_tree::recursive_size(Binary_node* sub_root)
{
if (!sub_root)return 0;
int left = recursive_size(sub_root->left_child);
int right = recursive_size(sub_root->right_child);
return (left + right + 1);
}
template
void Binary_tree::recursive_clear(Binary_node* sub_root)
{
if (sub_root) {
recursive_clear(sub_root->left_child);
recursive_clear(sub_root->right_child);
delete sub_root;
}
}
template
void Binary_tree::recursive_insert(Binary_node *sub_root,Binary_node *node)
{
if (node->data >= sub_root->data) {
if (sub_root->right_child == NULL)
sub_root->right_child = new Binary_node(node->data);
else recursive_insert(sub_root->right_child, node);
}
else {
if (sub_root->left_child == NULL)
sub_root->left_child = new Binary_node(node->data);
else recursive_insert(sub_root->left_child, node);
}
}
template
Binary_node * Binary_tree::creat(Binary_node* sub_root)
{
if (!sub_root)return NULL;
Binary_node *tmp_root = new Binary_node(sub_root->data);
tmp_root->left_child = creat(sub_root->left_child);
tmp_root->right_child = creat(sub_root->right_child);
return tmp_root;
}
template
bool Binary_tree::empty()
{
return (root==NULL);
}
template
void Binary_tree::preorder(void(*visit)(Entry &))
{
recursive_preorder(root, visit);
}
template
void Binary_tree::inorder(void(*visit)(Entry &))
{
recursive_inorder(root, visit);
}
template
void Binary_tree::postorder(void(*visit)(Entry &))
{
recursive_postorder(root, visit);
}
template
int Binary_tree::size()
{
return recursive_size(root);
}
template
void Binary_tree::clear()
{
recursive_clear(root);
root = nullptr;
count = 0;
}
template
int Binary_tree::height()
{
return recursive_height(root);
}
template
void Binary_tree::insert(const Entry & item)
{
if (empty()) {
root = new Binary_node(item);
count++;
return;
}
stacknumber;
int tmpcount = size();
while (tmpcount > 0) {
if (tmpcount % 2 == 0)number.push(2);
else number.push(1);
tmpcount = (tmpcount - 1) / 2;
}
int direction = 0;
Binary_node *p = root;
while (number.size() > 1) {
direction = number.top();
if (direction == 2)p = p->right_child;
if (direction == 1)p = p->left_child;
number.pop();
}
direction = number.top();
if (direction == 2)p->right_child=new Binary_node(item);
if (direction == 1)p->left_child = new Binary_node(item);
count++;
}
template
Binary_node* Binary_tree::Get_root()
{
return root;
}
template
Binary_tree::Binary_tree(const Binary_tree& original)
{
Binary_node * original_root = original.Get_root();
root = creat(original_root);
}
template
Binary_tree & Binary_tree::operator=(const Binary_tree& original)
{
Binary_node * original_root = original.Get_root();
if (root == original_root)return;
clear();
Binary_tree(original);
}
template
Binary_tree::~Binary_tree()
{
clear();
}
//Binary_search_tree.h
#include"Binary_tree.cpp"
template
class Binary_search_tree:public Binary_tree
{
public:
Binary_search_tree();
Error_code insert(const Record &item);
Error_code remove(const Record &item);
Error_code search(Record &item);
private:
Error_code recursive_insert(Binary_node *&sub_root, const Record &item);
Binary_node * recursive_search(Binary_node *&sub_root, const Record &item);
Error_code search_and_destroy(Binary_node *&sub_root, const Record &item);
Error_code destroy(Binary_node *&sub_root);
};
//Binary_search_tree.cpp
#include"Binary_search_tree.h"
#include
using namespace std;
template
Binary_search_tree::Binary_search_tree()
{
count = 0;
root = NULL;
}
template
Error_code Binary_search_tree::insert(const Record & item)
{
if (recursive_insert(root, item) == success) {
count++;
return success;
}
else return duplicate_error;
}
template
Error_code Binary_search_tree::remove(const Record & item)
{
if (search_and_destroy(root, item) == success) {
count--;
return success;
}
else return not_present;
}
template
Error_code Binary_search_tree::search(Record & item)
{
Binary_node *p = recursive_search(root, item);
if (p == NULL)return not_present;
else {
item = p->data;
return success;
}
}
template
Error_code Binary_search_tree::recursive_insert(Binary_node*& sub_root, const Record & item)
{
if (!sub_root) {
sub_root = new Binary_node(item);
return success;
}
else if (sub_root->data > item)return recursive_insert(sub_root->left_child, item);
else if (sub_root->data < item)return recursive_insert(sub_root->right_child, item);
else return duplicate_error;
}
template
Binary_node * Binary_search_tree::recursive_search(Binary_node*& sub_root, const Record & item)
{
if (sub_root == NULL || sub_root->data == item)return sub_root;
else if (sub_root->data > item)return recursive_insert(sub_root->left_child, item);
else if (sub_root->data < item)return recursive_insert(sub_root->right_child, item);
}
template
Error_code Binary_search_tree::search_and_destroy(Binary_node*& sub_root, const Record & item)
{
if (sub_root == NULL || sub_root->data == item)
return destroy(sub_root);
else if (sub_root->data > item)return destory(sub_root->left_child);
else if (sub_root->data < item)return destory(sub_root->right_child);
}
template
Error_code Binary_search_tree::destroy(Binary_node*& sub_root)
{
if (sub_root == NULL)return not_present;
Binary_node *to_delete = sub_root;
if (sub_root->left_child == NULL) sub_root = sub_root->right_child;
else if (sub_root->right_child == NULL) sub_root = sub_root->left_child;
else {
to_delete = sub_root->left_child;
Binary_node *parent = sub_root;
while (!to_delete->right_child) {
parent = to_delete;
to_delete = to_delete->right_child;
}
sub_root->data = to_delete->data;
if (parent == sub_root)sub_root->left_child = to_delete->left_child; //Special condition:no right_child under to_delete
else parent->right_child = to_delete->right_child;
}
delete to_delete;
return success;
}
//AVL_tree.h
#include"Binary_search_tree.cpp"
template
struct AVL_node :public Binary_node
{
Balance_factor balance;
AVL_node();
AVL_node(const Record &x);
void set_balance(Balance_factor b);
Balance_factor get_balance()const;
};
template
inline AVL_node::AVL_node()
{
balance = equal_height;
}
template
inline AVL_node::AVL_node(const Record & x)
{
data = x;
balance = equal_height;
}
template
inline void AVL_node::set_balance(Balance_factor b)
{
balance = b;
}
template
inline Balance_factor AVL_node::get_balance() const
{
return balance;
}
template
class AVL_tree:public Binary_search_tree
{
public:
Error_code insert(const Record &new_data);
Error_code remove(Record &old_data);
private:
Error_code avl_insert(Binary_node * &sub_root, const Record &new_data, bool &taller);
void rotate_left(Binary_node * &sub_root);
void rotate_right(Binary_node * &sub_root);
void right_balance(Binary_node * &sub_root);
void left_balance(Binary_node * &sub_root);
//add for remove
Error_code avl_remove(Binary_node * &sub_root, Record &new_data, bool &shorter);
bool right_balance2(Binary_node * &sub_root);
bool left_balance2(Binary_node * &sub_root);
};
//AVL_tree.cpp
#include "AVL_Tree.h"
template
Error_code AVL_tree::insert(const Record & new_data)
{
bool taller;
return avl_insert(root, new_data, taller);
}
template
Error_code AVL_tree::remove(Record & old_data)
{
bool shorter = true;
return avl_remove(root, old_data, shorter);
}
template
Error_code AVL_tree::avl_insert(Binary_node*& sub_root, const Record & new_data, bool & taller)
{
if (sub_root == NULL) {
sub_root = new AVL_node(new_data);
taller = true;
return success;
}
else if (sub_root->data == new_data) {
taller = false;
return duplicate_error;
}
else if (sub_root->data > new_data) {
Error_code result = avl_insert(sub_root->left_child, new_data, taller);
if (taller == true) {
switch (sub_root->get_balance())
{
case left_higher:
left_balance(sub_root);
taller = false;
break;
case equal_height:
sub_root->set_balance(left_higher);
break;
case right_higher:
sub_root->set_balance(equal_height);
taller = false;
break;
}
}
return result;
}
else {
Error_code result = avl_insert(sub_root->right_child, new_data, taller);
if (taller == true) {
switch (sub_root->get_balance())
{
case left_higher:
sub_root->set_balance(equal_height);
taller = false;
break;
case equal_height:
sub_root->set_balance(right_higher);
break;
case right_higher:
right_balance(sub_root);
taller = false;
break;
}
}
return result;
}
}
template
void AVL_tree::rotate_left(Binary_node*& sub_root)
{
if (sub_root == nullptr || sub_root->right_child == nullptr)
cout << "WARNING:program error detected in rotate left" << endl;
else {
Binary_node *right_tree = sub_root->right_child;
sub_root->right_child = right_tree->left_child;
right_tree->left_child = sub_root;
sub_root = right_tree;
}
}
template
void AVL_tree::rotate_right(Binary_node*& sub_root)
{
if (sub_root == nullptr || sub_root->left_child == nullptr)
cout << "WARNING:program error detected in rotate right" << endl;
else {
Binary_node *left_tree = sub_root->left_child;
sub_root->left_child = left_tree->right_child;
left_tree->right_child = sub_root;
sub_root = left_tree;
}
}
template
void AVL_tree::right_balance(Binary_node*& sub_root)
{
Binary_node *&right_tree = sub_root->right_child;
switch (right_tree->get_balance())
{
case right_higher:
sub_root->set_balance(equal_height);
right_tree->set_balance(equal_height);
rotate_left(sub_root);
break;
case equal_height:
cout << "WARNING:program error detected in right balance" << endl;
break;
case left_higher:
Binary_node *sub_tree = right_tree->left_child;
switch (sub_tree->get_balance())
{
case equal_height:
sub_root->set_balance(equal_height);
right_tree->set_balance(equal_height);
break;
case left_higher:
sub_root->set_balance(equal_height);
right_tree->set_balance(right_higher);
break;
case right_higher:
sub_root->set_balance(left_higher);
right_tree->set_balance(equal_height);
break;
}
sub_tree->set_balance(equal_height);
rotate_right(right_tree);
rotate_left(sub_root);
break;
}
}
template
void AVL_tree::left_balance(Binary_node*& sub_root)
{
Binary_node *&left_tree = sub_root->left_child;
switch (left_tree->get_balance())
{
case left_higher:
sub_root->set_balance(equal_height);
left_tree->set_balance(equal_height);
rotate_right(sub_root);
break;
case equal_height:
cout << "WARNING:program error detected in left balance" << endl;
break;
case right_higher:
Binary_node *sub_tree = left_tree->right_child;
switch (sub_tree->get_balance())
{
case equal_height:
sub_root->set_balance(equal_height);
left_tree->set_balance(equal_height);
break;
case right_higher:
sub_root->set_balance(equal_height);
left_tree->set_balance(left_higher);
break;
case left_higher:
sub_root->set_balance(right_higher);
left_tree->set_balance(equal_height);
break;
}
sub_tree->set_balance(equal_height);
rotate_left(left_tree);
rotate_right(sub_root);
break;
}
}
template
Error_code AVL_tree::avl_remove(Binary_node*& sub_root, Record & new_data, bool & shorter)
{
Error_code result;
if (sub_root == NULL) {
shorter = false;
return not_present;
}
else if (new_data == sub_root->data) {
Binary_node*to_delete = sub_root;
if (sub_root->right_child == NULL) {
sub_root = sub_root->left_child;
shorter = true;
delete to_delete;
return success;
}
else if (sub_root->left_child == NULL) {
sub_root = sub_root->right_child;
shorter = true;
delete to_delete;
return success;
}
else {
to_delete = sub_root->left_child;
Binary_node *parent = sub_root;
while (!to_delete->right_child) {
parent = to_delete;
to_delete = to_delete->left_child;
}
sub_root->data = to_delete->data;
new_data = to_delete->data;
delete to_delete;
}
}
if (new_data < sub_root->data) {
result = avl_remove(sub_root->left_child, new_data, shorter);
if (shorter == true) {
switch (sub_root->get_balance())
{
case left_higher:
sub_root->set_balance(equal_height);
break;
case equal_height:
sub_root->set_balance(right_higher);
break;
case right_higher:
shorter = right_balance2(sub_root);
break;
}
}
}
if (new_data > sub_root->data) {
result = avl_remove(sub_root->right_child, new_data, shorter);
if (shorter == true) {
switch (sub_root->get_balance())
{
case left_higher:
shorter=left_balance2(sub_root);
break;
case equal_height:
break;
sub_root->set_balance(left_higher);
case right_higher:
sub_root->set_balance(equal_height);
break;
}
}
}
return result;
}
template
bool AVL_tree::right_balance2(Binary_node*& sub_root)
{
bool shorter;
Binary_node *&right_tree = sub_root->right_child;
switch (right_tree->get_balance()) {
case right_higher: //RR height--
sub_root->set_balance(equal_height);
right_tree->set_balance(equal_height);
rotate_left(sub_root);
shorter = true;
break;
case equal_height: //R- height doesn't change
right_tree->set_balance(left_higher);
rotate_left(sub_root);
shorter = false;
break;
case left_higher: //RL height--
Binary_node *sub_tree = right_tree->left_child;
switch (sub_tree->get_balance()) {
case equal_height:
sub_root->set_balance(equal_height);
right_tree->set_balance(equal_height);
break;
case left_higher:
sub_root->set_balance(equal_height);
right_tree->set_balance(right_higher);
break;
case right_higher:
sub_root->set_balance(left_higher);
right_tree->set_balance(equal_height);
break;
}
sub_tree->set_balance(equal_height);
rotate_right(right_tree);
rotate_left(sub_root);
shorter = true;
break;
}
return shorter;
}
template
bool AVL_tree::left_balance2(Binary_node*& sub_root)
{
bool shorter;
Binary_node *&left_tree = sub_root->left_child;
switch (left_tree->get_balance()) {
case left_higher: //LL height--
sub_root->set_balance(equal_height);
left_tree->set_balance(equal_height);
rotate_right(sub_root);
shorter = true;
break;
case equal_height: //L- height doesn't change
left_tree->set_balance(right_higher);
rotate_right(sub_root);
shorter = false;
break;
case right_higher: //LR height--
Binary_node *sub_tree = left_tree->left_child;
switch (sub_tree->get_balance()) {
case equal_height:
sub_root->set_balance(equal_height);
left_tree->set_balance(equal_height);
break;
case left_higher:
left_tree->set_balance(equal_height);
sub_root->set_balance(right_higher);
break;
case right_higher:
sub_root->set_balance(equal_height);
left_tree->set_balance(left_higher);
break;
}
sub_tree->set_balance(equal_height);
rotate_left(left_tree);
rotate_right(sub_root);
shorter = true;
break;
}
return shorter;
}
/****************************************************************************************************************************************/
아래 에 제 구동 함수 main 을 놓 으 면 코드 를 테스트 할 수 있 습 니 다. 세 개 있 습 니 다. 두 개 는 제 가 주석 을 달 았 습 니 다. 각각 테스트 Binary 입 니 다.tree、Binary_search_tree 와 avltree:
//main.cpp
#include"AVL_Tree.cpp"
#include
#include
using namespace std;
template
void print(Entry &item) {
cout << item << ' ';
}
void UI() {
cout << "***************************Binary_tree*********************************" << endl;
cout << "MENU:" << endl;
cout << "1.Preorder ." << endl;
cout << "2.Inorder." << endl;
cout << "3.Postorder." << endl;
cout << "4.Size." << endl;
cout << "5.Clear." << endl;
cout << "6.Height." << endl;
cout << "7.Insert." << endl;
cout << "0.Exit." << endl;
}
void flash() {
system("pause");
system("cls");
UI();
}
int Get_command() {
cout << "Please choose one of the number above." << endl;
string tmp;
int command;
cin >> tmp;
command = tmp[0] - '0';
while (command != 0 && command != 1 && command != 2 && command != 3
&& command != 4 && command != 5 && command != 6 && command != 7) {
cout << "Invalid command,please enter again." << endl;
cin >> tmp;
command = tmp[0] - '0';
}
return command;
}
//Binary_tree's main
//void main() {
// UI();
// Binary_tree tmp;
// int command;
// for (int i = 0; i < 11; i++)tmp.insert(i);
// do
// {
// command = Get_command();
// switch (command)
// {
// case 1: {
// cout << "This is the preorder tranverse." << endl;
// tmp.preorder(print);
// flash();
// break;
// }
// case 2: {
// cout << "This is the inorder tranverse." << endl;
// tmp.inorder(print);
// flash();
// break;
// }
// case 3: {
// cout << "This is the postorder tranverse." << endl;
// tmp.postorder(print);
// flash();
// break;
// }
// case 4: {
// cout << "The size of this Binary_tree is : ";
// cout << tmp.size() << endl;
// flash();
// break;
// }
// case 5: {
// tmp.clear();
// cout << "Clear Success!" << endl;
// flash();
// break;
// }
// case 6: {
// cout << "The height of this Binary_tree is : ";
// cout << tmp.height() << endl;
// flash();
// break;
// }
// case 7: {
// cout << "Please enter the item you want to insert." << endl;
// int item;
// cin >> item;
// tmp.insert(item);
// cout << "Insert success." << endl;
// flash();
// break;
// }
// case 0: {
// cout << "You'll exit the programm later." << endl;
// system("pause");
// return ;
// }
// default:
// break;
// }
// } while (command);
//}
//Binary_search_tree's main
//void main() {
// UI();
// Binary_search_tree tmp;
// int command;
// for (int i = 0; i < 10; i++)tmp.insert(i);
// do
// {
// command = Get_command();
// switch (command)
// {
// case 1: {
// cout << "This is the preorder tranverse." << endl;
// tmp.preorder(print);
// flash();
// break;
// }
// case 2: {
// cout << "This is the inorder tranverse." << endl;
// tmp.inorder(print);
// flash();
// break;
// }
// case 3: {
// cout << "This is the postorder tranverse." << endl;
// tmp.postorder(print);
// flash();
// break;
// }
// case 4: {
// cout << "The size of this Binary_tree is : ";
// cout << tmp.size() << endl;
// flash();
// break;
// }
// case 5: {
// tmp.clear();
// cout << "Clear Success!" << endl;
// flash();
// break;
// }
// case 6: {
// cout << "The height of this Binary_tree is : ";
// cout << tmp.height() << endl;
// flash();
// break;
// }
// case 7: {
// cout << "Please enter the item you want to insert." << endl;
// int item;
// cin >> item;
// tmp.insert(item);
// cout << "Insert success." << endl;
// flash();
// break;
// }
// case 0: {
// cout << "You'll exit the programm later." << endl;
// system("pause");
// return;
// }
// default:
// break;
// }
// } while (command);
//}
//avl_tree's main:
void main() {
UI();
AVL_treetmp;
int command;
for (int i = 0; i < 10; i++) {
tmp.insert(i);
}
do
{
command = Get_command();
switch (command)
{
case 1: {
cout << "This is the preorder tranverse." << endl;
tmp.preorder(print);
cout << endl;
flash();
break;
}
case 2: {
cout << "This is the inorder tranverse." << endl;
tmp.inorder(print);
cout << endl;
flash();
break;
}
case 3: {
cout << "This is the postorder tranverse." << endl;
tmp.postorder(print);
cout << endl;
flash();
break;
}
case 4: {
cout << "The size of this Binary_tree is : ";
cout << tmp.size() << endl;
flash();
break;
}
case 5: {
tmp.clear();
cout << "Clear Success!" << endl;
flash();
break;
}
case 6: {
cout << "The height of this Binary_tree is : ";
cout << tmp.height() << endl;
flash();
break;
}
case 7: {
cout << "Please enter the item you want to insert." << endl;
int item;
cin >> item;
tmp.insert(item);
cout << "Insert success." << endl;
flash();
break;
}
case 0: {
cout << "You'll exit the programm later." << endl;
system("pause");
return;
}
default:
break;
}
} while (command);
}
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.