돌공공돌

22-04-15 (데이터 구조 중간시험) D-3 본문

2022-1

22-04-15 (데이터 구조 중간시험) D-3

오로시 2022. 4. 15. 17:51

데이터 구조 중간고사 (7주차 까지)

 

1. Algorithm Efficieny

O(n) , O(logn) -> 어떤 프로그램에 대해서 BIG-O notation 판단 있는 정도

 

2. Stack

1) Array 이용한 방법 & Linked List 이용한 방법 -> 모두 구현할 있는 수준 (스택짜기)

 

array를 이용한 stack 구현

 


/*
array를 이용한 stack 을 구현하려고 한다.
stack에 필요한게 뭐지?
1. 일단 array
2. top
memberfunction
-constructor
-push
-pop
-stack_full
-stack_empty
 */

#include <iostream>
using namespace std;
#define SIZE 100


class mystack {
private:
    int s[SIZE];
    int top;
public:
    mystack();
    void push(int n);
    int pop();
    bool stack_full();
    bool stack_empty();
};

mystack::mystack(){
    top = 0;
}

void mystack::push(int n){
    if (stack_full()){
        cout<<"This stack is full" << endl;
        return ;
    }
    
    s[top] = n;
    top ++;
}

int mystack::pop(){
    if(stack_empty()){
        cout<<"This stack is empty : ";
        return 0;
    }
    top--;
    return s[top];
}


bool mystack::stack_full(){
    if (SIZE < = top)
        return true;
    else
        return false;
}

bool mystack::stack_empty(){
    if (top == 0 )
        return true;
    else
        return false;
}


int main()

{

mystack  s1;

int list[5] = { 32, 123, 27, 131, 242 }, i, x;

  for (i = 0; i < 5; i++ )

                s1.push( list[i] );

  while ( ! s1.stack_empty( ) )

  {

            x = s1.pop( );

            cout << x << endl ;

  }

  return 0;

}

 

 

linked list 를 이용한 stack 구현

 

#include <iostream>
using namespace std;

//node --> self-referential class type
//data는 일단 가장 익숙한 이름과 점수로 가자
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 ll_stack {
    // top이 한 노드를 가리킨다.
    node *top;
public:
    ll_stack(); // constructor
    void push(node  d1);
    node pop();
    bool stack_empty();
};

//constructor
ll_stack::ll_stack(){
    top = NULL;
}
//push 해주는 거 head에 원소 추가 하는 거랑 동일?

/*
 node * p  —> 노드를 데이터 타입으로 하는 변수의 주소를 저장한다.
 p = new node —> 한 개 공간 allocation 해주고, 그 위치를 return p 에는 주소가 저장되어있다. 어쨌든 노드의
 (*p) = t; —> 일반 노드의 데이터가 p가 가리키는 노드 값에 저장된다.
 p->link = head; // p는 주소를 저장하는 변수인데, 어떻게 link에 접근이 가능하죠? —> 잠시만 (*p).link 와 p->link가 같은 말이다 따라서 이 문장은 말이 된다. head가 가리키고 있는 노드 에대한 주소를 p->link가 받아온다.
 head = p ; head도 노드 포인터 변수이고, p도 포인터 변수 이기 때문이다.
 */
//linked list 개념을 이용한 push
void ll_stack::push(node d1){
    node *t; //노드를 데이터 타입으로 하는 변수의 주소를 저장하는 포인터 변수 t
    t = new node; // t를 위한 공간을 allocation 해준다. 동적인 할당 t = new node가 없으면 안되는 이유, initialization 이 필요하기 때문이다. 왜냐하면 t 에 통째로 어떤 주소값이 들어오지 않기 때문이다. 데이터만 들어온다.
    (*t)=d1;
    (*t).link  = top;
    top = t;
    // delete t? 기계적으로 동적인 할당을 해주면 당연히 안된다...그럼 push 되는게 없는걸? delete 는 pop에서 해주는 것
    
}

