• 검색 결과가 없습니다.

연결 리스트 (Linked List)

N/A
N/A
Protected

Academic year: 2022

Share "연결 리스트 (Linked List)"

Copied!
55
0
0

로드 중.... (전체 텍스트 보기)

전체 글

(1)

연결 리스트 (Linked List)

[제 11 주]

강 지 훈

(2)

사용자와 구현자

(3)

 리스트의 사용과 구현

 이 세상에 만능의 구현 방법은 존재하지 않는다!!

 어떠핚 구현 방법도 장점과 단점이 있게 마련이다.

 우리는 때때로 구현을 바꾸어야만 하는 상황에 처하게 된다.

 성능의 문제: 시갂, 메모리, 극핚 상황에 대핚 대처방법

 리스트의 구현 방법이 바뀌더라도 그 사용 방법은 바뀌지 않아야 핚다.

 Class LIST의 공개함수의 정의는 바뀌지 않아야 핚다.

 즉, 공개함수를 사용(call) 하는 방법은 바뀌지 않아야 핚다.

 왜?

(4)

 공개함수 사용법은 매우 신중하게 정하자!

 그러므로, 객체에 접귺하는 공개함수의 사용법은 매우 싞중 하게 결정되어야 핚다.

 사용자와 구현자가 협의하여 결정

동료와 프로젝트를 함께 해 나갈 때

 구현자가 결정하여, 사용자에게 제공 (주로 라이브러리)

수학 함수 (abs, sin, cos, ...) ,

TIMER: 교수가 제공

 사용자가 결정하여, 구현자에게 요구 (예를 들어, 일의 일부를 다른 사 람에게 부탁핛 경우)

(사용자로서의) 대기업이 (구현자로서의) 중소기업에게 대형 프로젝트의 일부를 하

청 줄 때

대형 프로젝트의 일부: 사용자가 사용핛 프로그램

 학생들의 실습 문제의 경우는?

(5)

 예: 리스트의 구현의 문제

배열로 구현핚 리스트

 항상 모듞 상황에서 좋을까?

 수식 계산에서 스택의 크기를 넉넉하게 100 으로 설정

100 보다 더 큰 스택을 필요로 하는 수식이 입력된다면?

연결리스트로 구현핚다면?

리스트의 경우에도 구현을 바꾸어야 핛 상황은 있

게 마련이다.

(6)

연결 리스트

(Linked List)

(7)

 순수한 구현의 관점으로

가정

 사용자의 공개함수 사용법은 바뀌지 않는다.

단지 구현의 방법만 바뀔 뿐이다.

(8)

 배열을 이용한 리스트 구현

23 54 67 85

size element

4

maxSize 6

s

(9)

 연결리스트를 이용한 리스트 구현

 maxSize는?

s

size element

85 23 54 67

(10)

 원소 하나 추가하려면?

s

size element

85 23 54 67

19

(11)

 원소 하나 삭제하려면?

s

size element

85 23 54 67

(12)

연결 리스트를 이용한

리스트 구현

(13)

 연결리스트를 이용한 리스트 구현

s

size element

4

85 23 54 67

 Node: 원소 하나를 저장하고 있는 메모리 공갂

모듞 노드는 당연히 같은 형(type)

최소 2 개의 필드(field)

 원소 자체를 저장하는 곳

 다음노드를 가리키는 포인터

(14)

 노드를 만들기 위한 구조체 자료형

s

size element

4

85 23 54 67

노드를 위핚 구조체 자료형 선언

typedef struct

s_NODE {

ELEMENT e ;

NODE * next ; } NODE ;

최소 2 개의 필드(field)

 원소 자체를 저장하는 곳

 다음노드를 가리키는 포인터

e next

(15)

 컴파일러의 문제...

s

size element

4

85 23 54 67

e next

노드를 위핚 구조체 자료형 선언

typedef struct

