Docsity
Docsity

Prepare for your exams
Prepare for your exams

Study with the several resources on Docsity


Earn points to download
Earn points to download

Earn points by helping other students or get them with a premium plan


Guidelines and tips
Guidelines and tips

Circular Sorted Linked List: Implementation and Operations, Lecture notes of Algorithms and Programming

The concept of a circular sorted linked list, its advantages over regular linked lists, and provides the implementation of search, insert, and delete operations. It also discusses the differences between circular sorted linked list and double linked list.

Typology: Lecture notes

2017/2018

Uploaded on 10/23/2018

keehwan
keehwan 🇨🇦

20 documents

1 / 17

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
- 97 -
제8장 연결구조 확장
장에서는 연결구조를 이용한 리스트 구현의 여러 가지 확장을 살펴본다.
8.1. 교육목표
장의 교육목표는 다음과 같다.
교육목표
순환 연결 리스트
이중 연결 리스트
배열을 이용한 연결구조 방식의 리스트 구현
8.2. 순환 연결 리스트
순환 연결 리스트
연결 리스트에서 마지막 노드의 링크를 노드를 가리키도록 변형한
리스트
장점
장점.
. 어떤 노드에서 출발해도 전체 리스트를 방문할 있다.
기존 연산들은 항상 마지막 노드가 노드를 가리키도록 수정해야
한다.
수정시
수정시 가장
가장
문제점
문제점
노드의 삭제 또는 리스트 앞에 노드의 삽입:
마지막 노드까지 이동하여야 한다.
info
10
info
20
info
30
list
순환 연결 리스트는 7장에서 살펴본 순환 연결 큐와 유사하다. 보통 연결구조 방식의 리스
트에서는 번째 노드를 가리키는 정보만 유지하고 마지막 노드의 연결 정보는 null 값을
가지게 된다. 이것을 변형하여 마지막 노드가 노드를 가리키도록 하면 어떤 노드에서
발하여도 전체 리스트를 방문할 있는 이점을 얻을 있다. 하지만 이전처럼 번째
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff

Partial preview of the text

Download Circular Sorted Linked List: Implementation and Operations and more Lecture notes Algorithms and Programming in PDF only on Docsity!

info

info

info

list

트에서는 첫 번째 노드를 가리키는 정보만 유지하고 마지막 노드의 연결 정보는 null 값을

마지막 노드의 내용: list.info

첫 노드의 내용: list.next.info

info

info

info

list

info

list

List

ArrayList LinkedList

UnsortedArrayList SortedArrayList UnsortedLinkedList SortedLinkedList CircularSortedLinkedList

List Class Hierarchy

로 ListNode라는 내부 클래스를 사용해야 하며, isFull, isEmpty, size와 같은 메소드들은

LinkedList 추상 클래스에 정의되어 있는 것을 그대로 사용할 수 있다. 하지만 삽입, 삭제,

CircularSortedLinkedList의 delete

info

info

info

info

prevLoc list 일반적인 경우 prevLoc.next = loc.next;

info

10

info

info

info

40

loc list

loc

prevLoc

list.next = list.next.next; 또는 list.next = loc.next;

CircularSortedLinkedList의 delete

info

info

info

info

prevLoc list

prevLoc.next = loc.next; list = prevLoc;

info

10

list

loc

list = null;

prevLoc

loc