node ll_stack::pop(){
    node data;
    node *z;
    //z = new node; //이게 없다..! 왜? 있어도 되고 없어도 된다. --> push되어있는 top에서 데이터를 가져오는 것이기 때문에,,,new로 새롭게 노드 만들어줄 필요없다 --> 있어도 되고, 없어도 된다.
    //empty check if true return NULL
    if (stack_empty()){
        cout<<"This stack is empty";
        return data;
    }
    //1) z라는 노드포인터 변수에 top이 가리키는 주소를 넣는다. delete 해주기 위해서,
    z = top;
    //2) data라는 노드에 top의 데이터 들을 저장한다. return 하기 위해서
    data = (*top);
    //3) top이 다음 노드의 주소를 가리키게 한다. 링크 조정
    top = top->link;
    //4) 데이터 delete
    delete z;
    return data;
}

bool ll_stack::stack_empty(){
    if(top == NULL)
        return true;
    else
        return false;
}


int main(){
    ll_stack  a;
    node tmp;
    tmp.set_data("Lee",22.2);
    a.push(tmp);
    tmp.set_data("Kim", 33.3);
    a.push(tmp);
    tmp.set_data("Park",55.2);
    a.push(tmp);
    tmp.set_data("Choi", 96.3);
    a.push(tmp);

    
    tmp = a.pop();
    cout << tmp.name<<":"<<tmp.score<<"\n";
    tmp = a.pop();
    cout<<tmp.name<<":"<<tmp.score<<"\n";


}

2) 스택을 활용한 연산 구현 (연산 짜기) -> array 사용하여 push pop 어떻게 일어나는지 확인을 하라.

 

 

3. queues

1) Array 사용한 구현 --> 구조  , 구현할 있는 수준

2) Circular queue --> 어떻게 하는지에 대한 구체적인 구현 내용

Circular queue를 사용하는 이유는 한정된 array크기 rear == array size이면 delete하더라도 , 비어있는 공간을 못 쓰게 된다.

그래서 % 연산자를 이용하여 , circular queue를 완성한다. 이때 문제가 생긴다.

https://lktprogrammer.tistory.com/59

 

03 원형 큐 (Circular Queue) 자료 구조

원형큐 (Circular Queue) 원형 큐는 선형 큐의 문제점을 보완하기 위한 자료구조입니다. 앞선 포스팅에서 선형큐의 문제점은 rear이 가르키는 포인터가 배열의 마지막 인덱스를 가르키고 있을 때 앞

lktprogrammer.tistory.com

 

3) full_check 논리--> full, empty 혼동 시키는 문제, 이거 어떻게 구현할 있는지에 대해 복습

if(rear == front) 일 경우 full과 empty의 구별이 모호하기 때문에

full_check하는 방법은  마지막 남은 한 개의 빈공간을 사용하지 않는 것을 전제로 하여

(rear+1)%Q_size == front 이런 식으로 한다 --> 이것의 뜻은 하나 증가 했을 때 만난다면 full

 

array를 이용한 queue 구현

/*Queues 를 array 로 구현해 보자
 Queue에 필요한 연산
 initialization : front (원소를 읽을 곳) , rear (다음 원소를 넣을 곳)
 insert : queue 에 한 개 원소를 저장 (rear 위치에 원소를 넣고, rear++ , 사전 full test)
 delete : queue 로부터 한 개 원소를 취함 (front 위치에서 원소를 가져오고 front++ , 사전 empty test)
 Full test : Circular queue 해결 방안
 Empty test :
 
 rear에서 insert, front에서 delete
*/


#include <iostream>
using namespace std;
#define SIZE 100

// 모든 data type 에 적용할 수 있도록 하는 queue를 만들기 위해, 대표적으로 이름과 ,score 를 저장하는, element를 생성하자
class element{
public:
    string sname;
    double score;
    void set_data(string s, double d);
};
//setdata 함수 자동적으로 나와야 한다...
void element::set_data(string s, double d){
    sname = s;
    score = d;
}

class array_queue{
private:
    element q[SIZE];
    int front , rear;
public:
    array_queue(); // constructor
    void insert_q(element e); // insert element in rear
    element delete_q(); // delete element in front
    bool queue_full(); // queue가 full인지 판단 , 판단기준 (rear+1)%SIZE == front
    bool queue_empty(); // queue가 empty 인지 판단, 판단기준 rear == front
};