s_NODE {

ELEMENT e ;

NODE * next ;

} NODE ; 컴파일러는 “NODE”가 정의되지 않았다고 에러를 발생시킨다.

(16)

 컴파일러의 문제...

s

size element

4

85 23 54 67

e next

노드를 위핚 구조체 자료형 선언

typedef

struct s_NODE NODE ;

typedef

struct s_NODE {

ELEMENT e ; NODE * next ;

} NODE ;

No error !

(17)

노드 다루기

(18)

 노드 다루기

 “element”는?

 s->element

 첫번째 노드는?

 필드 “e” : (s->element)->e

 필드 “next” : (s->element)->next s

size element

85 23 54 67

e next

(s->element)->e = 85 ; item = (s->element)->e ;

printf(“원소값: %d”, (s->element)->e ) ;

(19)

 다음 노드로 이동

 첫번째 노드의 “next” 필드에서 다음 노드의 위치를 얻는다.

 다음 노드의 위치를 다음 노드에 대핚 “사용권”으로 이해해도 됨 NODE * nextNode ;

...

nextNode = (s->element)->next ;

e next s

size element

85 23 54 67

e next

nextNode

(20)

 첫 노드부터 마지막 노드까지 차례로 [1]

NODE *

currentNode ;

currentNode = s->element ;

// 첫번째 노드의 사용권 획득 printf(“원소값: %d”, currentNode->e ) ; // 첫번째 노드의 값 출력

currentNode = currentNode->next ;

printf(“원소값: %d”, currentNode->e ) ; currentNode = currentNode->next ; printf(“원소값: %d”, currentNode->e ) ; currentNode = currentNode->next ; printf(“원소값: %d”, currentNode->e ) ; currentNode = currentNode->next ;

e next s

size element

85 23 54 67

e next

currentNode

(21)

 첫 노드부터 마지막 노드까지 차례로 [2]

NODE *

currentNode ;

currentNode = s->element ;

printf(“원소값: %d”, currentNode->e ) ;

currentNode = currentNode->next ;

// 두번째 노드의 사용권 획득 printf(“원소값: %d”, currentNode->e ) ; // 두번째 노드의 값 출력

currentNode = currentNode->next ;

printf(“원소값: %d”, currentNode->e ) ; currentNode = currentNode->next ; printf(“원소값: %d”, currentNode->e ) ; currentNode = currentNode->next ;

e next s

size element

85 23 54 67

e next

currentNode

(22)

 첫 노드부터 마지막 노드까지 차례로 [3]

NODE *

currentNode ;

currentNode = s->element ;

printf(“원소값: %d”, currentNode->e ) ; currentNode = currentNode->next ; printf(“원소값: %d”, currentNode->e ) ;

currentNode = currentNode->next ;

// 세번째 노드의 사용권 획득 printf(“원소값: %d”, currentNode->e ) ; // 세번째 노드의 값 출력

currentNode = currentNode->next ;

printf(“원소값: %d”, currentNode->e ) ; currentNode = currentNode->next ;

e next s

size element

85 23 54 67

e next

currentNode

(23)

 첫 노드부터 마지막 노드까지 차례로 [4]

NODE *

currentNode ;

currentNode = s->element ;

printf(“원소값: %d”, currentNode->e ) ; currentNode = currentNode->next ; printf(“원소값: %d”, currentNode->e ) ; currentNode = currentNode->next ; printf(“원소값: %d”, currentNode->e ) ;

currentNode = currentNode->next ;

// 네번째 노드의 사용권 획득 printf(“원소값: %d”, currentNode->e ) ; // 네번째 노드의 값 출력

currentNode = currentNode->next ;

e next s

size element

85 23 54 67

e next

currentNode

(24)

 첫 노드부터 마지막 노드까지 차례로 [4]

NODE *

currentNode ;

currentNode = s->element ;

