View
361
Download
2
Category
Preview:
Citation preview
project #3
다항식 연산
자료구조
조장:
20113311 장동규
조원:
20113309 이태구
20113318 정예린
20113315 정민정
20083657 서영진
순서
•조원별 업무분담
•일정 계획-회의록
•문제 파악-Linked List라는 자료구조를 활용하여
다항식 연산
•알고리즘 계획
•소스구현
•문제점 파악 / 해결법 토의
•최종소스
•시간 복잡도/공간 복잡도
이름 역할
장동규 조장, 보고서 작성, 자료 조사
정예린 코딩, 자료 조사
정민정 알고리즘, 자료조사
서영진 보고서 작성, 자료 조사
날짜 계획
04.12~04.17링크드 리스트, 다항식 연산에 대해 조사
해오기, 보고서 작성
04.17~04.19
링크드 리스트 소스 구현하는 알고리즘, 이
것을 어떻게 다항식 연산에 활용할 것인지
생각 해오기, 보고서 작성04.19~04.24 알고리즘을 소스로 구현, 최종 보고서 작성
1) 조원별 업무 분담
2)일정계획
3)문제파악
링크드 리스트(Linked List)
배열 구조의 단점을 보안한 자료구조이다. 배열은 인덱스를 이용하여 해당 주소에 바로 접
근할 수 있는 장점이 있지만, 단점으로는 삽입, 삭제 연산이 복잡하다. 그리고 배열은 선언
시 크기를 미리 정해줘야 하기 때문에 배열 공간 100개를 선언해 놓고 10개만 쓰는 경우
(메모리 낭비), 200개가 필요한 경우(공간 부족) 라면 배열 선언 부분을 다시 수정해줘야 하
는 불편함이 있다.
이러한 단점을 보안해 필요할 때 마다 공간을 할당하고 또 그것들을 연결하여 마치 배열처
럼 사용하는 기법이 바로 링크드 리스트이다.
링크드 리스트의 장점으로는 프로그래머가 미리 공간을 할당할 필요 없이 필요할때마다 공
간을 할당 받을 수 있도록 설계가 가능하다. 그러기 때문에 필요한 변수의 크기를 모를 경
우에는 배열보다 효율적이다. 하지만 링크드 리스트에도 단점은 있다. 다음 노드의 주소를
가리키기 위해 4byte(32비트 운영체제 기준)의 공간이 낭비된다. 또, 인덱스로 해당 주소에
접근하는 것이 아닌 노드의 첫 번째를 가리키는 Head라는 포인터 변수를 통해서 순차적으
로 접근하는 단점으로 인해 검색속도가 느리다는 단점이있다.
(3번지 주소까지 가기 위해서는 1, 2번 주소지를 거쳐야 갈 수 있다.)
다음은 링크드 리스트를 구현한 소스이다.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct tagNode{
char Name[20];
struct tagNode* NextNode;
}NODE;
구조체로 이루어져 있는 링크드 리스트. Name은 데이터를 담을 변수로 데이터가 들어가
며, tagNode*는 다음 노드의 주소를 가리킬 포인터 변수이다.
NODE* CreateNode(char name[])
{
NODE* NewNode = (NODE*)malloc(sizeof(NODE));
strcpy(NewNode -> Name, name);
NewNode -> NextNode = NULL;
return NewNode;
}
NODE를 생성하는 함수이다. 인자로는 해당 노드에 들어갈 값을 저장하기 위한 값을 받고
있다. NewNode라는 포인터를 생성후 그 해당하는 주소에 malloc함수로 공간을 동적할당하
고 있다.
void ConnectNode(NODE** Head, NODE* Node)
{
if(NULL == (*Head))
{
*Head = Node;
}
else
{ NODE* Current = *Head;
while(Current -> nextNode != NULL)
{
Current = Current -> NextNode;
}
Current -> NextNode = Node;
}
}
인자로는 첫 노드를 가리키는 Head의 주소값과 연결한 노드를 인자로 받는다. 여기서 헤
드가 가리키는 노드가 없는(NULL)새로운 노드라면, main에 선언한 Head값 자체를 바꾸기
위해 포인터 Head의 주소값을 가져오기 때문에 결과적으론 이중 포인터를 사용해야 한다.
노드의 맨 마지막에 삽입할 경우의 수는 두 가지가 있다.
1. Head가 NULL일 경우(텅 빈 노드)
2. Head가 NULL이 아닐 경우(하나 이상의 연결된 노드)
1번의 경우는 Head의 다음 노드에 새 노드의 주소값을 넣으면 되지만, 2번의 경우엔 끝
노드를 찾아서(NULL이 있는곳) 연결 해 줘야 한다. NODE* Current;를 선언하는 이유는
Head의 값을 바꾸지 않고 연결된 다른 노드에 접근하기 위해서 사용한다.
void PrintNode(NODE* Head, int index)
{
NODE* Current = Head;
while(Current != NULL && 0 < index)
{
index--;
Current = Current -> NextNode;
}
printf(“%s\n”, Current -> Name);
}
노드의 값을 출력하는 함수이다. 인자로는 Head와 몇 번째 노드를 출력할건지 정하는 변
수이다. while의 경우 Current != NULL은 NULL일 경우엔 노드의 끝이므로 이 이상 접근을
하지않기 위해 쓴다.
int GetCountNode(NODE * Head)
{
NODE* Current = Head;
int count = 0;
while(Current != NULL)
{
count++;
Current = Current -> NextNode;
}
return count;
}
PrintNode로 전체 출력을 하기를 원할 경우 노드의 개수를 알아내는데 사용되는 함수이다.
void InsertNode(NODE** Head, NODE* ConnectNode, int index)
{
if(0 == index)
{
ConnectNode -> NextNode = (*Head);
*Head = ConnectNode;
}
else
{
NODE* Current = *Head;
while(0 < (--index))
{
Current = Current -> NextNode;
}
ConnectNode -> NextNode = Current -> NextNode;
Current -> NextNode = ConnectNode;
}
}
노드와 노드 사이에 새로운 노드를 삽입하는 함수이다.
1. 4번지 주소의 다음 노드 주소값을 2번지 노드의 주소값으로 변경
2. 2번지 노드의 주소값을 4번지 노드의 주소로 변경
void DestroyNode(NODE** Head, int index)
{
NODE* Remove = *Head;
while(Remove != NULL && 0<(--index))
{
Remove = Remove -> NextNode;
}
if(*Head == Remove)
{
*Head = Remove -> NextNode;
}
else
{
NODE* Current = *Head;
while(Current != NULL && Current -> NextNode != Remove)
{
Current = Current -> NextNode;
}
if(Current != NULL)
{
Current -> NextNode = Remove -> NextNode;
}
}
free(Remove);
}
삭제 연산은 삽입 연산보다 훨씬 간단하다. 삭제할 노드의 앞노드(1번노드)의 다음 주소를
삭제할 노드의 다음 주소를 가리키도록만 하면 된다.
프로그래머가 할당하는 공간은 프로그래머가 해제해 줘야한다. 그러기 위해서 사용하는 함
수 free가 있다. 인자는 해제할 메모리의 주소값만 넘기면 되기 때문에 삭제할 노드의 주소
를 보내주면 된다.
구조체
다른 종류의 데이터를 하나로 묶어서 사용하는 데이터 결합법을 말한다.
각기 다른 형이나 다른 의미의 값을 가지고 있는 배열들을 한 번에 나타낼 수 있도록 구현
한 자료구 조형이다.
DATA LINK
계수부분 지수부분
struct 구조체 이름
{
자료형 자료이름;
자료형 자료이름;
}
과 같이 정의 할 수 있으며, 정의를 할 때에는 다음의 조건이 따른다.
①구조체 정의에서 바로 초기화 할 수 없다.
②구조체 선언은 문자에 해당되기 때문에 끝마친 후 ;을 붙여 주어야 한다.
구조체를 통해서 여러개의 테이타 값을 묶을 수 있게 하여 연결리스트를 구현 할 수 있도록
한다.
4)알고리즘 계획
① 다항식의 표현
1) 각 노드의 구조체 배열에서 DATA를 저장하는 배열과 다른 노드를 가리킬 수 있는 LINK
가 존재한다. 이 때 DATA는 처리하는 값을 저장하는 배열이고, LINK는 다음 노드를 가리키
는 포인터의 역할을 한다.
다항식을 구현하기 위해서는,
2) 다항식의 각 항을 계수부분 지수부분으로 나누어 생각한다.
DATA의 배열도 계수와 지수를 저장할 수 있는 구조체를 선언한다.
D A T A - 계수부분
DATA-지수부분 LINK
즉,
과 같이 한 노드가 만들어지게 된다.
3) 이러한 노드를 다항식에서 항의 개수만큼 LINK를 이용해 다음 항을 가리키고 다음 항을
가리키는 식으로 한 개의 다항식에 대해 한 개의 연결리스트를 만들어준다.
② 다항식의 덧셈과 뺄셈
1) 연결리스트로 작성 된 다항식1과 다항식2에 대해서 지수부분이 같은 노드끼리 연산을 해
준 다.
2) 다항식 3을 연결리스트로 작성하게 되는데, 이 때 1과2로 연산된 값을 3에 저장한다.
3) 1과 2에 같은 지수부분이 없어서 연산되지 않은 나머지 항들은 그대로 복사되어서 3에
저 장된다.
4) 다항식의 연산을 마쳐서 3에 저장된 결과 값을 출력한다.
③ 다항식의 곱셈
1) 다항식 1의 노드와 다항식2의 노드의 계수부분은 곱셈연산을 하고 지수부분은 더하기 연
산 을 해준다.
2) 다항식1의 노드와 다항식2의 노드는 서로의 노드가 모두 순서쌍을 한 번 씩은 이루어야
된 다.
3) 순서쌍을 이루어서 연산 된 결과 값은 새로운 연결리스트 3에 저장된다.
4) 각 노드들을 모두 더할 수 있도록, 위에서 작성하였던 ADD연산을 한 번 더 해준다.
5) 정리 되어 3에 저장된 결과 값을 출력한다.
④ 다항식의 나눗셈
1) 나누는 다항식의 최고차항이 나눠지는 최고차항과 같아지게 할 수 있는 X가 계속해서 몫
이 될 수 있다.
2) 계수의 계산은,
p(x)의 계수 = s(x)계수*q(x) 계수, q(x)계수는 p/s과 같이 된다.
3) 계속 최고차항에 맞춰서 X가 곱해져서 나눠지는 다항식에 그 결과 값을 뺄셈을 하고 있
다.
이 때 곱해지는 차수가 0이거나 나머지가 0이면 스톱한다.
만약 뺄셈을 하다가 다항식이 남게 되면 그 남은 다항식은 나머지가 된다.
5)소스 구현
①소스 분석
#include <stdio.h>
#include <stdlib.h>
typedef struct polynomial
{
int degree; //차수
double coef;//계수
struct polynomial *link;
}polynomial;
typedef struct node
{
int l;
polynomial *s;
polynomial *f;//다음을 가리킬 link
}node;
void st(node *point) //초기화
{
point->l=0;
point->s=point->f=NULL;
}
void insert_node(node *point,double coef,int degree)
{
//char *temp;
polynomial *temp=(polynomial *)malloc(sizeof(node));//연길리스트의 메모리할당을
temp의 포인터 옮기는 정도에 따라 할당해줌
if(temp==NULL)
{
exit(1);
}
temp->coef=coef;//포인터로 polynomial타입의 구조체를 불러와서 계수를 삽입
temp->degree=degree;//차수를 삽입
if(point->f==NULL)//만약 연결리스트에서 빈값을 만나면 다시 temp로 포인터를 돌
려줌
{
point->s=point->f=temp;
}
else//그 이외의 경우 point는
{
point->f->link=temp;
point->f=temp;
}
point->l++;//포인터를 길이만큼 증가해줘서 마지막 자리를 가리키도록 설정
}
/*void polynomial_add(node point1, node point2, node point3)
{
polynomial *a=point1->
*/
//다항식 출력
void print(node *point)
{
polynomial *p=point->s;//p포인터를 point를 통해 연결리스트의 처음 자리인s의
주소값을 받아옴
for(;p;p=p->link)
{
if(p->coef==0 || p->degree==0)//만약 계수나 차수가 모두 0이면 출력하
지 않음
{
printf("");
}
else if(p->degree==1)//차수가 1이면 계수만 출력
{
printf("%d",p->coef);
}
else
{
printf("%dx^%d",p->coef,p->degree); //계수가나 지수가 0이 아니
면 계수x^지수 형태로 표시
}
if(p->link==NULL)//P가 가리킨 곳이 빈값이면 아무것도 출력하지 않음.
{
printf("");
}
else//값이 있는경우 가운데에 +를 출력
{
printf(" + ");
}
}
printf("\n");
}
void poly_add(node *plist1, node *plist2, node *plist3)
{
polynomial *a=plist1->s;//다항식 1을 탐색할 포인터
polynomial *b=plist2->s;//다항식 2를 탐색할 포인터
int sum;//계수를 담을 변수
while(a&&b)
{
if(a->coef == b->coef)//지수가 같은 경우
{
sum=a->coef+b->coef;//a,b 받은 계수과 차수를 coef구조체에 저
장
if(sum!=0)
{
insert_node(plist3,sum,a->degree);//삽입함수를 불러옴,
다항식1의 계수를 저장하는 polynomial구조체. 앞에서 계산 해준 sum은 계수로 저장.a는 다
항식1의 차수를 가리킴.
}
b=b->link;//둘다 연결리스트의 link를 가리키게 함.
a=a->link;
}
else if(a->degree >b->degree)
{
insert_node(plist3,a->coef,a->degree);//차수를 가리키고 있을때,
다항식3번째=1+2의 결과값을 저장.
a=a->link;
}
else
{
insert_node(plist3,b->coef,b->degree);
b=b->link;
}
for(;a!=NULL;a=a->link)
{
insert_node(plist3,a->coef,a->degree);
}
for(;b!=NULL;b=b->link)
{
insert_node(plist3,b->coef,b->degree);
}
}
}
void main()
{
node list1, list2, list3; //다항식 입력받을 변수 선언
double a;
int b; //항의 계수와 지수를 입력받기 위한 변수
st(&list1);//st 함수 호출로 공백 리스트
st(&list2);
st(&list3);
//다항식1을 입력받는 부분
printf("다항식1의 항(계수,지수)을 입력하세요. (0 0 이면 입력종료)\n");
while (1)
{
scanf("%f %d",&a,&b);
if (a==0.0 && b==0)
{
break;
}
insert_node(&list1, a, b);
}
printf("다항식1 : ");
print(&list1); //다항식1 출력
printf("\n");
//다항식2을 입력받는 부분
printf("다항식2의 항(계수,지수)을 입력하세요. (0 0 이면 입력종료)\n");
while (1)
{
scanf("%f %d",&a,&b);
if (a==0.0 && b==0)
{
break;
}
insert_node(&list2, a, b);
}
printf("다항식2 : ");
print(&list2); //다항식2 출력
printf("\n");
// 다항식3 = 다항식1 + 다항식2
poly_add(&list1, &list2, &list3);
printf("결과 : ");
print(&list3); //다항식3 출력
}
7)최종소스#include <stdio.h>
#include <stdlib.h>
// 연결리스트 노드의 구조
typedef struct listnode
{
double coef;
int expon;
struct listnode *link;
} listnode;
// 연결리스트의 헤더
typedef struct listheader
{
int length;
listnode *head;
listnode *tail;
} listheader;
//초기화 함수
int reset(listheader *plist)
{
plist->length=0;
plist->head=plist->tail=NULL;
return 0;
}
//plist는 연결 리스트의 헤더를 가르키는 포인터
//expon는 지수 coef는 계수
int poly_make(listheader *plist,double coef,int expon)
{
listnode *n=(listnode*)malloc(sizeof(listnode));
n->coef=coef;
n->expon=expon;
n->link=NULL;
if(plist->tail==NULL)
{
plist->head=plist->tail=n;
}
else
{
plist->tail->link=n;
plist->tail=n;
}
plist->length++;
return 0;
}
//list3 = list1 + list2
int poly_add(listheader *plist1,listheader *plist2, listheader *plist3)
{
double sum;
listnode *a=plist1->head;
listnode *b=plist2->head;
while(a,b)
{
if(a->expon == b->expon)
{
sum=a->coef+b->coef;
if(sum!=0)
{
poly_make(plist3, sum, a->expon);
a=a->link;
b=b->link;
}
}
else if(a->expon > b->expon)
{
poly_make(plist3, a->coef, a->expon);
a=a->link;
}
else
{
poly_make(plist3, b->coef, b->expon);
b=b->link;
}
}
return 0;
}
//출력 하기 위한 함수
int poly_print(listheader *plist)
{
listnode *p = plist->head;
for(1; p; p = p->link)
{
printf("%.1fX^%d", p->coef, p->expon);
if(p->link!=NULL)
{
printf(" + ");
}
}
printf("\n");
return 0;
}
//메모리 해제 함수
int poly_delete(listheader *plist)
{
listnode *n=plist->head;
listnode *del;
while(n->link!=NULL)
{
del=n;
n=del->link;
plist->head=n;
free(del);
}
free(n);
return 0;
}
//메인 함수
int main()
{
listheader list1, list2, list3; //리스트 변수 선언
// 연결 리스트이 초기화
reset(&list1);
reset(&list2);
reset(&list3);
// P(X)를 생성
poly_make(&list1, -3.7, 3);
poly_make(&list1, 2.5, 2);
poly_make(&list1, 1, 1);
// S(X)를 생성
poly_make(&list2, 8.2, 3);
poly_make(&list2, -3.1, 2);
poly_make(&list2, 10.2, 1);
// T(X)를 생성
// T(X)=P(X)+S(X)
poly_add(&list1, &list2, &list3);
printf("P(X)의 다항식 : ");
poly_print(&list1);
printf("S(X)의 다항식 : ");
poly_print(&list2);
printf("T(X)의 다항식 : ");
poly_print(&list3);
// 할당한 메모리의 리스트를 해제
poly_delete(&list1);
poly_delete(&list2);
poly_delete(&list3);
return 0;
}
•실행 화면
8)시간복잡도/공간복잡도
Recommended