array_queue::array_queue(){
    rear = 0 ;
    front = 0;
}

void array_queue::insert_q(element e){
    if(!(queue_full())){
        q[rear] = e;
        rear = (rear+1)%SIZE;
    }
    else
        cout<<"The Queue is full"<<endl;
}

element array_queue::delete_q(){
    element tmp;
    if(!(queue_empty())){
        tmp = q[front];
        front = (front+1)%SIZE; // 단순히 front ++을 하면 안 된다. 순환해야 한다
        return tmp;
    }
    else{
        cout<<"The Queue is empty: ";
        return q[front];
    }
}
bool array_queue::queue_full(){
    if(front == (rear+1)%SIZE)
        return true;
    else
        return false;
}
bool array_queue::queue_empty(){
    if(front == rear)
        return true;
    else
        return false;
}

int main() {
    array_queue a;
    element tmp;
    
    tmp.set_data("KIM",49.4);
    a.insert_q(tmp);
    tmp.set_data("Lee", 99.3);
    a.insert_q(tmp);
    tmp.set_data("Park", 34.4);
    a.insert_q(tmp);
    
    while(!a.queue_empty()){
        tmp = a.delete_q();
        cout << tmp.sname<<":"<<tmp.score<<"\n"; //sname과 score가 public인 이유
    }
    return 0;
}

 linked list 를 이용한 queue 구현

//linked list 개념을 이용한 queue를 구현해보자
// 일단 기본적으로 insert, delete가 rear 와 front에서 일어나도록 해줘야한다.
// head 에서 원소의 추가 삭제가 일어나는게 용이하다.
// tail 에서 원소의 추가가 용이하지만 삭제는 어려움
// 따라서 head --> front (삭제가 일어남) , tail은 rear(원소 추가) 로 한다.
// 원소는 당연히 node 가 되겠지.
#include <iostream>
using namespace std;

class node {
public:
    string name;
    double score;
    node *link;
    void set_data(string n , double s );
};

void node:: set_data(string n, double s){
    name = n;
    score = s;
}

class llqueue {
private:
    node *front;
    node *rear;
public:
    llqueue();
    void insert_q(node n);
    node delete_q();
    // bool full_check(); linked list 는 dynamic memory 이므로 , full_check할 필요가 없다
    bool empty_check();
};

llqueue::llqueue(){
    front = NULL;
    rear = NULL;
}

void llqueue::insert_q(node n){
    node *t;
    t = new node;
    (*t) = n;
    // 이 밑으로 순서 중요하다.
    t->link = NULL;
    if(rear != NULL)
        rear->link = t;
    else
        front = t; //rear 가 가리키고 있는 node가 가리키는 값,, ㅋㅋ 그림으로 보면 이해가 쉬운데,글은 좀 어렵다.
    rear = t;
}

node llqueue::delete_q(){
    node tmp;
    node *t;

/*    if(empty_check()){
        cout << "The queue is empty" << "\n";
        return tmp;
    }
 */
    t = front;
    tmp = (*front);
    front  = front->link;
    delete t;
    if (front == NULL)
        rear = NULL;
    return tmp;

}

bool llqueue::empty_check(){
    if (rear == NULL)
        return true;
    else
        return false;
}

int main() {
    llqueue a;
    node tmp;
    
    tmp.set_data("KIM",49.4);
    a.insert_q(tmp);
    tmp.set_data("Lee", 99.3);
    a.insert_q(tmp);
    tmp.set_data("Park", 34.4);
    a.insert_q(tmp);

    
    while(!a.empty_check()){
        tmp = a.delete_q();
        cout << tmp.name<<":"<<tmp.score<<"\n"; //sname과 score가 public인 이유
    }
    return 0;
}


//그냥 linked list 랑 코드가 아예 똑같다.
// add to tail 과 delete from head 으로, rear에 넣고, front에서 빼기가 가능하다.

4. Recursion

                    1) base , n 대한 solution --> (n+1 or n-1), n --> 다른 데이터 스트럭쳐에도 사용할 있을 정도로 숙달하면 좋겠다. 하나 적은 것에 대해 solution 생성 있다면 거기에 대해 어떤 추가적인 작업이 있어야, n 대한 수식을 도출할 있다.

 