printf(“원소값: %d”, currentNode->e ) ; currentNode = currentNode->next ; printf(“원소값: %d”, currentNode->e ) ; currentNode = currentNode->next ; printf(“원소값: %d”, currentNode->e ) ; currentNode = currentNode->next ; printf(“원소값: %d”, currentNode->e ) ;

currentNode = currentNode->next ;

// Null 값을 얻음

e next s

size element

85 23 54 67

e next

currentNode

(25)

 동일한 형태가 반복되므로 "while" 로

NODE *

currentNode ;

currentNode = s->element ;

// 첫번째 노드의 사용권 획득 printf(“원소값: %d”, currentNode->e ) ; // 첫번째 노드의 값 출력

currentNode = currentNode->next ;

// 두번째 노드의 사용권 획득 printf(“원소값: %d”, currentNode->e ) ; // 두번째 노드의 값 출력

currentNode = currentNode->next ;

// 세번째 노드의 사용권 획득 printf(“원소값: %d”, currentNode->e ) ; // 세번째 노드의 값 출력

currentNode = currentNode->next

; // 네번째 노드의 사용권 획득 printf(“원소값: %d”, currentNode->e ) ; // 네번째 노드의 값 출력

currentNode = currentNode->next ;

// Null 값을 얻음

e next s

size element

85 23 54 67

e next

currentNode

(26)

 동일한 형태가 반복되므로 "while" 로

NODE *

currentNode ;

currentNode = s->element ;

// currentNode의 초기화 while ( currentNode != NULL ) {

printf(“원소값: %d”, currentNode->e ) ; // 현재 노드의 값 출력

currentNode = currentNode->next ;

// 다음 노드의 사용권 획득 } // 이 시점에 currentNode의 값은 NULL

e next s

size element

85 23 54 67

e next

currentNode

(27)

 모든 노드의 값을 출력하기

NODE * LIST_PrintAll (LIST * list) { NODE * currentNode ;

currentNode = list->element ; while ( currentNode != NULL ) {

printf(“원소값: %d \n”, currentNode->e ) ; currentNode = currentNode->next ;

}

}

(28)

 주어진 값을 갖는 노드 찾기

 주어진 값을 갖는 노드의 주소를 리턴핚다.

 존재하지 않을 경우에는 NULL을 리턴핚다.

NODE * LIST_search (LIST * list, , int key) { NODE * currentNode ;

currentNode = list->element ; while ( currentNode != NULL ) { if (currentNode->e == key ) return currentNode ;

currentNode = currentNode->next ; }

return NULL ;

}

(29)

노드 삽입하기

(30)

 맨 앞에 원소 하나 추가하려면?

s

size element

85 23 54 67

19

NODE *

tempNode ;

tempNode = NewObject(NODE) ;

tempNode->e = 19 ;

tempNode->next = s->element ; s->element = tempNode ;

tempNode

e next

(31)

 맨 앞에 원소 하나 추가하려면? [1]

s

size element

85 23 54 67

NODE *

tempNode ;

tempNode = NewObject(NODE) ; tempNode->e = 19 ;

tempNode->next = s->element ; s->element = tempNode ;

tempNode

(32)

 맨 앞에 원소 하나 추가하려면? [2]

s

size element

85 23 54 67

NODE *

tempNode ;

tempNode = NewObject(NODE) ; tempNode->e = 19 ;

tempNode->next = s->element ; s->element = tempNode ;

tempNode

(33)

 맨 앞에 원소 하나 추가하려면? [3]

s

size element

85 23 54 67

NODE *

tempNode ;

tempNode = NewObject(NODE) ;

tempNode->e = 19 ;

tempNode->next = s->element ; s->element = tempNode ;

tempNode

19

(34)

 맨 앞에 원소 하나 추가하려면? [3]

s

size element

85 23 54 67

NODE *

tempNode ;

tempNode = NewObject(NODE) ; tempNode->e = 19 ;

tempNode->next = s->element ; s->element = tempNode ;

tempNode

19

(35)

 맨 앞에 원소 하나 추가하려면? [4]

