1 / 50

연결 리스트 (Linked List)

연결 리스트 (Linked List). 단순 연결 리스트 (Simple Linked List). 단순 연결 리스트 > 정의. 정보를 저정하는 노드와 바로 다음의 노드를 가리키는 링크 하나로 구성됨. 단순연결리스트의 노드 정의. Struct _node { int key; /* 정보저장 * / struct _node *next; /* 다음 노드의 위치 저장 * / };. 개선. Typedef struct _node { int key;

gracie
Download Presentation

연결 리스트 (Linked List)

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 연결 리스트 (Linked List) 단순 연결 리스트 (Simple Linked List)

  2. 단순 연결 리스트 > 정의 • 정보를 저정하는 노드와 바로 다음의 노드를 가리키는 링크 하나로 구성됨 • 단순연결리스트의 노드 정의 Struct _node { int key; /* 정보저장 */ struct _node *next; /* 다음 노드의 위치 저장 */ }; 개선 Typedef struct _node { int key; struct _node *next; } node;

  3. 단순연결리스트 > 초기화 • Init_list() 함수 node *head, *tail; /* 전역변수로 정의 */ void init_list(void) { head = (node*) malloc(sizeof(node)); tail = (node*) malloc(sizeof(node)); head->next = tail; tail->next = tail; }

  4. 단순연결리스트 > 삽입 • insert_after() 함수 node *insert_after(int k, node* t) { node *s; s = (node *)malloc(sizeof(node)); s->key = k; s->next = t->next; t->next = s; return s; }

  5. 단순연결리스트 > 삭제 • delete_next() 함수 int delete_next(node *t) { node *s; if(t->next==tail) return 0; s = t->next; t->next = t->next->next; free(s); return 1; }

  6. 단순연결리스트 > 찾기 • find_node 함수 • 인자 k 의 값을 가지는 노드를 찾는다. • 호출측에서는 리턴값이 tail 인지 아닌지 검사하면 검색에 실패했는지 알수 있도록 한다. node *find_node(int k) { node *s; s = head->next; while(s->key != k && s != tail) s = s->next; return s; }

  7. 단순연결리스트 > 찾아서 삭제 • delete_node() 함수 int delete_node(int k) { node *s; /* 검색할 노드 */ node *p; /* s 가 가리키는 노드의 앞노드 */ p = head; s = p->next; while(s->key != k && s != tail) { p = p->next; /* p는 다음 노드로 */ s = p->next; /* s는 p의 다음 노드로 */ } if(s != tail) { /* 키 값을 찾음 */ p->next = s->next; /* p의 다음 노드는 s의 다음노드 */ free(s); /* s는 연결에서 빠진다(삭제) */ return 1; } else return 0; } }

  8. 단순연결리스트 > 찾아서 삽입 • insert_node() 함수 • 주어진 키 k 앞의 노드에 t 값을 갖는 노드를 삽입 node *insert_node(int t, int k) { /* k 앞에 t를 삽입 */ node *s; /* 키 검색을 따라가는 포인터*/ node *p; /* s의 앞 노드를 가리키는 포인터 */ node *r; /* 삽입하는 노드를 만들기 위한 포인터 */ p = head; s = p->next; while(s->key != k && s != tail) { p = p->next; s = p->next; } if(s != tail){ /* 찾았으면 */ r = (node *)malloc(sizeof(node)); r->key = t; p->next = r; r->next = s; } return p->next; /* 삽입된 노드의 주소값 */ }

  9. 단순연결리스트 > 동적인 정렬 • ordered_sort() • 지금의 연결리스트가 오름차순으로 정렬되어 있다고 가정함 • 주어진 키 값에 대해서 정렬순서를 깨지 않도록 삽입 node *ordered_insert(int k) { node *s; /* 검색을 따르는 포인터 */ node *p; /* s의 앞노드를 가리키는 포인터 */ node *r; /* p와 s 사이에 삽입될 노드의 포인터 */ p = head; s = p->next; while(s->key <= k && s != tail) { /* k가 들어갈 장소를 찾음 */ p = p->next; s = p->next; } r = (node *)malloc(sizeof(node)); r->key = k; p->next = r; r->next = s; return r; }

  10. 단순연결리스트 > 출력 • print_list() 함수 void print_list(node* t) { printf(“\n”); while(t != tail) { printf(“%-8d”, t->key); t = t->next; } }

  11. 단순연결리스트 > 모든요소 삭제 • delete_all() 함수 node *delete_all(void) { node *s; node *t; t = head->next; /* 머리 다음부터 삭제 */ while( t != tail) { s = t; /* s는 삭제할 다음 노드를 물고 있음 */ t = t->next; /* t는 다음 노드로 */ free(s); } head->next = tail; return head; }

  12. 단순연결리스트 > 정리 #include<stdio.h> typedef struct_node { int key; struct _node *next; } node; node *head, *tail; void main(void) { node *t; init_list(); ordered_insert(10); ordered_insert(5); ordered_insert(8); ordered_insert(3); ordered_insert(1); ordered_insert(7); ordered_insert(8); printf(“\nInitial Linked List is “); print_list(head->next); printf(“\nFinding 4 is %ssuccessful “, find_node(4) == tail ? “un” : “”); t = fined_node(5); printf(“\nFinding 5 is %ssuccessful “, t==tail ? “un” : “”); printf(“\nInserting 9 after 5”); insert_after(9, t); print_list(head->next);

  13. 단순연결리스트 > 정리(계속) t = find_node(10); printf(“\nDeleting next last node”); delete_next(t); print_list(head->next); t=find_node(3); printf(“\nDeleting next 3”); delete_next(t); print_list(head->next); printf(“\nInsert node 2 before 3”); insert_node(2, 3); print_list(head->next); printf(“\nDeleting node 2”); if(!delete_node(2)) printf(“\n deleting 2 is unsuccessful “); printf_list(head->next); printf(“\nDeleting node 1”); delete_node(1); print_list(head->next); printf(“\nDeleting all node”); delete_all(); print_list(head->next); }

  14. 연결 리스트(Linked List) 환형 연결 리스트(Circular Linked List)

  15. 환형 연결 리스트 > 개념 • 환형 연결 리스트 • 단순 연결 리스트와 같은 노드 구조를 가지고 있다. • 제일 마지막 노드는 가장 처음의 노드를 가리키고 있다. • tail 이라는 개념이 없다.

  16. 환형연결리스트 > 문제 제시 • 요셉의 문제 • A 부터 J 까지의 10명의 사람이 시계 방향 순서대로 원을 지어 앉아있다. • A 부터 시작하여 4명 간격으로 사람을 그 원에서 뽑아낸다고 하면 그 순서는 어떻게 될 것인가? • 프로그램은 사용자로부터 키보드로 사람의 수 n 과 간격 step 을 입력받아 사람의 수만큼 1 부터 n까지의 정수를 키로 가지는 노드를 환형 연결리스트로 구성한다. • 다음에 처음의 노드로부터 시작하여 step만큼 이동한 다음 그 위치의 노드를 삭제하고, 다음에 또 step 만큼 이동하고 그 노드를 삭제하는 식으로 계속하여 노드가 하나도 남지 않을 때까지 반복한다.

  17. 환형연결리스트 > Code 1 #include<stdio.h> #include<stdlib.h> typedef struct _node { int key; struct _node *next; } node; node *head; void insert_nodes(int k) { /* 1부터 k 까지의 값을 가지는 환형 연결리스트 구성 */ node *t; int i; t = (node *)malloc(sizeof(node)); t->key = 1; head = t; /* 연렬 리스트의 시작점 */ for(i=2 ; i<=k ; i++) { t->next = (node *)malloc(sizeof(node)); t = t->next; t->key = i; } t->next = head; } void delete_after(node *t) { /* t 다음의 노드를 삭제 */ node *s; s = t->next; t->next = t->next->next; free(s); }

  18. 환형연결리스트 > Code 2 void josephus(int n, int m) { /* 요셉의 문제를 풂, n 개의 노드를 m 간격으로 */ node *t; int i; insert_nodes(n); /* 환영 연결 리스트를 구성 */ t = head; printf("\nAnswer : "); while( t != t->next){ /* 연결 리스트가 남아있을 동안 */ for(i=0 ; i<m-1 ; i++) t = t->next; printf("%d ", t->next->key); delete_after(t); /* 출력하고 삭제 */ } printf("%d", t->key); /* 마지막 노드 출력 */ } void main(void) { int n, m; printf("\nIf you want to quit, enter 0 or minus value"); while(1) { printf("\nEnter N and M -> "); scanf("%d %d", &n, &m); if(n<=0 || m<=0) return; josephus(n, m); } }

  19. 이중 연결 리스트(Doubly Linked List)

  20. 이중 연결 리스트 • 장단점 • 바로 전의 노드에도 접근할 수 있다. • 노드당 2~4 바이트 정도가 더 소모된다. • 함수들이 일관된 방식을 가지고 있다. • 삽입 삭제시 좀더 복잡하다. Typedef struct _dnode { int key; struct _dnode *prev; struct _dnode *next; } dnode;

  21. Init_dlist() 함수 Dnode *head, *tail; Void init_dlist(void) { head = (dnode *)malloc(sizeof(dnode)); tail = (dnode *)malloc(sizeof(dnode)); head->next = tail; head->prev = head; tail->next = tail; tail->prev = head; } 그림

  22. Insert_dnode_ptr() 함수 • 포인터 t의 앞에 k를 가지는 노드를 하나 삽입 dnode * insert_dnode_ptr(int k, dnode *t) { /* t 앞에 k를 삽입 */ dnode *i; // 삽입될 노드 if(t==head) //머리 앞에는 아무것도 삽입할 수 없다. return NULL; i = (dnode *)malloc(sizeof(dnode)); i->key = k; t->prev->next = i; i->prev = t->prev; t->prev = i; i->next = t; return i; }

  23. delete_dnode_ptr() 함수 • 주어진 노드의 포인터 p를 이중 연결 리스트에서 삭제한다. int delete_dnode_ptr(dnode *p) { if (p==head || p==tail) return 0; /* 머리나 꼬리는 지울 수 없다 */ p->prev->next = p->next; p->next->prev = p->prev; free(p); return 1; }

  24. find_dnode() 함수 dnode *find_dnode(int k) { dnode *s; s = head->next; while(s->key != k && s != tail) s = s->next; return s; }

  25. delete_dnode() 함수 • k 값을 찾아서 해당 노드를 삭제한다. • find_dnode() 함수를 사용한다. int delete_dnode(int k) { dnode *s; s = find_dnode(k); if(s != tail) { /* s가 tail 이 아니면 찾은 것이다 */ s->prev->next = s->next; s->next->prev = s->prev; free(s); return 1; } return 0; }

  26. insert_dnode() 함수 • t 값을 가지는 노드를 찾아서 그 앞에 k값을 가지는 노드를 삽입 dnode *insert_dnode(int k, int t) { dnode *s; dnode *i = NULL; s = find_node(t); if(s != tail) { /* 찾았다면 */ i = (dnode *)malloc(sizeof(dnode)); i->key = k; s->prev->next = i; i->prev = s->prev; s->prev = i; i->next = s; } return i; /* 못찾았으면 NULL 을 리턴한다 */ }

  27. ordered_insert() 함수 • 입력되는 정수들을 오름차순으로 정렬된 상태로 삽입 dnode *ordered_insert(int k) { dnode *s; dnode *i; s = head->next; while(s->key <= k && s != tail) s = s->next; i = (dnode*)malloc(sizeof(dnode)); i->key = k; s->prev->next = i; i->prev = s->prev; s->prev = i; i->next = s; return i; }

  28. 단순연결리스트의 응용 명함관리

  29. 명함관리 프로그램 • NAMECARD.C • 개선사상 • gets() 함수를 이용한 입력은 배열의 크기를 넘는 문자열을 입력받을 수 있다. • 명함의 입력시 순서를 유지하면서 노드를 삽입하게 하면 이진검색을 사용할 수 있다.

  30. 스택

  31. 스택의 개념 • 개념

  32. 스택의 구현(1) • 구현 #define MAX 10 int stack[MAX]; // 스택의 긴 통 int top; // 스택의 상단 void init_stack(void) { top = -1; } int push(int t) { if(top >= MAX-1) //스택이 꽉 찼는가? { printf("\n Stack overflow."); return -1; } stack[++top] = t; return t; } int pop(void) { if(top < 0) //스택이 텅 비었는가? { printf("\n Stack underflow."); return -1; //에러 표시 } return stack[top--]; }

  33. 스택의 구현(2) void main(void) { int i; init_stack(); printf("\nPush 5, 4, 7, 8, 2, 1"); push(5); push(4); push(7); push(8); push(2); push(1); print_stack(); printf("\nPop"); i = pop(); print_stack(); printf("\n popping value is %d", i); printf("\nPush 3, 2, 5, 7, 2"); push(3); push(2); push(5); push(7); push(2); print_stack(); printf("\nNow stack is full, push 3"); push(3); print_stack(); printf("\nInitialize stack"); init_stack(); print_stack(); printf("\nNow stack is empty, pop"); i = pop(); print_stack(); printf("\n popping value is %d", i); }

  34. 연결리스트를 이용한 스택의 구현

  35. 연결리스트를 이용한 스택의 구현 int pop(void) { node *t; int i; if(head->next == tail) { /* 스택이 비었는가? */ printf("\n Stack underflow."); return -1; } t = head->next; i = t->key; head->next = t->next; free(t); return i; } void clear_stack(void) { node *t, *s; t = head->next; while(t != tail) { s = t; t = t->next; free(s); } head->next = tail; } void print_stack(void) { node *t; t = head->next; printf("\n Stack contents : Top --> Bottom\n"); while(t != tail) { printf("%-6d", t->key); t = t->next; } } typedef struct _node { int key; struct _node *next; } node; node *head, *tail; void init_stack(void) { head = (node*)malloc(sizeof(node)); tail = (node*)malloc(sizeof(node)); head->next = tail; tail->next = tail; } int push(int k) { node * t; if((t=(node*)malloc(sizeof(node)) == NULL) { /* 메모리가 부족한 경우 에러 */ printf("\n Out of memory..."); return -1; } t->key = k; t->next = head->next; head->next = t; return k; }

  36. 스택의 활용

  37. 수식의 표기법 • 중위표기법(Infid notation) • (A + (B * C)) • 후위표기법(Prefix notation ) • A B C * + • 우선순위를 고려하지 않은 중위표기법을 후위표기법으로 변환하는 방법 • ‘(‘문자는 무시하고 넘어간다 • 피연산자는 그대로 출력한다 • 연산자는 스택에 푸시한다 • ‘)’를 만나면 스택에서 팝하여 출력한다.

  38. 우선선위를 고려하지 않은 구현 • postfix1() 함수 void postfix1(char *dst, char *src) { char c; init_stack(); /* 스택 초기화 */ while(*src) { /* 중위표기법의 수식이 남아있는 동안 */ if(*src == ')') { *dst++ = pop(); *dst++ = ' '; /* 문자의 구분을 위해 출력 */ src++; } else if(*src=='+' || *src=='-' || *src=='*' || *src=='/') { push(*src); src++; } else if(*src>='0' && *src<='9') { do { *dst++ = *src++; }while(*src>='0' && *src<='9'); *dst++ = ' '; } else src++; } *dst = 0; }

  39. 우선순위 고려 • 우선순위를 고려한 중위표기법을 후위표기법으로 변환하는 방법 • ‘(‘를 만나면 스택에 푸시한다. • ‘)’를 만나면 스택에서 ‘(‘가 나올 때까지 팝하여 출력하고 ‘(‘는 팝하여 버린다. • 연산자를 만나면 스택에서 그 연산자보다 낮은 우선순위의 연산자를 만날 때까지 팝하여 출력한뒤 자신을 푸시한다. • ‘(‘ = 0 • +, - = 1 • *, / = 2 • 피연산자는 그냥 출력한다. • 오든 입력이 끝나면 스택에 있는 연산자들을 모두 팝하여 버린다. • (2*(3+6/2)+2)/4+3  2 3 6 2 / + * 2 + 4 / 3 +

  40. 연습문제 • 다음의 수식을 후위표기법으로 변환하세요. • 2 + 3 * 4 • ((5+4*7)+3)/7 • (3+4)*(4-7) • ((3+4)*(4-7))+(2+7*8/7)

  41. 우선순위를 고려한 구현 void postfix(char *dst, char *src) { char c; init_stack(); /* 스택의 초기화 */ while(*src) { if(*src == '(') { /* 스택에 푸시 */ push(*src); src++; } else if(*src==')') { /* (가 나올 때까지 팝 */ while(get_stack_top() != '(') { *dst++ = pop(); *dst++ = ' '; } pop(); /* (는 버린다 */ src++; } else if(is_operator(*src)) { /* 연산자 이면 */ while(!is_stack_empty() && precedence(get_stack_top()) >= precedence(*src)) { /* 우선순위가 높은 연산자들은 모두 팝 */ *dst++ = pop(); *dst++ = ' '; } push(*src); src++; } else if(*src>='0' && *src<='9') { /* 피연산자는 그냥 출력 */ do { *dst++ = *src++; }while (*src>='0' && *src<='9'); *dst++ = ' '; } else src++; } while(!is_stack_empty()) { /* 모두 끝났으면 스택에 있는 내용을 모두 팝 */ *dst++ = pop(); *dst++ = ' '; } dst--; *dst = 0; }

  42. 수식의 평가 int cal(char *p) { int i; init_stack(); while (*p) { if(*p>='0' && *p<='9') { i=0; do { i=i*10 + *p-'0'; p++; }while(*p>='0' && *p<='9'); push(i); } else if(*p=='+') { push(pop() + pop()); p++; } else if(*p=='*') { push(pop() * pop()); p++; } else if(*p == '-') { /* 교환법칙이 성립하지 않는 연산자들 */ i = pop(); push(pop()-i); p++; } else if(*p=='/') { i=pop(); push(pop()/i); p++; } else p++; } return pop(); }

  43. 큐의 개념 • 개념

  44. 큐의 구현(배열) • 배열을 이용한 큐의 구현 #define MAX 10 int queue[MAX]; int front, rear; int put(int k) { if((rear+1)%MAX == front) { printf("\n Queue overflow."); return -1; } queue[rear] = k ; rear = (++rear) % MAX return k; } int get(void) { int i; if(front == rear) //큐가 비어있는가 { printf("\n Queue underflow."); return -1; } i = queue[front]; front = ++front % MAX; return i; }

  45. 큐의 구현(연결리스트) • 이중연결리스트 이용 • 단순연결리스트를 이용할 경우 get은 자연스러우나 • put 동작의 꼬리노드의 앞에 새로운 노드를 삽입해야 한다. • 노드의 정의 typedef struct _dnode { int key; struct _dnode *prev; struct _dnode *next; } dnode; dnode *head, *tail;

  46. 큐의 초기화 • init_queue() 함수 void init_queue(void) { head = (dnode *)malloc(sizeof(dnode)); tail = (dnode *)malloc(sizeof(dnode)); head->prev = head; head->next = tail; tail->prev = head; tail->next = tail; }

  47. put 동작 • put() 함수 int put(int k) { dnode *t; if((t=(dnode *)malloc(sizeof(dnode)) == NULL) { /* 메모리가 다 되었으면 */ printf(“\n Out of memory.”); return –1; } t->key = k; tail->prev->next = t; /* t를 꼬리의 앞에 삽입함 */ t->prev = tail->prev; tail->prev = t; t->next = tail; return k; }

  48. get 동작 • get() 함수 int get(void) { dnode *t; int i; t = head->next; if(t==tail) { printf(“\n Queue underflow.”); return –1; } i = t->key; /* 리턴 할 값을 물려둠 */ head->next = t->next; /* 머리 다음 노드를 삭제 */ t->next->prev = head; free(t); return i; }

  49. 큐의 모든 내용을 삭제 • clear_queue() 함수 void clear_queue(void) { dnode *t; dnode *s; t = head->next; while(t!=tail) { s = t; /* 삭제를 위해 물려둠 */ t = t->next; /* t는 다음으로 넘어감 */ free(s); } head->next = tail; tail->prev = head; }

More Related