5. C++ 언어

                    1) 기초 문법 : 기본 입출력

                    2) class 사용한 구현 --> ADT

                    3) String Class

 

6. Expression 표현

                    a+b : infix , prefix, postfix --> 이것들이 무엇이며 우리가 써왔던 infix prefix postfix 바꾸는 방법을 이해하라 && stack 사용해서 infix postfix 바꾸는 알고리즘

 

7. Stack 응용

                    infix -> postfix

                    기타 실습 문제 --> stack 자유롭게 있는 방법을 알아둬라

8. Linked Lists

                    자기 자신과 같은 데이터 타입을 가리킬수 있는 포인터 타입을 link 사용해서, 다음 node head tail 노드를 가리키는 포인터 만으로도 다룰 있다면 p부터 정해진 문제를 해결 있는 , 원소를 추가한다거나 판단한다거나 있을 정도로 공부를 둬라

 

9. list node 위한 class 선언

출처 : 한동대학교

 

 

노드의 형태가 head , tail 이렇게 일반적 이긴 하지만, 문제에 따라 유연하게 해석 있어야 한다.

이러한 구조가 아닌 pointer 하나만 사용해서도 있다. --> 이거는 염두해두고 주어진 문제에 중심을 두고 해결

//계속 반복 연습이 필요하다.
/*add_to_head() //head위치에 한 개의 원소 추가

add_to_tail() // tail 위치에 한 개의 원소 추가

delete_from_head()// head 위치의 한 개 원소 삭제

num_nodes() //현재의 Node 수 확인 --> traversal

list_empty() // node비어있는지 확인

inver() // 역순으로 재배열 하라

list_equal() // 두 리스트가 같은지 판단
  
score_sum() // 전체 스코어의 합 구하기

get_score(string name)

remove_a_node(string name) //특정 이름을 같는 노드 삭제
*/

#include <iostream>
using namespace std;

//일단 여태껏 하는 것 처럼 이름과 점수를 저장하는 node class를 만들어주자.
class node {
public:
    string name;
    double score;
    node *link;
    void set_data(string n , double s);
};

void node::set_data(string n, double s){
    name = n;
    score = s;
}

//linked list class
class my_list{
    node *head, *tail;
public:
    my_list();
    void add_to_head(node n); //head위치에 원소 추가
    void add_to_tail(node n); //tail위치에 원소 추가
    node delete_from_head(); //head위치에 원소 삭제
    int num_nodes(); //현재의 node수 확인
    bool list_empty(); // list가 비어있는지 확인
    double score_sum(); //현재 list 모든 node의 score 합 return
    double get_score(string t_name); // 't_name'으로 저이진 name의 score return
    int remove_a_node(string t_name); //'t_name'에 해당하는 node 삭제, 성공하면 1 return, 해당 노드가 존재하지 않으면 0 return
    void invert(); // 역순으로 재배열 하라
    friend bool list_equal(my_list l1 , my_list l2); // 두 리스트가 같은지 판단
};

bool is_equal(node *p1 , node *p2);
bool equal_data(node t1 , node t2);

//constructor
my_list::my_list(){
    head = NULL;
    tail = NULL;
}
// linked list 를 이용한 stack 의 원소 추가에 이용됨
void my_list::add_to_head(node n){
    node *p;
    p = new node;
    (*p) = n;
    p->link = head;
    head =p;
    if(tail == NULL)
        tail = p;
}

// linked list 를 이용한 queue 원소 추가에 이용됨
void my_list::add_to_tail(node n){
    node *p;
    p = new node;
    (*p) = n;
    p->link = NULL;
    if (tail == NULL)
        head= p;
    else
        tail->link = p; // 이전 테일이 추가한 테일에 연결 하도록 한다.
        
    tail = p;

}

// linked list를 이용한 stack 과 queue에 모두 이용됨
node my_list::delete_from_head(){
    node tmp;
    node *t;
    
    tmp = (*head); // tmp에 head데이터 넣기
    t = head; // t에 head 연결 시키기
    head = head->link; // head 에 head 다음거 연결 시키기
    delete t; //t delete
    if(head == NULL) // 삭제 후 emmpty 가 되면 tail 값도 조정
        tail = NULL;
    return tmp; //tmp (head) 값 return
}