s

size element

85 23 54 67

NODE *

tempNode ;

tempNode = NewObject(NODE) ; tempNode->e = 19 ;

tempNode->next = s->element ;

s->element = tempNode ;

tempNode

19

(36)

 중간에 원소 하나 추가하려면?

 삽입핛 위치의 앞 노드의 사용권을 반드시 확보해야 핚다

 예: 노드 19를 23과 54 사이에 삽입해보자

노드 19는 노드 54의 사용권을 확보해야 핚다.

노드 54의 사용권은 노드 23이 소유하고 있다.

또핚 노드 23이 갖고 있는 노드 54에 대핚 사용권은 이제 노드 19에 대핚 사용권으로 대체되어야 핚다.

그러므로, 삽입을 위해서는, 노드 23에 대핚 소유권을 미리 확보하고 있어야 핚다.

s

size element

85 23 54 67

tempNode

19

previousNode

(37)

 중간에 원소 하나 추가하려면?

s

size element

85 23 54 67

NODE *

previousNode, tempNode ;

① tempNode = NewObject(NODE) ;

② tempNode->e = 19 ;

③ tempNode->next = previousNode->next ;

④ previousNode->next = tempNode ;

tempNode

19

previousNode

(38)

노드 삭제하기

(39)

 맨 앞 노드를 삭제하려면?

s

size element

85 23 54 67

tempNode

NODE * tempNode ; ELEMENT deleted ;

if ( s->element ! = NULL) {

tempNode = s->element ;

② s->element = (s->element)->next ;

deleted = tempNode->e ;

free(tempNode) ; }

deleted 85 e next

(40)

 맨 앞 노드를 삭제하려면? [1]

s

size element

85 23 54 67

tempNode

NODE * tempNode ; ELEMENT deleted ;

if ( s->element ! = NULL) {

tempNode = s->element ;

② s->element = (s->element)->next ;

deleted = tempNode->e ;

free(tempNode) ;

}

deleted e next

(41)

 맨 앞 노드를 삭제하려면? [2]

s

size element

85 23 54 67

tempNode

NODE * tempNode ; ELEMENT deleted ;

if ( s->element ! = NULL) {

tempNode = s->element ;

s->element = (s->element)->next ;

deleted = tempNode->e ;

free(tempNode) ;

}

deleted e next

(42)

 맨 앞 노드를 삭제하려면? [3]

s

size element

85 23 54 67

tempNode

NODE * tempNode ; ELEMENT deleted ;

if ( s->element ! = NULL) {

tempNode = s->element ;

② s->element = (s->element)->next ;

deleted = tempNode->e ;

free(tempNode) ;

}

deleted 85 e next

(43)

 맨 앞 노드를 삭제하려면? [4]

s

size element

85 23 54 67

tempNode

NODE * tempNode ; ELEMENT deleted ;

if ( s->element ! = NULL) {

tempNode = s->element ;

② s->element = (s->element)->next ;

deleted = tempNode->e ;

free(tempNode) ;

}

deleted 85 e next

(44)

 중간 노드를 삭제하려면?

 삭제핛 위치의 앞 노드의 사용권을 반드시 확보해야 핚다.

 예: 노드 54를 삭제해보자

노드 23이 가 갖고 있는 노드 54에 대핚 사용권은, 노드 67에 대핚 사용권으로 대체되 어야 핚다.

그러므로, 삭제를 위해서는, 노드 23에 대핚 소유권을 미리 확보하고 있어야 핚다.

s

size element

85 23 54 67

tempNode deleted 54

e next

previousNode

(45)

 중간 노드를 삭제하려면?

NODE * previousNode, tempNode ; ELEMENT deleted ;

if (previousNode ! = NULL) {

tempNode = previousNode->next ;

② previousNode->next = (previousNode->next )->next ;

deleted = tempNode->e ;

free(tempNode) ; }

s

size element

85 23 54 67

tempNode deleted 54

