2022-1
22-04-17 (데이터 구조)
오로시
2022. 4. 17. 16:40
데이터 구조
Binary tree
lemma -1 (일반트리)
n개의 node를 갖는 , k-degree tree에서 child를 가리키는 pointer n*k개 중 (n(k-1)+1) 개가 NULL이다. (NULL의 개수)
- Level 이 i 인 node는 최대 2^(i -1) 개 이다.
- Height 가 k인 binary tree의 최대 node 수(full binary tree의 노드 수)는 (2^k -1 )개 이다.
- Node 수가 n 인 full binary tree 의 height는 log2(n+1)이다. --> 꽉 채워진 height는 log 값으로 나타난다.
lemma -2 (바이너리트리)
어떤 binary tree 에서 degree가 0인 node(leaf)의 수는 dgree가 2인 node수 보다 1개 많다.
n0= n2+1
링크드 리스트 연습
#include <iostream>
using namespace std;
//linked list practice
class node {
public: // public에 안하면, 저 밑에서 cout 할때 사용 못함
string name;
double score;
node *link;
void set_data(string s , double n);
};
void node::set_data(string s , double n){
name = s;
score = n;
}
class my_list{ // 괄호 없다.
private:
node *head , *tail;
public:
my_list();//constructor
void add_to_head(node t);
void add_to_tail(node t);
node delete_from_head();
int num_nodes();
bool list_empty();
double score_sum();
double get_score(string name);
int remove_a_node(string name);
};
my_list::my_list(){
head = NULL;
tail = NULL;
}
void my_list::add_to_head(node t){
node *p;
p = new node;
(*p) = t;
p->link = head;
head = p;
if ( tail == NULL)
tail = p;
}
//head 랑 tail 이랑 좀 다르다.
//tail은 이전 상태가 empty였냐 아니엇냐가 중요하다.
//head는 어차피 이전 노드가 없으므로, 신경 안써도 되지만,
//tail != NULL이면 tail의 link를 추가되는 노드에 연결시켜 줘야 한다.
void my_list::add_to_tail(node t){
node *p;
p = new node;
(*p) = t;
p->link = NULL;
//만약 이전 상태가 empty 가 아니었다면
if (tail != NULL)
tail -> link = p;
else
head = p;
tail = p; // 이게 가장 마지막에 와야한다 그렇지 않으면
//tail 이 먼저 변경 되어 값이 망가질 것이다.
}
node my_list::delete_from_head(){
node tmp;
node *t;
tmp = (*head);
t = head;
head = head->link;
delete t;
//예외 상황 처리 하는 거 꼭 잊지 말자!!!
//삭제 후 empty가 되면, tail 값도 조정
if(head == NULL)
tail =NULL;
return tmp;
}
int my_list::num_nodes(){
int count=0;
node *t;
if(!list_empty()){
for(t=head ; t!=NULL ; t= t->link){
count++;
}
}
return count;
}
bool my_list::list_empty(){
if (head == NULL)
return true;
else
return false;
}
double my_list::score_sum(){
node *p;
double sum=0;
p = head;
while(p != NULL){
sum += p->score;
p = p->link;
}
return sum;
}
double my_list::get_score(string name){
node *p;
for(p=head; p != NULL ; p= p->link){
if ( p -> name == name){
return p->score;
}
}
return 0;
}
int my_list::remove_a_node(string name){
node *p ,*tmp;
p=head;
// empty면 종료
if(list_empty())
return 0;
// head의 값이 삭제 대상일 경우
if (p->name == name){
tmp = p;
head = p ->link;
delete tmp;
return 1;
}
// 중간값이나 tail 값이 삭제 대상일 경우
while ( p->link != NULL){
if ( p ->link->name == name){
tmp = p->link;
// 중간 값 일 때
if(tmp->link != NULL)
p->link = tmp->link;
//tail 일 때
else
p->link = NULL;
delete tmp;
return 1;
}
p = p->link;
}
return 0;
}
int main( )
{
my_list a;
node tmp;
// ryu- kim - park - choi
tmp.set_data("Kim", 20);
a.add_to_head(tmp);
tmp.set_data("Lee", 78.2);
a.add_to_head(tmp); // head 위치로 2개의 원소 추가
cout << a.num_nodes() << " : " << a.score_sum() << "\n"; // 1단계 점검
tmp.set_data("Park", 30);
a.add_to_tail(tmp); // tail 위치로 1개의 원소 추가
cout << a.num_nodes() << " : " << a.score_sum() << "\n"; //2단계 점검
tmp = a.delete_from_head();
cout << tmp.name << " is deleted.\n"; // 3단계 점검
tmp.set_data("Choi", 50);
a.add_to_tail(tmp);
tmp.set_data("Ryu", 10);
a.add_to_head(tmp); // 2개의 원소 추가
cout << a.num_nodes()<<" : " << a.score_sum() << "\n";
cout << "Park’s score : " << a.get_score("Park")<< "\n"; // 4단계 점검
if ( a.remove_a_node("Ryu") == 1)
cout << "choi is deleted from the list. \n"; // 5단계 점검
cout << a.num_nodes()<< " : " << a.score_sum() << "\n"; // 최종 점검
return 0;
}
특정 이름 입력 받아서, 삭제하는 int remove_a_node(string name) 함수 수정함.
스택 연습
/* int array 말고 , elemnet로 작성함
#include <iostream>
using namespace std;
#define SIZE 100
class element{
string name;
double score;
void set_data(string n , double s);
};
void element::set_data(string n , double s){
name = n;
score = s;
}
class mystack{
private:
element s[SIZE];
int top;
public:
mystack();
void push(element n);
element pop ();
bool stack_full();
bool stack_empty();
};
mystack::mystack(){
top = 0;
}
void mystack::push(element n){
mystack sta;
if(stack_full())
return;
else{
sta.s[top] = n;
top ++;
}
}
element mystack::pop(){
element e;
if(stack_empty())
return e;
else{
top--;
return e[top];
}
}
bool mystack::stack_full(){
if (top >= SIZE )
return true;
else
return false;
}
bool mystack::stack_empty(){
if (top == 0)
return true;
else
return false;
}
큐 연습
#include <iostream>
using namespace std;
#define SIZE 5
class element {
public:
string name;
int score;
void set_data(string n , int s);
};
void element::set_data(string n , int s){
name = n;
score = s;
}
class queue{
private: //여기서 실수 발생 , array q 생성 안했음. front rear int형인데 생각 못함
element q[SIZE];
int front, rear;
public:
queue();
void add_to_rear(element e);
element delete_from_front();
bool queue_full();
bool queue_empty();
};
queue::queue(){
front = 0;
rear =0;
}
void queue::add_to_rear(element e){
if(!queue_full()){
q[rear] = e;
rear = (rear+1)%SIZE;
}
else{
cout<<"the queue is full\n";
return;
}
}
/* 실수 포인트임. return 다음에 문장을 쓰니 실행이 안 될게 뻔함
element queue::delete_from_front(){
if(!queue_empty()){
return q[front];
front = (front+1)%SIZE; // return 뒤에 있으므로 실행이 안된다.ㅜ
}
}
*/
element queue::delete_from_front(){
element tmp;
if(!queue_empty()){
tmp = q[front];
front = (front+1)%SIZE;
return tmp;
}
else{
cout<<"the queue is empty";
return tmp;
}
}
bool queue::queue_empty(){
if(front == rear)
return true;
else
return false;
}
bool queue::queue_full(){
if ((rear+1)%SIZE == front)
return true;
else
return false;
}
int main() {
queue a;
element tmp;
tmp = a.delete_from_front();
cout << tmp.name<<":"<<tmp.score<<"\n"; //sname과 score가 public인 이유
tmp.set_data("KIM",30);
a.add_to_rear(tmp);
tmp.set_data("Lee", 10);
a.add_to_rear(tmp);
tmp.set_data("Park", 50);
a.add_to_rear(tmp);
while(!a.queue_empty()){
tmp = a.delete_from_front();
cout << tmp.name<<":"<<tmp.score<<"\n"; //sname과 score가 public인 이유
}
return 0;
}
링크드 리스트를 이용한 스택
링크드 리스트를 이용한 큐
#include <iostream>
using namespace std;
//링크드 리스트를 이용한 스택
//node
class node {
public :
string name;
double score;
node *link;
void set_data(string s, double n);
};
void node::set_data(string s , double n){
name = s;
score =n;
}
class stack{
//array 도 필요 없게 되고, 오직 남은 건 포인터 노드 변수
private:
node *top;
public:
stack();
void push_top(node t);
node pop_top();
bool stack_empty();
};
stack::stack(){
top = NULL;
}
void stack::push_top(node t){
node *p;
p = new node; //dynamic memory 로부터 node 공간 확보
(*p) =t; // node에 데이터 값 저장
p->link = top; // p가 원래 top이 가리키는 곳을 가리키도록 함
top = p; //top의 내용을 바꿔준다.
}
node stack::pop_top(){
node tmp;
node *t;
if(!stack_empty()){
tmp = (*top);
t = top;
top = top->link;
delete t;
return tmp;
}
else
return tmp;
}
//linked list 를 이용한 queue 구현
class node1{
public:
string name1;
double score1;
node link1;
void set_data(string n1 , double s1);
};
void node1::set_data(string n1, double s1){
name1 = n1;
score1 = s1;
}
class queue{
private:
node *front, *rear;
public:
queue();
void add_to_rear(node t);
node de_from_front();
bool queue_empty();
};
queue::queue(){
front = NULL;
rear = NULL;
}
void queue::add_to_rear(node t){
//새로운 데이터 p가 저장
node *p;
p = new node;
(*p) = t;
//p는 rear이므로 NULL 을 가리키도록 함
p->link = NULL;
if(rear == NULL)
front = p;
else
rear->link = p;
rear = p;
}
node queue::de_from_front(){
node *p;
node tmp;
tmp = (*front);
p = front;
front = front->link;
delete p;
if (front ==NULL)
rear = NULL;
return tmp;
}
bool queue::queue_empty(){
if (rear == NULL)
return true;
else
return false;
}
int main() {
std::cout << "Hello World!\n";
}
바이너리 트리 연산
#include <iostream>
#include <string>
using namespace std;
//linked list 개념이 다 들어가 있어.
class node {
public:
string name;
double score;
node *left, *right;
void set_data(string n, double s);
};
void node :: set_data(string n , double s){
name = n;
score = s;
}
class my_tree{
public :
int node_count; // 현재 노드 수
node *root; // root를 가리키는 pointer
my_tree(); // constructor
int insert_root(node t); // root 로 node 내용 t 추가
int insert_left(string tname, node tnode); // tname 의 왼쪽에 t 추가
int insert_right(string tname, node tnode); // tname의 오른쪽에 t 추가
double score_sum(); // 모든 node의 score 합
double score_average(); //모든 node의 score 평균
void print_data_inorder(); // inorder 순서로 모든 node의 값 출력
void print_data_preorder(); //preorder 순서로 모든 node의 값 출력
void print_data_postorder(); //postorder 순서로 모든 node의 값 출력
};
// 초기 empty 상태 설정
my_tree::my_tree(){
node_count = 0;
root = NULL;
}
//성공하면 1, 실패하면 0 출력
int my_tree::insert_root(node t){
// root 가 비어있지 않다면 실패(0) return
if(root != NULL)
return 0;
node *p;
p = new node; //node 공간 생성
(*p) = t; //주어진 node 내용 copy
p->left = NULL; // left NULL 로 설정 왜냐하면 root가 비어있었으니까
p->right = NULL; // right NULL로 설정
root = p; // 생성한 node를 root로 -->root 가 p를 가리키는 포인터 노드 변수임. root자체에 데이터가 들어가는게 아니다.
node_count++; // count ++
return 1; // 성공(1) return
}
//p를 root로 하는 binary tree 에서 성명이 tname인 node를 찾고 그 node의 왼쪽에 t 추가
// algorithm
// p가 null이면 return 0 : 해당 node 없음
//root 가 찾는 node(name이 tname)이면
// root의 left가 null 아니면 return -1 (이미 왼쪽에 값이 있는 것임)
// root의 left가 null이면 왼쪽에 새로운 node 추가
// .node 공간확보 - data내용저장 - left,right 를 null 로 - root 의 왼쪽에 연결
// 성공(1)을 return
//root가 찾는 node(name이 tname)이 아니면
// left subtree에 해당 작업 수행
// return 0 이 아닌경우 (해당 노드를 아직 못찾은 경우가 아니면) 결과값을 그대로 return
// return 0 인 경우 right subtree에 해당 작업을 수행하고 그 결과를 return
int node_insert_left(node *p, string tname, node tnode){
if(p==NULL) return 0; //p가 null이면 0 출력. 아예 비워있으니 찾는게 불가능
if(p->name == tname){
if(p->left != NULL) // 이미 left에 뭔가 있으면 추가 못하도록 한다.
return -1;
node *t;
t = new node;
(*t) = tnode;
t->left = NULL;
t->right = NULL;
p->left = t;
return 1;
}
else{
int result = node_insert_left(p->left, tname, tnode);
if(result!=0)
return result;
return node_insert_left(p->right,tname,tnode);
}
}
int node_insert_right(node *p, string tname, node tnode){
if(p==NULL) return 0;
//p가 null이 아닐때 p->name == tname 인지 확인
if(p->name == tname){
// 찾았는데, 오른쪽에 뭔가 있으면 -1
if(p->right != NULL)
return -1;
//찾았는데, 오른쪽에 아무것도 없으면 추가
node *t;
t = new node;
(*t) = tnode;
t-> left = NULL;
t-> right = NULL;
p-> right = t;
return 1;
}
//p가 null이 아닌데, p->name == tname 이 아닌 경우 //한 단계 더 가야지
else{
//순서가 left 먼저니까 p->left먼저 하는게 맞지 않을까?--> 그림을 그려서 해본 결과 이게 맞는듯 , 왜냐하면 왼쪽먼저 오른쪽 나중이 binary tree 의 기본 순서임! 왼쪽이 children 이고 ,오른쪽이 sibling 이기 때문이다.!
int result = node_insert_right(p->left, tname, tnode);
if(result!=0)
return result;
return node_insert_right(p->right,tname, tnode);
}
}
//성명이 'tname' 인 node를 찾고 그 node의 왼쪽에 t 추가
// return value
// 1 : insert 과정 성공
// 0 : 'tname' 갖는 Node 없음
// -1: 'tname' 을 갖는 노드는 있으나 왼쪽이 NULL이 아닌 경우
// recursive 하게 코딩 ('root'를 root로 하는 tree 에 해당 내용을 수행하는 function 을 call 한다.
// 결과가 성공이면 node_count를 1 증가 시킴
int my_tree::insert_left(string tname, node tnode){
int result;
result = node_insert_left(root,tname,tnode); // member 함수가 아니다.
if(result ==1)
node_count ++;
return result;
}
int my_tree::insert_right(string tname , node tnode){
int result;
result = node_insert_right(root, tname, tnode); //member 함수가 아니다. 굳이 이렇게 하는 이유는 class 내부 자체에서 recursive 함수 만드는게 매우 어렵기 때문이다.
if(result == 1)
node_count++;
return result;
}
//recursive 함수의 특성 때문에, p 가 계속 subtree의 root 가 된다.
double sum_allnodes(node *p){
if (p==NULL)
return 0;
return sum_allnodes(p->left) + sum_allnodes(p->right)+p->score;
}
// 'root'를 root으로 하는 binary tree의 모든 node에 대한 score의 합을 계산 : recursive function 으로 구현
// root 가 null 이면 return 0 , 아니면 left subtree의 모든 node의 score 합 +
// right subtree의 모든 node의 score 합 + root 의 score 합 을 계산하여 return
double my_tree::score_sum(){
return sum_allnodes(root); // 이렇게 함수를 하나 더 만드는 이유는 clas member function으로 recursive 함수를 만들기 어렵기 때문이다. 파라미터의 인수들을 서로 공유할기 때문이다.(?)
}
// score_sum 으로 얻은 결과를 전부 node_count로 나눠준다.
double my_tree::score_average(){
return score_sum()/node_count;
}
void inorder_print(node *p)
{
if(p==NULL) return;
inorder_print(p->left);
cout <<p->name<<" : " << p->score << "\n";
inorder_print(p->right);
}
// 현재 tree 의 모든 node의 data값을 inorder 순서로 출력
// 'root'를 root로 하는 binary tree의 모든 node의 data 값을 Indoer 순서로 출력 : recursive function으로 구현
//root 가 null이면 그대로 return
//아니면
// left subtree 의 모든 node의 data 값을 inorder로 출력
// rooot node의 data 값을 출력
// right subtree의 모든 node의 data 값을 inorder로 출력
void my_tree::print_data_inorder(){
inorder_print(root);
}
void preorder_print(node *p)
{
if(p==NULL) return;
cout <<p->name<<" : " << p->score << "\n";
preorder_print(p->left);
preorder_print(p->right);
}
void my_tree::print_data_preorder(){
preorder_print(root);
}
void postorder_print(node *p)
{
if(p==NULL) return;
postorder_print(p->left);
postorder_print(p->right);
cout <<p->name<<" : " << p->score << "\n";
}
void my_tree::print_data_postorder(){
postorder_print(root);
}
int main(int argc, const char * argv[]) {
my_tree thetree;
node tmp;
tmp.set_data("Kim" , 8.1);
thetree.insert_root(tmp);
tmp.set_data("Lee", 6.5);
thetree.insert_left("Kim",tmp);
tmp.set_data("Park", 8.3);
thetree.insert_right("Kim", tmp);
tmp.set_data("Choi", 7.2);
thetree.insert_left("Lee", tmp);
tmp.set_data("Ryu", 9.0);
thetree.insert_right("Lee", tmp);
tmp.set_data("Cho", 7.7);
thetree.insert_right("Park", tmp);
cout<< "Score Sum : " << thetree.score_sum() << "\n";
cout<< "Score Average : " << thetree.score_average() << "\n";
cout <<"\n Inorder Traversal Result \n";
thetree.print_data_inorder();
cout << "\n Preorder Traversal Result \n";
thetree.print_data_preorder();
cout << "\n Postorder Traversal Result \n";
thetree.print_data_postorder();
return 0;
}
다음 3가지 문제는 서로 equivalent한 문제이다.
- n개의 node로 이루어진 binary tree의 형태는 총 몇가지 인가?
- n개의 원소로 이루어진 stack permutaition 문제
- n+1 개의 matrix를 곱하는 방법의 수