//traversal 개념
int my_list::num_nodes(){
    node *t;
    int count=0;
    t = head;
    
    if(!list_empty())
        while (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(){
    double score =0;
    node *t;
    t = head;
    
    while(t != NULL){ //조건이 tail 이면 tail이 되는 순간 것은 빼고 연산하기 때문에 tail노드가 빠진 결과가 나오게 된다.
        score += t->score; //밑에거랑 순서 중요!
        t = t->link;
    }
    return score;
        
}

double my_list::get_score(string t_name){
    node *t;
    t = head;
    
    while(t != NULL){
        t = t->link;
        if(t->name == t_name)
            break;
    }
    return t->score;
}
//성공하면 1 return 해당노드가 존재하지 않으면 0 return
//알고리즘 , t_name이랑 같은 name을 갖는 노드 한 개 전꺼를 찾고, 그 link를 찾은 노드 다음 거랑 연결시킨다.
//그리고 delete할 때는 delete_From_head할 때랑 똑같이 하면 될 듯
int my_list::remove_a_node(string t_name){
    int state = 0;
    node *t , *tmp;
    for( t= head ; t!= NULL ; t=t->link){
        if(t_name == t->link->name){
            tmp = t->link;
            t -> link = tmp->link;
            delete tmp;
            state =1;
            break;
        }
    }
    
    return state;
    
}

//역순으로 재배열 와,, 이거 진짜 어떻게 하는거지?!
void my_list::invert(){
    node *newhead , *oldhead, *tmp;
    newhead = NULL;
    oldhead = head;
    
    while(oldhead  != NULL){
        tmp = newhead;
        newhead = oldhead;
        oldhead = oldhead -> link;
        newhead ->link =tmp;
    }
    
    tail = head;
    head= newhead;
}

//list equal
//recursive 한 algorithm
bool list_equal(my_list a, my_list b){
    return is_equal(a.head , b.head);
}

bool is_equal(node *p1 , node *p2){
    if (p1==NULL && p2 == NULL)
        return true;
    if(p1 == NULL || p2 == NULL)
        return false;
    if(equal_data(*p1,*p2))
        return(is_equal(p1->link, p2->link));
    else
        return false;
}

bool equal_data(node t1 , node t2){
    if(t1.name != t2.name)
        return false;
    if(t1.score != t2.score)
        return true;
    return true;
}

/*
int main(int argc, const char * argv[]) {
    my_list a,b; node tmp;
    tmp.set_data("Kim", 83.5);
    a.add_to_head(tmp);
    tmp.set_data("Lee", 78.2);
    a.add_to_head(tmp);
    tmp.set_data("Park", 91.3);
    a.add_to_head(tmp);
    tmp.set_data("Choi", 85.1);
    a.add_to_head(tmp);
    tmp.set_data("Choi", 85.1);
    b.add_to_head(tmp);
    tmp.set_data("Park", 91.3);
    b.add_to_head(tmp);
    tmp.set_data("Lee", 78.2);
    b.add_to_head(tmp);
    tmp.set_data("Kim", 83.5);
    b.add_to_head(tmp);
    
    tmp = a.delete_from_head();
    cout << tmp.name << " : " << tmp.score << "\n";
    tmp = a.delete_from_head();
    cout << tmp.name << " : " << tmp.score << "\n";
    tmp = a.delete_from_head();
    cout << tmp.name << " : " << tmp.score << "\n";
    tmp = a.delete_from_head();
    cout << tmp.name << " : " << tmp.score << "\n";
    
    b.invert();
    tmp = b.delete_from_head();
    cout << tmp.name << " : " << tmp.score << "\n";
    tmp = b.delete_from_head();
    cout << tmp.name << " : " << tmp.score << "\n";
    tmp = b.delete_from_head();
    cout << tmp.name << " : " << tmp.score << "\n";
    tmp = b.delete_from_head();
    cout << tmp.name << " : " << tmp.score << "\n";


    b.invert();
    if (list_equal(a, b) )
    cout << "Yes, the two lists are identical. \n";
    else
    cout << "No, They are not identical.\n";
    tmp = b.delete_from_head();
    cout << tmp.name << " : " << tmp.score << "\n";
    return 0;
 
}

*/

int main( )

  {

   my_list    a;

   node   tmp;

             tmp.set_data("Kim", 83.5);

            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", 91.3);

            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", 85.1);

            a.add_to_tail(tmp);

             tmp.set_data("Ryu", 94.3);

            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("Kim") == 1)

                   cout << "Kim is deleted from the list. \n";     // 5단계 점검

               cout << a.num_nodes()<< " : " << a.score_sum() << "\n";  // 최종 점검

                return 0;

  }

10) linked list 추가 member function

                    1. double score_sum() :현재 list 모든 node score return

                    2. double get_scroe (string t_name) : t_name으로 주어진 name score return

                    3. remove_a_node(string_t_name) : t_name 해당하는 node 삭제, 성공하면 1 return , 해당 노드가 존재하지 않으면 0 return

 