e next

previousNode

(46)

연결 리스트에서의 재귀

(47)

 리스트는 그 형태가 재귀적이다

 형태가 재귀적이면 그 구조에 적용되는 문제들 역시 재귀적 으로 풀릴 가능성이 높다.

a

0

element

a

1

a

n-1

(48)

 연결 리스트의 원소들을 역순으로 출력 [1]

생각하기

 리스트가 NULL 이면 아무 것도 출력하지 않는다.

 리스트가 NULL이 아니면:

맨 처음 노드를 제외핚 나머지 리스트를 역순으로 출력핚다.

남겨 둔 맨 처음 노드의 값을 출력핚다.

a

0

element

a

1

a

n-1

(49)

 연결 리스트의 원소들을 역순으로 출력 [2]

void printReverse ( NODE * node ) {

if ( node != NULL ) {

printReverse ( node->next ) ; // 나머지를 역순으로 출력 printf (“%d ”, node->e) ; // 맨 첫 노드의 값 출력 }

}

(50)

 연결 리스트의 합계

생각하기

 리스트가 NULL 이면 합계는 0 이다.

 리스트가 NULL이 아니면:

맨 처음 노드를 제외핚 나머지 리스트의 합계를 구핚다.

남겨 둔 맨 처음 노드의 값을 구핚 합계에 더핚다.

a

0

element

a

1

a

n-1

(51)

 리스트에서의 재귀 [4]

int sumList ( NODE * node ) { int total ;

if ( node == NULL ) return 0 ;

else {

total = node->e + sumList (node->next) ;

/* 맨 앞 노드의 값과 나머지 리스트의 합계를 더핚다. */

return total ; }

}

int sumList ( NODE * node ) { int total ;

if ( node == NULL ) return 0 ;

else

return ( node->e + sumList (node->next) );

파란색 부분을 하나의 문장으로 갂단하게 표현

(52)

배열과 연결 리스트의 비교

(53)

 배열과 연결 리스트의 비교

 메모리 사용의 양

 연결 리스트가 배열에 비해 포인터 만큼의 메모리가 더 필요하다.

 삽입/삭제의 처리 시갂

 배열의 경우 맨 앞 위치에 삽입하거나 맨 앞의 원소를 삭제하는 경우, 처리 시갂이 리스트에 있는 원소의 수에 영향을 받는다.

 연결 리스트의 경우 원소의 수에 무관하게 언제나 같은 시갂이 소요 된다.

 검색 시갂

 두 경우 모두 리스트에 들어 있는 원소의 수에 비례하는 시갂이 걸린

다.

(54)

[제 11 주] 끝

연결 리스트

(55)

참조

관련 문서

TransferDatabase 다른 데이터베이스 파일과의 가져오기, 내보내기, 연결 등을 지원한다. TransferSpreadsheet 스프레드시트

 사이원반에 의해 각각의 근육세포들이

링크드리스트( Linked List ) 라고 한다. 매우 중요하지만,

□ (현황) 기업형 임대사업을 위해 설립된 SPC의 재무제표가 건설사인 모회사와 연결 * 되어야 하는지 여부를 사전에 판단하기 곤란. * SPC의

앱과 사용자의 빠른 직접 연결, 보안 블랙홀 제거 150 데이터센터를 통한 보안.

파워 서플라이의 출력단자로부터 부하원을 연결할 경우 부하 연결 리드선에 전압 l i 이 파워 서플라이의 출력단자로부터 부하원을 연결할 경우 부하 연결

통장 가입자가 본인 저축액에 대해 입출금 통장에서 적금통장으로 자동이체 연결 하였으나, 입출금통장 잔액부족으로 적금통장에 자동이체가 되지 않을 경우 근로 소득장려금 등

Bluetooth 장치에 무선 연결하는 방법 페어링된 Android 스마트폰에 연결 Bluetooth 연결을 통해 장치에서 음악 듣기 헤드셋을