public boolean delete(Object item) throws ListUnderflowException{ if(isEmpty()) throw new ListUnderflowException(“…”); Comparable x = (Comparable)item; ListNode loc = list.next; ListNode prevLoc = null; for(int i=0;i<size;i++){ int compResult = x.compareTo(loc.info); if(compResult==0){ // 삭제할 노드가 존재하는 경우 if(prevLoc==null){ // 첫 노드를 삭제하는 경우 if(loc == loc.next) list = null; // 첫 노드가 유일 노드인 경우 else list.next = loc.next; } else{ prevLoc.next = loc.next; if(loc==list) list = prevLoc; // 마지막 노드를 삭제하는 경우 } size--; return true; } else if(compResult<0) return false; else{ prevLoc = loc; loc = loc.next; } } // for return false; } // CircularSortedList::delete

if(loc == loc.next) 대신에 if(size==1)를 사용할 수 있음

첫 노드와 마지막 노드를 모두 접근하기가 용이하다는 순환 연결 구조의 특성을 이용하여

보다 효율적으로 delete 메소드를 구현할 수 있다. 즉, 첫 노드와 마지막 노드와 비교하여

첫 노드보다 작은 경우와 첫 노드와 큰 경우에 대해서는 리스트 전체를 검색하여 삭제할 노

드가 있는지 찾는 과정을 생략할 수 있다.

public boolean delete(Object item) throws ListUnderflowException{ if(isEmpty()) throw new ListUnderflowException(“…”); Comparable x = (Comparable)item; if(x.compareTo(list.next.info)<0||x.compareTo(list.info)>0){ return false; } // 나머지 부분은 동일

} // CircularSortedList::delete

이용한 delete 메소드

public void insert(Object item){ Comparable x = (Comparable)item; ListNode newNode = new ListNode(); newNode.info = item; newNode.next = null; ListNode loc = (list==null)? null: list.next; ListNode prevLoc = null; for(int i=0;i<size;i++){ // 삽입할 위치 찾기 if(x.compareTo(loc.info)<0) break; else{ prevLoc = loc; loc = loc.next; } } // for if(prevLoc==null){ // 리스트 맨 앞에 삽입되는 경우 // 리스트가 비어있는 경우 if(list==null){ list = newNode; newNode.next = newNode; } else{ newNode.next = loc; list.next = newNode; } } else{ newNode.next = loc; prevLoc.next = newNode; // 리스트의 맨 마지막에 삽입되는 경우 if(prevLoc == list) list = newNode; } size++; } // CircularSortedList::insert

위 슬라이드에 있는 삽입 연산은 기존 정렬 연결 리스트와 동일한 방법으로 구현한 것이다.

먼저 삽입할 위치를 찾은 다음, 삽입할 위치에 따라 필요한 각 종 연결을 변경하는 방식이

다. 다음 슬라이드에 있는 삽입 연산은 순환 연결 리스트의 특성을 활용하고 있다. 즉, 새

요소를 추가할 때 그 요소를 리스트의 첫 요소와 마지막 요소와 비교하여 첫 요소보다 작거

나 마지막 요소보다 클 경우에는 이것을 먼저 처리하여 준다. 두 경우는 정렬된 순환 연결

구조의 특성 상 같은 위치에 삽입되는 형태가 된다.

public void insert(Object item){ Comparable x = (Comparable)item; ListNode newNode = new ListNode(); newNode.info = item; newNode.next = null; size++; if(list==null){ list = newNode; newNode.next = newNode; return; } // 리스트가 비어 있는 경우 if(x.compareTo(list.next.info)<0||x.compareTo(list.info)>0){ newNode.next = list.next; list.next = newNode; if(x.compareTo(list.info)>0) list = newNode; // 맨 끝에 삽입되는 경우 return; } // 삽입하고자 하는 노드가 첫 노드보다 작거나 마지막 노드보다 큰 경우 // 첫 노드와 비교할 필요 없음 ListNode loc = list.next.next; ListNode prevLoc = list.next; for(int i=0;i<size;i++){ if(x.compareTo(loc.info)<0){ newNode.next = loc; prevLoc.next = newNode; return; } else{ prevLoc = loc; loc = loc.next; } } // for } // CircularSortedList::insert

이용한 insert 메소드

CircularSortedLinkedList의 장단점

연산 측면에서는 SortedLinkedList에 비해 향상된 것이 없다.

CircularSortedLinkedList는 일반 정렬 연결리스트와 달리

하나 더 유지하는 것은? good alternative

순환 정렬 연결 리스트의 장점을 기존 정렬 연결 리스트와 비교하여 보자. 연산의 성능 측

면에서 보면 향상된 것이 거의 없다. 그러나 순환 정렬 연결 리스트는 첫 노드뿐만 아니라

마지막 노드에 대한 접근이 용이하므로 다음과 같은 서비스는 기존 정렬 연결 리스트보다

우수하다.

첫째, 가장 큰 값의 추출

둘째, 리스트에 저장된 값들 사이에 있는 값인지 알 필요가 있는 경우

셋째, 정렬된 데이터를 연속해서 삽입하는 경우

마지막 서비스에 경우에는 삽입 연산이 순환 연결 리스트의 특성을 활용하고 있어야 한다.

이런 측면에서 보았을 때 일반 정렬 연결 리스트에서 마지막 노드를 가리키는 포인터를 하

나 더 유지하는 방법을 생각해 볼 수 있다. 오히려 이 방법이 순환 연결 리스트에 비해 우

수할 수 있다.

public abstract class DoubleLinkedList implements List{ protected class ListNode{ public Object info; public ListNode back; public ListNode next; } protected class ListIterator{ … } protected ListNode list = null; protected int size = 0; public DoubleLinkedList() {} public abstract boolean search(Object item) throws ListUnderflowException; public abstract void insert(Object item); public abstract boolean delete(Object item) throws ListUnderflowException; public abstract Object retrieve(Object item) throws ListUnderflowException; public boolean isFull(){ return false; } public boolean isEmpty(){ return (list == null); } public void clear{ list = null; } public int size(){ return size; } public Iterator iterator() { return new ListIterator(list); } } // DoubleLinkedList class

DoubleLinkedList에서 노드를 나타내는 클래스의 이름은 ListNode이고, 반복자 클래스의 이

름은 ListIterator이다. 즉, LinkedList가 사용하는 이름과 동일한 이름을 사용한다. 하지만 내

부 클래스의 이름이므로 이렇게 같은 이름을 사용하여도 문제가 되지 않는다.

protected class ListIterator{ public ListNode cursor; public int traverseCount = 0; public ListIterator(ListNode node){ cursor = node; } // ListIterator(ListNode) public boolean hasBack(){ return (traverseCount>0); } public boolean hasNext(){ return (traverseCount<size); } public Object back(){ Object tmp = cursor.info; cursor = cursor.back; traverseCount--; return tmp; } // back public Object next(){ Object tmp = cursor.info; cursor = cursor.next; traverseCount++; return tmp; } // next public void remove(){ throw new UnsupportedOperationException(); } // remove }

이중 연결 리스트에서 반복자 클래스에는 기존 연결 리스트에는 없는 back과 hasBack 메소

드가 추가로 구현되어야 한다. 즉, 주어진 노드로부터 그것의 선행 노드를 방문할 수 있는

메소드를 제공해주어야 한다.

SortedDoubleLinkedList의 Insert

info

list info

info

info

loc

newNode.back = prevLoc; newNode.next = loc; prevLoc.next = newNode; loc.back = newNode;

info

prevLoc

newNode.next = loc; loc.back = newNode; list = newNode;

loc

info

newNode.back = prevLoc; prevLoc.next = newNode;

prevLoc

이중 정렬 연결 리스트에서 삽입 연산은 일반 정렬 연결 리스트의 삽입과 마찬가지로 크게

네 가지 경우를 고려해야 한다. 위 슬라이드에서 이 중 리스트가 빈 경우에 삽입하는 경우

를 제외한 나머지 세 가지 경우에 대해 연결을 변경하는 방법을 설명하고 있다.

public void insert(Object item){ Comparable x = (Comparable)item; ListNode newNode = new ListNode(); newNode.info = item; newNode.back = null; newNode.next = null; ListNode loc = list; ListNode prevLoc = null; for(int i=0;i<size;i++){ // 삽입할 위치를 찾는다. if(x.compareTo(loc.info)<0) break; else{ prevLoc = loc; loc = loc.next; } } // for if(prevLoc==null){ // 리스트 맨 앞에 삽입되는 경우 list = newNode; newNode.next = loc; if(list!=null) loc.back = newNode; // 빈 리스트가 아닌 경우 } else{ newNode.back = prevLoc; newNode.next = loc; prevLoc.next = newNode; if(loc!=null) loc.back = newNode; // 맨 마지막에 삽입되는 경우가 아니면 } size++; } // SortedDoubleLinkedList::insert

if(prevLoc==null)이 참이면 새 요소를 리스트의 첫 노드로 삽입하는 경우이다. 이 경우는 다

시 리스트가 빈 경우와 그렇지 않은 경우로 나누어진다. 반대로 조건이 거짓이면 새 요소를

리스트 중간 또는 끝에 삽입하는 경우이다. 리스트 중간에 삽입되는 경우에는 새 요소가 선

행 및 후속 요소를 모두 가지게 되지만 리스트 마지막에 삽입되는 경우에는 선행요소만 가

지게 된다.

public boolean delete(Object item) throws ListUnderflowException{ if(isEmpty()) throw new ListUnderflowException("…"); Comparable x = (Comparable)item; ListNode loc = list; List prevLoc = null; for(int i=0;i<size;i++){ int compResult = x.compareTo(loc.info); if(compResult==0){ // 삭제할 노드가 존재하는 경우 if(prevLoc==null){ // 첫 노드를 삭제하는 경우 if(loc.next==null) list = null; // 삭제할 노드가 유일 노드인 경우 else{ loc.next.back = null; list = loc.next; } } else{ prevLoc.next = loc.next; if(loc.next != null) loc.next.back = prevLoc; // 마지막 노드가 아닌 경우 } return true; } else if(compResult<0) return false; else{ prevLoc = loc; loc = loc.next; } } // for return false; }

색인 info next

0 David 2

1 null 5

2 John 4

3 Peter 6

4 Mary 3

5 null END

6 Robert END

list 0

class ArrayLinkedList{ public static final int DEF_MAX_CAPACITY = 50; public static final int END = -1; private class ListNode{ public Object info; public int next; } private int list = END; private ListNode[] nodes; private int size = 0; private int free = 0; … }

free 1 size 5

따라서 null 대신에 -1을 이용하여 리스트의 끝을 나타낸다.

일반적인 연결구조에서 마지막 노드임을 나타내기 위해 그 노드의 연결 값을 null 값으로

사용한다. 배열을 이용한 연결 리스트의 구현에서는 null 값 대신에 -1이라는 값을 사용한다.

배열을 이용한 연결구조의 구현 – 계속

info

David

info

John

info

Mary

info

Peter

list info

Robert

free null null

info

David

info

John

info

Peter

list info

Robert

free null null null

Mary의 삭제

보다 자세하게 구현 원리를 살펴보면 배열을 이용한 연결구조는 내부적으로 두 개의 리스트

를 유지한다. 하나는 요소들이 저장되어 있는 유효한 노드들을 연결해 놓은 리스트이고, 다

른 하나는 요소들이 저장되어 있지 않은 빈 노드를 연결해 놓은 리스트이다. 만약 새 요소

를 추가해야 하면 빈 노드들의 리스트에서 하나의 노드를 선택하여 이 노드에 새 요소를 대

입하고 이 노드를 유효한 노드들의 리스트에 포함한다. 반대로 기존 요소를 삭제해야 하면

그 노드를 유효한 노드들의 리스트에서 제거하고 빈 노드들의 리스트로 옮긴다.

배열을 이용한 연결구조의 구현 – 계속

David John Peter Mary Robert

배열을 이용한 연결 리스트는 일반 연결 리스트와 달리 매번 삽입할 때마다 공간 할당이 이

루어지지 않는다는 장점을 지니고 있다. 하지만 배열의 본질적인 특성 때문에 삽입 연산에

서 공간이 부족한 경우를 고려해야 한다.

SortedArrayLinkedList의 insert

색인 info next

0 john END

1 null 2

2 null 3

3 null 4

4 null 5

5 null 6

6 null END

list 0 free 1 size 1

insert(Robert);

색인 info next

0 john 1

1 Robert END

2 null 3

3 null 4

4 null 5

5 null 6

6 null END

list 0 free 2 size 2

int tmp = nodes[free].next; nodes[free].info = item; nodes[free].next = END; nodes[prevLoc].next = free; free = tmp;

loc == END

SortedArrayLinkedList의 insert

list 0 free 2 size 2

insert(mary);

색인 info next

0 john 2

1 Robert END

2 mary 1

3 null 4

4 null 5

5 null 6

6 null END

list 0 free 3 size 3

int tmp = nodes[free].next; nodes[free].info = item; nodes[free].next = loc; nodes[prevLoc].next = free; free = tmp;

색인 info next

0 john 1

1 Robert END

2 null 3

3 null 4

4 null 5

5 null 6

6 null END

SortedArrayLinkedList의 insert

list 0 free 3 size 3

insert(Bob);

색인 info next

0 john 2

1 Robert END

2 mary 1

3 Bob 0

4 null 5

5 null 6

6 null END

list 3 free 4 size 4

int tmp = nodes[free].next; nodes[free].info = item; nodes[free].next; = loc; list = free; free = tmp;

색인 info next

0 john 2

1 Robert END

2 mary 1

3 null 4

4 null 5

5 null 6

6 null END

prevLoc == END

SortedArrayLinkedList의 delete

list 3 free 4 size 4

delete(Mary);

색인 info next

0 John 1

1 Robert END

2 null 4

3 Bob 0

4 null 5

5 null 6

6 null END

list 3 free 2 size 3

nodes[prevLoc].next = nodes[loc].next; nodes[loc].info = null; nodes[loc].next; = free; free = loc;

색인 info next

0 John 2

1 Robert END

2 Mary 1

3 Bob 0

4 null 5

5 null 6

6 null END

리스트 중간에 있는 Mary가 삭제되는 경우 이 노드는 빈 리스트로 옮겨져야 한다. 리스트의