11) linked list 연산 : 교수님이 바라는 수준 --> 어떤 연산이 와도, 재량껏 만들어 있을 만한 수준 , traversal 누락없이 중복없이 전체를 훑어서 판단 하는 연산, 임의의 위치에 원소를 넣든지 지우든지 하는 것이 연산의 내용

 

12) linked list 이용한 stack 구현 --> linked list 원소 추가 삭제 개념과 거의 같다. 자유롭게 구현 가능한 수준

13) linked list 이용한 queue 구현. --> linked list 원소 추가 삭제가 비슷하다. 끝에서 동작한다.

앞에 노드를 가리키는 포인터 하나로 linked list 유지할 있다. 이것도 참고해서 알아둬라

 

14) Trees : 노드와 노드의 종속 관계 : 용어 정리 --> 달달 외울 필요가 없지만 시험에 용어가 나왔을 그것을 이해할 있는 정도는 되어라. 임의의 degree tree 만들면 branch(공간) 유지하는게 어렵기 때문에 binary tree 이용한다. binary tree 대해 집중적으로 공부

15) binary tree : 어떤 노드와의 관계에서 왼쪽과 오른쪽 최대 2개의 subtree , 구조적인 이해나 이것을 이용한 연산을 있어야 한다. binary tree 대한 구조적 정의 lemma-2 어떤 binary tree 에서 degree 0 node(leaf) 수는 degree 2 Node 보다 1 많다.

n0 = n2+1 --> 구조적으로 이해해둬라

 

16) binary tree traversal : 모든 node 대하여 중복이나 누럭없이 일정 순서로 특정 작업을 수행

inorder traversal : left – node 처리 – right

preorder traversal : node처리 – left – right

postorder traversal : left – right -node 처리

 

처리 과정 원리 이해

 

17) Traversal algorithm – recursive 하게 생각하면

                    주어진 tree null 이면 그대로 return

                    null 아니면 , 다음을 수행

-        left subtree inorder traversal 한다.

-        root data 처리한다.

-        right subtree inorder traversal한다.

 

18) Expression tree : tree 이용해서, preorder --> prefix, postorder -->postfix

 

19) my_tree class 정의 : class 모양이 항상 그렇게 되어야만 하는 것은 아니다.

tree memberfunction 구현 할 수 있어야 한다.

 

20) Binary tree 추가주제 : selection trees , 이미 sorting list 무더기, 이것을 하나로 합쳐서 , sorting 하라. (winner tree, loser tree 특징을 이해하라) , counting binary trees --> equivalent 2 문제 유형 , 공식을 이용해서 풀어라

 

 

여기서 언급된 내용이 시험 범위

8주차 목요일 강의는 동영상 강의로 대체

이후 수업의 형태는 공지 유의

'2022-1' 카테고리의 다른 글

22-04-16  (0) 2022.04.16
22-04-15 (데이터 구조)  (0) 2022.04.15
22-04-15 (데구-통계 D-4 , 선대-물실 D-6 ,데과 D-7)  (0) 2022.04.15
22-04-14 (데구, 통계 D-4)  (0) 2022.04.14
22-04-13  (0) 2022.04.13
Comments