SlideShare ist ein Scribd-Unternehmen logo
1 von 192
Downloaden Sie, um offline zu lesen
Algorithms & Data Structure 
김영기책임 
네트워크사업부 
SE Lab
Contents 
1 
Data Structure 
1.1 
알고리즘성능측정 
1.2 
순환(Recursion) 
1.4 
스택(Stack) 
2.1 
정렬(Sort) 
1.3 
리스트(List) 
2.2 
트리(Tree) 
1.5 
큐(Queue) 
2 
Algorithm 
1.6 
우선순위큐(Priority queue) 
2.3 
그래프(Graph) 
2.5 
탐색(Search) 
2.4 
해싱(Hashing) 
2.7 
NP-완비 
2.6 
동적프로그래밍(Dynamic Programming)
알고리즘성능측정
알고리즘 성능 분석 
 알고리즘 성능 분석 기법 
 수행 시간 측정 
 두 개의 알고리즘의 실제 수행 시간을 측정한다. 
 실제로 구현이 필요 
 동일 하드웨어를 사용하여 측정해야 함 
 알고리즘 복잡도 분석 
 직접 구현하지 않고서도 수행시간을 분석하는 방법 
 알고리즘이 수행하는 연산의 횟수를 측정하여 비교한다. 
 일반적으로 연산의 횟수는 n의 함수 
 시간 복잡도(time complexity) : 수행 시간 분석 
 공간 복잡도(space complexity) : 수행 시 필요로 하는 메모리 공간 분석 
연산의 수 = 8 
3n+2 
연산의 수 =26 
5n2 +6 
프로그램 A 프로그램 B 
워드2008 워드2012
복잡도 
알고리즘복잡도분석 
시간복잡도는알고리즘을이루고있는연산들이몇번수행되는지숫자로표시 
산술연산, 대입연산, 비교연산, 이동연산의기본적인연산 
수행시간이입력의크기에따라변화면안된다. 기본연산만 
알고리즘이수행하는연산의개수를계산하여두개의알고리즘비교가능 
연산의수행횟수는고정된숫자가아니라입력의개수n에대한함수 
시간복잡도함수라고하고T(n) 이라고표기 
알고리즘A 
알고리즘B 
알고리즘C 
대입연산 
1 
n+ 1 
n*n + 1 
덧셈연산 
n 
n*n 
곱셈연산 
1 
나눗셈연산 
Total 
2 
2n+ 1 
2n2+ 1 
입력의개수n 
연산의횟수 
알고리즘A 
알고리즘B 
알고리즘C
알고리즘차수비교 
알고리즘차수비교 
가정 
f와g를자연수집합N에서실수R로의함수, 
즉f, g : N R 
R+는양의실수집합 
O (big oh) 
f보다차수가작거나같은함수전체의집합 
(big theta) 
f와같은차수를갖는함수들의집합 
Ω (big omega) 
f보다차수가크거나같은함수전체의집합 
O(f) = {gcR+, n0N, nn0 , g(n)cf(n)} 
(f) = {ggO(f), fO(g)} 
(f) = {gcR+, n0N, nn0 , g(n)cf(n)} 
f 
(f): 적어도f보다 
빠르게증가하는함수들 
(f): f와같은비율로 
증가하는함수들 
즉, (f) = (f) O(f) 
O(f): f보다작은비율로 
증가하는함수들
Big O 표기법 
Big O 표기법 
자료의개수가많은경우차수가가장큰항의영향이크고, 다른항들은상대적으로무시가능시간복잡도함수에서가장영향이큰항만을고려해도충분!!! 
Big O 표기법: 연산의횟수를점근적(대략적)으로표기한다. 
주어진함수의가장높은항만끄집어내고, 계수를1로하면된다. 
예제 
g(n) = 5이면0(1)이다. 
g(n) = 2n+5이면이알고리즘은0(n)이다. 
g(n) = 3n2+100이면, O(n2)이다. 
G(n) = 5*2n+10n+100이면O(2n)이다. 
n=1000인경우 
T(n)= n2 + n + 1 
99% 
1% 
■수학적정의 
임의의상수N0와c가있어서N≥N0인N에대해서, c⋅f(N) ≥ g(N)이성립하면g(N) = O(f(N))이라한다.
Big O 표기법 
Big O 표기법종류 
O(1):상수형 
O(logn):로그형 
O(n):선형 
O(nlogn):로그선형 
O(n2):2차형 
O(n3):3차형 
O(nk):k차형 
O(2n):지수형 
O(n!) : 팩토리얼형 
시간복잡도 
n 
1 
2 
4 
8 
16 
32 
1 
1 
1 
1 
1 
1 
1 
logn 
0 
1 
2 
3 
4 
5 
n 
1 
2 
4 
8 
16 
32 
nlogn 
0 
2 
8 
24 
64 
160 
n2 
1 
4 
16 
64 
256 
1024 
n3 
1 
8 
64 
512 
4096 
32768 
2n 
2 
4 
16 
256 
65536 
4294967296 
n! 
1 
2 
24 
40326 
20922789888000 
26313×1033 
1 2 4 8 16 32 64 128 256 
65536 
32768 
16384 
8192 
4096 
2048 
1024 
512 
256 
128 
64 
32 
16 
8 
4 
2 
f(n)=2n 
f(n)=n3 
f(n)=n2 
f(n)=nlog2n 
f(n)=n 
f(n)=log2n 
n 
f(n)
Big 표기법, Big 표기법 
Big 표기법 
Big 는함수의하한을표시한다 
n≥5이면, 2n+1 <10n 이므로n = (n) 
Big 표기법 
Big 는함수의하한인동시에상한을표시 
f(n)=O(g(n))이면서, f(n)=(g(n))이면f(n)= (n)이다 
n≥1이면, n ≤2n+1 ≤3n이므로2n+1 = (n) 
■수학적정의 
모든n≥n0에대하여c1|g(n)| ≤ |f(n)| ≤ c2|g(n)|을만족하는3개의상수c1, c2와n0가존재하면f(n)=θ(g(n))이다. 
■수학적정의 
모든n≥n0에대하여|f(n)| ≥ c|g(n)|을만족하는2개의상수c와n0가존재하면f(n)=Ω(g(n))이다 
입력의개수n 
연산의수 
상한 
하한 
n0 
f(n) 
0(f(n)) 
(f(n))
Best, Average, Worst Cases 
알고리즘의수행시간 
입력자료의집합에따라다를수있다. 
정렬알고리즘의수행시간은입력집합에따라다를수있다. 
Case 구분 
최선의경우(best case): 수행시간이가장빠른경우 
실제로의미가없는경우가많다. 
평균의경우(average case): 수행시간이평균적인경우 
계산하기가상당히어렵다. 
최악의경우(worst case): 수행시간이가장늦은경우 
가장널리사용되며, 계산하기쉽다. 
응용에따라중요한의미를가질수있다. 
최악의경우 
최선의경우 
평균적인경우 
A 
B 
C 
D 
E 
F 
G 
입력집합 
수행시간 
100 
50
순환(Recursion)
순환(Recursion) 
순환이란… 
어떤알고리즘이나함수가자기자신을호출하여문제를해결하는기법 
예제: Factorial의정의 
Factorial의C언어구현 
n! = 
1 n=0 
n* (n-1)! 1>=0 
intfactorial(intn) 
{ 
if(n==1) return(1); 
else return(n*factorial(n-1)); 
} 
3!은? 
3!=3*2! 
1!=1 
2!=2*1! 
2!는? 
3!는? 
1!는? 
2 
6 
1 
factorial(3) = 3*factorial(2) 
= 3*2*factorial(1) 
= 3*2*1 
= 3*2 
= 6
순환(Recursion) 
순환호출의내부적인구현 
Factorial(2) 
{ 
if(2==1) return 1; 
else return(2*factorial(1)); 
} 
Factorial(2) 
{ 
if(2==1) return 1; 
else return(2*factorial(1)); 
} 
Factorial(3) 
{ 
if(3==1) return 1; 
else return(3*factorial(2)); 
} 
Factorial(3) 
{ 
if(3==1) return 1; 
else return(3*factorial(2)); 
} 
Factorial(3) 
{ 
if(3==1) return 1; 
else return(3*factorial(2)); 
} 
Factorial(2) 
{ 
if(1==1) return 1; 
… 
} 
Factorial(2) 
{ 
… 
else return(2*1); 
} 
Factorial(3) 
{ 
if(3==1) return 1; 
else return(3*factorial(2)); 
} 
Factorial(3) 
{ 
if(3==1) return 1; 
else return(3*2); 
} 
(1) 
(2) 
(3) 
(4) 
(5) 
intfactorial(intn) 
{ 
if(n==1) return(1); 
else return(n*factorial(n-1)); 
} 
순환알고리즘의구현 
순환을 
멈추는부분 
순환호출을하는부분
순환(Recursion) 
순환과반복 
프로그래밍언어의되풀이방법에는순환과반복이있다. 
많은경우, 순환은반복으로변경이가능하다. 
순환은반복에비해명확하고간결하게표현되나, 수행속도는느린단점이있다. 
알고리즘설명은순환으로, 구현은반복으로하는경우가많다. 
반복적인Factorial계산프로그램과순환원리 
3!=3*2! 
1!=1 
2!=2*1! 
2!는? 
1!는? 
2 
1 
1!=1 
2!=2*1 
3!=3*2*1 
4!=4*3*2*1 
반복 
순환 
반복의구현은 
for, while을이용 
intfactorial_iter(intn) 
{ 
intk, v=1; 
for(k=n; k>0; k--) 
v=v*k; 
return(v); 
} 
순환코드는 
수행시간에약점 
intfactorial(intn) 
{ 
if(n==1) return(1); 
else return (n * factorial(n-1)); 
} 
문제부분 
해결부분 
순환의원리는 
분할정복(Divide and conquer) 
즉,문제의크기를작게만든다는것이다.
거듭제곱값계산 
풙풏을구하는함수 
순환의경우호출할때마다문제의크기가반으로줄어든다. 
doublepower (doublex, int n) 
{ 
inti ; 
doubler = 1.0 ; 
for(i=0; i<n; i++) 
r = r*x ; 
return(r) ; 
} 
doublepower (doublex, int n) 
{ 
if( n==0 ) return1; 
else if ( (n%2) == 0) 
return power(x*x, n/2); 
else return x*power(x*x, (n-1)/2); 
} 
반복 
순환 
2500계산1000000번수행수행시간: 7.11 초 
2500계산1000000번수행수행시간: 0.47 초 
반복적인함수power 
순환적인함수power 
시간복잡도 
O(n) 
O(log2n) 
실제수행속도 
7.17초 
0.47초
Fibonacci sequence 
피보나치수열의계산 
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, … 
0 n = 0 
1 n = 1 
fib(n-2) + fib(n-1) otherwise 
fib(n) = 
Fib(0) 
Fib(1) 
Fib(0) 
Fib(1) 
Fib(0) 
Fib(1) 
Fib(1) 
Fib(2) 
Fib(0) 
Fib(1) 
Fib(1) 
Fib(2) 
Fib(0) 
Fib(1) 
Fib(1) 
Fib(2) 
Fib(2) 
Fib(3) 
Fib(2) 
Fib(3) 
Fib(3) 
Fib(4) 
Fib(4) 
Fib(5) 
Fib(6) 
■함수호출 
n=25 : 25만 
n=30 : 300백만
Fibonacci sequence 
피보나치수열계산함수 
이경우어느함수가더효율적인가? 
intfib (int n) 
{ 
if (n<1) returnn; 
else{ 
int i, tmp, current=1; last=0; 
for(i=2; i<=n; i++){ 
tmp= current; 
current += last; 
last = tmp; 
} 
return current; 
} 
intfib(intn) 
{ 
if(n==0) return0; 
if(n==1) return1; 
return(fib(n-1) + fib(n-1)); 
} 
반복 
순환
The Tower of Hanoi 
하노이탑 
이동조건 
한번에하나의원판만이동할수있다. 
맨위에있는원판만이동할수있다. 
크기가작은원판위에큰원판이쌓일수없다. 
중간의막대를임시적으로이용할수있으나앞의조건들을지켜야한다. 
하노이탑확장 
원판이늘어나는경우 
막대의수가늘어나는경우
The Tower of Hanoi 
하노이탑문제에대한의사코드 
순환이되는부분을찾는것이Key !!! 
voidhanoi_tower(intn, charfrom,char tmp, charto) 
{ 
if(n==1) { 
from에서to로원판을옮긴다. 
} 
else{ 
①from의맨밑에원판을제외한나머지원판들을tmp로옮긴다. 
// hanoi_tower(n-1, from, to, tmp); 
②from에있는한개의원판을to로옮긴다. 
③tmp의원판들을to로옮긴다. 
// hanoi_tower(n-1, tmp, from, to); 
} 
} 
원래문제의 
축소된형태이다.
The Tower of Hanoi 
하노이탑문제에대한전체코드 
#include <stdio.h> 
voidhanoi_tower(intn, charfrom,char tmp, charto) 
{ 
if(n==1) { 
printf(“원판1을%c 에서%c 로옮긴다n”, from, to); 
} 
else{ 
hanoi_tower(n-1, from, to, tmp); 
printf(“원판%d를%c 에서%c 로옮긴다n”, n, from, to); 
hanoi_tower(n-1, tmp, from, to); 
} 
} 
int main(){ 
hanoi_tower(4, ‘A’, ‘B’, ‘C’); 
return 0; 
} 
원판1를A에서B로옮긴다. 
원판2를A에서C로옮긴다. 
원판1를B에서C로옮긴다. 
원판3를A에서B로옮긴다. 
원판1를C에서A로옮긴다. 
원판2를C에서B로옮긴다. 
원판1를A에서B로옮긴다. 
원판4를A에서C로옮긴다. 
원판1를B에서C로옮긴다. 
원판2를B에서A로옮긴다. 
원판1를C에서A로옮긴다. 
원판3를B에서C로옮긴다. 
원판1를A에서B로옮긴다. 
원판2를A에서C로옮긴다. 
원판1를B에서C로옮긴다.
리스트(List)
리스트(List) 
 리스트, 선형 리스트 
 목록이나 도표처럼 여러 데이터를 관리할 수 있는 자료형을 추상화 
 순서를 가진 항목들의 모임 
 ※ 집합: 항목간의 순서의 개념이 없음 
 
 데이터 삽입, 삭제, 검색 등 필요 작업을 가함 
 스택과 큐는 리스트의 특수한 경우에 해당 
 리스트의 연산 
( , ,..., ) 0 1 1  n L item item item 
연 산 설 명 
Insert(Position, Data) 데이터를 해당 위치(Position)에 넣기 
Delete(Position) 해당 위치(Position)의 데이터를 삭제 
Retrieve(Position, Data) 해당 위치(Position)의 데이터를 Data 변수에 복사 
Create( ) 빈 리스트 만들기 
Destroy( ) 리스트 없애기 
IsEmpty( ) 빈 리스트인지 확인 (아무 것도 안 적혔는지 확인) 
Length( ) 몇 개의 항목인지 계산 (몇 개나 적혔는지 세기)
리스트구현방법 
배열을이용하는방법 
구현이간단 
삽입, 삭제시오버헤드 
항목의개수제한 
연결리스트를이용하는방법 
구현이복잡 
삽입, 삭제가효율적 
크기가제한되지않음 
A 
B 
C 
D 
n 
A 
B 
C 
배열이용 
연결리스트이용 
하나의노드가데이터와링크로구성되어있고링크가노드들을연결한다
Array List 
1차원배열에항목들을순서대로저장 
L=(A, B, C, D, E) 
삽입연산 
삽입위치다음의항목들을이동하여야함. 
삭제연산 
삭제위치다음의항목들을이동하여야함 
A 
B 
C 
D 
E 
0 1 2 3 4 5 6 7 8 9 
A 
B 
C 
D 
E 
0 1 2 3 4 5 6 7 8 9 
A 
B 
D 
E 
0 1 2 3 4 5 6 7 8 9 
N 
N
ArrayListType의구현 
구현코드 
항목들의타입은element로정의 
list라는1차원배열에항목들을차례대로저장 
length에항목의개수저장 
typedefint element; 
typedefstruct{ 
int list[MAX_LIST_SIZE];// 배열정의 
int length;// 현재배열에저장된항목들의개수 
} ArrayListType; 
// 리스트초기화 
void init(ArrayListType*L) 
{ 
L->length = 0; 
} 
int is_empty(ArrayListType*L) // 리스트가비어있으면1, 아니면0을반환 
{ 
return L->length == 0; 
} 
int is_full(ArrayListType*L) // 리스트가가득차있으면1을반환, 아니면0을반환 
{ 
return L->length == MAX_LIST_SIZE; 
}
ArrayListType의구현 
삽입과삭제 
// position: 삽입하고자하는위치 
// item: 삽입하고자하는자료 
void add(ArrayListType*L, int position, element item) 
{ 
if( !is_full(L) && (position >= 0) && 
(position <= L->length) ){ 
int i; 
for(i=(L->length-1); i>=position;i--) 
L->list[i+1] = L->list[i]; 
L->list[position] = item; 
L->length++; 
} 
} 
// position: 삭제하고자하는위치 
// 반환값: 삭제되는자료 
element delete(ArrayListType*L, int position) 
{ 
int i; 
element item; 
if( position < 0 || position >= L->length ) 
error("위치오류"); 
item = L->list[position]; 
for(i=position; i<(L->length-1);i++) 
L->list[i] = L->list[i+1]; 
L->length--; 
return item; 
}
연결리스트(Linked List) 
연결리스트 
리스트의항목들을노드(node)라고하는곳에분산하여저장 
다음항목을가리키는주소도같이저장 
노드(node) : <항목, 주소> 쌍 
노드는데이터필드와링크필드로구성 
데이터필드–리스트의원소, 즉데이터값을저장하는곳 
링크필드–다른노드의주소값을저장하는장소(포인터) 
메모리에서의노드의물리적순서가리스트의논리적순서와일치할필요없음 
A 
B 
C 
D 
E
연결리스트의장단점 
구조 
노드= 데이터필드+ 링크필드 
Head Pointer 
리스트의첫번째노드를가르키는변수 
노드의생성 
필요시동적메모리생성이용 
장점 
삽입, 삭제가보다용이하다. 
연속된메모리공간이필요없다. 
크기제한이없다 
단점 
구현이어렵다. 
오류가발생하기쉽다. 
A 
B 
C 
D 
E 
A 
B 
C 
D 
E 
X 
N 
X 
X 
메인메모리 
메인메모리 
삽입연산 
삭제연산 
Data 
Link 
HeadPointer
연결리스트의종류 
단순연결리스트 
원형연결리스트 
이중연결리스트 
A 
B 
C 
D 
NULL 
Head Pointer 
A 
B 
C 
D 
Head Pointer 
A 
B 
C 
D 
Head Pointer
단순연결리스트 
단순연결리스트 
하나의링크필드를이용연결 
마지막노드의링크값은NULL 
A 
B 
C 
D 
NULL 
Head Pointer 
insert_node(L,before,new) 
ifL=NULL 
thenL←new 
elsenew.link←before.link 
before.link←new 
C 
D 
C 
E 
D 
E 
삽입연산 
remove_node(L,before,removed) 
ifL≠NULL 
thenbefore.link←removed.link 
destroy(removed) 
C 
E 
D 
C 
E 
D 
before 
before 
before 
before 
removed 
after 
after 
after 
new 
new 
after 
removed
단순연결리스트구현 
단순연결리스트구현 
데이터필드: 구조체로정의 
링크필드: 포인터사용 
노드의생성: 동적메모리생성라이브러리malloc함수이용 
데이터필드와링크필드설정 
두번째노드생성과첫번째노드와의연결 
typedefintelement; 
typedefstructListNode{ 
elementdata; 
structListNode*link; 
}ListNode; 
p1 
ListNode*p1; 
p1=(ListNode*)malloc(sizeof(ListNode)); 
p1->data=10; 
p1->link=NULL; 
10 
n 
p1 
ListNode*p2; 
p2=(ListNode*)malloc(sizeof(ListNode)); 
p2->data=20; 
p2->link=NULL; 
p1->link=p2; 
10 
p1 
20 
n 
헤드포인터(head pointer): 연결리스트의맨처음노드를가리키는포인터
연결리스트연산 
삽입 
헤드포인터가함수안에서변경되므로헤드포인터의포인터필요 
삽입의3가지경우 
head가NULL인경우: 공백리스트에삽입 
head가NULL이라면현재삽입하려는노드가첫번째노드가된다. 따라서head의값만변경 
p가NULL인경우: 리스트의맨처음에삽입 
일반적인경우: 리스트의중간에삽입 
new_node의link에p->link값을복사한다음, p->link가new_node를가리키도록한다 
voidinsert_node(ListNode**phead,ListNode*p,ListNode*new_node) 
phead:헤드포인터head에대한포인터 
p:삽입될위치의선행노드를가리키는포인터,이노드다음에삽입된다. 
new_node:새로운노드를가리키는포인터 
head가NULL이라면현재삽입하려는노드가첫번째노드가된다. 따라서head의값만변경 
p1 
new_node 
10 
p1 
20 
n 
x 
new_node
연결리스트연산 
삭제연산 
삭제의2가지경우 
p가NULL인경우: 맨앞의노드를삭제 
연결리스트의첫번째노드를삭제한다. 헤드포인터변경 
p가NULL이아닌경우: 중간노드를삭제 
removed 앞의노드인p의링크가removed 다음노드를가리키도록변경 
//phead:헤드포인터head의포인터 
//p:삭제될노드의선행노드를가리키는포인터 
//removed:삭제될노드를가리키는포인터 
voidremove_node(ListNode**phead,ListNode*p,ListNode*removed) 
10 
20 
n 
11 
x 
list 
removed 
10 
20 
n 
11 
list 
removed 
x 
p
단순연결리스트구현 
삽입연산코드 
삭제연산코드 
//phead:리스트의헤드포인터의포인터 
//p:선행노드 
//new_node:삽입될노드 
voidinsert_node(ListNode**phead,ListNode*p,ListNode*new_node) 
{ 
if(*phead==NULL){//공백리스트인경우 
new_node->link=NULL; 
*phead=new_node; 
} 
elseif(p==NULL){//p가NULL이면첫번째노드로삽입 
new_node->link=*phead; 
*phead=new_node; 
} 
else{//p다음에삽입 
new_node->link=p->link; 
p->link=new_node; 
} 
} 
voidremove_node(ListNode**phead,ListNode*p,ListNode*removed) 
{ 
if(p==NULL) 
*phead=(*phead)->link; 
else 
p->link=removed->link; 
free(removed); 
}
연결리스트연산 
방문연산 
리스트상의노드를순차적으로방문 
반복과순환기법모두사용가능 
voiddisplay(ListNode*head) 
{ 
ListNode*p=head; 
while(p!=NULL){ 
printf("%d->",p->data); 
p=p->link; 
} 
printf("n"); 
} 
voiddisplay_recur(ListNode*head) 
{ 
ListNode*p=head; 
if(p!=NULL){ 
printf("%d->",p->data); 
display_recur(p->link); 
} 
} 
[반복버전] 
[순환버전]
연결리스트연산 
탐색연산 
특정한데이터값을갖는노드를찾는연산 
A 
B 
C 
D 
NULL 
Head Pointer 
p 
ListNode*search(ListNode*head,intx) 
{ 
ListNode*p; 
p=head; 
while(p!=NULL){ 
if(p->data==x)returnp;//탐색성공 
p=p->link; 
} 
returnp;//탐색실패일경우NULL반환 
}
연결리스트연산 
합병연산 
2개의리스트를합하는연산 
A 
B 
C 
D 
n 
Head 1 
J 
K 
L 
Q 
n 
Head 2 
ListNode*concat(ListNode*head1,ListNode*head2) 
{ 
ListNode*p; 
if(head1==NULL)returnhead2; 
elseif(head2==NULL)returnhead1; 
else{ 
p=head1; 
while(p->link!=NULL) 
p=p->link; 
p->link=head2; 
returnhead1; 
} 
}
연결리스트연산 
역순연산 
리스트의노드들을역순으로만드는연산 
A 
n 
B 
C 
D 
n 
Head 
r 
q 
p 
x 
x 
x 
ListNode*reverse(ListNode*head) 
{ 
//순회포인터로p,q,r을사용 
ListNode*p,*q,*r; 
p=head;//p는역순으로만들리스트 
q=NULL;//q는역순으로만들노드 
while(p!=NULL){ 
r=q;//r은역순으로된리스트.r은q,q는p를차례로따라간다. 
q=p; 
p=p->link; 
q->link=r;//q의링크방향을바꾼다. 
} 
returnq;//q는역순으로된리스트의헤드포인터 
}
원형연결리스트 
원형연결리스트 
마지막노드의링크가첫번째노드를가리키는리스트 
한노드에서다른모든노드로의접근이가능 
보통헤드포인터가마지막노드를가리키게끔구성 
리스트의처음이나마지막에노드를삽입하는연산이단순연결리스트에비하여용이 
A 
B 
C 
D 
Head 
A 
B 
C 
D 
Head 1
원형리스트연산 
삽입(처음에삽입) 
A 
B 
C 
D 
Head 
B 
x 
② 
① 
//phead:리스트의헤드포인터의포인터 
//p:선행노드 
//node:삽입될노드 
voidinsert_first(ListNode**phead,ListNode*node) 
{ 
if(*phead==NULL){ 
*phead=node; 
node->link=node; 
} 
else{ 
node->link=(*phead)->link; 
(*phead)->link=node; 
} 
} 
node
원형리스트연산 
삭제(끝에삽입) 
A 
B 
C 
D 
Head 
B 
x 
② 
① 
node 
//phead:리스트의헤드포인터의포인터 
//p:선행노드 
//node:삽입될노드 
voidinsert_last(ListNode**phead,ListNode*node) 
{ 
if(*phead==NULL){ 
*phead=node; 
node->link=node; 
} 
else{ 
node->link=(*phead)->link; 
(*phead)->link=node; 
*phead=node; 
} 
} 
x 
③
이중연결리스트 
이중연결리스트 
하나의노드가선행노드와후속노드에대한두개의링크를가지는리스트 
링크가양방향이므로양방향으로검색이가능 
단점은공간을많이차지하고코드가복잡 
실제사용되는이중연결리스트의형태 
헤드노드+ 이중연결리스트+ 원형연결리스트 
삽입이나삭제시반드시선행노드가필요 
단순연결리스트의문제점: 선행노드를찾기가힘들다 
헤드노드(Head node) 
데이터를가지지않고단지삽입, 삭제코드를간단하게할목적으로만들어진노드 
헤드포인터와의구별필요 
공백상태에서는헤드노드만존재 
Head node 
llink 
data 
rlink 
typedefintelement; 
typedefstructDlistNode{ 
elementdata; 
structDlistNode*llink; 
structDlistNode*rlink; 
}DlistNode;
이중연결리스트 
삽입 
before 
x 
x 
③ 
① 
② 
④ 
//노드new_node를노드before의오른쪽에삽입한다. 
voiddinsert_node(DlistNode*before,DlistNode*new_node) 
{ 
new_node->llink=before; 
new_node->rlink=before->rlink; 
before->rlink->llink=new_node; 
before->rlink=new_node; 
}
이중연결리스트 
삭제 
//노드removed를삭제한다. 
voiddremove_node(DlistNode*phead_node, 
DlistNode*removed) 
{ 
if(removed==phead_node)return; 
removed->llink->rlink=removed->rlink; 
removed->rlink->llink=removed->llink; 
free(removed); 
} 
x 
x 
x 
x
연결리스트를이용한리스트ADT 구현 
How to ? 
리스트ADT의연산을연결리스트를이용하여구현 
리스트ADT의add, delete 연산의파라미터는위치 
연결리스트의insert_node, remove_node의파리미터는노드포인터 
상황에따라연산을적절하게선택하여야함 
add(항목의위치) 
delete(항목의위치) 
insert_node(노드포인터) 
remove_node(노드포인터) 
리스트ADT 
연결리스트 
사용자
리스트ADT 구현 
리스트선언 
is_empty 
get_length 
typedefstruct{ 
ListNode*head;//첫번째노드를가르키는헤드포인터 
intlength;//연결리스트내존재하는노드의개수 
}ListType; 
ListTypelist1;//리스트ADT생성 
intis_empty(ListType*list) 
{ 
if(list->head==NULL)return1; 
elsereturn0; 
} 
//리스트의항목의개수를반환한다. 
intget_length(ListType*list) 
{ 
returnlist->length; 
}
리스트ADT 구현 
add 연산 
새로운데이터를임의의위치에삽입 
항목의위치를노드포인터로변환해주는함수get_node_at필요 
//리스트안에서pos위치의노드를반환한다. 
ListNode*get_node_at(ListType*list,intpos) 
{ 
inti; 
ListNode*tmp_node=list->head; 
if(pos<0)returnNULL; 
for(i=0;i<pos;i++) 
tmp_node=tmp_node->link; 
returntmp_node; 
} 
//주어진위치에데이터를삽입한다. 
voidadd(ListType*list,intposition,elementdata) 
{ 
ListNode*p; 
if((position>=0)&&(position<=list->length)){ 
ListNode*node=(ListNode*)malloc(sizeof(ListNode)); 
if(node==NULL)error("메모리할당에러"); 
node->data=data; 
p=get_node_at(list,position-1); 
insert_node(&(list->head),p,node); 
list->length++; 
} 
}
리스트ADT 구현 
delete 연산의구현 
임의의위치의데이터를삭제 
항목의위치를노드포인터로변환해주는함수get_node_at필요 
//주어진위치의데이터를삭제한다. 
voiddelete(ListType*list,intpos) 
{ 
if(!is_empty(list)&&(pos>=0)&&(pos<list->length)){ 
ListNode*p=get_node_at(list,pos-1); 
remove_node(&(list->head),p,(p!=NULL)?p->link:NULL); 
list->length--; 
} 
}
스택(Stack)
스택(Stack) 
스택 
선형리스트구조의특별한형태로, 데이터의삽입과삭제가리스트한쪽끝에서만일어나는자료구조 
후입선출(LIFO:Last-InFirst-Out) 
가장최근에들어온데이터가가장먼저나감 
A 
C 
삽입 
(Push) 
삭제 
(Pop) 
top 
스택의동작구조 
B 
bottom 
요소 
(element)
스택추상데이터타입(ADT) 
스택의용도 
에디터에서되돌리기기능, 함수호출에서복귀주소기억 
스택의연산 
객체: n개의element형의요소들의선형리스트 
연산: 
▪create() ::=스택을생성한다. 
▪is_empty(s) ::= 스택이비어있는지를검사한다. 
▪is_full(s) ::= 스택이가득찼는가를검사한다. 
▪push(s, e) ::= 스택의맨위에요소e를추가한다. 
▪pop(s) ::= 스택의맨위에있는요소를삭제한다. 
▪peek(s) ::= 스택의맨위에있는요소를삭제하지않고반환한다. 
초기상태 
push(A) 
push(B) 
push(C) 
pop() 
A 
A 
B 
A 
B 
C 
A 
B
배열을이용한스택의구현 
스택구현 
1차원배열stack[ ] 
스택에서가장최근에입력되었던자료를가리키는top 변수 
가장먼저들어온요소는stack[0]에, 가장최근에들어온요소는stack[top]에저장 
스택이공백상태이면top은-1 
Is_empty, is_full연산 
공백상태 
A 
B 
C 
D 
3 
2 
1 
0 
-1 
3 
2 
1 
0 
-1 
TOP 
A 
B 
3 
2 
1 
0 
-1 
TOP 
TOP 
is_empty(S) 
if top= -1 
then return TRUE 
else return FALSE 
is_full(S) 
if top= (MAX_STACK_SIZE-1) 
then return TRUE 
else return FALSE
배열을이용한스택의구현 
push 연산 
pop 연산 
push(S, x) 
if is_full(S) 
then error "overflow" 
else top←top+1 
stack[top]←x 
pop(S, x) 
if is_empty(S) 
then error "underflow" 
else e←stack[top] 
top←top-1 
return e 
push(C) 
A 
B 
A 
B 
C 
TOP 
TOP 
pop(C) 
A 
B 
A 
B 
C 
TOP 
TOP
연결스택 
연결리스트를이용하여구현한스택 
장점: 크기가제한되지않음 
단점: 구현이복잡하고삽입이나삭제시간이오래걸림 
연결스택(Linked Stack) 
A 
B 
3 
2 
1 
0 
-1 
TOP 
C 
C 
B 
A 
n 
TOP 
typedefint element; 
typedefstructStackNode{ 
element item; 
structStackNode*link; 
} StackeNode; 
typedefstruct{ 
StackNode*top; 
} LinkedStackType; 
요소타입 
노드타입 
연결스택의관련데이터
연결스택에서의연산 
push(), pop() 
// 삽입함수 
void push(LinkedStackType*s, element item) { 
StackNode*temp=(StackNode*)malloc(sizeof(StackNode)); 
if( temp == NULL ){ 
fprintf(stderr, "메모리할당에러n"); 
return; 
} 
else{ 
temp->item = item; 
temp->link = s->top; 
s->top = temp; 
} 
} 
// 삭제함수 
element pop(LinkedStackType*s) 
{ 
if( is_empty(s) ) { 
fprintf(stderr, "스택이비어있음n"); 
exit(1); 
} 
else{ 
StackNode*temp=s->top; 
int item = temp->item; 
s->top = s->top->link; 
free(temp); 
return item; 
} 
} 
A 
C 
B 
A 
n 
Top 
Temp 
② 
① 
C 
B 
A 
n 
Top 
Temp
스택의응용: 괄호검사 
괄호의종류 
괄호의종류: 대괄호(‘[’, ‘]’), 중괄호(‘{’, ‘}’), 소괄호(‘(’, ‘)’) 
조건 
왼쪽괄호의개수와오른쪽괄호의개수가같아야한다. 
같은괄호에서왼쪽괄호는오른쪽괄호보다먼저나와야한다. 
괄호사이에는포함관계만존재한다. 
잘못된괄호의사용예 
(a(b), a(b)c), a{b(c[d]e}f) 
if( ( i==0 ) && (j==0 ) 
비교 
비교 
오류 
{ A [ (i+1 ) ]=0; } 
비교 
비교 
비교 
성공 
( 
( 
( 
( 
( 
( 
( 
( 
{ 
{ 
[ 
{ 
[ 
{ 
{ 
[ 
( 
{ 
[ 
( 
( 
(
큐(Queue)
큐(QUEUE) 
큐 
대기열을모델링 
먼저들어온데이터가먼저나가는자료구조 
선입선출(FIFO: First-In First-Out) 
용어 
줄의맨앞을큐프런트(Queue Front) 
맨뒤를큐리어(Queue Rear) 
큐리어에데이터를삽입하는작업= 큐애드(Add) 
큐프런트의데이터를삭제하는작업= 큐리무브(Remove) 
삽입 
삭제 
검색 
ADT 리스트 
Insert 
Delete 
Retrieve 
ADT 스택 
Push 
Pop 
GetTop 
(PeekTop) 
ADT 큐 
Add(Enqueue) 
Remove 
(Dequeue) 
GetFront 
(PeekFront) 
전단(front) 
후단(rear)
큐ADT 
큐ADT 
삽입과삭제는FIFO순서를따른다. 
삽입은큐의후단에서, 삭제는전단에서이루어진다 
큐의응용 
시뮬레이션의대기열(공항에서의비행기들, 은행에서의대기열) 
통신에서의데이터패킷들의모델링에이용 
프린터와컴퓨터사이의버퍼링 
스택과마찬가지로프로그래머의도구 
객체: n개의element형으로구성된요소들의순서있는모임 
연산: 
▪create() ::=큐를생성한다. 
▪init(q) ::=큐를초기화한다. 
▪is_empty(q) ::=큐가비어있는지를검사한다. 
▪is_full(q) ::=큐가가득찼는가를검사한다. 
▪enqueue(q, e) ::= 큐의뒤에요소를추가한다. 
▪dequeue(q) ::=큐의앞에있는요소를반환한다음삭제한다. 
▪peek(q) ::= 큐에서삭제하지않고앞에있는요소를반환한다. 
생산자 
버퍼 
소비자
배열을이용한큐 
선형큐 
배열을선형으로사용하여큐를구현 
삽입을계속하기위해서는요소들을이용시켜야함 
문제점이많아사용되지않음 
원형큐 
배열을원형으로사용하여큐를구현 
front : 첫번째요소하나앞의인덱스 
rear : 마지막요소의인덱스 
[-1] [0] [1] [2] [3] [4] [5] 
[-1] [0] [1] [2] [3] [4] [5] 
front 
rear 
front 
rear 
rear 
a 
b 
front 
… 
[0] 
[1] 
[2] 
[3] 
[4] 
[5] 
[6] 
[7]
원형큐의삽입과삭제 
rear 
a 
front 
… 
[0] 
[1] 
[2] 
[3] 
[4] 
[5] 
[6] 
[7] 
rear 
a 
b 
front 
… 
[0] 
[1] 
[2] 
[3] 
[4] 
[5] 
[6] 
[7] 
rear 
b 
front 
… 
[0] 
[1] 
[2] 
[3] 
[4] 
[5] 
[6] 
[7] 
2. A 삽입 
rear 
front 
… 
[0] 
[1] 
[2] 
[3] 
[4] 
[5] 
[6] 
[7] 
1. 초기상태 
4. A 삭제 
3. B 삽입
공백상태, 포화상태 
공백상태 
front == rear 
포화상태 
front % M == (rear+1) % M 
공백상태와포화상태를구별하기위해서는하나의공간은항상비워둔다 
rear 
front 
… 
[0] 
[1] 
[2] 
[3] 
[4] 
[5] 
[6] 
[7] 
1. 공백상태 
rear 
front 
d 
[0] 
[1] 
[2] 
[3] 
[4] 
[5] 
[6] 
[7] 
1. 포화상태 
e 
f 
g 
rear 
front 
[0] 
[1] 
[2] 
[3] 
[4] 
[5] 
[6] 
[7] 
1. 초기상태 
a 
b 
c 
d 
e 
f 
g 
a 
b 
c 
h
큐의연산 
Modulo 연산을사용 
인덱스를원형으로회전시킴 
// 공백상태검출함수 
int is_empty(QueueType*q){ 
return (q->front == q->rear); 
} 
// 포화상태검출함수 
int is_full(QueueType*q){ 
return ((q->rear+1)%MAX_QUEUE_SIZE == q->front); 
} 
// 삽입함수 
void enqueue(QueueType*q, element item){ 
if( is_full(q) ) 
error("큐가포화상태입니다"); 
q->rear = (q->rear+1) % MAX_QUEUE_SIZE; 
q->queue[q->rear] = item; 
} 
// 삭제함수 
element dequeue(QueueType*q) { 
if( is_empty(q) ) 
error("큐가공백상태입니다"); 
q->front = (q->front+1) % MAX_QUEUE_SIZE; 
return q->queue[q->front]; 
}
연결큐(Linked Queue) 
연결큐: 연결리스트로구현된큐 
front 포인터는삭제와관련되며rear 포인터는삽입 
front는연결리스트의맨앞에있는요소를가리킨다 
rear 포인터는맨뒤에있는요소를가리킨다 
큐에요소가없는경우에는front와rear는NULL 
A 
B 
C 
D 
NULL 
rear 
front 
삽입 
삭제 
A 
B 
C 
D 
NULL 
rear 
front 
temp 
A 
B 
C 
D 
NULL 
rear 
front 
A 
B 
C 
D 
NULL 
rear 
front 
A 
B 
C 
D 
NULL 
rear 
front 
temp
덱(Deque) 
덱(Double-ended Queue) 
큐의전단(front)와후단(rear)에서모두삽입과삭제가가능한큐 
덱ADT 
전단(front) 
후단(rear) 
add_front 
delete_front 
get_front 
add_rear 
delete_rear 
get_rear 
객체: n개의element형으로구성된요소들의순서있는모임 
연산: 
▪create() ::=덱을생성한다. 
▪init(dq) ::=덱을초기화한다. 
▪is_empty(dq) ::= 덱이공백상태인지를검사한다. 
▪is_full(dq) ::=덱이포화상태인지를검사한다. 
▪add_front(dq, e) ::= 덱의앞에요소를추가한다. 
▪add_rear(dq, e) ::= 덱의뒤에요소를추가한다. 
▪delete_front(dq) ::=덱의앞에있는요소를반환한다음삭제한다 
▪delete_rear(dq) ::=덱의뒤에있는요소를반환한다음삭제한다. 
▪get_front(q) ::= 덱의앞에서삭제하지않고앞에있는요소를반환한다. 
▪get_rear(q) ::= 덱의뒤에서삭제하지않고뒤에있는요소를반환한다.
덱의연산 
C 
A 
ddd_front(dq, A) 
A 
ddd_rear(dq, B) 
B 
A 
ddd_front(dq, C) 
B 
C 
A 
ddd_rear(dq, D) 
B 
D 
A 
delete_front(dq) 
B 
D 
A 
delete_rear(dq) 
B 
front 
rear 
front 
rear 
front 
rear 
front 
rear 
front 
rear 
front 
rear
덱의구현 
덱의구현 
양쪽에서삽입, 삭제가가능하여야하므로일반적으로이중연결리스트사용 
typedefint element;// 요소의타입 
typedefstructDlistNode{// 노드의타입 
element data; 
structDlistNode*llink; 
structDlistNode*rlink; 
} DlistNode; 
typedefstructDequeType{// 덱의타입 
DlistNode*head; 
DlistNode*tail; 
} DequeType;
덱에서의삽입 
void add_rear(DequeType*dq, element item) { 
DlistNode*new_node= create_node(dq->tail, item, NULL); 
if( is_empty(dq)) 
dq->head = new_node; 
else 
dq->tail->rlink= new_node; 
dq->tail = new_node; 
} 
void add_front(DequeType*dq, element item){ 
DlistNode*new_node= create_node(NULL, item, dq->head); 
if( is_empty(dq)) 
dq->tail = new_node; 
else 
dq->head->llink= new_node; 
dq->head = new_node; 
} 
tail 
new node 
삽입전 
삽입후 
tail
덱에서의삭제 
// 전단에서의삭제 
element delete_front(DequeType*dq) 
{ 
element item; 
DlistNode*removed_node; 
if (is_empty(dq)) error("공백덱에서삭제"); 
else { 
removed_node= dq->head; // 삭제할노드 
item = removed_node->data; // 데이터추출 
dq->head = dq->head->rlink; // 헤드포인터변경 
free(removed_node);// 메모리공간반납 
if (dq->head == NULL)// 공백상태이면 
dq->tail = NULL; 
else// 공백상태가아니면 
dq->head->llink=NULL; 
} 
return item; 
} 
tail 
삭제전 
삭제후 
n 
tail 
head 
head
우선순위큐(Priority queue)
우선순위큐(Priority queue) 
우선순위큐 
우선순위를가진항목들을저장하는큐 
FIFO 순서가아니라우선순위가높은데이터가먼저나가게된다. 
가장일반적인큐로써, 스택이나큐로구현이가능하다. 
응용분야 
시뮬레이션시스템(여기서의우선순위는대개사건의시각이다.) 
네트워크트래픽제어 
운영체제에서의작업스케쥴링 
자료구조 
삭제되는요소 
스택 
가장최근에들어온데이터 
큐 
가장먼저들어온데이터 
우선순위큐 
가장우선순위가높은데이터 
> 
>
우선순위큐ADT 
가장중요한연산은 
insert 연산(요소삽입), delete 연산(요소삭제)이다. 
우선순위큐는2가지로구분 
최소우선순위큐 
최대우선순위큐 
객체: n개의element형의우선순위를가진요소들의모임 
연산: 
▪create() ::=우선순위큐를생성한다. 
▪init(q) ::= 우선순위큐q를초기화한다. 
▪is_empty(q) ::= 우선순위큐q가비어있는지를검사한다. 
▪is_full(q) ::= 우선순위큐q가가득찼는가를검사한다. 
▪insert(q, x) ::= 우선순위큐q에요소x를추가한다. 
▪delete(q) ::= 우선순위큐로부터가장우선순위가높은요소를삭제하고이요소를반환한다. 
▪find(q) ::= 우선순위가가장높은요소를반환한다.
우선순위큐구현 
구현방법 
배열을이용한우선순위큐 
연결리스트를이용한우선순위큐 
히프(heap)를이용한우선순위큐 
표현방법 
삽입 
삭제 
순서없는배열 
O(1) 
O(n) 
순서없는연결리스트 
O(1) 
O(n) 
정렬된배열 
O(n) 
O(1) 
정렬된연결리스트 
O(n) 
O(1) 
히프 
O(logn) 
O(logn) 
9 
7 
6 
5 
4 
3 
2 
2 
1 
3 
1 
2 
3 
7 
5 
n 
헤드포인트 
1 
2 
3 
7 
5 
0 1 2 3 4 5 6 
COUNT
히프(Heap) 
히프 
히프란노드들이저장하고있는키들이다음식을만족하는완전이진트리 
최대히프(max heap) 
부모노드의키값이자식노드의키값보다크거나같은완전이진트리 
key(부모노드) ≥key(자식노드) 
최소히프(min heap) 
부모노드의키값이자식노드의키값보다작거나같은완전이진트리 
key(부모노드) ≤key(자식노드) 
9 
7 
6 
5 
4 
2 
3 
3 
2 
1 
1 
4 
2 
7 
5 
3 
9 
3 
7 
8 
최대히프 
최소히프
히프의구현방법 
히프구현배열이용 
완전이진트리이므로각노드에번호를붙일수있다 
이번호를배열의인덱스라고생각 
부모노드와자식노드를찾기가쉽다. 
왼쪽자식의인덱스= (부모의인덱스)*2 
오른쪽자식의인덱스= (부모의인덱스)*2 + 1 
부모의인덱스= (자식의인덱스)/2 
9 
7 
6 
5 
4 
2 
3 
3 
2 
1 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
9 
7 
6 
5 
4 
3 
2 
2 
1 
3 
0 1 2 3 4 5 6 7 8 9 10 
왼쪽자식인덱스=(2*3)=6 
오른쪽자식인덱스= (2*3)+1=7
히프의높이 
N개의노드를가지고있는히프의높이는O(logn) 
히프는완전이진트리 
마지막레벨h를제외하고는각레벨의i에2i-1개의노드존재 
9 
7 
6 
5 
4 
2 
3 
3 
2 
1 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
깊이노드의개수 
1 1=20 
2 1=21 
3 1=22 
4 1=23
히프연산 
Upheap알고리즘 
Downheap알고리즘 
insert_max_heap(A, key) 
heap_size← heap_size+ 1; 
i ← heap_size; 
A[i] ← key; 
while i ≠ 1 and A[i] > A[PARENT(i)] do 
A[i] ↔ A[PARENT]; 
i ← PARENT(i); 
delete_max_heap(A) 
item ← A[1]; 
A[1] ← A[heap_size]; 
heap_size←heap_size-1; 
i ← 2; 
while i ≤ heap_sizedo 
if i < heap_sizeand A[LEFT(i)] > A[RIGHT(i)] 
then largest ← LEFT(i); 
else largest ← RIGHT(i); 
if A[PARENT(largest)] > A[largest] 
then break; 
A[PARENT(largest)] ↔ A[largest]; 
i ← CHILD(largest); 
return item;
히프에서의삽입 
upheap연산 
새로운키k의삽입연산후히프의성질이만족되지않을수있다 
upheap는삽입된노드로부터루트까지의경로에있는노드들을k와비교, 교환함으로써히프의성질을복원한다. 
키k가부모노드보다작거나같으면upheap는종료한다 
히프의높이가O(logn)이므로upheap연산은O(logn)이다. 
9 
7 
6 
5 
4 
2 
3 
3 
2 
1 
8 
9 
7 
6 
5 
8 
2 
3 
3 
2 
1 
4 
9 
8 
6 
5 
7 
2 
3 
3 
2 
1 
4
히프에서의삭제 
downheap연산 
최대히프에서의삭제는가장큰키값을가진노드를삭제하는것을의미 
따라서루트노드가삭제된다 
루트노드를삭제한다 
마지막노드를루트노드로이동한다. 
루트에서부터단말노드까지의경로에있는노드들을교환하여히프성질을만족시킨다. 
9 
7 
6 
5 
4 
2 
3 
3 
2 
1 
3 
7 
6 
5 
4 
2 
3 
2 
1 
7 
3 
6 
5 
4 
2 
3 
2 
1 
7 
5 
6 
3 
4 
2 
3 
2 
1
히프정렬(Heap Sort) 
히프정렬 
히프정렬이유용한경우는전체자료를정렬이아니라가장큰값몇개만필요할때이다. 
먼저정렬해야할n개의요소들을최대히프에삽입 
한번에하나씩요소를히프에서삭제하여저장하면된다. 
삭제되는요소들은값이증가되는순서(최소히프의경우) 
요소를히프에삽입/삭제할때시간:O(logn) 
요소의개수가n개이므로전체적으로O(nlogn)시간이걸린다. 
// 우선순위큐인히프를이용한정렬 
void heap_sort(element a[], int n) 
{ 
int i; 
HeapTypeh; 
init(&h); 
for(i=0;i<n;i++){ 
insert_max_heap(&h, a[i]); 
} 
for(i=(n-1);i>=0;i--){ 
a[i] = delete_max_heap(&h); 
} 
}
최대히프의시간복잡도 
최대히프는완전이진트리 
노드의수가n 일때높이는log n 
추가: log n 
최악의경우단말로부터루트까지삽입될위치탐색 
삭제: log n 
최악의경우루트부터단말까지교환이일어남
정렬(Sort)
정렬(Sort) 
정렬(Sort) 
물건을크기순으로오름차순이나내림차순으로나열하는것 
컴퓨터공학분야에서가장기본적이고중요한알고리즘중의하나 
가장많이, 그리고가장잘알려진알고리즘 
자료탐색에있어서필수적이다. 
정렬의단위 
정렬의기준: 정렬키(Sort Key) 필드 
이름 
학번 
주소 
연락처 
학생들의레코드 
필드 
필드 
필드 
필드 
키(key) 
레코드
정렬알고리즘 
많은정렬알고리즘존재 
단순하지만비효율적인방법 
삽입정렬, 선택정렬, 버블정렬… 
복잡하지만효율적인방법 
퀵정렬, 히프정렬, 합병정렬, 기수정렬… 
모든경우에최적인알고리즘은없다 
응용에맞추어선택 
정렬알고리즘의평가 
비교횟수 
이동횟수 
정렬의분류 
내부정렬과외부정렬 
안정정렬(Stable Sorting)과불안정정렬(Unstable Sorting) 
직접정렬(Direct Sorting)과간접정렬(Indirect Sorting)
선택정렬(Selection Sort) 
선택정렬 
정렬이안된숫자들중에서최소값을선택하여배열의첫번째요소와교환 
선택정렬분석 
숫자의개수n 
최소값을선택하는데걸리는시간:0(n) 
전체시간복잡도: 0(n2) 
안정성을만족하지않는다 
selection_sort(A, n) 
for i←0 to n-2 do 
least ← A[i], A[i+1],..., A[n-1] 중에서가장작은값의인덱스; 
A[i]와A[least]의교환; 
i++; 
5 
3 
8 
1 
2 
7 
1 
3 
8 
5 
2 
7 
1 
2 
8 
5 
3 
7 
1 
2 
3 
5 
8 
7 
1 
2 
3 
5 
8 
7 
1 
2 
3 
5 
7 
8 
1 
2 
3 
5 
7 
8
버블정렬(Bubble Sort) 
버블정렬 
인접한레코드가순서대로되어있지않으면교환 
전체가정렬될때까지비교/교환계속 
BubbleSort(A, n) 
for i←n-1 to 1 do 
for j←0 to i-1 do 
j와j+1번째의요소가크기순이아니면교환 
j++; 
i--; 
#define SWAP(x, y, t) ( (t)=(x), (x)=(y), (y)=(t) ) 
void bubble_sort(int list[], int n) 
{ 
int i, j, temp; 
for(i=n-1; i>0; i--){ 
for(j=0; j<i; j++) 
/* 앞뒤의레코드를비교한후교체*/ 
if(list[j]>list[j+1]) 
SWAP(list[j], list[j+1], temp); 
} 
}
버블정렬(Bubble Sort) 
버블정렬분석 
비교횟수 
버블정렬의비교횟수는최상, 평균, 최악의경우에도항상일정: 
이동횟수(평균: O(n2)) 
최악: 역순정렬= 3* 비교 
최상: 이미정렬= 0 
5 
3 
8 
1 
2 
7 
3 
5 
1 
2 
7 
8 
3 
1 
2 
5 
7 
8 
1 
2 
3 
4 
5 
8 
1 
2 
3 
5 
7 
8 
1 
2 
3 
5 
7 
8 
1 
2 
3 
5 
7 
8 
초기상태 
정렬완료 
스캔1 
스캔2 
스캔3 
스캔4 
스캔5 
풊=ퟏ 풏−ퟏ풏(풏−ퟏ) ퟐ =푶(풏ퟐ) 
5 
3 
8 
1 
2 
7 
3 
5 
8 
1 
2 
7 
3 
5 
8 
1 
2 
7 
3 
5 
8 
1 
2 
7 
3 
5 
1 
8 
2 
7 
3 
5 
1 
2 
8 
7 
3 
5 
1 
2 
7 
8 
초기상태 
스캔완료 
5와3교환 
교환없음 
8과1을교환 
8과2를교환 
8과7을교환
삽입정렬(Insertion Sort) 
삽입정렬 
정렬되어있는부분에새로운레코드를적절한위치에삽입하는과정을반복 
많은이동레코드가클경우불리 
안정된정렬방법(이미정렬되어있으면효율적) 
1 
3 
5 
8 
2 
7 
정렬된부분 
미정렬부분 
Algorithm InsertionSort(list, n): 
Input: n개의정수를저장하고있는배열list 
Output: 정렬된배열list 
for i←1 to n-1 do 
{ 
정렬된리스트에서list[i]보다더큰요소들이동; 
list[i]를정렬된리스트의적절한위치에삽입; 
i++; 
}
삽입정렬(Insertion Sort) 
삽입정렬의복잡도 
비교: n-1 
이동: 2(n-1) 
최악의경우: 역순으로정렬 
비교: 
이동 
// 삽입정렬 
void insertion_sort(int list[], int n) 
{ 
int i, j, key; 
for(i=1; i<n; i++){ 
key = list[i]; 
for(j=i-1; j>=0 && list[j]>key; j--) 
list[j+1] = list[j]; /* 레코드의오른쪽이동*/ 
list[j+1] = key; 
} 
} 
풊=ퟏ 풏−ퟏ풏(풏−ퟏ) ퟐ =푶(풏ퟐ) 풏(풏−ퟏ) ퟐ +ퟐ(풏−ퟏ)=푶(풏ퟐ) 
5 
3 
8 
1 
2 
7 
5 
3 
8 
1 
2 
7 
3 
5 
8 
1 
2 
7 
3 
5 
8 
1 
2 
7 
1 
3 
5 
8 
2 
7 
1 
2 
3 
5 
8 
7 
1 
2 
3 
5 
7 
8 
초기상태 
정렬완료 
3을삽입 
8은이미제자리에 
1을삽입 
2를삽입 
7을삽입
분할정복(Divide and Conquer) 
분할정복 
문제를작은2개의문제로분리하고각각을해결한다음, 결과를모아서원래의문제를해결하는전략 
분리된문제가아직도해결하기어렵다면, 즉충분히작지않다면분할정복방법을다시적용 
재귀호출을이용하여구현 
1. 분할(Divide): 배열을같은크기의2개의부분배열로분할한다. 
2. 정복(Conquer): 부분배열을정렬한다. 부분배열의크기가충분히작지않으면재귀호출을이용하여 
다시분할정복기법을적용한다. 
3. 결합(Combine): 정렬된부분배열을하나의배열에통합한다. 
입력파일: 27 10 12 20 25 13 15 22 
1.분할(Divide): 배열을27 10 12 20 과25 13 15 22의2개의부분배열로분리 
2.정복(Conquer): 부분배열을정렬하여10 12 20 27 과13 15 22 25를얻는다. 
3.결합(Combine): 부분배열을통합하여10 12 13 15 20 22 25 27을얻는다.
퀵정렬(Quick Sort) 
퀵정렬 
평균적으로가장빠른정렬방법: 분할정복사용 
퀵정렬은전체리스트를2개의부분리스트로분할하고, 각각의부분리스트를다시퀵정렬로정렬 
5 
3 
8 
4 
9 
1 
6 
2 
7 
5 
3 
8 
4 
9 
1 
6 
2 
7 
피봇 
피봇보다작은값 
피봇보다큰값 
void quick_sort(int list[], int left, int right) 
{ 
if(left<right){ 
int q=partition(list, left, right); 
quick_sort(list, left, q-1); 
quick_sort(list, q+1, right); 
} 
} 
3 정렬할범위가2개이상의데이터이면 
4 partition 함수를호출하여피벗을기준으로2개의리스트로분할한다. 
partition 함수의반환값은피봇의위치가된다. 
5 left에서피봇위치바로앞까지를대상으로순환호출한다 
(피봇은제외된다). 
6 피봇위치바로다음부터right까지를대상으로순환호출한다 
(피봇은제외된다).
퀵정렬(Quick Sort) 
Partition 함수 
피봇(pivot): 가장왼쪽숫자라고가정 
두개의변수low와high를사용 
low는피봇보다작으면통과, 크면정지 
high는피봇보다크면통과, 작으면정지 
정지된위치의숫자를교환: low와high가교차하면종료 
5 
3 
8 
4 
9 
1 
6 
2 
7 
5 
3 
8 
4 
9 
1 
6 
2 
7 
5 
3 
2 
4 
9 
1 
6 
8 
7 
5 
3 
2 
4 
9 
1 
6 
8 
7 
5 
3 
2 
4 
1 
9 
6 
8 
7 
5 
3 
2 
4 
1 
9 
6 
8 
7 
5 
3 
2 
4 
1 
9 
6 
8 
7 
1 
3 
2 
4 
5 
9 
6 
8 
7 
Left 
Right 
피봇 
Low 
Low 
Low 
Low 
Low 
Low 
Low 
Low 
High 
High 
High 
High 
High 
High 
High 
High 
Stop
퀵정렬(Quick Sort) 
Partition 함수의구현 
int partition(int list[], int left, int right) 
{ 
int pivot, temp; 
int low,high; 
low = left; 
high = right+1; 
pivot = list[left]; 
do { 
do 
low++; 
while(low<=right &&list[low]<pivot); 
do 
high--; 
while(high>=left && list[high]>pivot); 
if(low<high) SWAP(list[low], list[high], temp); 
} while(low<high); 
SWAP(list[left], list[high], temp); 
return high; 
}
퀵정렬(Quick Sort) 
퀵정렬과정 
5 
3 
8 
4 
9 
1 
6 
2 
7 
1 
3 
2 
4 
5 
9 
6 
8 
7 
1 
3 
2 
4 
5 
7 
6 
8 
9 
1 
2 
3 
4 
5 
6 
7 
8 
9 
1 
2 
3 
4 
5 
6 
7 
8 
9 
피봇
퀵정렬(Quick Sort) 
퀵정렬분석 
최상의경우: 거의균등한리스트로분할되는경우 
패스수: Log2n 
각패스안에서비교횟수: n 
총비교횟수: n Log2n 
총이동횟수: 비교횟수에비하여무시가능 
최악의경우: 불균등한리스트로분할되는경우 
패스수: n 
각패스안에서의비교횟수: n 
총비교횟수: n2 
총이동횟수: 비교횟수에비하여무시가능 
(1 23456789) 
1 (23456789) 
12 (3456789) 
123 (456789) 
1234 (56789) 
... 
123456789 
…
합병정렬(Merge Sort) 
합병정렬 
리스트를두개로나누어, 각각을정렬한다음, 다시하나로합치는방법 
분할정복기법에바탕 
27 
10 
12 
20 
25 
13 
15 
22 
10 
12 
13 
15 
20 
22 
25 
27 
27 
10 
12 
20 
25 
13 
15 
22 
10 
12 
20 
27 
13 
15 
22 
25 
27 
10 
25 
13 
10 
27 
13 
25 
12 
20 
15 
22 
12 
20 
15 
22 
27 
25 
12 
15 
10 
13 
20 
22
합병정렬(Merge Sort) 
합병정렬 
합병정렬에서실제로정렬이이루어지는시점은2개의리스트를합병하는단계 
merge_sort(list, left, right) 
if left < right 
mid = (left+right)/2; 
merge_sort(list, left, mid); 
merge_sort(list, mid+1, right); 
merge(list, left, mid, right); 
1 
1 
2 
1 
2 
3 
1 
2 
3 
4 
1 
2 
3 
4 
5 
1 
2 
3 
4 
5 
6 
1 
2 
3 
4 
5 
6 
7 
8 
2 
5 
7 
8 
2 
5 
7 
8 
5 
7 
8 
5 
7 
8 
5 
7 
8 
7 
8 
7 
8 
1 
3 
4 
6 
3 
4 
6 
3 
4 
6 
4 
6 
6 
6 
배열A 
배열B 
배열C
합병정렬(Merge Sort) 
합병알고리즘 
merge(list, left, mid, last): 
// 2개의인접한배열list[left..mid]와list[mid+1..right]를합병 
b1←left; 
e1←mid; 
b2←mid+1; 
e2←right; 
sorted 배열을생성; 
index←0; 
while b1≤e1 and b2≤e2 do 
if(list[b1]<list[b2]) 
then 
sorted[index]←list[b1]; 
b1++; 
index++; 
else 
sorted[index]←list[b2]; 
b2++; 
index++; 
요소가남아있는부분배열을sorted로복사한다; 
sorted를list로복사한다; 
2 
5 
7 
8 
1 
3 
4 
6 
Low 
i 
j 
Mid+1 
High 
Mid 
list 
1 
2 
3 
4 
sorted 
i 
정렬된리스트 
정렬된리스트
합병정렬(Merge Sort) 
합병정렬분석 
비교횟수 
합병정렬은크기n인리스트를정확히균등분배하므로퀵정렬의이상적인경우와마찬가지로정확히logn개의패스를가진다. 
각패스에서리스트의모든레코드n개를비교하여합병하므로n 번의비교연산이수행된다. 
따라서합병정렬은최적, 평균, 최악의경우모두큰차이없이nlogn번의비교를수행하므로O(nlogn)의복잡도를가지는알고리즘이다. 
합병정렬은안정적이며데이터의초기분산순서에영향을덜받는다. 
이동횟수 
배열을이용하는합병정렬은레코드의이동이각패스에서2n번발생하므로전체레코드의이동은2nlogn번발생한다. 이는레코드의크기가큰경우에는매우큰시간적낭비를초래한다. 
그러나레코드를연결리스트로구성하여합병정렬할경우, 링크인덱스만변경되므로데이터의이동은무시할수있을정도로작아진다. 
따라서크기가큰레코드를정렬할경우, 연결리스트를이용하는합병정렬은퀵정렬을포함한다른어떤정렬방법보다매우효율적이다
기수정렬(Radix Sort) 
기수정렬 
레코드를비교하지않고정렬 
단순히자리수에따라버킷에넣었다가꺼내면정렬됨 
O(nlogn)이라는이론적인하한선을깰수있는유일한방법 
기수정렬은O(kn) 의시간복잡도를가지는데대부분k<4 이하 
정렬할수있는레코드의타입이한정 
레코드의키들이동일한길이를가지는숫자나문자열로구성 
0 
1 
2 
3 
4 
5 
6 
7 
8 
9 
8 
2 
7 
3 
5 
2 
3 
5 
7 
8 
2 
3 
5 
7 
8
기수정렬(Radix Sort) 
Sorting a sequence of 4-bit integers 
1001 
0010 
1101 
0001 
1110 
0010 
1110 
1001 
1101 
0001 
1001 
1101 
0001 
0010 
1110 
1001 
0001 
0010 
1101 
1110 
0001 
0010 
1001 
1101 
1110
기수정렬(Radix Sort) 
기수정렬 
버킷은큐로구현 
버킷의개수는키의표현방법과밀접한관계 
이진법을사용한다면버킷은2개. 
알파벳문자를사용한다면버킷은26개 
십진법을사용한다면버킷은10개 
32비트의정수의경우, 8비트씩나누어기수정렬의개념을적용한다면 
필요한버킷수는256개, 대신에필요한패스의수는4개로십진수표현보다줄어듬. 
RadixSort(list, n): 
for d←LSD의위치to MSD의위치do 
{ 
d번째자릿수에따라0번부터9번버킷에집어놓는다. 
버킷에서숫자들을순차적으로읽어서하나의리스트로합친다. 
d++; 
}
계수정렬(Counting Sort) 
계수정렬 
선형시간에정렬하는효율적인알고리즘 
항목의순서를결정하기위해집합에각항목이몇개씩있는지세는작업한다 
속도가빠르고안정적이다. 
입력키가어떤범위에한정될때사용가능 
예) 입력이0부터K사이의정수 
카운트를위한충분한공간을할당하려면집합내의가장큰수를알아야한다. 
0 
3 
2 
1 
3 
2 
1 
2 
2 
3 
Input A: 
N=10 
M=4 
Count array C: 
-all elements in input between 0 and 3 
1 
2 
4 
3 
0 
1 
2 
3 
3 
3 
3 
2 
2 
2 
2 
1 
1 
0 
10 
9 
8 
7 
6 
5 
4 
3 
2 
1 
Output sorted array:
정렬알고리즘의비교 
알고리즘 
최선 
평균 
최악 
삽입정렬 
O(n) 
O(n2) 
O(n2) 
선택정렬 
O(n2) 
O(n2) 
O(n2) 
버블정렬 
O(n2) 
O(n2) 
O(n2) 
쉘정렬 
O(n) 
O(n1.5) 
O(n1.5) 
퀵정렬 
O(nLog2n) 
O(nLog2n) 
O(n2) 
히프정렬 
O(nLog2n) 
O(nLog2n) 
O(nLog2n) 
합병정렬 
O(nLog2n) 
O(nLog2n) 
O(nLog2n) 
기수정렬 
O(dn) 
O(dn) 
O(dn)
트리(Tree)
트리개념 
트리(Tree) 
계층적인구조(Hierarchical Structure)를나타내는자료구조 
트리는부모(Parent)-자식(Child) 관계로이루어진다. 
응용분야 
계층적인조직표현, 파일시스템등 
트리에서나타나는용어들 
일반트리와이진트리 
노드(node): 트리의구성요소 
루트(root): 부모가없는노드(A) 
서브트리(subtree): 하나의노드와그노드들의자손들로이루어진트리 
단말노드(terminal node): 자식이없는노드(A,B,C,D) 
비단말노드: 적어도하나의자식을가지는노드(E,F,G,H,I,J) 
자식, 부모, 형제, 조상, 자손노드: 인간과동일 
레벨(level): 트리의각층의번호 
높이(height): 트리의최대레벨(3) 
차수(degree): 노드가가지고있는자식노드의개수 
A 
B 
C 
D 
E 
F 
G 
H 
I 
J 
Level 1 
Level 2 
Level 3 
데이터 
데이터 
링크1 
링크1 
… 
링크2 
링크N 
이진트리 
일반트리
이진트리정의 
모든노드가2개의서브트리를가지고있는트리 
서브트리는공집합일수있다. 
이진트리의노드에는최대2개까지의자식노드가존재 
모든노드의차수가2 이하가된다. 구현이편리 
이진트리에는서브트리간순서가존재 
이진트리(Binary Tree) 
Tleft 
Tright
이진트리의분류 
포화이진트리(Full binary tree) 
트리의각레벨에노드가다차있는이진트리 
전체노드개수= 푖=0 푘−12푖= 2k-1 
노드의번호는레벨단위로왼쪽에서오른쪽으로 
완전이진트리(Complete binary tree) 
높이가h일때레벨1부터모두노가채워져있고, 
마지막레벨h에서는왼쪽부터오른쪽으로노드가순서대로채워져있는경우 
A 
B 
C 
D 
E 
F 
G 
A 
B 
C 
D 
E 
F 
G 
H 
I 
J 
A 
B 
C 
D 
F 
G 
I 
포화이진트리 
완전이진트리 
기타이진트리
이진트리의특성 
노드개수가n이면간선의개수는n-1 
높이가h인이진트리의경우, 
최소h개의노드를가지며, 최대2h-1개의노드를가진다. 
+ 
* 
/ 
a 
b 
c 
d 
노드의개수: 7 
간선의개수: 6 
1 
2 
3 
높이=3 
+ 
* 
/ 
a 
b 
c 
d 
21-1=1 
22-1=2 
23-1=4 
최대노드개수=3 
최대노드개수= 21-1 + 22-1+ 23-1= 1+2+4 =7
이진트리의특성 
n개의노드를가지는이진트리의높이는최대n이거나최소[log2(n+1)] 
1 
2 
3 
높이=3 
1 
2 
3 
4 
5 
6 
7 
최대높이=7 
최소높이=3 
4 
5 
6 
7
이진트리의표현 
배열표현법 
모든이진트리를포화이진로가정하고각노드에번호를붙여서그번호를배열의인덱스로삼아노드의데이터를배열에저장하는방법 
링크표현법 
포인터를이용하여부모노드가자식노드를가리키도록하는방법 
A 
B 
C 
D 
E 
F 
1 
3 
2 
4 
6 
5 
A 
B 
C 
D 
E 
F 
0 
1 
2 
3 
4 
5 
6 
7 
8 
A 
B 
C 
D 
A 
B 
C 
D 
0 
1 
2 
3 
4 
5 
6 
7 
8 
데이터 
B 
A 
C 
n 
E 
n 
n 
D 
n 
n 
F 
n 
n= null 
A 
n 
B 
n 
C 
n 
n 
D 
n
L 
V 
R 
이진트리의순회 
순회(Traversal) : 트리의노드를체계적으로방문하는것 
전위순회(Preorder traversal) : VLR 
자손노드보다루트노드를먼저방문한다. 
중위순회(Inordertraversal) : LVR 
왼쪽자손, 루트, 오른쪽자손순으로방문한다. 
후위순회(Postordertraversal) : LRV 
루트노드보다자손을먼저방문한다. 
전체트리나서브트리나 
구조는동일하다. !!!
전위순회 
루트를먼저방문하는순회방법 
알고리즘설명 
1. 노드X가NULL이면더이상순환호출을하지않는다. 
2. X의데이터를출력한다. 
3. X의왼쪽서브트리를순환호출하여방문한다. 
4. X의오른쪽서브트리를순환호출하여방문한다. 
③ 
① 
② 
// 전위순회 
preorder( TreeNode*root ){ 
if ( root ){ 
printf("%d", root->data );// 노드방문 
preorder( root->left );// 왼쪽서브트리순회 
preorder( root->right );// 오른쪽서브트리순회 
} 
} 
1 
2 
3 
4 
5 
6 
7 
1 2 4 5 3 6 7
중위순회 
왼쪽서브트리루트오른쪽서브트리순으로방문 
알고리즘설명 
1. 노드X가NULL이면더이상순환호출을하지않는다. 
2. X의왼쪽서브트리를순환호출하여방문한다. 
3. X의데이터를출력한다. 
4. X의오른쪽서브트리를순환호출하여방문한다. 
// 중위순회 
inorder( TreeNode*root ){ 
if ( root ){ 
inorder( root->left );// 왼쪽서브트리순회 
printf("%d", root->data );// 노드방문 
inorder( root->right );// 오른쪽서브트리순회 
} 
} 
1 
2 
3 
4 
5 
6 
7 
4 2 5 1 6 3 7 
③ 
① 
②
후위순회 
왼쪽서브트리오른쪽서브트리루트 
알고리즘설명 
1. 노드X가NULL이면더이상순환호출을하지않는다. 
2. X의왼쪽서브트리를순환호출하여방문한다. 
3. X의오른쪽서브트리를호출하여방문한다. 
4. X의데이터를출력한다. 
// 후위순회 
postorder( TreeNode*root ){ 
if ( root ){ 
postorder( root->left );// 왼쪽서브트리순회 
postorder( root->right );// 오른쪽서브트리순회 
printf("%d", root->data ); // 노드방문 
} 
} 
1 
2 
3 
4 
5 
6 
7 
4 5 2 6 7 3 1 
③ 
① 
②
수식트리 
수식트리: 산술식을트리형태로표현한것 
비단말노드: 연산자(Operator) 
단말노드: 피연산자(Operand) 
or 
< 
< 
a 
b 
c 
d 
- 
a 
x 
b 
c 
+ 
a 
b 
(a) 
(b) 
(c) 
수식 
a + b 
a -(b ×c) 
(a < b) or (c < d) 
전위순회 
+ a b 
-a ×b c 
or < a b < c d 
중위순회 
a + b 
a -b ×c 
a < b or c < d 
후위순회 
a b + 
a b c ×- 
a b < c d < or 
어떤순회방법이적합한가?
수식트리계산 
후위순회를사용 
서브트리의값을순환호출로계산 
비단말노드를방문할때양쪽서브트리의값을저장된연산자를이용하여계산한다. 
알고리즘설명 
1. 수식트리가공백이면 
2. 그냥복귀 
3. 그렇지않으면왼쪽서브트리를계산하기위한evaluate를순환호출. 이때파라미터는왼쪽자신노드가된다. 
4. 같은방식으로오른쪽서브트리를계산한다. 
5. 루트노드의데이터필드에서연산자를추출한다. 
6. 추출된연산자를가지고연산을수행해서반환한다. 
+ 
* 
/ 
3 
2 
5 
6 
evaluate(exp) 
if exp= NULL 
then return 0; 
else x←evaluate(exp->left); 
y←evaluate(exp->right); 
op←exp->data; 
return (x op y);
이진트리연산 
노드개수구하기 
탐색트리안의노드의개수를계산 
각각의서브트리에대하여순환호출한다음반환되는값에1을더하여반환 
높이구하기 
서브트리에대하여순환호출하고, 서브트리들의반환값중최대값을구한다 
int get_node_count(TreeNode*node) 
{ 
int count=0; 
if( node != NULL ) 
count = 1 + get_node_count(node->left) 
+ get_node_count(node->right); 
return count; 
} 
6 
3 
2 
1 
1 
1 
int get_height(TreeNode*node) 
{ 
int height=0; 
if( node != NULL ) 
height = 1 + max(get_height(node->left), 
get_height(node->right)); 
return height; 
} 
Hleft 
Hright 
높이=3 
H=1+ max(Hleft,Hright)
이진탐색트리(Binary search tree) 
이진탐색트리 
탐색작업을효율적으로하기위한자료 
Key(왼쪽서브트리) ≤Key(루트노드) ≤Key(오른쪽서브트리) 
이진탐색을중위순회하면오름차순으로정렬된값을얻을수있다. 
18 
7 
26 
3 
12 
27 
31 
왼쪽서브트리 
오른쪽서브트리 
루트보다 
작은값 
루트보다 
큰값
이진트리: 탐색연산 
탐색연산 
비교한결과가같으면탐색이성공적으로끝남 
비교한결과, 
주어진키값이루트노드의키값보다작으면탐색은이루트노드의왼쪽자식을 
기준으로다시시작 
주어진키값이루트노드의키값보다크면탐색은이루트노드의오른쪽자식을 
기준으로다시시작 
search(x, k) 
if x=NULL 
then return NULL; 
if k=x->key 
then return x; 
else if k<x->key 
then return search(x->left, k); 
else return search(x->right, k); 
18 
7 
26 
3 
12 
27 
31 
12 탐색
이진트리: 삽입연산 
삽입연산 
삽입을위해서는먼저탐색을수행하는것이필요 
탐색에실패한위치가바로새로운노드를삽입하는위치 
insert_node(T,z) 
p←NULL; //p : 부모노드포인터 
t←root; // t: 탐색포인터 
while t≠NULLdo 
p←t; 
if z->key < p->key 
then t←p->left; 
else t←p->right; 
if p=NULL 
then root←z;// 트리가비어있음 
else if z->key < p->key 
then p->left←z 
else p->right←z 
18 
7 
26 
3 
12 
27 
31 
9탐색 
NULL 
18 
7 
26 
3 
12 
27 
31 
9삽입 
9
이진트리: 삭제연산 
삭제연산Case 
Case 1 : 삭제하려는노드가단말노드일경우 
Case 2 : 삭제하려는노드가하나의서브트리만가지고있는경우 
Case 3 : 삭제하려는노드가두개의서브트리모두가지고있는경우 
Case 1 : 삭제하려는노드가단말노드일경우 
단말노드의부모노드를찾아서연결을끊는다. 
35 
18 
68 
7 
26 
99 
3 
12 
22 
30 
35 
18 
68 
7 
26 
99 
3 
12 
22
이진트리: 삭제연산 
Case 2 : 삭제하려는노드가하나의서브트리만가지고있는경우 
해당노드를삭제하고서브트리는부모노드에붙여준다. 
Case 3 : 삭제하려는노드가두개의서브트리모두가지고있는경우] 
삭제노드와가장비슷한값을가진노드를삭제노드위치로가져온다 
35 
18 
68 
7 
26 
99 
3 
12 
22 
30 
35 
18 
99 
7 
26 
3 
12 
22 
30 
35 
18 
68 
7 
26 
99 
3 
12 
22 
30 
왼쪽서브트리에서 
제일큰값 
왼쪽서브트리에서 
제일큰값 
35 
18 
68 
7 
26 
99 
3 
12 
22 
30 
35 
22 
68 
7 
26 
99 
3 
12 
30
이진트리: 성능 
이진탐색트리연산성능 
탐색, 삽입, 삭제연산의시간복잡도는트리의높이h에비례한다 
최선의경우 
이진트리가균형적으로생성되어있는경우 
h= log2n 
최악의경우 
한쪽으로치우친경사이진트리의경우 
h= n-1 
순차탐색과시간복잡도가같다. 
1 
2 
3 
높이=n-1 
1 
2 
3 
4 
5 
6 
7 
높이=log2n
그래프(Graph)
그래프(Graph) 
연결되어있는객체간의관계를표현하는자료구조 
예: 전기회로, 프로젝트관리, 지도에서도시의연결 
그래프이론(Graph Theory) 
그래프를문제해결의도구로이용하는연구분야 
그래프역사 
1800년대Euler에의해창안 
그래프(Graph) 
S 
B 
K 
J 
I 
D 
D 
A 
C 
B 
b 
a 
d 
c 
g 
e 
f 
위치: 정점(node) 
다리: 간선(edge)
그래프용어 
그래프는(V, E)로표시된다. 
V는정점(Vertices)들의집합 
E는간선(Edge)들의집합 
정점과간선은모두관련되는데이터를가질수있다. 
무방향간선(Undirected Graph) : (A, B) = (B, A) 
방향간선(Directed Graph) : <A, B> ≠<B, A> 
가중치그래프(Weighted Graph), 네트워크(Network) : 간선에비용이나가중치가할당 
S 
B 
K 
J 
I 
D 
9 
9 
7 
6 
5 
5 
5 
1 
가중치표시 
S 
B 
K 
J 
I 
D 
방향표시 
S 
B 
K 
J 
I 
D 
방향/가중치표시 
9 
9 
7 
6 
5 
5 
5 
1 
A 
B 
A 
B 
A 
B 
1200
그래프표현의예 
표현의예 
V(G1) ={0,1,2,3} E(G1) = {(0,1), (0,2), (0,3), (1,2), (2,3)} 
V(G2) ={0,1,2,3} E(G2) = {(0,1), (0,2)} 
V(G3) ={0,1,2} E(G3) = {<0,1>, <1,0>, <1,2>} 
용어 
인접정점(Adjacent vertex) : 간선에의해연결된정점 
차수(Degree) : 정점에연결된다른정점의개수 
경로(Path)는정점의나열로표현 
단순경로: 0,1,2,3 
사이클(Cycle) : 0,1,2,0 
경로의길이: 경로를구성하는사용된간선의수 
0 
3 
1 
2 
0 
3 
1 
2 
0 
1 
2 
G1 
G2 
G3 
완전그래프란모든정점이연결되어있는그래프를말한다.
그래프의표현 
그래프를표현하는2가지방법 
인접행렬(Adjacent Matrix) : 2차원배열사용표현 
인접리스트(Adjacency List) : 연결리스트를사용표현 
인접행렬방법 
If (간선(i, j)가그래프1에존재) M[i][j] = 1, 그렇지않으면M[i][j] = 0 
0 
3 
1 
2 
0 
3 
1 
2 
0 
1 
2 
G1 
G2 
G3 
0 
1 
1 
1 
1 
0 
1 
0 
1 
1 
0 
1 
1 
0 
1 
0 
0 
1 
1 
0 
1 
0 
0 
0 
1 
0 
0 
0 
0 
0 
0 
0 
0 
1 
0 
1 
0 
1 
0 
0 
0
그래프의표현 
인접리스트방법 
각정점에인접한정점들을연결리스트로표현 
0 
3 
1 
2 
G1 
0 
3 
1 
2 
G2 
0 
1 
2 
G3 
n 
n 
1 
2 
3 
4 
1 
2 
3 
4 
1 
2 
3 
1 
2 
3 
n 
0 
2 
n 
0 
1 
3 
n 
0 
2 
n 
1 
2 
n 
0 
2 
n 
1 
n 
0 
n 
0 
n 
가중치가있는경우는어떻게표현?
그래프탐색 
탐색 
가장기본적인연산으로 
하나의정점으로부터시작하여차례대로모든정점을한번씩방문 
많은문제의경우단순히그래프의노드를탐색하는것으로해결된다. 
예) 특정정점에서다른정점으로갈수있는지없는지, 회로의연결등 
그래프탐색의2가지방법 
깊이우선탐색(DFS : Depth-First Search) 
너비우선탐색(BFS : Breadth-First Search) 
1 
2 
3 
4 
5 
7 
6 
BFS 
1 
2 
3 
4 
5 
7 
6 
DFS
깊이우선탐색 
깊이우선탐색 
depth_first_search(v) 
v를방문되었다고표시; 
for all u ∈ (v에인접한정점) do 
if (u가아직방문되지않았으면) 
then depth_first_search(u) 
0 
1 
3 
4 
2 
0 
1 
3 
4 
2 
0 
1 
3 
4 
2 
0 
1 
3 
4 
2 
0 
1 
3 
4 
2 
0 
1 
3 
4 
2 
0 
1 
3 
4 
2 
0 
1 
3 
4 
2 
(a) 
(c) 
(d) 
(b) 
(e) 
(g) 
(h) 
(f)
깊이우선탐색 
깊이우선탐색의구현 
순환호출이용 
명시적인스택이용 
// 인접행렬로표현된그래프에대한깊이우선탐색 
void dfs_mat(GraphType*g, int v) 
{ 
int w; 
visited[v] = TRUE;// 정점v의방문표시 
printf("%d ", v);// 방문한정점출력 
for(w=0; w<g->n; w++) // 인접정점탐색 
if( g->adj_mat[v][w] && !visited[w] ) 
dfs_mat(g, w);//정점w에서DFS 새로시작 
} 
// 인접리스트로표현된그래프에대한깊이우선탐색 
void dfs_list(GraphType*g, int v) 
{ 
GraphNode*w; 
visited[v] = TRUE; // 정점v의방문표시 
printf("%d ", v); // 방문한정점출력 
for(w=g->adj_list[v]; w; w=w->link)// 인접정점탐색 
if(!visited[w->vertex]) 
dfs_list(g, w->vertex); //정점w에서DFS 새로시작 
}
너비우선탐색 
너비우선탐색 
breadth_first_search(v) 
v를방문되었다고표시; 
큐Q에정점v를삽입; 
while (not is_empty(Q)) do 
Q에서정점W를삭제; 
for all u ∈ (v에인접한정점) do 
if ( u가아직방문되지않았으면) 
then u를큐에삽입하고, u를방문되었다고표시; 
0 
1 
3 
4 
2 
0 
1 
3 
4 
2 
0 
1 
3 
4 
2 
0 
1 
3 
4 
2 
0 
1 
3 
4 
2 
0 
1 
3 
4 
2 
0 
1 
3 
4 
2 
(c) 
(d) 
(b) 
(e) 
(g) 
(h) 
(f) 
0 
1 
3 
4 
2 
(a) 
1 
1 
2 
1 
2 
4 
2 
4 
4 
3 
3 
1 
2 
4
너비우선탐색 
너비우선탐색의구현 
인접행렬이용 
void bfs_mat(GraphType*g, int v) 
{ 
int w; 
QueueTypeq; 
init(&q); // 큐초기화 
visited[v] = TRUE; // 정점v 방문표시 
printf("%d ", v); 
enqueue(&q, v);// 시작정점을큐에저장 
while(!is_empty(&q)){ 
v = dequeue(&q);// 큐에정점추출 
for(w=0; w<g->n; w++)// 인접정점탐색 
if(g->adj_mat[v][w] && !visited[w]){ 
visited[w] = TRUE; // 방문표시 
printf("%d ", w); 
enqueue(&q, w); // 방문한정점을큐에저장 
} 
} 
}
연결성분찾기 
연결성분 
최대로연결된부분그래프로연결된부분그래프중크기가최대인것 
그래프탐색으로구할수있다. 
visited[i] = count; 
0 
3 
1 
4 
2 
visited 
1 
1 
2 
2 
1 
0 
1 
2 
3 
4 
void find_connected_component(GraphType*g) 
{ 
int i; 
count = 0; 
for(i=0; i<g->n; i++) 
if(!visited[i]){ // 방문되지않았으면 
count++; 
dfs_mat(g, i); 
} 
}
신장트리(Spanning Tree) 
신장트리(Spanning Tree) 
그래프내의모든정점을포함하는트리 
신장트리는모든정점들이연결되어있어야하고, 사이클을포함해서는안됨 
용도: 최소의링크를사용하여네트워크구축시, … 
0 
1 
3 
4 
2 
연결그래프 
0 
1 
3 
4 
2 
0 
1 
3 
4 
2 
0 
1 
3 
4 
2 
신장트리중일부 
depth_first_search(v) 
v를방문되었다고표시; 
for all u ∈ (v에인접한정점) do 
if (u가아직방문되지않았으면) 
then (v,u)를신장트리간선이라고표시; 
depth_first_search(u)
최소비용신장트리(MTS) 
최소비용신장트리(MST) 
Minimum spanning tree 
네트워크에있는모든정점들을가장적은간선과비용으로연결하는신장트리 
MST 응용 
도로건설–도시를모두연결하면서도로의길이가최소가되도록하는문제 
전기회로–단자를모두연결하면서전선의길이가가장최소가되도록… 
통신–전화선의길이가최소가되도록전화망을구성하는문제 
배관–파이프를모두연결하면서파이프의총길이가최소가되도록연결 
10 
12 
18 
14 
22 
4 
5 
3 
6 
17 
15
MST 알고리즘 
2가지대표적인알고리즘 
Kruskal의알고리즘 
Prim의알고리즘 
탐욕적인방법(Greedy Method) 
알고리즘설계에서있어서중요한기법중의하나 
결정을해야할때마다그순간에가장좋다고생각되는것을해답으로선택함으로써최종적인해답에도달 
탐욕적인방법은항상최적의해답을주는지를반드시검증해야한다. 
Kruskal의알고리즘은최적의해답을주는것으로증명
Prim의MST 알고리즘 
Prim의MST 알고리즘 
시작정점에서부터출발하여신장트리집합을단계적으로확장해나가는방법 
시작단계에서는시작정점만이신장트리집합에포함 
앞단계에서만들어진신장트리집합에, 인접한정점들중에서최저간선으로연결된정점을선택하여트리를확장 
이과정은트리가n-1개의간선을가질때까지계속된다 
간선(a,b)와간선(f,e)의가중치를비교해보면, (f,e)가27로서(a,b)의29보다낮다 
따라서(f,e)간선이선택되고, 정점e가신장트리집합에포함된다. 
f 
a 
b 
g 
e 
c 
d 
10 
29 
15 
25 
27 
22 
12 
16 
8
Prim의MST 알고리즘 
f 
a 
b 
g 
e 
c 
d 
10 
29 
15 
25 
27 
22 
12 
16 
8 
f 
a 
b 
g 
e 
c 
d 
10 
29 
15 
25 
27 
22 
12 
16 
8 
f 
a 
b 
g 
e 
c 
d 
10 
29 
15 
25 
27 
22 
12 
16 
8 
f 
a 
b 
g 
e 
c 
d 
10 
29 
15 
25 
27 
22 
12 
16 
8 
f 
a 
b 
g 
e 
c 
d 
10 
29 
15 
25 
27 
22 
12 
16 
8 
f 
a 
b 
g 
e 
c 
d 
10 
29 
15 
25 
27 
22 
12 
16 
8 
f 
a 
b 
g 
e 
c 
d 
10 
29 
15 
25 
27 
22 
12 
16 
8
Kruskal의MST 알고리즘 
Kruskal의MST 알고리즘 
최소비용신장트리가최소비용의간선으로구성됨과동시에사이클을포함하지않는다는조건에근거 
각단계에서사이클을이루지않는최소비용간선을선택 
그래프의간선들을가중치의오름차순으로정렬한다 
정렬된간선들의리스트에서사이클을형성하지않는간선을찾아서현재의최소비용신장트리의집합에추가 
사이클을형성하면그간선은제외
Kruskal의MST 알고리즘 
f 
a 
b 
g 
e 
c 
d 
10 
29 
15 
25 
27 
22 
12 
16 
8 
f 
a 
b 
g 
e 
c 
d 
10 
29 
15 
25 
27 
22 
12 
16 
8 
f 
a 
b 
g 
e 
c 
d 
10 
29 
15 
25 
27 
22 
12 
16 
8 
f 
a 
b 
g 
e 
c 
d 
10 
29 
15 
25 
27 
22 
12 
16 
8 
f 
a 
b 
g 
e 
c 
d 
10 
29 
15 
25 
27 
22 
12 
16 
8 
f 
a 
b 
g 
e 
c 
d 
10 
29 
15 
25 
27 
22 
12 
16 
8 
f 
a 
b 
g 
e 
c 
d 
10 
29 
15 
25 
27 
22 
12 
16 
8 
f 
a 
b 
g 
e 
c 
d 
10 
29 
15 
25 
27 
22 
12 
16 
8 
10 
12 
15 
16 
18 
22 
25 
27 
29 
afcd bgbcdg de egefab 
10 
12 
15 
16 
18 
22 
25 
27 
29 
afcd bgbcdg de egefab 
10 
12 
15 
16 
18 
22 
25 
27 
29 
afcd bgbcdg de egefab 
10 
12 
15 
16 
18 
22 
25 
27 
29 
afcd bgbcdg de egefab 
10 
12 
15 
16 
18 
22 
25 
27 
29 
afcd bgbcdg de egefab 
10 
12 
15 
16 
18 
22 
25 
27 
29 
afcd bgbcdg de egefab 
10 
12 
15 
16 
18 
22 
25 
27 
29 
afcd bgbcdg de egefab 
10 
12 
15 
16 
18 
22 
25 
27 
29 
afcd bgbcdg de egefab 
x 
x
kruskal의MST 알고리즘의구현 
union-find 알고리즘 
집합들의합집합을구하고집합의원소가어떤집합에속하는지를계산하는알고리즘 
여러가지방법으로구현이가능하다 
Kruskal의MST 알고리즘에서사이클검사에사용된다. 
a와b가같은집합에속함 
a와b가다른집합에속함 
사이클형성 
사이클이형성되지않음
최단경로알고리즘 
최단경로(Shortest Path) 
네트워크에서정점i와정점j를연결하는경로중에서간선들의가중치합이최소가되는경로를찾는문제 
간선의가중치는비용, 거리, 시간등을나타낸다. 
4 
0 
5 
1 
6 
2 
3 
3 
10 
7 
2 
5 
4 
11 
10 
6 
9 
2 
4 
0 
7 
∞ 
∞ 
3 
10 
∞ 
7 
0 
4 
10 
2 
6 
∞ 
∞ 
4 
0 
2 
∞ 
∞ 
∞ 
∞ 
10 
2 
0 
11 
9 
4 
3 
2 
∞ 
11 
0 
∞ 
5 
10 
6 
∞ 
9 
∞ 
0 
∞ 
∞ 
∞ 
∞ 
4 
5 
∞ 
0 
0 1 2 3 4 5 6 
0 
1 
2 
3 
4 
5 
6
Dijkstra의최단경로알고리즘 
Dijkstra의최단경로알고리즘 
네트워크에서하나의시작정점으로부터모든다른정점까지의최단경로를찾는알고리즘 
집합S: 시작정점v로부터의최단경로가이미발견된정점들의집합 
distance 배열: 최단경로를알려진정점만을통하여각정점까지가는최단경로의길이 
매단계에서가장distance 값이적은정점을S에추가한다 
v 
v 
w 
u 
s 
s 
시작노드 
distance=7 
distance=3 
distance=5 
①< (②+③) 
① 
② 
③ 
최단경로 
다른경로 
distance 값이 
최소인노드 
v 
w 
u 
s 
새롭게 
추가된노드 
distance[u] 
distance[w] 
weight[u][w] 
distance 값갱신 
distance[w]=min(distance[w],distance[u] 
+weight[u][w])
Dijkstra의최단경로알고리즘 
// 입력: 가중치그래프G, 가중치는음수가아님. 
// 출력: distance 배열, distance[u]는v에서u까지의최단거리이다. 
shortest_path(G, v) 
S←{v} 
for 각정점w∈Gdo 
distance[w]←weight[v][w]; 
while 모든정점이S에포함되지않으면do 
u←집합S에속하지않는정점중에서최소distance 정점; 
S←S∪{u} 
for u에인접하고S에있는각정점z do 
if distance[u]+weight[u][z] < distance[z] 
then distance[z]←distance[u]+weight[u][z];
Dijkstra의최단경로알고리즘 
4 
0 
5 
1 
6 
2 
3 
3 
10 
7 
2 
5 
4 
11 
10 
6 
9 
2 
4 
4 
0 
5 
1 
6 
2 
3 
3 
10 
7 
2 
5 
4 
11 
10 
6 
9 
2 
4 
4 
0 
5 
1 
6 
2 
3 
3 
10 
7 
2 
5 
4 
11 
10 
6 
9 
2 
4 
4 
0 
5 
1 
6 
2 
3 
3 
10 
7 
2 
5 
4 
11 
10 
6 
9 
2 
4 
4 
0 
5 
1 
6 
2 
3 
3 
10 
7 
2 
5 
4 
11 
10 
6 
9 
2 
4 
4 
0 
5 
1 
6 
2 
3 
3 
10 
7 
2 
5 
4 
11 
10 
6 
9 
2 
4 
4 
0 
5 
1 
6 
2 
3 
3 
10 
7 
2 
5 
4 
11 
10 
6 
9 
2 
4 
S={0} 
distance[] = 
0 
7 
∞ 
∞ 
3 
10 
∞ 
0 1 2 3 4 5 6 
0 
10 
7 
3 
∞ 
∞ 
∞ 
0 
10 
5 
3 
ퟖ 
ퟏퟒ 
∞ 
S={0,4} 
distance[] = 
0 
5 
∞ 
ퟏퟒ 
3 
10 
ퟖ 
0 1 2 3 4 5 6 
0 
10 
5 
3 
ퟖ 
ퟏퟒ 
ퟗ 
0 
10 
5 
3 
ퟖ 
ퟏퟐ 
ퟗ 
S={0,4,1} 
distance[] = 
0 
5 
ퟗ 
ퟏퟒ 
3 
10 
ퟖ 
0 1 2 3 4 5 6 
S={0,4,1,6} 
distance[] = 
0 
5 
ퟗ 
ퟏퟒ 
3 
10 
ퟖ 
0 1 2 3 4 5 6 
0 
10 
5 
3 
ퟖ 
ퟏퟏ 
ퟗ 
0 
10 
5 
3 
ퟖ 
ퟏퟏ 
ퟗ 
0 
10 
5 
3 
ퟖ 
ퟏퟏ 
ퟗ 
S={0,4,1,6,2} 
distance[] = 
0 
5 
ퟗ 
ퟏퟏ 
3 
10 
ퟖ 
0 1 2 3 4 5 6 
S={0,4,1,6,2,5} 
distance[] = 
0 
5 
ퟗ 
ퟏퟏ 
3 
10 
ퟖ 
0 1 2 3 4 5 6 
S={0,4,1,6,2,5,3} 
distance[] = 
0 
5 
ퟗ 
ퟏퟏ 
3 
10 
ퟖ 
0 1 2 3 4 5 6
위상정렬 
방향그래프에서간선<u, v>가있다면정점u는정점v를선행한다고말한다 
방향그래프에존재하는각정점들의선행순서를위배하지않으면서모든정점을나열하는것을방향그래프의위상정렬(topological sort)이라고한다. 
예제: 
위상정렬: (0,1,2,3,4,5) , (1,0,2,3,4,5) 
(2,0,1,2,4,5)는위상정렬이아니다. 2번정점이0번정점앞에오기때문이다. 간선<0,2>가존재하기때문에0번정점이끝나야만이2번정점을시작할수있다. 
위상정렬(Topological Sort) 
U 
진입간선 
진출간선 
0 
1 
2 
5 
3 
4
위상정렬(Topological Sort) 
Input: 그래프G=(V,E) 
Output: 위상정렬순서 
topo_sort(G) 
for i←0 to do 
if( 모든정점이선행정점을가지면) 
then 사이클이존재하고위상정렬불가; 
선행정점을가지지않는정점v 선택; 
v를출력; 
v와v에서나온모든간선들을그래프에서삭제; 
0 
1 
2 
5 
3 
4 
0 
2 
5 
3 
4 
0 
2 
5 
3 
2 
5 
3 
5 
3 
5 
초기상태 
1 제거 
4 제거 
0 제거 
2 제거 
3 제거
해싱(Hashing)
해싱(Hashing) 
해싱 
키값에직접산술적인연산을적용하여항목이저장되어있는테이블의주소를계산하여항목에접근 
값의연산에의해직접접근이가능한구조를해시테이블(hash table)이라부르며, 해시테이블을이용한탐색을해싱(hashing) 
사전구조(dictionary): 맵(map)이나테이블(table)로불리우기도한다. 
사전구조는다음과같이탐색키와탐색키와관련된값의2가지종류의필드를가짐 
영어단어나사람의이름같은탐색키(search key) 
단어의정의나주소또는전화번호같은탐색키와관련된값(value) 
객체: 일련의(key, value) 쌍의집합 
연산: 
-add(key, value) ::= (key, value)를사전에추가한다. 
-delete(key) ::= key에해당되는(key, value)를찾아서삭제한다. 관련된value를반환한다. 
만약탐색이실패하면NULL를반환한다. 
-search(key) ::= key에해당되는value를찾아서반환한다.만약탐색이실패하면NULL를반환한다.
해싱의구조 
해시함수 
탐색키를입력으로받아해시주소(hash address)를생성 
해시주소가배열로구현된해시테이블(hash table)의인덱스가된다. 
예제 
학생들에대한정보를해싱으로저장, 탐색 
학번=탐색키, 
학번은5자리이고앞의2개의숫자가학과, 뒤의3자리숫자는학과의학생번호 
같은학과학생들만저장된다고가정하면뒤의3자리만사용할수있다. 
해시함수 
h(x) 
해시테이블 
ht[] 
0 
1 
2 
… 
… 
M-1 
해시주소 
안철수 
0 
1 
2 
… 
23 
… 
M-1 
해시테이블 
h(01023)
해싱의구조 
해시테이블 
해시테이블ht는M개의버켓(bucket)으로이루어지는테이블로서ht[0], ht[1], ...,ht[M-1]의원소를가진다 
하나의버켓은s개의슬롯(slot)을가질수있다 
충돌(Collision) 
서로다른두개의탐색키k1과k2에대하여h(k1) = h(k2)인경우 
오버플로우(Overflow) 
충돌이버켓에할당된슬롯수보다많이발생하게되면버켓에더이상항목을저장할수없게됨 
S개의슬롯 
M개의버킷 
버킷0 
버킷1 
버킷M-1 
슬롯0 
슬롯S-1
실제해싱 
해시테이블 
크기가제한되어있어, 탐색키마다하나의공간을할당할수가없다 
충돌과오버플로우발생 
해싱함수가필요: h(k)= k mod M 
좋은해시함수조건 
충돌이적어야한다. 
해시함수값이해시테이블의주소영역내에서고르게분포되어야한다. 
계산이빨라야한다. 
안철수 
0 
1 
2 
… 
23 
… 
M-1 
해시테이블 
h(01023) 
h(01055)
제산함수 
h(k)=k mod M 
해시테이블의크기M는소수(prime number) 
폴딩함수 
hash_index=(short)(key ^ (key>>16)) 
이동폴딩(shift folding) 
경계폴딩(boundary folding) 
중간제곱함수 
중간제곱함수는탐색키를제곱한다음, 중간의몇bit를선택해시주소를생성 
비트추출함수 
탐색키를이진수로간주하여임의의위치의k개의비트를해시주소로사용 
숫자분석방법 
키의각각의위치에있는숫자중에서편중되지않는수들을해시테이블의크기에적합한만큼조합하여해시주소로사용 
해시함수 
123 203 241 112 20 
123 
203 
241 
112 
20 
699 
123 
302 
241 
211 
20 
897 
+ 
+ 
+ 
+ 
+ 
+ 
+ 
+ 
= 
=
충돌해결 
충돌해결전략 
완전해시(Perfect hashing) 
한개의데이터영역에한개의키만이대입->(일반적) 구현불가능 
버킷크기크게-> 메모리용량낭비 
Open addressing(개방주소법) 
오버플로우가일어났을때다른데이터주소로다시해시시키는방법(반복수행) 
Closed addressing(폐쇄주소법, 체인법) 
같은데이터주소내에서끝까지해결을보는방법 
충돌해결방법 
선형조사법: 충돌이일어난항목을해시테이블의다른위치에저장한다 
체이닝: 해시테이블의하나의위치가여러개의항목을저장할수있도록해시테이블의구조를변경
선형조사법 
선형조사법 
충돌이ht[k]에서충돌이발생했다면ht[k+1]이비어있는지를조사 
만약비어있지않다면ht[k+2]를살펴본다. 
이런식으로비어있는공간이나올때까지계속하는조사하는방법이다. 
만약테이블의끝에도달하게되면다시테이블의처음으로간다. 
조사를시작했던곳으로다시되돌아오게되면테이블이가득찬것이된다. 
조사되는위치 
h(k), h(k)+1, h(k)+2,… 
예: h(k)=k mod 7 
1단계(8) : ∙h(8) = 8 mod 7 = 1(저장) 
2단계(1) : ∙h(1) = 1 mod 7 = 1(충돌발생) 
∙(h(1)+1) mod 7 = 2(저장) 
3단계(9) : ∙h(9) = 9 mod 7 = 2(충돌발생) 
∙(h(9)+1) mod 7 = 3(저장) 
4단계(6) : ∙h(6) = 6 mod 7 = 6(저장) 
5단계(13) :∙h(13) = 13 mod 7 = 6(충돌발생) 
1단계 
2단계 
3단계 
4단계 
5단계 
[0] 
13 
[1] 
8 
8 
8 
8 
8 
[2] 
1 
1 
1 
1 
[3] 
9 
9 
9 
[4] 
[5] 
[6] 
6 
6 
환치발생(단점) 
해시테이블구조가간단(장점)
이차조사법(Quadratic probing) 
이차조사법 
선형조사법에서의문제점인집중과결합을크게완화 
선형조사법과유사하지만, 다음조사할위치를다음식에의하여결정 
(h(k)+i*i) mod M 
조사되는위치 
h(k), h(k)+1, h(k)+4,… 
테이블의크기가소수여야함(테이블의반정도의영역만이탐색가능) 
아닐경우탐색영역현저히감소
이중해싱법(Double Hashing) 
이중해싱법 
오버플로우가발생함에따라항목을저장할다음위치를결정할때, 원래해시함수와다른별개의해시함수를이용하는방법이다 
step=C-(k mod C) 
h(k), h(k)+step, h(k)+2*step, … 
(예) 크기가7인해시테이블에서첫번째해시함수가k mod M이고. 
오버플로우발생시의해시함수step=5-(5 mod 5) 
입력파일(8, 1, 9, 6, 13 ) 
1단계(8) : ∙h(8) = 8 mod 7 = 1(저장) 
2단계(1) : ∙h(1) = 1 mod 7 = 1(충돌발생) 
∙(h(1)+h‘(1)) mod 7 = (1+5-(1 mod 5)) mod 7 = 5(저장) 
3단계(9) : ∙h(9) = 9 mod 7 = 2(저장) 
4단계(6) : ∙h(6) = 6 mod 7 = 6(저장) 
5단계(13) :∙h(13) = 13 mod 7 = 6(충돌발생) 
∙(h(13)+h‘(13)) mod 7 = (6+5-(13 mod 5)) mod 7= 1(충돌발생) 
∙(h(13)+2*h‘(13)) mod 7 = (6+2*2) mod 7= 3(저장) 
1단계 
2단계 
3단계 
4단계 
5단계 
[0] 
[1] 
8 
8 
8 
8 
8 
[2] 
9 
9 
9 
[3] 
13 
[4] 
[5] 
1 
1 
1 
1 
[6] 
6 
6
체이닝(chaining) 
체이닝 
오버플로우문제를연결리스트로해결 
각버켓에고정된슬롯을할당하는것이아니라각버켓에, 삽입과삭제가용이한연결리스트를할당한다 
버켓내에서는원하는항목을찾을때는연결리스트를순차탐색한다 
(예) 크기가7인해시테이블에h(k)=k mod 7의해시함수를이용하여, 
8, 1, 9, 6, 13 을삽입할때에의체이닝에의한충돌처리 
1단계(8) : ∙h(8) = 8 mod 7 = 1(저장) 
2단계(1) : ∙h(1) = 1 mod 7 = 1(충돌발생->새로운노드생성저장) 
3단계(9) : ∙h(9) = 9 mod 7 = 2(저장) 
4단계(6) : ∙h(6) = 6 mod 7 = 6(저장) 
5단계(13) :∙h(13) = 13 mod 7 = 6(충돌발생->새로운노드생성저장) 
0 
1 
2 
3 
4 
5 
6 
8 
1 
n 
9 
6 
2 
n 
(1) 
(2) 
(3) 
(4) 
(5)
해싱의성능분석 
적재밀도(loading density) 
적재비율(loading factor) 
저장되는항목의개수n과해시테이블의크기M의비율이다. 
선형조사법 
실패한검색: 
성공한검색: 
체이닝 
실패한검색: α 
성공한검색: 
α= 저장된항목의개수해싱테이블의버킷의개수= 풏 푴 ퟏ ퟐ ퟏ+ ퟏ ퟏ+αퟐ ퟏ ퟐ ퟏ+ ퟏ ퟏ+α 
1 + α ퟐ
해싱과다른탐색방법의비교 
탐색 
삽입 
삭제 
순차탐색 
O(n) 
O(1) 
O(n) 
이진탐색 
O(Log2n) 
O(Log2n+n) 
O(Log2n+n) 
이진탐색트리 
균형트리 
O(Log2n) 
O(Log2n) 
O(Log2n) 
경사트리 
O(n) 
O(n) 
O(n) 
해싱 
최선의경우 
O(1) 
O(1) 
O(1) 
최악의경우 
O(n) 
O(n) 
O(n)
탐색(Search)
탐색(Search) 
탐색이란… 
여러개의자료중에서원하는자료를찾는작업 
컴퓨터가가장많이하는작업중의하나 
탐색을효율적으로수행하는것은매우중요 
탐색키(Search key): 항목과항목을구별해주는키(key) 
탐색을위해사용되는자료구조 
배열, 연결리스트, 트리, 그래프등 
탐색에의한문제해결 
문제의해에도달하기위한탐색과정을직접수행함으로써보다포괄적이며, 자동화된해결방안 
문제해결과정중에지적판단이요구되는경우탐색기법이유용 
문제해결의최적의방법보다적당한방법을찾는것이쉽다
순차탐색(Sequential Search) 
순차탐색 
가장간단하고직접적인탐색방법 
정렬되지않은배열의항목들을처음부터마지막까지하나씩검사하여원하는항목을찾아가는방법 
int seq_search(int key, int low, int high) 
{ 
int i; 
for(i=low; i<=high; i++) 
if(list[i]==key) 
return i; // 탐색성공 
return -1; // 탐색실패 
} 
9 
5 
8 
3 
7 
8을찾는경우 
9 
5 
8 
3 
7 
9 
5 
8 
3 
7 
9 
5 
8 
3 
7 
2를찾는경우 
9 
5 
8 
3 
7 
9 
5 
8 
3 
7 
9 
5 
8 
3 
7 
9 
5 
8 
3 
7 
탐색성공 
탐색실패 
9≠8이므로탐색계속 
5≠8이므로탐색계속 
8=8이므로탐색성공 
9≠2이므로탐색계속 
5≠2이므로탐색계속 
8≠2이므로탐색계속 
3≠2이므로탐색계속 
7≠2이므로탐색계속 
더이상항목이없으므로실패
개선된순차탐색 
How to ? 
비교횟수를줄이기위해리스트의끝에찾고자하는키값을저장하고반복문의탈출조건을키값을찾을때까지로설정 
int seq_search2(int key, int low, int high) 
{ 
int i; 
list[high+1] = key; 
// 키값을찾으면종료 
for(i=low; list[i] != key; i++) 
; 
if(i==(high+1)) return -1; // 탐색실패 
else return i; // 탐색성공 
} 
9 
5 
8 
3 
7 
8 
8을찾는경우 
9 
5 
8 
3 
7 
8 
9 
5 
8 
3 
7 
8 
9 
5 
8 
3 
7 
2 
2를찾는경우 
9 
5 
8 
3 
7 
2 
9 
5 
8 
3 
7 
2 
9 
5 
8 
3 
7 
2 
9 
5 
8 
3 
7 
2 
탐색성공 
탐색실패 
9≠8이므로탐색계속 
5≠8이므로탐색계속 
8=8이므로탐색성공 
9≠2이므로탐색계속 
5≠2이므로탐색계속 
8≠2이므로탐색계속 
3≠2이므로탐색계속 
2=2이지만마지막항목 
따라서탐색실패
이진탐색(Binary Search) 
이진탐색 
정렬된배열의탐색에적합 
배열의중앙에있는값을조사하여찾고자하는항목이왼쪽또는오른쪽부분배열에있는지를알아내어탐색의범위를반으로줄임 
(예) 10억명중에서이진탐색을이용하여특정한이름을탐색 
이진탐색은단지30번의비교 
순차탐색에서는평균5억번의비교 
1 
3 
5 
6 
7 
9 
11 
20 
30 
1 
3 
5 
6 
7 
9 
11 
20 
30 
1 
3 
5 
6 
7 
9 
11 
20 
30 
1 
3 
5 
6 
7 
9 
11 
20 
30 
1 
3 
5 
6 
7 
9 
11 
20 
30 
5을찾는경우 
7과비교 
5<7이므로앞부분만을다시탐색 
5와3을비교 
5>3이므로뒷부분만을다시탐색 
5=5이므로탐색성공
이진탐색알고리즘 
search_binary(list, low, high) 
middle ← low에서high사이의중간위치 
if( 탐색값≠ list[middle] ) 
return TRUE; 
else if (탐색값< list[middle] ) 
return list[0]부터list[middle-1]에서의탐색; 
else if (탐색값> list[middle] ) 
return list[middle+1]부터list[high]에서의탐색; 
2 
6 
11 
13 
18 
20 
22 
27 
29 
30 
34 
38 
41 
42 
45 
47 
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 
2 
6 
11 
13 
18 
20 
22 
27 
29 
30 
34 
38 
41 
42 
45 
47 
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 
2 
6 
11 
13 
18 
20 
22 
27 
29 
30 
34 
38 
41 
42 
45 
47 
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 
2 
6 
11 
13 
18 
20 
22 
27 
29 
30 
34 
38 
41 
42 
45 
47 
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 
Low 
High 
middle 
middle 
Low 
High 
middle 
Low 
High 
middle 
Low 
High
색인순차탐색(indexed sequential search) 
색인순차탐색 
인덱스(index)라불리는테이블을사용하여탐색의효율을높이는방법 
인덱스테이블은주자료리스트에서일정간격으로발췌한자료를가지고있다 
주자료리스트와인덱스테이블은모두정렬되어있어야한다 
3 
0 
15 
3 
67 
6 
3 
9 
15 
22 
31 
55 
67 
88 
91 
인덱스테이블 
주자료테이블 
0 
1 
2 
3 
4 
5 
6 
7 
8 
0 
1 
2
균형이진탐색트리 
이진탐색(binary search)과이진탐색트리(binary search tree)와의차이점 
이진탐색과이진탐색트리는근본적으로같은원리에의한탐색구조 
이진탐색은자료들이배열에저장되어있으므로삽입과삭제가상당히힘들다. 즉자료를삽입하고삭제할때마다앞뒤의원소들을이동시켜야한다. 
이진탐색트리는비교적빠른시간안에삽입과삭제를끝마칠수있는구조 
삽입, 삭제가빈번히이루어진다면반드시이진탐색트리를사용하여야한다. 
이진탐색트리에서의시간복잡도 
균형트리: O(logn) 
불균형트리: O(n), 순차탐색과동일 
1 
2 
3 
4 
5 
6 
7 
1 
2 
3 
4 
5 
6 
7
AVL 트리 
AVL 트리 
Adelson-Velskii와Landis에의해1962년에제안 
각노드에서왼쪽서브트리의높이와오른쪽서브트리의높이차이가1 이하인이진탐색트리 
트리가비균형상태로되면스스로노드들을재배치하여균형상태로만든다 
AVL 트리는균형트리가항상보장되기때문에탐색이O(logn)시간안에끝나게된다 
균형인수(balance factor)=(왼쪽서브트리의높이-오른쪽서브트리의높이) 
모든노드의균형인수가±1 이하이면AVL 트리이다 
7 
5 
9 
3 
1 
균형인수+2, -2 
균형인수+1, -1 
균형인수0 
AVL 트리 
Non-AVL 트리 
1 
0 
0 
7 
5 
9 
3 
1 
1 
0 
0 
2 
2
AVL 트리의연산 
AVL 트리의연산 
탐색연산: 이진탐색트리와동일 
균형을이룬이진탐색트리에서균형상태가깨지는경우 
삽입연산과삭제연산 
삽입연산시에는삽입되는위치에서루트까지의경로에있는조상노드들의균형인수에영향을줄수있다. 
따라서즉새로운노드의삽입후에불균형상태로변한가장가까운조상노드, 즉균형인수가±2가된가장가까운조상노드의서브트리들에대하여다시균형을잡아야한다. 
균형인수+2, -2 
균형인수+1, -1 
균형인수0 
7 
5 
9 
3 
1 
1 
0 
0 
7 
5 
9 
3 
1 
1 
0 
0 
2 
2
AVL 트리의삽입연산 
균형트리로만드는방법 
회전(rotation) 
AVL 트리에서균형이깨지는경우에는다음의4가지의경우 
새로삽입된노드N로부터가장가까우면서균형인수가±2가된조상노드를A라고가정 
LL 타입: N이A의왼쪽서브트리의왼쪽서브트리에삽입된다. 
LR 타입: N이A의왼쪽서브트리의오른쪽서브트리에삽입된다. 
RR 타입: N이A의오른쪽서브트리의오른쪽서브트리에삽입된다. 
RL 타입: N이A의오른쪽서브트리의왼쪽서브트리에삽입된다. 
7 
3 
9 
1 
1 
0 
0 
5 
0 
0
회전방법 
회전종류 
LL 회전: A부터N까지의경로상의노드들을오른쪽으로회전시킨다. 
LR 회전: A부터N까지의경로상의노드들을왼쪽-오른쪽으로회전시킨다. 
RR 회전: A부터N까지의경로상의노드들을왼쪽으로회전시킨다. 
RL 회전: A부터N까지의경로상의노드들을오른쪽-왼쪽으로회전시킨다. 
A 
B 
T1 
T2 
T3 
A 
B 
T1 
T2 
T3 
B 
A 
T2 
T3 
T1 
일반적인LL 회전 
A 
T4 
B 
C 
T2 
T3 
T1 
A 
T4 
B 
C 
T2 
T3 
T1 
삽입 
LL 회전 
A 
T4 
C 
B 
T2 
T1 
T3 
RR 회전 
C 
B 
A 
T2 
T1 
T3 
T4
AVL Tree 삽입예제(1/2) 
8 
7 
9 
2 
1 
1 
0 
0 
7 
0 
(1) 7삽입 
7 
8 
-1 
0 
(2) 8삽입 
7 
8 
-2 
1 
9 
0 
RR 회전 
8 
7 
9 
0 
0 
0 
(3) 9삽입 
(4) 2삽입 
8 
7 
9 
2 
2 
2 
1 
0 
(5) 1삽입 
1 
0 
LL 회전 
8 
2 
9 
1 
7 
1 
0 
0 
0 
0
AVL Tree 삽입예제(2/2) 
(6) 5삽입 
8 
2 
9 
1 
7 
2 
-1 
0 
0 
1 
5 
0 
LR 회전 
7 
2 
8 
1 
5 
9 
0 
0 
0 
0 
0 
-1 
(7) 3삽입 
7 
2 
8 
1 
5 
9 
1 
-1 
0 
0 
-1 
3 
1 
(8) 6삽입 
7 
2 
8 
1 
5 
9 
1 
-1 
0 
0 
-1 
3 
0 
6 
0 
0 
(9) 4삽입 
7 
2 
8 
1 
5 
9 
2 
-2 
0 
0 
-1 
3 
1 
6 
0 
-1 
4 
0 
RL 회전 
7 
3 
8 
2 
5 
9 
1 
0 
1 
0 
-1 
4 
0 
6 
0 
0 
1 
0
동적프로그래밍(Dynamic Programming)
알고리즘과 자료구조
알고리즘과 자료구조
알고리즘과 자료구조
알고리즘과 자료구조
알고리즘과 자료구조
알고리즘과 자료구조
알고리즘과 자료구조
알고리즘과 자료구조
알고리즘과 자료구조
알고리즘과 자료구조
알고리즘과 자료구조
알고리즘과 자료구조
알고리즘과 자료구조
알고리즘과 자료구조

Weitere ähnliche Inhalte

Was ist angesagt?

Alphorm.com Formation Laravel : Le Guide Complet du Débutant
Alphorm.com Formation Laravel : Le Guide Complet du DébutantAlphorm.com Formation Laravel : Le Guide Complet du Débutant
Alphorm.com Formation Laravel : Le Guide Complet du Débutant
Alphorm
 
Ch5 base de données
Ch5   base de donnéesCh5   base de données
Ch5 base de données
Wael Ismail
 
Alphorm.com Formation Laravel : Construire une Application de A à Z
Alphorm.com Formation Laravel : Construire une Application de A à ZAlphorm.com Formation Laravel : Construire une Application de A à Z
Alphorm.com Formation Laravel : Construire une Application de A à Z
Alphorm
 

Was ist angesagt? (14)

React js
React jsReact js
React js
 
Exercices sur-python-turtle-corrige
Exercices sur-python-turtle-corrigeExercices sur-python-turtle-corrige
Exercices sur-python-turtle-corrige
 
Python avancé : Lecture et écriture de fichiers
Python avancé : Lecture et écriture de fichiersPython avancé : Lecture et écriture de fichiers
Python avancé : Lecture et écriture de fichiers
 
ArrayList Java
ArrayList JavaArrayList Java
ArrayList Java
 
Java Practical File Diploma
Java Practical File DiplomaJava Practical File Diploma
Java Practical File Diploma
 
Java generics
Java genericsJava generics
Java generics
 
Introduction to headless browsers
Introduction to headless browsersIntroduction to headless browsers
Introduction to headless browsers
 
[C++ Korea] Effective Modern C++ Study item 24-26
[C++ Korea] Effective Modern C++ Study item 24-26[C++ Korea] Effective Modern C++ Study item 24-26
[C++ Korea] Effective Modern C++ Study item 24-26
 
Alphorm.com Formation Laravel : Le Guide Complet du Débutant
Alphorm.com Formation Laravel : Le Guide Complet du DébutantAlphorm.com Formation Laravel : Le Guide Complet du Débutant
Alphorm.com Formation Laravel : Le Guide Complet du Débutant
 
Ch5 base de données
Ch5   base de donnéesCh5   base de données
Ch5 base de données
 
Alphorm.com Formation Laravel : Construire une Application de A à Z
Alphorm.com Formation Laravel : Construire une Application de A à ZAlphorm.com Formation Laravel : Construire une Application de A à Z
Alphorm.com Formation Laravel : Construire une Application de A à Z
 
MDD: Models, frameworks, & code generation
MDD: Models, frameworks, & code generationMDD: Models, frameworks, & code generation
MDD: Models, frameworks, & code generation
 
Angular.pdf
Angular.pdfAngular.pdf
Angular.pdf
 
Java Swing JFC
Java Swing JFCJava Swing JFC
Java Swing JFC
 

Andere mochten auch

Mi power point flores
Mi power point floresMi power point flores
Mi power point flores
AilinTomas
 
1.자료구조와 알고리즘(강의자료)
1.자료구조와 알고리즘(강의자료)1.자료구조와 알고리즘(강의자료)
1.자료구조와 알고리즘(강의자료)
fmbvbfhs
 
[1116 박민근] c++11에 추가된 새로운 기능들
[1116 박민근] c++11에 추가된 새로운 기능들[1116 박민근] c++11에 추가된 새로운 기능들
[1116 박민근] c++11에 추가된 새로운 기능들
MinGeun Park
 
Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심
흥배 최
 
통신시스템(Cdma network)
통신시스템(Cdma network)통신시스템(Cdma network)
통신시스템(Cdma network)
영기 김
 
칸반(Kanban)
칸반(Kanban)칸반(Kanban)
칸반(Kanban)
영기 김
 
스크럼(Scrum)
스크럼(Scrum)스크럼(Scrum)
스크럼(Scrum)
영기 김
 
애자일 S/W 개발
애자일 S/W 개발애자일 S/W 개발
애자일 S/W 개발
영기 김
 
Si 프로젝트에서 바라보는...traditional vs agile
Si 프로젝트에서 바라보는...traditional vs agileSi 프로젝트에서 바라보는...traditional vs agile
Si 프로젝트에서 바라보는...traditional vs agile
Kiwon Kyung
 

Andere mochten auch (20)

Mi power point flores
Mi power point floresMi power point flores
Mi power point flores
 
Somul 2017-이민석
Somul 2017-이민석Somul 2017-이민석
Somul 2017-이민석
 
왜 우리는 개발자에 집중하지 않는가?
왜 우리는 개발자에 집중하지 않는가?왜 우리는 개발자에 집중하지 않는가?
왜 우리는 개발자에 집중하지 않는가?
 
프로그래밍 언어의 기본 개념과 주요 프로그래밍 언어
프로그래밍 언어의 기본 개념과 주요 프로그래밍 언어프로그래밍 언어의 기본 개념과 주요 프로그래밍 언어
프로그래밍 언어의 기본 개념과 주요 프로그래밍 언어
 
10분만에 이해하는 프로그래밍
10분만에 이해하는 프로그래밍10분만에 이해하는 프로그래밍
10분만에 이해하는 프로그래밍
 
1.자료구조와 알고리즘(강의자료)
1.자료구조와 알고리즘(강의자료)1.자료구조와 알고리즘(강의자료)
1.자료구조와 알고리즘(강의자료)
 
[1116 박민근] c++11에 추가된 새로운 기능들
[1116 박민근] c++11에 추가된 새로운 기능들[1116 박민근] c++11에 추가된 새로운 기능들
[1116 박민근] c++11에 추가된 새로운 기능들
 
Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심
 
Intro Of Agile
Intro Of AgileIntro Of Agile
Intro Of Agile
 
통신시스템(Cdma network)
통신시스템(Cdma network)통신시스템(Cdma network)
통신시스템(Cdma network)
 
[2012 11 12]애자일 회고
[2012 11 12]애자일 회고[2012 11 12]애자일 회고
[2012 11 12]애자일 회고
 
칸반(Kanban)
칸반(Kanban)칸반(Kanban)
칸반(Kanban)
 
린 소프트웨어 개발(Lean software development)
린 소프트웨어 개발(Lean software development)린 소프트웨어 개발(Lean software development)
린 소프트웨어 개발(Lean software development)
 
소프트웨어 아키텍처 평가(Atam)
소프트웨어 아키텍처 평가(Atam)소프트웨어 아키텍처 평가(Atam)
소프트웨어 아키텍처 평가(Atam)
 
스크럼(Scrum)
스크럼(Scrum)스크럼(Scrum)
스크럼(Scrum)
 
소프트웨어 아키텍처 문서화
소프트웨어 아키텍처 문서화소프트웨어 아키텍처 문서화
소프트웨어 아키텍처 문서화
 
애자일 코치
애자일 코치애자일 코치
애자일 코치
 
애자일 S/W 개발
애자일 S/W 개발애자일 S/W 개발
애자일 S/W 개발
 
Si 프로젝트에서 바라보는...traditional vs agile
Si 프로젝트에서 바라보는...traditional vs agileSi 프로젝트에서 바라보는...traditional vs agile
Si 프로젝트에서 바라보는...traditional vs agile
 
What is agile
What is agileWhat is agile
What is agile
 

Ähnlich wie 알고리즘과 자료구조

Linq to object using c#
Linq to object using c#Linq to object using c#
Linq to object using c#
병걸 윤
 
자료구조5보고서
자료구조5보고서자료구조5보고서
자료구조5보고서
KimChangHoen
 
Project#5 최단거리 찾기 D0 Hwp
Project#5 최단거리 찾기 D0 HwpProject#5 최단거리 찾기 D0 Hwp
Project#5 최단거리 찾기 D0 Hwp
Kimjeongmoo
 
2012 Dm A0 02 Pdf
2012 Dm A0 02 Pdf2012 Dm A0 02 Pdf
2012 Dm A0 02 Pdf
kd19h
 
2012 Dm A0 02 Pdf
2012 Dm A0 02 Pdf2012 Dm A0 02 Pdf
2012 Dm A0 02 Pdf
jinwookhong
 
2012 Ds A1 05
2012 Ds A1 052012 Ds A1 05
2012 Ds A1 05
seonhyung
 
2012 Ds B2 02 Pdf
2012 Ds B2 02 Pdf2012 Ds B2 02 Pdf
2012 Ds B2 02 Pdf
kd19h
 

Ähnlich wie 알고리즘과 자료구조 (20)

R 프로그래밍 기본 문법
R 프로그래밍 기본 문법R 프로그래밍 기본 문법
R 프로그래밍 기본 문법
 
자료구조02
자료구조02자료구조02
자료구조02
 
Linq to object using c#
Linq to object using c#Linq to object using c#
Linq to object using c#
 
자료구조5보고서
자료구조5보고서자료구조5보고서
자료구조5보고서
 
Project#5 최단거리 찾기 D0 Hwp
Project#5 최단거리 찾기 D0 HwpProject#5 최단거리 찾기 D0 Hwp
Project#5 최단거리 찾기 D0 Hwp
 
2.linear regression and logistic regression
2.linear regression and logistic regression2.linear regression and logistic regression
2.linear regression and logistic regression
 
2012 Dm A0 02 Pdf
2012 Dm A0 02 Pdf2012 Dm A0 02 Pdf
2012 Dm A0 02 Pdf
 
2012 Dm A0 02 Pdf
2012 Dm A0 02 Pdf2012 Dm A0 02 Pdf
2012 Dm A0 02 Pdf
 
이산치2번
이산치2번이산치2번
이산치2번
 
스칼라와 스파크 영혼의 듀오
스칼라와 스파크 영혼의 듀오스칼라와 스파크 영혼의 듀오
스칼라와 스파크 영혼의 듀오
 
Computational Complexity
Computational ComplexityComputational Complexity
Computational Complexity
 
2012 Ds A1 05
2012 Ds A1 052012 Ds A1 05
2012 Ds A1 05
 
하스켈 프로그래밍 입문 2
하스켈 프로그래밍 입문 2하스켈 프로그래밍 입문 2
하스켈 프로그래밍 입문 2
 
함수형 프로그래밍? 그래서 어떻게
함수형 프로그래밍? 그래서 어떻게함수형 프로그래밍? 그래서 어떻게
함수형 프로그래밍? 그래서 어떻게
 
강의자료3
강의자료3강의자료3
강의자료3
 
[D2CAMPUS] Algorithm tips - ALGOS
[D2CAMPUS] Algorithm tips - ALGOS[D2CAMPUS] Algorithm tips - ALGOS
[D2CAMPUS] Algorithm tips - ALGOS
 
자료구조 프로젝트
자료구조 프로젝트자료구조 프로젝트
자료구조 프로젝트
 
강의자료 2
강의자료 2강의자료 2
강의자료 2
 
Fp basic-kotlin
Fp basic-kotlinFp basic-kotlin
Fp basic-kotlin
 
2012 Ds B2 02 Pdf
2012 Ds B2 02 Pdf2012 Ds B2 02 Pdf
2012 Ds B2 02 Pdf
 

Mehr von 영기 김 (12)

Ms Azure fundamentals
Ms Azure fundamentalsMs Azure fundamentals
Ms Azure fundamentals
 
AWS Certified Cloud Practitioner
AWS Certified Cloud PractitionerAWS Certified Cloud Practitioner
AWS Certified Cloud Practitioner
 
Microservices
Microservices Microservices
Microservices
 
Dev ops Introduction
Dev ops IntroductionDev ops Introduction
Dev ops Introduction
 
배열과 포인터
배열과 포인터배열과 포인터
배열과 포인터
 
익스트림 프로그래밍(Xp)
익스트림 프로그래밍(Xp)익스트림 프로그래밍(Xp)
익스트림 프로그래밍(Xp)
 
통신시스템(Wcdma network)
통신시스템(Wcdma network)통신시스템(Wcdma network)
통신시스템(Wcdma network)
 
통신시스템(Gprs network)
통신시스템(Gprs network)통신시스템(Gprs network)
통신시스템(Gprs network)
 
통신시스템(Gsm network)
통신시스템(Gsm network)통신시스템(Gsm network)
통신시스템(Gsm network)
 
소프트웨어 아키텍처
소프트웨어 아키텍처소프트웨어 아키텍처
소프트웨어 아키텍처
 
통신시스템(Cellular concepts)
통신시스템(Cellular concepts)통신시스템(Cellular concepts)
통신시스템(Cellular concepts)
 
소프트웨어 테스팅
소프트웨어 테스팅소프트웨어 테스팅
소프트웨어 테스팅
 

알고리즘과 자료구조

  • 1. Algorithms & Data Structure 김영기책임 네트워크사업부 SE Lab
  • 2. Contents 1 Data Structure 1.1 알고리즘성능측정 1.2 순환(Recursion) 1.4 스택(Stack) 2.1 정렬(Sort) 1.3 리스트(List) 2.2 트리(Tree) 1.5 큐(Queue) 2 Algorithm 1.6 우선순위큐(Priority queue) 2.3 그래프(Graph) 2.5 탐색(Search) 2.4 해싱(Hashing) 2.7 NP-완비 2.6 동적프로그래밍(Dynamic Programming)
  • 4. 알고리즘 성능 분석  알고리즘 성능 분석 기법  수행 시간 측정  두 개의 알고리즘의 실제 수행 시간을 측정한다.  실제로 구현이 필요  동일 하드웨어를 사용하여 측정해야 함  알고리즘 복잡도 분석  직접 구현하지 않고서도 수행시간을 분석하는 방법  알고리즘이 수행하는 연산의 횟수를 측정하여 비교한다.  일반적으로 연산의 횟수는 n의 함수  시간 복잡도(time complexity) : 수행 시간 분석  공간 복잡도(space complexity) : 수행 시 필요로 하는 메모리 공간 분석 연산의 수 = 8 3n+2 연산의 수 =26 5n2 +6 프로그램 A 프로그램 B 워드2008 워드2012
  • 5. 복잡도 알고리즘복잡도분석 시간복잡도는알고리즘을이루고있는연산들이몇번수행되는지숫자로표시 산술연산, 대입연산, 비교연산, 이동연산의기본적인연산 수행시간이입력의크기에따라변화면안된다. 기본연산만 알고리즘이수행하는연산의개수를계산하여두개의알고리즘비교가능 연산의수행횟수는고정된숫자가아니라입력의개수n에대한함수 시간복잡도함수라고하고T(n) 이라고표기 알고리즘A 알고리즘B 알고리즘C 대입연산 1 n+ 1 n*n + 1 덧셈연산 n n*n 곱셈연산 1 나눗셈연산 Total 2 2n+ 1 2n2+ 1 입력의개수n 연산의횟수 알고리즘A 알고리즘B 알고리즘C
  • 6. 알고리즘차수비교 알고리즘차수비교 가정 f와g를자연수집합N에서실수R로의함수, 즉f, g : N R R+는양의실수집합 O (big oh) f보다차수가작거나같은함수전체의집합 (big theta) f와같은차수를갖는함수들의집합 Ω (big omega) f보다차수가크거나같은함수전체의집합 O(f) = {gcR+, n0N, nn0 , g(n)cf(n)} (f) = {ggO(f), fO(g)} (f) = {gcR+, n0N, nn0 , g(n)cf(n)} f (f): 적어도f보다 빠르게증가하는함수들 (f): f와같은비율로 증가하는함수들 즉, (f) = (f) O(f) O(f): f보다작은비율로 증가하는함수들
  • 7. Big O 표기법 Big O 표기법 자료의개수가많은경우차수가가장큰항의영향이크고, 다른항들은상대적으로무시가능시간복잡도함수에서가장영향이큰항만을고려해도충분!!! Big O 표기법: 연산의횟수를점근적(대략적)으로표기한다. 주어진함수의가장높은항만끄집어내고, 계수를1로하면된다. 예제 g(n) = 5이면0(1)이다. g(n) = 2n+5이면이알고리즘은0(n)이다. g(n) = 3n2+100이면, O(n2)이다. G(n) = 5*2n+10n+100이면O(2n)이다. n=1000인경우 T(n)= n2 + n + 1 99% 1% ■수학적정의 임의의상수N0와c가있어서N≥N0인N에대해서, c⋅f(N) ≥ g(N)이성립하면g(N) = O(f(N))이라한다.
  • 8. Big O 표기법 Big O 표기법종류 O(1):상수형 O(logn):로그형 O(n):선형 O(nlogn):로그선형 O(n2):2차형 O(n3):3차형 O(nk):k차형 O(2n):지수형 O(n!) : 팩토리얼형 시간복잡도 n 1 2 4 8 16 32 1 1 1 1 1 1 1 logn 0 1 2 3 4 5 n 1 2 4 8 16 32 nlogn 0 2 8 24 64 160 n2 1 4 16 64 256 1024 n3 1 8 64 512 4096 32768 2n 2 4 16 256 65536 4294967296 n! 1 2 24 40326 20922789888000 26313×1033 1 2 4 8 16 32 64 128 256 65536 32768 16384 8192 4096 2048 1024 512 256 128 64 32 16 8 4 2 f(n)=2n f(n)=n3 f(n)=n2 f(n)=nlog2n f(n)=n f(n)=log2n n f(n)
  • 9. Big 표기법, Big 표기법 Big 표기법 Big 는함수의하한을표시한다 n≥5이면, 2n+1 <10n 이므로n = (n) Big 표기법 Big 는함수의하한인동시에상한을표시 f(n)=O(g(n))이면서, f(n)=(g(n))이면f(n)= (n)이다 n≥1이면, n ≤2n+1 ≤3n이므로2n+1 = (n) ■수학적정의 모든n≥n0에대하여c1|g(n)| ≤ |f(n)| ≤ c2|g(n)|을만족하는3개의상수c1, c2와n0가존재하면f(n)=θ(g(n))이다. ■수학적정의 모든n≥n0에대하여|f(n)| ≥ c|g(n)|을만족하는2개의상수c와n0가존재하면f(n)=Ω(g(n))이다 입력의개수n 연산의수 상한 하한 n0 f(n) 0(f(n)) (f(n))
  • 10. Best, Average, Worst Cases 알고리즘의수행시간 입력자료의집합에따라다를수있다. 정렬알고리즘의수행시간은입력집합에따라다를수있다. Case 구분 최선의경우(best case): 수행시간이가장빠른경우 실제로의미가없는경우가많다. 평균의경우(average case): 수행시간이평균적인경우 계산하기가상당히어렵다. 최악의경우(worst case): 수행시간이가장늦은경우 가장널리사용되며, 계산하기쉽다. 응용에따라중요한의미를가질수있다. 최악의경우 최선의경우 평균적인경우 A B C D E F G 입력집합 수행시간 100 50
  • 12. 순환(Recursion) 순환이란… 어떤알고리즘이나함수가자기자신을호출하여문제를해결하는기법 예제: Factorial의정의 Factorial의C언어구현 n! = 1 n=0 n* (n-1)! 1>=0 intfactorial(intn) { if(n==1) return(1); else return(n*factorial(n-1)); } 3!은? 3!=3*2! 1!=1 2!=2*1! 2!는? 3!는? 1!는? 2 6 1 factorial(3) = 3*factorial(2) = 3*2*factorial(1) = 3*2*1 = 3*2 = 6
  • 13. 순환(Recursion) 순환호출의내부적인구현 Factorial(2) { if(2==1) return 1; else return(2*factorial(1)); } Factorial(2) { if(2==1) return 1; else return(2*factorial(1)); } Factorial(3) { if(3==1) return 1; else return(3*factorial(2)); } Factorial(3) { if(3==1) return 1; else return(3*factorial(2)); } Factorial(3) { if(3==1) return 1; else return(3*factorial(2)); } Factorial(2) { if(1==1) return 1; … } Factorial(2) { … else return(2*1); } Factorial(3) { if(3==1) return 1; else return(3*factorial(2)); } Factorial(3) { if(3==1) return 1; else return(3*2); } (1) (2) (3) (4) (5) intfactorial(intn) { if(n==1) return(1); else return(n*factorial(n-1)); } 순환알고리즘의구현 순환을 멈추는부분 순환호출을하는부분
  • 14. 순환(Recursion) 순환과반복 프로그래밍언어의되풀이방법에는순환과반복이있다. 많은경우, 순환은반복으로변경이가능하다. 순환은반복에비해명확하고간결하게표현되나, 수행속도는느린단점이있다. 알고리즘설명은순환으로, 구현은반복으로하는경우가많다. 반복적인Factorial계산프로그램과순환원리 3!=3*2! 1!=1 2!=2*1! 2!는? 1!는? 2 1 1!=1 2!=2*1 3!=3*2*1 4!=4*3*2*1 반복 순환 반복의구현은 for, while을이용 intfactorial_iter(intn) { intk, v=1; for(k=n; k>0; k--) v=v*k; return(v); } 순환코드는 수행시간에약점 intfactorial(intn) { if(n==1) return(1); else return (n * factorial(n-1)); } 문제부분 해결부분 순환의원리는 분할정복(Divide and conquer) 즉,문제의크기를작게만든다는것이다.
  • 15. 거듭제곱값계산 풙풏을구하는함수 순환의경우호출할때마다문제의크기가반으로줄어든다. doublepower (doublex, int n) { inti ; doubler = 1.0 ; for(i=0; i<n; i++) r = r*x ; return(r) ; } doublepower (doublex, int n) { if( n==0 ) return1; else if ( (n%2) == 0) return power(x*x, n/2); else return x*power(x*x, (n-1)/2); } 반복 순환 2500계산1000000번수행수행시간: 7.11 초 2500계산1000000번수행수행시간: 0.47 초 반복적인함수power 순환적인함수power 시간복잡도 O(n) O(log2n) 실제수행속도 7.17초 0.47초
  • 16. Fibonacci sequence 피보나치수열의계산 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, … 0 n = 0 1 n = 1 fib(n-2) + fib(n-1) otherwise fib(n) = Fib(0) Fib(1) Fib(0) Fib(1) Fib(0) Fib(1) Fib(1) Fib(2) Fib(0) Fib(1) Fib(1) Fib(2) Fib(0) Fib(1) Fib(1) Fib(2) Fib(2) Fib(3) Fib(2) Fib(3) Fib(3) Fib(4) Fib(4) Fib(5) Fib(6) ■함수호출 n=25 : 25만 n=30 : 300백만
  • 17. Fibonacci sequence 피보나치수열계산함수 이경우어느함수가더효율적인가? intfib (int n) { if (n<1) returnn; else{ int i, tmp, current=1; last=0; for(i=2; i<=n; i++){ tmp= current; current += last; last = tmp; } return current; } intfib(intn) { if(n==0) return0; if(n==1) return1; return(fib(n-1) + fib(n-1)); } 반복 순환
  • 18. The Tower of Hanoi 하노이탑 이동조건 한번에하나의원판만이동할수있다. 맨위에있는원판만이동할수있다. 크기가작은원판위에큰원판이쌓일수없다. 중간의막대를임시적으로이용할수있으나앞의조건들을지켜야한다. 하노이탑확장 원판이늘어나는경우 막대의수가늘어나는경우
  • 19. The Tower of Hanoi 하노이탑문제에대한의사코드 순환이되는부분을찾는것이Key !!! voidhanoi_tower(intn, charfrom,char tmp, charto) { if(n==1) { from에서to로원판을옮긴다. } else{ ①from의맨밑에원판을제외한나머지원판들을tmp로옮긴다. // hanoi_tower(n-1, from, to, tmp); ②from에있는한개의원판을to로옮긴다. ③tmp의원판들을to로옮긴다. // hanoi_tower(n-1, tmp, from, to); } } 원래문제의 축소된형태이다.
  • 20. The Tower of Hanoi 하노이탑문제에대한전체코드 #include <stdio.h> voidhanoi_tower(intn, charfrom,char tmp, charto) { if(n==1) { printf(“원판1을%c 에서%c 로옮긴다n”, from, to); } else{ hanoi_tower(n-1, from, to, tmp); printf(“원판%d를%c 에서%c 로옮긴다n”, n, from, to); hanoi_tower(n-1, tmp, from, to); } } int main(){ hanoi_tower(4, ‘A’, ‘B’, ‘C’); return 0; } 원판1를A에서B로옮긴다. 원판2를A에서C로옮긴다. 원판1를B에서C로옮긴다. 원판3를A에서B로옮긴다. 원판1를C에서A로옮긴다. 원판2를C에서B로옮긴다. 원판1를A에서B로옮긴다. 원판4를A에서C로옮긴다. 원판1를B에서C로옮긴다. 원판2를B에서A로옮긴다. 원판1를C에서A로옮긴다. 원판3를B에서C로옮긴다. 원판1를A에서B로옮긴다. 원판2를A에서C로옮긴다. 원판1를B에서C로옮긴다.
  • 22. 리스트(List)  리스트, 선형 리스트  목록이나 도표처럼 여러 데이터를 관리할 수 있는 자료형을 추상화  순서를 가진 항목들의 모임  ※ 집합: 항목간의 순서의 개념이 없음   데이터 삽입, 삭제, 검색 등 필요 작업을 가함  스택과 큐는 리스트의 특수한 경우에 해당  리스트의 연산 ( , ,..., ) 0 1 1  n L item item item 연 산 설 명 Insert(Position, Data) 데이터를 해당 위치(Position)에 넣기 Delete(Position) 해당 위치(Position)의 데이터를 삭제 Retrieve(Position, Data) 해당 위치(Position)의 데이터를 Data 변수에 복사 Create( ) 빈 리스트 만들기 Destroy( ) 리스트 없애기 IsEmpty( ) 빈 리스트인지 확인 (아무 것도 안 적혔는지 확인) Length( ) 몇 개의 항목인지 계산 (몇 개나 적혔는지 세기)
  • 23. 리스트구현방법 배열을이용하는방법 구현이간단 삽입, 삭제시오버헤드 항목의개수제한 연결리스트를이용하는방법 구현이복잡 삽입, 삭제가효율적 크기가제한되지않음 A B C D n A B C 배열이용 연결리스트이용 하나의노드가데이터와링크로구성되어있고링크가노드들을연결한다
  • 24. Array List 1차원배열에항목들을순서대로저장 L=(A, B, C, D, E) 삽입연산 삽입위치다음의항목들을이동하여야함. 삭제연산 삭제위치다음의항목들을이동하여야함 A B C D E 0 1 2 3 4 5 6 7 8 9 A B C D E 0 1 2 3 4 5 6 7 8 9 A B D E 0 1 2 3 4 5 6 7 8 9 N N
  • 25. ArrayListType의구현 구현코드 항목들의타입은element로정의 list라는1차원배열에항목들을차례대로저장 length에항목의개수저장 typedefint element; typedefstruct{ int list[MAX_LIST_SIZE];// 배열정의 int length;// 현재배열에저장된항목들의개수 } ArrayListType; // 리스트초기화 void init(ArrayListType*L) { L->length = 0; } int is_empty(ArrayListType*L) // 리스트가비어있으면1, 아니면0을반환 { return L->length == 0; } int is_full(ArrayListType*L) // 리스트가가득차있으면1을반환, 아니면0을반환 { return L->length == MAX_LIST_SIZE; }
  • 26. ArrayListType의구현 삽입과삭제 // position: 삽입하고자하는위치 // item: 삽입하고자하는자료 void add(ArrayListType*L, int position, element item) { if( !is_full(L) && (position >= 0) && (position <= L->length) ){ int i; for(i=(L->length-1); i>=position;i--) L->list[i+1] = L->list[i]; L->list[position] = item; L->length++; } } // position: 삭제하고자하는위치 // 반환값: 삭제되는자료 element delete(ArrayListType*L, int position) { int i; element item; if( position < 0 || position >= L->length ) error("위치오류"); item = L->list[position]; for(i=position; i<(L->length-1);i++) L->list[i] = L->list[i+1]; L->length--; return item; }
  • 27. 연결리스트(Linked List) 연결리스트 리스트의항목들을노드(node)라고하는곳에분산하여저장 다음항목을가리키는주소도같이저장 노드(node) : <항목, 주소> 쌍 노드는데이터필드와링크필드로구성 데이터필드–리스트의원소, 즉데이터값을저장하는곳 링크필드–다른노드의주소값을저장하는장소(포인터) 메모리에서의노드의물리적순서가리스트의논리적순서와일치할필요없음 A B C D E
  • 28. 연결리스트의장단점 구조 노드= 데이터필드+ 링크필드 Head Pointer 리스트의첫번째노드를가르키는변수 노드의생성 필요시동적메모리생성이용 장점 삽입, 삭제가보다용이하다. 연속된메모리공간이필요없다. 크기제한이없다 단점 구현이어렵다. 오류가발생하기쉽다. A B C D E A B C D E X N X X 메인메모리 메인메모리 삽입연산 삭제연산 Data Link HeadPointer
  • 29. 연결리스트의종류 단순연결리스트 원형연결리스트 이중연결리스트 A B C D NULL Head Pointer A B C D Head Pointer A B C D Head Pointer
  • 30. 단순연결리스트 단순연결리스트 하나의링크필드를이용연결 마지막노드의링크값은NULL A B C D NULL Head Pointer insert_node(L,before,new) ifL=NULL thenL←new elsenew.link←before.link before.link←new C D C E D E 삽입연산 remove_node(L,before,removed) ifL≠NULL thenbefore.link←removed.link destroy(removed) C E D C E D before before before before removed after after after new new after removed
  • 31. 단순연결리스트구현 단순연결리스트구현 데이터필드: 구조체로정의 링크필드: 포인터사용 노드의생성: 동적메모리생성라이브러리malloc함수이용 데이터필드와링크필드설정 두번째노드생성과첫번째노드와의연결 typedefintelement; typedefstructListNode{ elementdata; structListNode*link; }ListNode; p1 ListNode*p1; p1=(ListNode*)malloc(sizeof(ListNode)); p1->data=10; p1->link=NULL; 10 n p1 ListNode*p2; p2=(ListNode*)malloc(sizeof(ListNode)); p2->data=20; p2->link=NULL; p1->link=p2; 10 p1 20 n 헤드포인터(head pointer): 연결리스트의맨처음노드를가리키는포인터
  • 32. 연결리스트연산 삽입 헤드포인터가함수안에서변경되므로헤드포인터의포인터필요 삽입의3가지경우 head가NULL인경우: 공백리스트에삽입 head가NULL이라면현재삽입하려는노드가첫번째노드가된다. 따라서head의값만변경 p가NULL인경우: 리스트의맨처음에삽입 일반적인경우: 리스트의중간에삽입 new_node의link에p->link값을복사한다음, p->link가new_node를가리키도록한다 voidinsert_node(ListNode**phead,ListNode*p,ListNode*new_node) phead:헤드포인터head에대한포인터 p:삽입될위치의선행노드를가리키는포인터,이노드다음에삽입된다. new_node:새로운노드를가리키는포인터 head가NULL이라면현재삽입하려는노드가첫번째노드가된다. 따라서head의값만변경 p1 new_node 10 p1 20 n x new_node
  • 33. 연결리스트연산 삭제연산 삭제의2가지경우 p가NULL인경우: 맨앞의노드를삭제 연결리스트의첫번째노드를삭제한다. 헤드포인터변경 p가NULL이아닌경우: 중간노드를삭제 removed 앞의노드인p의링크가removed 다음노드를가리키도록변경 //phead:헤드포인터head의포인터 //p:삭제될노드의선행노드를가리키는포인터 //removed:삭제될노드를가리키는포인터 voidremove_node(ListNode**phead,ListNode*p,ListNode*removed) 10 20 n 11 x list removed 10 20 n 11 list removed x p
  • 34. 단순연결리스트구현 삽입연산코드 삭제연산코드 //phead:리스트의헤드포인터의포인터 //p:선행노드 //new_node:삽입될노드 voidinsert_node(ListNode**phead,ListNode*p,ListNode*new_node) { if(*phead==NULL){//공백리스트인경우 new_node->link=NULL; *phead=new_node; } elseif(p==NULL){//p가NULL이면첫번째노드로삽입 new_node->link=*phead; *phead=new_node; } else{//p다음에삽입 new_node->link=p->link; p->link=new_node; } } voidremove_node(ListNode**phead,ListNode*p,ListNode*removed) { if(p==NULL) *phead=(*phead)->link; else p->link=removed->link; free(removed); }
  • 35. 연결리스트연산 방문연산 리스트상의노드를순차적으로방문 반복과순환기법모두사용가능 voiddisplay(ListNode*head) { ListNode*p=head; while(p!=NULL){ printf("%d->",p->data); p=p->link; } printf("n"); } voiddisplay_recur(ListNode*head) { ListNode*p=head; if(p!=NULL){ printf("%d->",p->data); display_recur(p->link); } } [반복버전] [순환버전]
  • 36. 연결리스트연산 탐색연산 특정한데이터값을갖는노드를찾는연산 A B C D NULL Head Pointer p ListNode*search(ListNode*head,intx) { ListNode*p; p=head; while(p!=NULL){ if(p->data==x)returnp;//탐색성공 p=p->link; } returnp;//탐색실패일경우NULL반환 }
  • 37. 연결리스트연산 합병연산 2개의리스트를합하는연산 A B C D n Head 1 J K L Q n Head 2 ListNode*concat(ListNode*head1,ListNode*head2) { ListNode*p; if(head1==NULL)returnhead2; elseif(head2==NULL)returnhead1; else{ p=head1; while(p->link!=NULL) p=p->link; p->link=head2; returnhead1; } }
  • 38. 연결리스트연산 역순연산 리스트의노드들을역순으로만드는연산 A n B C D n Head r q p x x x ListNode*reverse(ListNode*head) { //순회포인터로p,q,r을사용 ListNode*p,*q,*r; p=head;//p는역순으로만들리스트 q=NULL;//q는역순으로만들노드 while(p!=NULL){ r=q;//r은역순으로된리스트.r은q,q는p를차례로따라간다. q=p; p=p->link; q->link=r;//q의링크방향을바꾼다. } returnq;//q는역순으로된리스트의헤드포인터 }
  • 39. 원형연결리스트 원형연결리스트 마지막노드의링크가첫번째노드를가리키는리스트 한노드에서다른모든노드로의접근이가능 보통헤드포인터가마지막노드를가리키게끔구성 리스트의처음이나마지막에노드를삽입하는연산이단순연결리스트에비하여용이 A B C D Head A B C D Head 1
  • 40. 원형리스트연산 삽입(처음에삽입) A B C D Head B x ② ① //phead:리스트의헤드포인터의포인터 //p:선행노드 //node:삽입될노드 voidinsert_first(ListNode**phead,ListNode*node) { if(*phead==NULL){ *phead=node; node->link=node; } else{ node->link=(*phead)->link; (*phead)->link=node; } } node
  • 41. 원형리스트연산 삭제(끝에삽입) A B C D Head B x ② ① node //phead:리스트의헤드포인터의포인터 //p:선행노드 //node:삽입될노드 voidinsert_last(ListNode**phead,ListNode*node) { if(*phead==NULL){ *phead=node; node->link=node; } else{ node->link=(*phead)->link; (*phead)->link=node; *phead=node; } } x ③
  • 42. 이중연결리스트 이중연결리스트 하나의노드가선행노드와후속노드에대한두개의링크를가지는리스트 링크가양방향이므로양방향으로검색이가능 단점은공간을많이차지하고코드가복잡 실제사용되는이중연결리스트의형태 헤드노드+ 이중연결리스트+ 원형연결리스트 삽입이나삭제시반드시선행노드가필요 단순연결리스트의문제점: 선행노드를찾기가힘들다 헤드노드(Head node) 데이터를가지지않고단지삽입, 삭제코드를간단하게할목적으로만들어진노드 헤드포인터와의구별필요 공백상태에서는헤드노드만존재 Head node llink data rlink typedefintelement; typedefstructDlistNode{ elementdata; structDlistNode*llink; structDlistNode*rlink; }DlistNode;
  • 43. 이중연결리스트 삽입 before x x ③ ① ② ④ //노드new_node를노드before의오른쪽에삽입한다. voiddinsert_node(DlistNode*before,DlistNode*new_node) { new_node->llink=before; new_node->rlink=before->rlink; before->rlink->llink=new_node; before->rlink=new_node; }
  • 44. 이중연결리스트 삭제 //노드removed를삭제한다. voiddremove_node(DlistNode*phead_node, DlistNode*removed) { if(removed==phead_node)return; removed->llink->rlink=removed->rlink; removed->rlink->llink=removed->llink; free(removed); } x x x x
  • 45. 연결리스트를이용한리스트ADT 구현 How to ? 리스트ADT의연산을연결리스트를이용하여구현 리스트ADT의add, delete 연산의파라미터는위치 연결리스트의insert_node, remove_node의파리미터는노드포인터 상황에따라연산을적절하게선택하여야함 add(항목의위치) delete(항목의위치) insert_node(노드포인터) remove_node(노드포인터) 리스트ADT 연결리스트 사용자
  • 46. 리스트ADT 구현 리스트선언 is_empty get_length typedefstruct{ ListNode*head;//첫번째노드를가르키는헤드포인터 intlength;//연결리스트내존재하는노드의개수 }ListType; ListTypelist1;//리스트ADT생성 intis_empty(ListType*list) { if(list->head==NULL)return1; elsereturn0; } //리스트의항목의개수를반환한다. intget_length(ListType*list) { returnlist->length; }
  • 47. 리스트ADT 구현 add 연산 새로운데이터를임의의위치에삽입 항목의위치를노드포인터로변환해주는함수get_node_at필요 //리스트안에서pos위치의노드를반환한다. ListNode*get_node_at(ListType*list,intpos) { inti; ListNode*tmp_node=list->head; if(pos<0)returnNULL; for(i=0;i<pos;i++) tmp_node=tmp_node->link; returntmp_node; } //주어진위치에데이터를삽입한다. voidadd(ListType*list,intposition,elementdata) { ListNode*p; if((position>=0)&&(position<=list->length)){ ListNode*node=(ListNode*)malloc(sizeof(ListNode)); if(node==NULL)error("메모리할당에러"); node->data=data; p=get_node_at(list,position-1); insert_node(&(list->head),p,node); list->length++; } }
  • 48. 리스트ADT 구현 delete 연산의구현 임의의위치의데이터를삭제 항목의위치를노드포인터로변환해주는함수get_node_at필요 //주어진위치의데이터를삭제한다. voiddelete(ListType*list,intpos) { if(!is_empty(list)&&(pos>=0)&&(pos<list->length)){ ListNode*p=get_node_at(list,pos-1); remove_node(&(list->head),p,(p!=NULL)?p->link:NULL); list->length--; } }
  • 50. 스택(Stack) 스택 선형리스트구조의특별한형태로, 데이터의삽입과삭제가리스트한쪽끝에서만일어나는자료구조 후입선출(LIFO:Last-InFirst-Out) 가장최근에들어온데이터가가장먼저나감 A C 삽입 (Push) 삭제 (Pop) top 스택의동작구조 B bottom 요소 (element)
  • 51. 스택추상데이터타입(ADT) 스택의용도 에디터에서되돌리기기능, 함수호출에서복귀주소기억 스택의연산 객체: n개의element형의요소들의선형리스트 연산: ▪create() ::=스택을생성한다. ▪is_empty(s) ::= 스택이비어있는지를검사한다. ▪is_full(s) ::= 스택이가득찼는가를검사한다. ▪push(s, e) ::= 스택의맨위에요소e를추가한다. ▪pop(s) ::= 스택의맨위에있는요소를삭제한다. ▪peek(s) ::= 스택의맨위에있는요소를삭제하지않고반환한다. 초기상태 push(A) push(B) push(C) pop() A A B A B C A B
  • 52. 배열을이용한스택의구현 스택구현 1차원배열stack[ ] 스택에서가장최근에입력되었던자료를가리키는top 변수 가장먼저들어온요소는stack[0]에, 가장최근에들어온요소는stack[top]에저장 스택이공백상태이면top은-1 Is_empty, is_full연산 공백상태 A B C D 3 2 1 0 -1 3 2 1 0 -1 TOP A B 3 2 1 0 -1 TOP TOP is_empty(S) if top= -1 then return TRUE else return FALSE is_full(S) if top= (MAX_STACK_SIZE-1) then return TRUE else return FALSE
  • 53. 배열을이용한스택의구현 push 연산 pop 연산 push(S, x) if is_full(S) then error "overflow" else top←top+1 stack[top]←x pop(S, x) if is_empty(S) then error "underflow" else e←stack[top] top←top-1 return e push(C) A B A B C TOP TOP pop(C) A B A B C TOP TOP
  • 54. 연결스택 연결리스트를이용하여구현한스택 장점: 크기가제한되지않음 단점: 구현이복잡하고삽입이나삭제시간이오래걸림 연결스택(Linked Stack) A B 3 2 1 0 -1 TOP C C B A n TOP typedefint element; typedefstructStackNode{ element item; structStackNode*link; } StackeNode; typedefstruct{ StackNode*top; } LinkedStackType; 요소타입 노드타입 연결스택의관련데이터
  • 55. 연결스택에서의연산 push(), pop() // 삽입함수 void push(LinkedStackType*s, element item) { StackNode*temp=(StackNode*)malloc(sizeof(StackNode)); if( temp == NULL ){ fprintf(stderr, "메모리할당에러n"); return; } else{ temp->item = item; temp->link = s->top; s->top = temp; } } // 삭제함수 element pop(LinkedStackType*s) { if( is_empty(s) ) { fprintf(stderr, "스택이비어있음n"); exit(1); } else{ StackNode*temp=s->top; int item = temp->item; s->top = s->top->link; free(temp); return item; } } A C B A n Top Temp ② ① C B A n Top Temp
  • 56. 스택의응용: 괄호검사 괄호의종류 괄호의종류: 대괄호(‘[’, ‘]’), 중괄호(‘{’, ‘}’), 소괄호(‘(’, ‘)’) 조건 왼쪽괄호의개수와오른쪽괄호의개수가같아야한다. 같은괄호에서왼쪽괄호는오른쪽괄호보다먼저나와야한다. 괄호사이에는포함관계만존재한다. 잘못된괄호의사용예 (a(b), a(b)c), a{b(c[d]e}f) if( ( i==0 ) && (j==0 ) 비교 비교 오류 { A [ (i+1 ) ]=0; } 비교 비교 비교 성공 ( ( ( ( ( ( ( ( { { [ { [ { { [ ( { [ ( ( (
  • 58. 큐(QUEUE) 큐 대기열을모델링 먼저들어온데이터가먼저나가는자료구조 선입선출(FIFO: First-In First-Out) 용어 줄의맨앞을큐프런트(Queue Front) 맨뒤를큐리어(Queue Rear) 큐리어에데이터를삽입하는작업= 큐애드(Add) 큐프런트의데이터를삭제하는작업= 큐리무브(Remove) 삽입 삭제 검색 ADT 리스트 Insert Delete Retrieve ADT 스택 Push Pop GetTop (PeekTop) ADT 큐 Add(Enqueue) Remove (Dequeue) GetFront (PeekFront) 전단(front) 후단(rear)
  • 59. 큐ADT 큐ADT 삽입과삭제는FIFO순서를따른다. 삽입은큐의후단에서, 삭제는전단에서이루어진다 큐의응용 시뮬레이션의대기열(공항에서의비행기들, 은행에서의대기열) 통신에서의데이터패킷들의모델링에이용 프린터와컴퓨터사이의버퍼링 스택과마찬가지로프로그래머의도구 객체: n개의element형으로구성된요소들의순서있는모임 연산: ▪create() ::=큐를생성한다. ▪init(q) ::=큐를초기화한다. ▪is_empty(q) ::=큐가비어있는지를검사한다. ▪is_full(q) ::=큐가가득찼는가를검사한다. ▪enqueue(q, e) ::= 큐의뒤에요소를추가한다. ▪dequeue(q) ::=큐의앞에있는요소를반환한다음삭제한다. ▪peek(q) ::= 큐에서삭제하지않고앞에있는요소를반환한다. 생산자 버퍼 소비자
  • 60. 배열을이용한큐 선형큐 배열을선형으로사용하여큐를구현 삽입을계속하기위해서는요소들을이용시켜야함 문제점이많아사용되지않음 원형큐 배열을원형으로사용하여큐를구현 front : 첫번째요소하나앞의인덱스 rear : 마지막요소의인덱스 [-1] [0] [1] [2] [3] [4] [5] [-1] [0] [1] [2] [3] [4] [5] front rear front rear rear a b front … [0] [1] [2] [3] [4] [5] [6] [7]
  • 61. 원형큐의삽입과삭제 rear a front … [0] [1] [2] [3] [4] [5] [6] [7] rear a b front … [0] [1] [2] [3] [4] [5] [6] [7] rear b front … [0] [1] [2] [3] [4] [5] [6] [7] 2. A 삽입 rear front … [0] [1] [2] [3] [4] [5] [6] [7] 1. 초기상태 4. A 삭제 3. B 삽입
  • 62. 공백상태, 포화상태 공백상태 front == rear 포화상태 front % M == (rear+1) % M 공백상태와포화상태를구별하기위해서는하나의공간은항상비워둔다 rear front … [0] [1] [2] [3] [4] [5] [6] [7] 1. 공백상태 rear front d [0] [1] [2] [3] [4] [5] [6] [7] 1. 포화상태 e f g rear front [0] [1] [2] [3] [4] [5] [6] [7] 1. 초기상태 a b c d e f g a b c h
  • 63. 큐의연산 Modulo 연산을사용 인덱스를원형으로회전시킴 // 공백상태검출함수 int is_empty(QueueType*q){ return (q->front == q->rear); } // 포화상태검출함수 int is_full(QueueType*q){ return ((q->rear+1)%MAX_QUEUE_SIZE == q->front); } // 삽입함수 void enqueue(QueueType*q, element item){ if( is_full(q) ) error("큐가포화상태입니다"); q->rear = (q->rear+1) % MAX_QUEUE_SIZE; q->queue[q->rear] = item; } // 삭제함수 element dequeue(QueueType*q) { if( is_empty(q) ) error("큐가공백상태입니다"); q->front = (q->front+1) % MAX_QUEUE_SIZE; return q->queue[q->front]; }
  • 64. 연결큐(Linked Queue) 연결큐: 연결리스트로구현된큐 front 포인터는삭제와관련되며rear 포인터는삽입 front는연결리스트의맨앞에있는요소를가리킨다 rear 포인터는맨뒤에있는요소를가리킨다 큐에요소가없는경우에는front와rear는NULL A B C D NULL rear front 삽입 삭제 A B C D NULL rear front temp A B C D NULL rear front A B C D NULL rear front A B C D NULL rear front temp
  • 65. 덱(Deque) 덱(Double-ended Queue) 큐의전단(front)와후단(rear)에서모두삽입과삭제가가능한큐 덱ADT 전단(front) 후단(rear) add_front delete_front get_front add_rear delete_rear get_rear 객체: n개의element형으로구성된요소들의순서있는모임 연산: ▪create() ::=덱을생성한다. ▪init(dq) ::=덱을초기화한다. ▪is_empty(dq) ::= 덱이공백상태인지를검사한다. ▪is_full(dq) ::=덱이포화상태인지를검사한다. ▪add_front(dq, e) ::= 덱의앞에요소를추가한다. ▪add_rear(dq, e) ::= 덱의뒤에요소를추가한다. ▪delete_front(dq) ::=덱의앞에있는요소를반환한다음삭제한다 ▪delete_rear(dq) ::=덱의뒤에있는요소를반환한다음삭제한다. ▪get_front(q) ::= 덱의앞에서삭제하지않고앞에있는요소를반환한다. ▪get_rear(q) ::= 덱의뒤에서삭제하지않고뒤에있는요소를반환한다.
  • 66. 덱의연산 C A ddd_front(dq, A) A ddd_rear(dq, B) B A ddd_front(dq, C) B C A ddd_rear(dq, D) B D A delete_front(dq) B D A delete_rear(dq) B front rear front rear front rear front rear front rear front rear
  • 67. 덱의구현 덱의구현 양쪽에서삽입, 삭제가가능하여야하므로일반적으로이중연결리스트사용 typedefint element;// 요소의타입 typedefstructDlistNode{// 노드의타입 element data; structDlistNode*llink; structDlistNode*rlink; } DlistNode; typedefstructDequeType{// 덱의타입 DlistNode*head; DlistNode*tail; } DequeType;
  • 68. 덱에서의삽입 void add_rear(DequeType*dq, element item) { DlistNode*new_node= create_node(dq->tail, item, NULL); if( is_empty(dq)) dq->head = new_node; else dq->tail->rlink= new_node; dq->tail = new_node; } void add_front(DequeType*dq, element item){ DlistNode*new_node= create_node(NULL, item, dq->head); if( is_empty(dq)) dq->tail = new_node; else dq->head->llink= new_node; dq->head = new_node; } tail new node 삽입전 삽입후 tail
  • 69. 덱에서의삭제 // 전단에서의삭제 element delete_front(DequeType*dq) { element item; DlistNode*removed_node; if (is_empty(dq)) error("공백덱에서삭제"); else { removed_node= dq->head; // 삭제할노드 item = removed_node->data; // 데이터추출 dq->head = dq->head->rlink; // 헤드포인터변경 free(removed_node);// 메모리공간반납 if (dq->head == NULL)// 공백상태이면 dq->tail = NULL; else// 공백상태가아니면 dq->head->llink=NULL; } return item; } tail 삭제전 삭제후 n tail head head
  • 71. 우선순위큐(Priority queue) 우선순위큐 우선순위를가진항목들을저장하는큐 FIFO 순서가아니라우선순위가높은데이터가먼저나가게된다. 가장일반적인큐로써, 스택이나큐로구현이가능하다. 응용분야 시뮬레이션시스템(여기서의우선순위는대개사건의시각이다.) 네트워크트래픽제어 운영체제에서의작업스케쥴링 자료구조 삭제되는요소 스택 가장최근에들어온데이터 큐 가장먼저들어온데이터 우선순위큐 가장우선순위가높은데이터 > >
  • 72. 우선순위큐ADT 가장중요한연산은 insert 연산(요소삽입), delete 연산(요소삭제)이다. 우선순위큐는2가지로구분 최소우선순위큐 최대우선순위큐 객체: n개의element형의우선순위를가진요소들의모임 연산: ▪create() ::=우선순위큐를생성한다. ▪init(q) ::= 우선순위큐q를초기화한다. ▪is_empty(q) ::= 우선순위큐q가비어있는지를검사한다. ▪is_full(q) ::= 우선순위큐q가가득찼는가를검사한다. ▪insert(q, x) ::= 우선순위큐q에요소x를추가한다. ▪delete(q) ::= 우선순위큐로부터가장우선순위가높은요소를삭제하고이요소를반환한다. ▪find(q) ::= 우선순위가가장높은요소를반환한다.
  • 73. 우선순위큐구현 구현방법 배열을이용한우선순위큐 연결리스트를이용한우선순위큐 히프(heap)를이용한우선순위큐 표현방법 삽입 삭제 순서없는배열 O(1) O(n) 순서없는연결리스트 O(1) O(n) 정렬된배열 O(n) O(1) 정렬된연결리스트 O(n) O(1) 히프 O(logn) O(logn) 9 7 6 5 4 3 2 2 1 3 1 2 3 7 5 n 헤드포인트 1 2 3 7 5 0 1 2 3 4 5 6 COUNT
  • 74. 히프(Heap) 히프 히프란노드들이저장하고있는키들이다음식을만족하는완전이진트리 최대히프(max heap) 부모노드의키값이자식노드의키값보다크거나같은완전이진트리 key(부모노드) ≥key(자식노드) 최소히프(min heap) 부모노드의키값이자식노드의키값보다작거나같은완전이진트리 key(부모노드) ≤key(자식노드) 9 7 6 5 4 2 3 3 2 1 1 4 2 7 5 3 9 3 7 8 최대히프 최소히프
  • 75. 히프의구현방법 히프구현배열이용 완전이진트리이므로각노드에번호를붙일수있다 이번호를배열의인덱스라고생각 부모노드와자식노드를찾기가쉽다. 왼쪽자식의인덱스= (부모의인덱스)*2 오른쪽자식의인덱스= (부모의인덱스)*2 + 1 부모의인덱스= (자식의인덱스)/2 9 7 6 5 4 2 3 3 2 1 1 2 3 4 5 6 7 8 9 10 9 7 6 5 4 3 2 2 1 3 0 1 2 3 4 5 6 7 8 9 10 왼쪽자식인덱스=(2*3)=6 오른쪽자식인덱스= (2*3)+1=7
  • 76. 히프의높이 N개의노드를가지고있는히프의높이는O(logn) 히프는완전이진트리 마지막레벨h를제외하고는각레벨의i에2i-1개의노드존재 9 7 6 5 4 2 3 3 2 1 1 2 3 4 5 6 7 8 9 10 깊이노드의개수 1 1=20 2 1=21 3 1=22 4 1=23
  • 77. 히프연산 Upheap알고리즘 Downheap알고리즘 insert_max_heap(A, key) heap_size← heap_size+ 1; i ← heap_size; A[i] ← key; while i ≠ 1 and A[i] > A[PARENT(i)] do A[i] ↔ A[PARENT]; i ← PARENT(i); delete_max_heap(A) item ← A[1]; A[1] ← A[heap_size]; heap_size←heap_size-1; i ← 2; while i ≤ heap_sizedo if i < heap_sizeand A[LEFT(i)] > A[RIGHT(i)] then largest ← LEFT(i); else largest ← RIGHT(i); if A[PARENT(largest)] > A[largest] then break; A[PARENT(largest)] ↔ A[largest]; i ← CHILD(largest); return item;
  • 78. 히프에서의삽입 upheap연산 새로운키k의삽입연산후히프의성질이만족되지않을수있다 upheap는삽입된노드로부터루트까지의경로에있는노드들을k와비교, 교환함으로써히프의성질을복원한다. 키k가부모노드보다작거나같으면upheap는종료한다 히프의높이가O(logn)이므로upheap연산은O(logn)이다. 9 7 6 5 4 2 3 3 2 1 8 9 7 6 5 8 2 3 3 2 1 4 9 8 6 5 7 2 3 3 2 1 4
  • 79. 히프에서의삭제 downheap연산 최대히프에서의삭제는가장큰키값을가진노드를삭제하는것을의미 따라서루트노드가삭제된다 루트노드를삭제한다 마지막노드를루트노드로이동한다. 루트에서부터단말노드까지의경로에있는노드들을교환하여히프성질을만족시킨다. 9 7 6 5 4 2 3 3 2 1 3 7 6 5 4 2 3 2 1 7 3 6 5 4 2 3 2 1 7 5 6 3 4 2 3 2 1
  • 80. 히프정렬(Heap Sort) 히프정렬 히프정렬이유용한경우는전체자료를정렬이아니라가장큰값몇개만필요할때이다. 먼저정렬해야할n개의요소들을최대히프에삽입 한번에하나씩요소를히프에서삭제하여저장하면된다. 삭제되는요소들은값이증가되는순서(최소히프의경우) 요소를히프에삽입/삭제할때시간:O(logn) 요소의개수가n개이므로전체적으로O(nlogn)시간이걸린다. // 우선순위큐인히프를이용한정렬 void heap_sort(element a[], int n) { int i; HeapTypeh; init(&h); for(i=0;i<n;i++){ insert_max_heap(&h, a[i]); } for(i=(n-1);i>=0;i--){ a[i] = delete_max_heap(&h); } }
  • 81. 최대히프의시간복잡도 최대히프는완전이진트리 노드의수가n 일때높이는log n 추가: log n 최악의경우단말로부터루트까지삽입될위치탐색 삭제: log n 최악의경우루트부터단말까지교환이일어남
  • 83. 정렬(Sort) 정렬(Sort) 물건을크기순으로오름차순이나내림차순으로나열하는것 컴퓨터공학분야에서가장기본적이고중요한알고리즘중의하나 가장많이, 그리고가장잘알려진알고리즘 자료탐색에있어서필수적이다. 정렬의단위 정렬의기준: 정렬키(Sort Key) 필드 이름 학번 주소 연락처 학생들의레코드 필드 필드 필드 필드 키(key) 레코드
  • 84. 정렬알고리즘 많은정렬알고리즘존재 단순하지만비효율적인방법 삽입정렬, 선택정렬, 버블정렬… 복잡하지만효율적인방법 퀵정렬, 히프정렬, 합병정렬, 기수정렬… 모든경우에최적인알고리즘은없다 응용에맞추어선택 정렬알고리즘의평가 비교횟수 이동횟수 정렬의분류 내부정렬과외부정렬 안정정렬(Stable Sorting)과불안정정렬(Unstable Sorting) 직접정렬(Direct Sorting)과간접정렬(Indirect Sorting)
  • 85. 선택정렬(Selection Sort) 선택정렬 정렬이안된숫자들중에서최소값을선택하여배열의첫번째요소와교환 선택정렬분석 숫자의개수n 최소값을선택하는데걸리는시간:0(n) 전체시간복잡도: 0(n2) 안정성을만족하지않는다 selection_sort(A, n) for i←0 to n-2 do least ← A[i], A[i+1],..., A[n-1] 중에서가장작은값의인덱스; A[i]와A[least]의교환; i++; 5 3 8 1 2 7 1 3 8 5 2 7 1 2 8 5 3 7 1 2 3 5 8 7 1 2 3 5 8 7 1 2 3 5 7 8 1 2 3 5 7 8
  • 86. 버블정렬(Bubble Sort) 버블정렬 인접한레코드가순서대로되어있지않으면교환 전체가정렬될때까지비교/교환계속 BubbleSort(A, n) for i←n-1 to 1 do for j←0 to i-1 do j와j+1번째의요소가크기순이아니면교환 j++; i--; #define SWAP(x, y, t) ( (t)=(x), (x)=(y), (y)=(t) ) void bubble_sort(int list[], int n) { int i, j, temp; for(i=n-1; i>0; i--){ for(j=0; j<i; j++) /* 앞뒤의레코드를비교한후교체*/ if(list[j]>list[j+1]) SWAP(list[j], list[j+1], temp); } }
  • 87. 버블정렬(Bubble Sort) 버블정렬분석 비교횟수 버블정렬의비교횟수는최상, 평균, 최악의경우에도항상일정: 이동횟수(평균: O(n2)) 최악: 역순정렬= 3* 비교 최상: 이미정렬= 0 5 3 8 1 2 7 3 5 1 2 7 8 3 1 2 5 7 8 1 2 3 4 5 8 1 2 3 5 7 8 1 2 3 5 7 8 1 2 3 5 7 8 초기상태 정렬완료 스캔1 스캔2 스캔3 스캔4 스캔5 풊=ퟏ 풏−ퟏ풏(풏−ퟏ) ퟐ =푶(풏ퟐ) 5 3 8 1 2 7 3 5 8 1 2 7 3 5 8 1 2 7 3 5 8 1 2 7 3 5 1 8 2 7 3 5 1 2 8 7 3 5 1 2 7 8 초기상태 스캔완료 5와3교환 교환없음 8과1을교환 8과2를교환 8과7을교환
  • 88. 삽입정렬(Insertion Sort) 삽입정렬 정렬되어있는부분에새로운레코드를적절한위치에삽입하는과정을반복 많은이동레코드가클경우불리 안정된정렬방법(이미정렬되어있으면효율적) 1 3 5 8 2 7 정렬된부분 미정렬부분 Algorithm InsertionSort(list, n): Input: n개의정수를저장하고있는배열list Output: 정렬된배열list for i←1 to n-1 do { 정렬된리스트에서list[i]보다더큰요소들이동; list[i]를정렬된리스트의적절한위치에삽입; i++; }
  • 89. 삽입정렬(Insertion Sort) 삽입정렬의복잡도 비교: n-1 이동: 2(n-1) 최악의경우: 역순으로정렬 비교: 이동 // 삽입정렬 void insertion_sort(int list[], int n) { int i, j, key; for(i=1; i<n; i++){ key = list[i]; for(j=i-1; j>=0 && list[j]>key; j--) list[j+1] = list[j]; /* 레코드의오른쪽이동*/ list[j+1] = key; } } 풊=ퟏ 풏−ퟏ풏(풏−ퟏ) ퟐ =푶(풏ퟐ) 풏(풏−ퟏ) ퟐ +ퟐ(풏−ퟏ)=푶(풏ퟐ) 5 3 8 1 2 7 5 3 8 1 2 7 3 5 8 1 2 7 3 5 8 1 2 7 1 3 5 8 2 7 1 2 3 5 8 7 1 2 3 5 7 8 초기상태 정렬완료 3을삽입 8은이미제자리에 1을삽입 2를삽입 7을삽입
  • 90. 분할정복(Divide and Conquer) 분할정복 문제를작은2개의문제로분리하고각각을해결한다음, 결과를모아서원래의문제를해결하는전략 분리된문제가아직도해결하기어렵다면, 즉충분히작지않다면분할정복방법을다시적용 재귀호출을이용하여구현 1. 분할(Divide): 배열을같은크기의2개의부분배열로분할한다. 2. 정복(Conquer): 부분배열을정렬한다. 부분배열의크기가충분히작지않으면재귀호출을이용하여 다시분할정복기법을적용한다. 3. 결합(Combine): 정렬된부분배열을하나의배열에통합한다. 입력파일: 27 10 12 20 25 13 15 22 1.분할(Divide): 배열을27 10 12 20 과25 13 15 22의2개의부분배열로분리 2.정복(Conquer): 부분배열을정렬하여10 12 20 27 과13 15 22 25를얻는다. 3.결합(Combine): 부분배열을통합하여10 12 13 15 20 22 25 27을얻는다.
  • 91. 퀵정렬(Quick Sort) 퀵정렬 평균적으로가장빠른정렬방법: 분할정복사용 퀵정렬은전체리스트를2개의부분리스트로분할하고, 각각의부분리스트를다시퀵정렬로정렬 5 3 8 4 9 1 6 2 7 5 3 8 4 9 1 6 2 7 피봇 피봇보다작은값 피봇보다큰값 void quick_sort(int list[], int left, int right) { if(left<right){ int q=partition(list, left, right); quick_sort(list, left, q-1); quick_sort(list, q+1, right); } } 3 정렬할범위가2개이상의데이터이면 4 partition 함수를호출하여피벗을기준으로2개의리스트로분할한다. partition 함수의반환값은피봇의위치가된다. 5 left에서피봇위치바로앞까지를대상으로순환호출한다 (피봇은제외된다). 6 피봇위치바로다음부터right까지를대상으로순환호출한다 (피봇은제외된다).
  • 92. 퀵정렬(Quick Sort) Partition 함수 피봇(pivot): 가장왼쪽숫자라고가정 두개의변수low와high를사용 low는피봇보다작으면통과, 크면정지 high는피봇보다크면통과, 작으면정지 정지된위치의숫자를교환: low와high가교차하면종료 5 3 8 4 9 1 6 2 7 5 3 8 4 9 1 6 2 7 5 3 2 4 9 1 6 8 7 5 3 2 4 9 1 6 8 7 5 3 2 4 1 9 6 8 7 5 3 2 4 1 9 6 8 7 5 3 2 4 1 9 6 8 7 1 3 2 4 5 9 6 8 7 Left Right 피봇 Low Low Low Low Low Low Low Low High High High High High High High High Stop
  • 93. 퀵정렬(Quick Sort) Partition 함수의구현 int partition(int list[], int left, int right) { int pivot, temp; int low,high; low = left; high = right+1; pivot = list[left]; do { do low++; while(low<=right &&list[low]<pivot); do high--; while(high>=left && list[high]>pivot); if(low<high) SWAP(list[low], list[high], temp); } while(low<high); SWAP(list[left], list[high], temp); return high; }
  • 94. 퀵정렬(Quick Sort) 퀵정렬과정 5 3 8 4 9 1 6 2 7 1 3 2 4 5 9 6 8 7 1 3 2 4 5 7 6 8 9 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 피봇
  • 95. 퀵정렬(Quick Sort) 퀵정렬분석 최상의경우: 거의균등한리스트로분할되는경우 패스수: Log2n 각패스안에서비교횟수: n 총비교횟수: n Log2n 총이동횟수: 비교횟수에비하여무시가능 최악의경우: 불균등한리스트로분할되는경우 패스수: n 각패스안에서의비교횟수: n 총비교횟수: n2 총이동횟수: 비교횟수에비하여무시가능 (1 23456789) 1 (23456789) 12 (3456789) 123 (456789) 1234 (56789) ... 123456789 …
  • 96. 합병정렬(Merge Sort) 합병정렬 리스트를두개로나누어, 각각을정렬한다음, 다시하나로합치는방법 분할정복기법에바탕 27 10 12 20 25 13 15 22 10 12 13 15 20 22 25 27 27 10 12 20 25 13 15 22 10 12 20 27 13 15 22 25 27 10 25 13 10 27 13 25 12 20 15 22 12 20 15 22 27 25 12 15 10 13 20 22
  • 97. 합병정렬(Merge Sort) 합병정렬 합병정렬에서실제로정렬이이루어지는시점은2개의리스트를합병하는단계 merge_sort(list, left, right) if left < right mid = (left+right)/2; merge_sort(list, left, mid); merge_sort(list, mid+1, right); merge(list, left, mid, right); 1 1 2 1 2 3 1 2 3 4 1 2 3 4 5 1 2 3 4 5 6 1 2 3 4 5 6 7 8 2 5 7 8 2 5 7 8 5 7 8 5 7 8 5 7 8 7 8 7 8 1 3 4 6 3 4 6 3 4 6 4 6 6 6 배열A 배열B 배열C
  • 98. 합병정렬(Merge Sort) 합병알고리즘 merge(list, left, mid, last): // 2개의인접한배열list[left..mid]와list[mid+1..right]를합병 b1←left; e1←mid; b2←mid+1; e2←right; sorted 배열을생성; index←0; while b1≤e1 and b2≤e2 do if(list[b1]<list[b2]) then sorted[index]←list[b1]; b1++; index++; else sorted[index]←list[b2]; b2++; index++; 요소가남아있는부분배열을sorted로복사한다; sorted를list로복사한다; 2 5 7 8 1 3 4 6 Low i j Mid+1 High Mid list 1 2 3 4 sorted i 정렬된리스트 정렬된리스트
  • 99. 합병정렬(Merge Sort) 합병정렬분석 비교횟수 합병정렬은크기n인리스트를정확히균등분배하므로퀵정렬의이상적인경우와마찬가지로정확히logn개의패스를가진다. 각패스에서리스트의모든레코드n개를비교하여합병하므로n 번의비교연산이수행된다. 따라서합병정렬은최적, 평균, 최악의경우모두큰차이없이nlogn번의비교를수행하므로O(nlogn)의복잡도를가지는알고리즘이다. 합병정렬은안정적이며데이터의초기분산순서에영향을덜받는다. 이동횟수 배열을이용하는합병정렬은레코드의이동이각패스에서2n번발생하므로전체레코드의이동은2nlogn번발생한다. 이는레코드의크기가큰경우에는매우큰시간적낭비를초래한다. 그러나레코드를연결리스트로구성하여합병정렬할경우, 링크인덱스만변경되므로데이터의이동은무시할수있을정도로작아진다. 따라서크기가큰레코드를정렬할경우, 연결리스트를이용하는합병정렬은퀵정렬을포함한다른어떤정렬방법보다매우효율적이다
  • 100. 기수정렬(Radix Sort) 기수정렬 레코드를비교하지않고정렬 단순히자리수에따라버킷에넣었다가꺼내면정렬됨 O(nlogn)이라는이론적인하한선을깰수있는유일한방법 기수정렬은O(kn) 의시간복잡도를가지는데대부분k<4 이하 정렬할수있는레코드의타입이한정 레코드의키들이동일한길이를가지는숫자나문자열로구성 0 1 2 3 4 5 6 7 8 9 8 2 7 3 5 2 3 5 7 8 2 3 5 7 8
  • 101. 기수정렬(Radix Sort) Sorting a sequence of 4-bit integers 1001 0010 1101 0001 1110 0010 1110 1001 1101 0001 1001 1101 0001 0010 1110 1001 0001 0010 1101 1110 0001 0010 1001 1101 1110
  • 102. 기수정렬(Radix Sort) 기수정렬 버킷은큐로구현 버킷의개수는키의표현방법과밀접한관계 이진법을사용한다면버킷은2개. 알파벳문자를사용한다면버킷은26개 십진법을사용한다면버킷은10개 32비트의정수의경우, 8비트씩나누어기수정렬의개념을적용한다면 필요한버킷수는256개, 대신에필요한패스의수는4개로십진수표현보다줄어듬. RadixSort(list, n): for d←LSD의위치to MSD의위치do { d번째자릿수에따라0번부터9번버킷에집어놓는다. 버킷에서숫자들을순차적으로읽어서하나의리스트로합친다. d++; }
  • 103. 계수정렬(Counting Sort) 계수정렬 선형시간에정렬하는효율적인알고리즘 항목의순서를결정하기위해집합에각항목이몇개씩있는지세는작업한다 속도가빠르고안정적이다. 입력키가어떤범위에한정될때사용가능 예) 입력이0부터K사이의정수 카운트를위한충분한공간을할당하려면집합내의가장큰수를알아야한다. 0 3 2 1 3 2 1 2 2 3 Input A: N=10 M=4 Count array C: -all elements in input between 0 and 3 1 2 4 3 0 1 2 3 3 3 3 2 2 2 2 1 1 0 10 9 8 7 6 5 4 3 2 1 Output sorted array:
  • 104. 정렬알고리즘의비교 알고리즘 최선 평균 최악 삽입정렬 O(n) O(n2) O(n2) 선택정렬 O(n2) O(n2) O(n2) 버블정렬 O(n2) O(n2) O(n2) 쉘정렬 O(n) O(n1.5) O(n1.5) 퀵정렬 O(nLog2n) O(nLog2n) O(n2) 히프정렬 O(nLog2n) O(nLog2n) O(nLog2n) 합병정렬 O(nLog2n) O(nLog2n) O(nLog2n) 기수정렬 O(dn) O(dn) O(dn)
  • 106. 트리개념 트리(Tree) 계층적인구조(Hierarchical Structure)를나타내는자료구조 트리는부모(Parent)-자식(Child) 관계로이루어진다. 응용분야 계층적인조직표현, 파일시스템등 트리에서나타나는용어들 일반트리와이진트리 노드(node): 트리의구성요소 루트(root): 부모가없는노드(A) 서브트리(subtree): 하나의노드와그노드들의자손들로이루어진트리 단말노드(terminal node): 자식이없는노드(A,B,C,D) 비단말노드: 적어도하나의자식을가지는노드(E,F,G,H,I,J) 자식, 부모, 형제, 조상, 자손노드: 인간과동일 레벨(level): 트리의각층의번호 높이(height): 트리의최대레벨(3) 차수(degree): 노드가가지고있는자식노드의개수 A B C D E F G H I J Level 1 Level 2 Level 3 데이터 데이터 링크1 링크1 … 링크2 링크N 이진트리 일반트리
  • 107. 이진트리정의 모든노드가2개의서브트리를가지고있는트리 서브트리는공집합일수있다. 이진트리의노드에는최대2개까지의자식노드가존재 모든노드의차수가2 이하가된다. 구현이편리 이진트리에는서브트리간순서가존재 이진트리(Binary Tree) Tleft Tright
  • 108. 이진트리의분류 포화이진트리(Full binary tree) 트리의각레벨에노드가다차있는이진트리 전체노드개수= 푖=0 푘−12푖= 2k-1 노드의번호는레벨단위로왼쪽에서오른쪽으로 완전이진트리(Complete binary tree) 높이가h일때레벨1부터모두노가채워져있고, 마지막레벨h에서는왼쪽부터오른쪽으로노드가순서대로채워져있는경우 A B C D E F G A B C D E F G H I J A B C D F G I 포화이진트리 완전이진트리 기타이진트리
  • 109. 이진트리의특성 노드개수가n이면간선의개수는n-1 높이가h인이진트리의경우, 최소h개의노드를가지며, 최대2h-1개의노드를가진다. + * / a b c d 노드의개수: 7 간선의개수: 6 1 2 3 높이=3 + * / a b c d 21-1=1 22-1=2 23-1=4 최대노드개수=3 최대노드개수= 21-1 + 22-1+ 23-1= 1+2+4 =7
  • 111. 이진트리의표현 배열표현법 모든이진트리를포화이진로가정하고각노드에번호를붙여서그번호를배열의인덱스로삼아노드의데이터를배열에저장하는방법 링크표현법 포인터를이용하여부모노드가자식노드를가리키도록하는방법 A B C D E F 1 3 2 4 6 5 A B C D E F 0 1 2 3 4 5 6 7 8 A B C D A B C D 0 1 2 3 4 5 6 7 8 데이터 B A C n E n n D n n F n n= null A n B n C n n D n
  • 112. L V R 이진트리의순회 순회(Traversal) : 트리의노드를체계적으로방문하는것 전위순회(Preorder traversal) : VLR 자손노드보다루트노드를먼저방문한다. 중위순회(Inordertraversal) : LVR 왼쪽자손, 루트, 오른쪽자손순으로방문한다. 후위순회(Postordertraversal) : LRV 루트노드보다자손을먼저방문한다. 전체트리나서브트리나 구조는동일하다. !!!
  • 113. 전위순회 루트를먼저방문하는순회방법 알고리즘설명 1. 노드X가NULL이면더이상순환호출을하지않는다. 2. X의데이터를출력한다. 3. X의왼쪽서브트리를순환호출하여방문한다. 4. X의오른쪽서브트리를순환호출하여방문한다. ③ ① ② // 전위순회 preorder( TreeNode*root ){ if ( root ){ printf("%d", root->data );// 노드방문 preorder( root->left );// 왼쪽서브트리순회 preorder( root->right );// 오른쪽서브트리순회 } } 1 2 3 4 5 6 7 1 2 4 5 3 6 7
  • 114. 중위순회 왼쪽서브트리루트오른쪽서브트리순으로방문 알고리즘설명 1. 노드X가NULL이면더이상순환호출을하지않는다. 2. X의왼쪽서브트리를순환호출하여방문한다. 3. X의데이터를출력한다. 4. X의오른쪽서브트리를순환호출하여방문한다. // 중위순회 inorder( TreeNode*root ){ if ( root ){ inorder( root->left );// 왼쪽서브트리순회 printf("%d", root->data );// 노드방문 inorder( root->right );// 오른쪽서브트리순회 } } 1 2 3 4 5 6 7 4 2 5 1 6 3 7 ③ ① ②
  • 115. 후위순회 왼쪽서브트리오른쪽서브트리루트 알고리즘설명 1. 노드X가NULL이면더이상순환호출을하지않는다. 2. X의왼쪽서브트리를순환호출하여방문한다. 3. X의오른쪽서브트리를호출하여방문한다. 4. X의데이터를출력한다. // 후위순회 postorder( TreeNode*root ){ if ( root ){ postorder( root->left );// 왼쪽서브트리순회 postorder( root->right );// 오른쪽서브트리순회 printf("%d", root->data ); // 노드방문 } } 1 2 3 4 5 6 7 4 5 2 6 7 3 1 ③ ① ②
  • 116. 수식트리 수식트리: 산술식을트리형태로표현한것 비단말노드: 연산자(Operator) 단말노드: 피연산자(Operand) or < < a b c d - a x b c + a b (a) (b) (c) 수식 a + b a -(b ×c) (a < b) or (c < d) 전위순회 + a b -a ×b c or < a b < c d 중위순회 a + b a -b ×c a < b or c < d 후위순회 a b + a b c ×- a b < c d < or 어떤순회방법이적합한가?
  • 117. 수식트리계산 후위순회를사용 서브트리의값을순환호출로계산 비단말노드를방문할때양쪽서브트리의값을저장된연산자를이용하여계산한다. 알고리즘설명 1. 수식트리가공백이면 2. 그냥복귀 3. 그렇지않으면왼쪽서브트리를계산하기위한evaluate를순환호출. 이때파라미터는왼쪽자신노드가된다. 4. 같은방식으로오른쪽서브트리를계산한다. 5. 루트노드의데이터필드에서연산자를추출한다. 6. 추출된연산자를가지고연산을수행해서반환한다. + * / 3 2 5 6 evaluate(exp) if exp= NULL then return 0; else x←evaluate(exp->left); y←evaluate(exp->right); op←exp->data; return (x op y);
  • 118. 이진트리연산 노드개수구하기 탐색트리안의노드의개수를계산 각각의서브트리에대하여순환호출한다음반환되는값에1을더하여반환 높이구하기 서브트리에대하여순환호출하고, 서브트리들의반환값중최대값을구한다 int get_node_count(TreeNode*node) { int count=0; if( node != NULL ) count = 1 + get_node_count(node->left) + get_node_count(node->right); return count; } 6 3 2 1 1 1 int get_height(TreeNode*node) { int height=0; if( node != NULL ) height = 1 + max(get_height(node->left), get_height(node->right)); return height; } Hleft Hright 높이=3 H=1+ max(Hleft,Hright)
  • 119. 이진탐색트리(Binary search tree) 이진탐색트리 탐색작업을효율적으로하기위한자료 Key(왼쪽서브트리) ≤Key(루트노드) ≤Key(오른쪽서브트리) 이진탐색을중위순회하면오름차순으로정렬된값을얻을수있다. 18 7 26 3 12 27 31 왼쪽서브트리 오른쪽서브트리 루트보다 작은값 루트보다 큰값
  • 120. 이진트리: 탐색연산 탐색연산 비교한결과가같으면탐색이성공적으로끝남 비교한결과, 주어진키값이루트노드의키값보다작으면탐색은이루트노드의왼쪽자식을 기준으로다시시작 주어진키값이루트노드의키값보다크면탐색은이루트노드의오른쪽자식을 기준으로다시시작 search(x, k) if x=NULL then return NULL; if k=x->key then return x; else if k<x->key then return search(x->left, k); else return search(x->right, k); 18 7 26 3 12 27 31 12 탐색
  • 121. 이진트리: 삽입연산 삽입연산 삽입을위해서는먼저탐색을수행하는것이필요 탐색에실패한위치가바로새로운노드를삽입하는위치 insert_node(T,z) p←NULL; //p : 부모노드포인터 t←root; // t: 탐색포인터 while t≠NULLdo p←t; if z->key < p->key then t←p->left; else t←p->right; if p=NULL then root←z;// 트리가비어있음 else if z->key < p->key then p->left←z else p->right←z 18 7 26 3 12 27 31 9탐색 NULL 18 7 26 3 12 27 31 9삽입 9
  • 122. 이진트리: 삭제연산 삭제연산Case Case 1 : 삭제하려는노드가단말노드일경우 Case 2 : 삭제하려는노드가하나의서브트리만가지고있는경우 Case 3 : 삭제하려는노드가두개의서브트리모두가지고있는경우 Case 1 : 삭제하려는노드가단말노드일경우 단말노드의부모노드를찾아서연결을끊는다. 35 18 68 7 26 99 3 12 22 30 35 18 68 7 26 99 3 12 22
  • 123. 이진트리: 삭제연산 Case 2 : 삭제하려는노드가하나의서브트리만가지고있는경우 해당노드를삭제하고서브트리는부모노드에붙여준다. Case 3 : 삭제하려는노드가두개의서브트리모두가지고있는경우] 삭제노드와가장비슷한값을가진노드를삭제노드위치로가져온다 35 18 68 7 26 99 3 12 22 30 35 18 99 7 26 3 12 22 30 35 18 68 7 26 99 3 12 22 30 왼쪽서브트리에서 제일큰값 왼쪽서브트리에서 제일큰값 35 18 68 7 26 99 3 12 22 30 35 22 68 7 26 99 3 12 30
  • 124. 이진트리: 성능 이진탐색트리연산성능 탐색, 삽입, 삭제연산의시간복잡도는트리의높이h에비례한다 최선의경우 이진트리가균형적으로생성되어있는경우 h= log2n 최악의경우 한쪽으로치우친경사이진트리의경우 h= n-1 순차탐색과시간복잡도가같다. 1 2 3 높이=n-1 1 2 3 4 5 6 7 높이=log2n
  • 126. 그래프(Graph) 연결되어있는객체간의관계를표현하는자료구조 예: 전기회로, 프로젝트관리, 지도에서도시의연결 그래프이론(Graph Theory) 그래프를문제해결의도구로이용하는연구분야 그래프역사 1800년대Euler에의해창안 그래프(Graph) S B K J I D D A C B b a d c g e f 위치: 정점(node) 다리: 간선(edge)
  • 127. 그래프용어 그래프는(V, E)로표시된다. V는정점(Vertices)들의집합 E는간선(Edge)들의집합 정점과간선은모두관련되는데이터를가질수있다. 무방향간선(Undirected Graph) : (A, B) = (B, A) 방향간선(Directed Graph) : <A, B> ≠<B, A> 가중치그래프(Weighted Graph), 네트워크(Network) : 간선에비용이나가중치가할당 S B K J I D 9 9 7 6 5 5 5 1 가중치표시 S B K J I D 방향표시 S B K J I D 방향/가중치표시 9 9 7 6 5 5 5 1 A B A B A B 1200
  • 128. 그래프표현의예 표현의예 V(G1) ={0,1,2,3} E(G1) = {(0,1), (0,2), (0,3), (1,2), (2,3)} V(G2) ={0,1,2,3} E(G2) = {(0,1), (0,2)} V(G3) ={0,1,2} E(G3) = {<0,1>, <1,0>, <1,2>} 용어 인접정점(Adjacent vertex) : 간선에의해연결된정점 차수(Degree) : 정점에연결된다른정점의개수 경로(Path)는정점의나열로표현 단순경로: 0,1,2,3 사이클(Cycle) : 0,1,2,0 경로의길이: 경로를구성하는사용된간선의수 0 3 1 2 0 3 1 2 0 1 2 G1 G2 G3 완전그래프란모든정점이연결되어있는그래프를말한다.
  • 129. 그래프의표현 그래프를표현하는2가지방법 인접행렬(Adjacent Matrix) : 2차원배열사용표현 인접리스트(Adjacency List) : 연결리스트를사용표현 인접행렬방법 If (간선(i, j)가그래프1에존재) M[i][j] = 1, 그렇지않으면M[i][j] = 0 0 3 1 2 0 3 1 2 0 1 2 G1 G2 G3 0 1 1 1 1 0 1 0 1 1 0 1 1 0 1 0 0 1 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 1 0 1 0 1 0 0 0
  • 130. 그래프의표현 인접리스트방법 각정점에인접한정점들을연결리스트로표현 0 3 1 2 G1 0 3 1 2 G2 0 1 2 G3 n n 1 2 3 4 1 2 3 4 1 2 3 1 2 3 n 0 2 n 0 1 3 n 0 2 n 1 2 n 0 2 n 1 n 0 n 0 n 가중치가있는경우는어떻게표현?
  • 131. 그래프탐색 탐색 가장기본적인연산으로 하나의정점으로부터시작하여차례대로모든정점을한번씩방문 많은문제의경우단순히그래프의노드를탐색하는것으로해결된다. 예) 특정정점에서다른정점으로갈수있는지없는지, 회로의연결등 그래프탐색의2가지방법 깊이우선탐색(DFS : Depth-First Search) 너비우선탐색(BFS : Breadth-First Search) 1 2 3 4 5 7 6 BFS 1 2 3 4 5 7 6 DFS
  • 132. 깊이우선탐색 깊이우선탐색 depth_first_search(v) v를방문되었다고표시; for all u ∈ (v에인접한정점) do if (u가아직방문되지않았으면) then depth_first_search(u) 0 1 3 4 2 0 1 3 4 2 0 1 3 4 2 0 1 3 4 2 0 1 3 4 2 0 1 3 4 2 0 1 3 4 2 0 1 3 4 2 (a) (c) (d) (b) (e) (g) (h) (f)
  • 133. 깊이우선탐색 깊이우선탐색의구현 순환호출이용 명시적인스택이용 // 인접행렬로표현된그래프에대한깊이우선탐색 void dfs_mat(GraphType*g, int v) { int w; visited[v] = TRUE;// 정점v의방문표시 printf("%d ", v);// 방문한정점출력 for(w=0; w<g->n; w++) // 인접정점탐색 if( g->adj_mat[v][w] && !visited[w] ) dfs_mat(g, w);//정점w에서DFS 새로시작 } // 인접리스트로표현된그래프에대한깊이우선탐색 void dfs_list(GraphType*g, int v) { GraphNode*w; visited[v] = TRUE; // 정점v의방문표시 printf("%d ", v); // 방문한정점출력 for(w=g->adj_list[v]; w; w=w->link)// 인접정점탐색 if(!visited[w->vertex]) dfs_list(g, w->vertex); //정점w에서DFS 새로시작 }
  • 134. 너비우선탐색 너비우선탐색 breadth_first_search(v) v를방문되었다고표시; 큐Q에정점v를삽입; while (not is_empty(Q)) do Q에서정점W를삭제; for all u ∈ (v에인접한정점) do if ( u가아직방문되지않았으면) then u를큐에삽입하고, u를방문되었다고표시; 0 1 3 4 2 0 1 3 4 2 0 1 3 4 2 0 1 3 4 2 0 1 3 4 2 0 1 3 4 2 0 1 3 4 2 (c) (d) (b) (e) (g) (h) (f) 0 1 3 4 2 (a) 1 1 2 1 2 4 2 4 4 3 3 1 2 4
  • 135. 너비우선탐색 너비우선탐색의구현 인접행렬이용 void bfs_mat(GraphType*g, int v) { int w; QueueTypeq; init(&q); // 큐초기화 visited[v] = TRUE; // 정점v 방문표시 printf("%d ", v); enqueue(&q, v);// 시작정점을큐에저장 while(!is_empty(&q)){ v = dequeue(&q);// 큐에정점추출 for(w=0; w<g->n; w++)// 인접정점탐색 if(g->adj_mat[v][w] && !visited[w]){ visited[w] = TRUE; // 방문표시 printf("%d ", w); enqueue(&q, w); // 방문한정점을큐에저장 } } }
  • 136. 연결성분찾기 연결성분 최대로연결된부분그래프로연결된부분그래프중크기가최대인것 그래프탐색으로구할수있다. visited[i] = count; 0 3 1 4 2 visited 1 1 2 2 1 0 1 2 3 4 void find_connected_component(GraphType*g) { int i; count = 0; for(i=0; i<g->n; i++) if(!visited[i]){ // 방문되지않았으면 count++; dfs_mat(g, i); } }
  • 137. 신장트리(Spanning Tree) 신장트리(Spanning Tree) 그래프내의모든정점을포함하는트리 신장트리는모든정점들이연결되어있어야하고, 사이클을포함해서는안됨 용도: 최소의링크를사용하여네트워크구축시, … 0 1 3 4 2 연결그래프 0 1 3 4 2 0 1 3 4 2 0 1 3 4 2 신장트리중일부 depth_first_search(v) v를방문되었다고표시; for all u ∈ (v에인접한정점) do if (u가아직방문되지않았으면) then (v,u)를신장트리간선이라고표시; depth_first_search(u)
  • 138. 최소비용신장트리(MTS) 최소비용신장트리(MST) Minimum spanning tree 네트워크에있는모든정점들을가장적은간선과비용으로연결하는신장트리 MST 응용 도로건설–도시를모두연결하면서도로의길이가최소가되도록하는문제 전기회로–단자를모두연결하면서전선의길이가가장최소가되도록… 통신–전화선의길이가최소가되도록전화망을구성하는문제 배관–파이프를모두연결하면서파이프의총길이가최소가되도록연결 10 12 18 14 22 4 5 3 6 17 15
  • 139. MST 알고리즘 2가지대표적인알고리즘 Kruskal의알고리즘 Prim의알고리즘 탐욕적인방법(Greedy Method) 알고리즘설계에서있어서중요한기법중의하나 결정을해야할때마다그순간에가장좋다고생각되는것을해답으로선택함으로써최종적인해답에도달 탐욕적인방법은항상최적의해답을주는지를반드시검증해야한다. Kruskal의알고리즘은최적의해답을주는것으로증명
  • 140. Prim의MST 알고리즘 Prim의MST 알고리즘 시작정점에서부터출발하여신장트리집합을단계적으로확장해나가는방법 시작단계에서는시작정점만이신장트리집합에포함 앞단계에서만들어진신장트리집합에, 인접한정점들중에서최저간선으로연결된정점을선택하여트리를확장 이과정은트리가n-1개의간선을가질때까지계속된다 간선(a,b)와간선(f,e)의가중치를비교해보면, (f,e)가27로서(a,b)의29보다낮다 따라서(f,e)간선이선택되고, 정점e가신장트리집합에포함된다. f a b g e c d 10 29 15 25 27 22 12 16 8
  • 141. Prim의MST 알고리즘 f a b g e c d 10 29 15 25 27 22 12 16 8 f a b g e c d 10 29 15 25 27 22 12 16 8 f a b g e c d 10 29 15 25 27 22 12 16 8 f a b g e c d 10 29 15 25 27 22 12 16 8 f a b g e c d 10 29 15 25 27 22 12 16 8 f a b g e c d 10 29 15 25 27 22 12 16 8 f a b g e c d 10 29 15 25 27 22 12 16 8
  • 142. Kruskal의MST 알고리즘 Kruskal의MST 알고리즘 최소비용신장트리가최소비용의간선으로구성됨과동시에사이클을포함하지않는다는조건에근거 각단계에서사이클을이루지않는최소비용간선을선택 그래프의간선들을가중치의오름차순으로정렬한다 정렬된간선들의리스트에서사이클을형성하지않는간선을찾아서현재의최소비용신장트리의집합에추가 사이클을형성하면그간선은제외
  • 143. Kruskal의MST 알고리즘 f a b g e c d 10 29 15 25 27 22 12 16 8 f a b g e c d 10 29 15 25 27 22 12 16 8 f a b g e c d 10 29 15 25 27 22 12 16 8 f a b g e c d 10 29 15 25 27 22 12 16 8 f a b g e c d 10 29 15 25 27 22 12 16 8 f a b g e c d 10 29 15 25 27 22 12 16 8 f a b g e c d 10 29 15 25 27 22 12 16 8 f a b g e c d 10 29 15 25 27 22 12 16 8 10 12 15 16 18 22 25 27 29 afcd bgbcdg de egefab 10 12 15 16 18 22 25 27 29 afcd bgbcdg de egefab 10 12 15 16 18 22 25 27 29 afcd bgbcdg de egefab 10 12 15 16 18 22 25 27 29 afcd bgbcdg de egefab 10 12 15 16 18 22 25 27 29 afcd bgbcdg de egefab 10 12 15 16 18 22 25 27 29 afcd bgbcdg de egefab 10 12 15 16 18 22 25 27 29 afcd bgbcdg de egefab 10 12 15 16 18 22 25 27 29 afcd bgbcdg de egefab x x
  • 144. kruskal의MST 알고리즘의구현 union-find 알고리즘 집합들의합집합을구하고집합의원소가어떤집합에속하는지를계산하는알고리즘 여러가지방법으로구현이가능하다 Kruskal의MST 알고리즘에서사이클검사에사용된다. a와b가같은집합에속함 a와b가다른집합에속함 사이클형성 사이클이형성되지않음
  • 145. 최단경로알고리즘 최단경로(Shortest Path) 네트워크에서정점i와정점j를연결하는경로중에서간선들의가중치합이최소가되는경로를찾는문제 간선의가중치는비용, 거리, 시간등을나타낸다. 4 0 5 1 6 2 3 3 10 7 2 5 4 11 10 6 9 2 4 0 7 ∞ ∞ 3 10 ∞ 7 0 4 10 2 6 ∞ ∞ 4 0 2 ∞ ∞ ∞ ∞ 10 2 0 11 9 4 3 2 ∞ 11 0 ∞ 5 10 6 ∞ 9 ∞ 0 ∞ ∞ ∞ ∞ 4 5 ∞ 0 0 1 2 3 4 5 6 0 1 2 3 4 5 6
  • 146. Dijkstra의최단경로알고리즘 Dijkstra의최단경로알고리즘 네트워크에서하나의시작정점으로부터모든다른정점까지의최단경로를찾는알고리즘 집합S: 시작정점v로부터의최단경로가이미발견된정점들의집합 distance 배열: 최단경로를알려진정점만을통하여각정점까지가는최단경로의길이 매단계에서가장distance 값이적은정점을S에추가한다 v v w u s s 시작노드 distance=7 distance=3 distance=5 ①< (②+③) ① ② ③ 최단경로 다른경로 distance 값이 최소인노드 v w u s 새롭게 추가된노드 distance[u] distance[w] weight[u][w] distance 값갱신 distance[w]=min(distance[w],distance[u] +weight[u][w])
  • 147. Dijkstra의최단경로알고리즘 // 입력: 가중치그래프G, 가중치는음수가아님. // 출력: distance 배열, distance[u]는v에서u까지의최단거리이다. shortest_path(G, v) S←{v} for 각정점w∈Gdo distance[w]←weight[v][w]; while 모든정점이S에포함되지않으면do u←집합S에속하지않는정점중에서최소distance 정점; S←S∪{u} for u에인접하고S에있는각정점z do if distance[u]+weight[u][z] < distance[z] then distance[z]←distance[u]+weight[u][z];
  • 148. Dijkstra의최단경로알고리즘 4 0 5 1 6 2 3 3 10 7 2 5 4 11 10 6 9 2 4 4 0 5 1 6 2 3 3 10 7 2 5 4 11 10 6 9 2 4 4 0 5 1 6 2 3 3 10 7 2 5 4 11 10 6 9 2 4 4 0 5 1 6 2 3 3 10 7 2 5 4 11 10 6 9 2 4 4 0 5 1 6 2 3 3 10 7 2 5 4 11 10 6 9 2 4 4 0 5 1 6 2 3 3 10 7 2 5 4 11 10 6 9 2 4 4 0 5 1 6 2 3 3 10 7 2 5 4 11 10 6 9 2 4 S={0} distance[] = 0 7 ∞ ∞ 3 10 ∞ 0 1 2 3 4 5 6 0 10 7 3 ∞ ∞ ∞ 0 10 5 3 ퟖ ퟏퟒ ∞ S={0,4} distance[] = 0 5 ∞ ퟏퟒ 3 10 ퟖ 0 1 2 3 4 5 6 0 10 5 3 ퟖ ퟏퟒ ퟗ 0 10 5 3 ퟖ ퟏퟐ ퟗ S={0,4,1} distance[] = 0 5 ퟗ ퟏퟒ 3 10 ퟖ 0 1 2 3 4 5 6 S={0,4,1,6} distance[] = 0 5 ퟗ ퟏퟒ 3 10 ퟖ 0 1 2 3 4 5 6 0 10 5 3 ퟖ ퟏퟏ ퟗ 0 10 5 3 ퟖ ퟏퟏ ퟗ 0 10 5 3 ퟖ ퟏퟏ ퟗ S={0,4,1,6,2} distance[] = 0 5 ퟗ ퟏퟏ 3 10 ퟖ 0 1 2 3 4 5 6 S={0,4,1,6,2,5} distance[] = 0 5 ퟗ ퟏퟏ 3 10 ퟖ 0 1 2 3 4 5 6 S={0,4,1,6,2,5,3} distance[] = 0 5 ퟗ ퟏퟏ 3 10 ퟖ 0 1 2 3 4 5 6
  • 149. 위상정렬 방향그래프에서간선<u, v>가있다면정점u는정점v를선행한다고말한다 방향그래프에존재하는각정점들의선행순서를위배하지않으면서모든정점을나열하는것을방향그래프의위상정렬(topological sort)이라고한다. 예제: 위상정렬: (0,1,2,3,4,5) , (1,0,2,3,4,5) (2,0,1,2,4,5)는위상정렬이아니다. 2번정점이0번정점앞에오기때문이다. 간선<0,2>가존재하기때문에0번정점이끝나야만이2번정점을시작할수있다. 위상정렬(Topological Sort) U 진입간선 진출간선 0 1 2 5 3 4
  • 150. 위상정렬(Topological Sort) Input: 그래프G=(V,E) Output: 위상정렬순서 topo_sort(G) for i←0 to do if( 모든정점이선행정점을가지면) then 사이클이존재하고위상정렬불가; 선행정점을가지지않는정점v 선택; v를출력; v와v에서나온모든간선들을그래프에서삭제; 0 1 2 5 3 4 0 2 5 3 4 0 2 5 3 2 5 3 5 3 5 초기상태 1 제거 4 제거 0 제거 2 제거 3 제거
  • 152. 해싱(Hashing) 해싱 키값에직접산술적인연산을적용하여항목이저장되어있는테이블의주소를계산하여항목에접근 값의연산에의해직접접근이가능한구조를해시테이블(hash table)이라부르며, 해시테이블을이용한탐색을해싱(hashing) 사전구조(dictionary): 맵(map)이나테이블(table)로불리우기도한다. 사전구조는다음과같이탐색키와탐색키와관련된값의2가지종류의필드를가짐 영어단어나사람의이름같은탐색키(search key) 단어의정의나주소또는전화번호같은탐색키와관련된값(value) 객체: 일련의(key, value) 쌍의집합 연산: -add(key, value) ::= (key, value)를사전에추가한다. -delete(key) ::= key에해당되는(key, value)를찾아서삭제한다. 관련된value를반환한다. 만약탐색이실패하면NULL를반환한다. -search(key) ::= key에해당되는value를찾아서반환한다.만약탐색이실패하면NULL를반환한다.
  • 153. 해싱의구조 해시함수 탐색키를입력으로받아해시주소(hash address)를생성 해시주소가배열로구현된해시테이블(hash table)의인덱스가된다. 예제 학생들에대한정보를해싱으로저장, 탐색 학번=탐색키, 학번은5자리이고앞의2개의숫자가학과, 뒤의3자리숫자는학과의학생번호 같은학과학생들만저장된다고가정하면뒤의3자리만사용할수있다. 해시함수 h(x) 해시테이블 ht[] 0 1 2 … … M-1 해시주소 안철수 0 1 2 … 23 … M-1 해시테이블 h(01023)
  • 154. 해싱의구조 해시테이블 해시테이블ht는M개의버켓(bucket)으로이루어지는테이블로서ht[0], ht[1], ...,ht[M-1]의원소를가진다 하나의버켓은s개의슬롯(slot)을가질수있다 충돌(Collision) 서로다른두개의탐색키k1과k2에대하여h(k1) = h(k2)인경우 오버플로우(Overflow) 충돌이버켓에할당된슬롯수보다많이발생하게되면버켓에더이상항목을저장할수없게됨 S개의슬롯 M개의버킷 버킷0 버킷1 버킷M-1 슬롯0 슬롯S-1
  • 155. 실제해싱 해시테이블 크기가제한되어있어, 탐색키마다하나의공간을할당할수가없다 충돌과오버플로우발생 해싱함수가필요: h(k)= k mod M 좋은해시함수조건 충돌이적어야한다. 해시함수값이해시테이블의주소영역내에서고르게분포되어야한다. 계산이빨라야한다. 안철수 0 1 2 … 23 … M-1 해시테이블 h(01023) h(01055)
  • 156. 제산함수 h(k)=k mod M 해시테이블의크기M는소수(prime number) 폴딩함수 hash_index=(short)(key ^ (key>>16)) 이동폴딩(shift folding) 경계폴딩(boundary folding) 중간제곱함수 중간제곱함수는탐색키를제곱한다음, 중간의몇bit를선택해시주소를생성 비트추출함수 탐색키를이진수로간주하여임의의위치의k개의비트를해시주소로사용 숫자분석방법 키의각각의위치에있는숫자중에서편중되지않는수들을해시테이블의크기에적합한만큼조합하여해시주소로사용 해시함수 123 203 241 112 20 123 203 241 112 20 699 123 302 241 211 20 897 + + + + + + + + = =
  • 157. 충돌해결 충돌해결전략 완전해시(Perfect hashing) 한개의데이터영역에한개의키만이대입->(일반적) 구현불가능 버킷크기크게-> 메모리용량낭비 Open addressing(개방주소법) 오버플로우가일어났을때다른데이터주소로다시해시시키는방법(반복수행) Closed addressing(폐쇄주소법, 체인법) 같은데이터주소내에서끝까지해결을보는방법 충돌해결방법 선형조사법: 충돌이일어난항목을해시테이블의다른위치에저장한다 체이닝: 해시테이블의하나의위치가여러개의항목을저장할수있도록해시테이블의구조를변경
  • 158. 선형조사법 선형조사법 충돌이ht[k]에서충돌이발생했다면ht[k+1]이비어있는지를조사 만약비어있지않다면ht[k+2]를살펴본다. 이런식으로비어있는공간이나올때까지계속하는조사하는방법이다. 만약테이블의끝에도달하게되면다시테이블의처음으로간다. 조사를시작했던곳으로다시되돌아오게되면테이블이가득찬것이된다. 조사되는위치 h(k), h(k)+1, h(k)+2,… 예: h(k)=k mod 7 1단계(8) : ∙h(8) = 8 mod 7 = 1(저장) 2단계(1) : ∙h(1) = 1 mod 7 = 1(충돌발생) ∙(h(1)+1) mod 7 = 2(저장) 3단계(9) : ∙h(9) = 9 mod 7 = 2(충돌발생) ∙(h(9)+1) mod 7 = 3(저장) 4단계(6) : ∙h(6) = 6 mod 7 = 6(저장) 5단계(13) :∙h(13) = 13 mod 7 = 6(충돌발생) 1단계 2단계 3단계 4단계 5단계 [0] 13 [1] 8 8 8 8 8 [2] 1 1 1 1 [3] 9 9 9 [4] [5] [6] 6 6 환치발생(단점) 해시테이블구조가간단(장점)
  • 159. 이차조사법(Quadratic probing) 이차조사법 선형조사법에서의문제점인집중과결합을크게완화 선형조사법과유사하지만, 다음조사할위치를다음식에의하여결정 (h(k)+i*i) mod M 조사되는위치 h(k), h(k)+1, h(k)+4,… 테이블의크기가소수여야함(테이블의반정도의영역만이탐색가능) 아닐경우탐색영역현저히감소
  • 160. 이중해싱법(Double Hashing) 이중해싱법 오버플로우가발생함에따라항목을저장할다음위치를결정할때, 원래해시함수와다른별개의해시함수를이용하는방법이다 step=C-(k mod C) h(k), h(k)+step, h(k)+2*step, … (예) 크기가7인해시테이블에서첫번째해시함수가k mod M이고. 오버플로우발생시의해시함수step=5-(5 mod 5) 입력파일(8, 1, 9, 6, 13 ) 1단계(8) : ∙h(8) = 8 mod 7 = 1(저장) 2단계(1) : ∙h(1) = 1 mod 7 = 1(충돌발생) ∙(h(1)+h‘(1)) mod 7 = (1+5-(1 mod 5)) mod 7 = 5(저장) 3단계(9) : ∙h(9) = 9 mod 7 = 2(저장) 4단계(6) : ∙h(6) = 6 mod 7 = 6(저장) 5단계(13) :∙h(13) = 13 mod 7 = 6(충돌발생) ∙(h(13)+h‘(13)) mod 7 = (6+5-(13 mod 5)) mod 7= 1(충돌발생) ∙(h(13)+2*h‘(13)) mod 7 = (6+2*2) mod 7= 3(저장) 1단계 2단계 3단계 4단계 5단계 [0] [1] 8 8 8 8 8 [2] 9 9 9 [3] 13 [4] [5] 1 1 1 1 [6] 6 6
  • 161. 체이닝(chaining) 체이닝 오버플로우문제를연결리스트로해결 각버켓에고정된슬롯을할당하는것이아니라각버켓에, 삽입과삭제가용이한연결리스트를할당한다 버켓내에서는원하는항목을찾을때는연결리스트를순차탐색한다 (예) 크기가7인해시테이블에h(k)=k mod 7의해시함수를이용하여, 8, 1, 9, 6, 13 을삽입할때에의체이닝에의한충돌처리 1단계(8) : ∙h(8) = 8 mod 7 = 1(저장) 2단계(1) : ∙h(1) = 1 mod 7 = 1(충돌발생->새로운노드생성저장) 3단계(9) : ∙h(9) = 9 mod 7 = 2(저장) 4단계(6) : ∙h(6) = 6 mod 7 = 6(저장) 5단계(13) :∙h(13) = 13 mod 7 = 6(충돌발생->새로운노드생성저장) 0 1 2 3 4 5 6 8 1 n 9 6 2 n (1) (2) (3) (4) (5)
  • 162. 해싱의성능분석 적재밀도(loading density) 적재비율(loading factor) 저장되는항목의개수n과해시테이블의크기M의비율이다. 선형조사법 실패한검색: 성공한검색: 체이닝 실패한검색: α 성공한검색: α= 저장된항목의개수해싱테이블의버킷의개수= 풏 푴 ퟏ ퟐ ퟏ+ ퟏ ퟏ+αퟐ ퟏ ퟐ ퟏ+ ퟏ ퟏ+α 1 + α ퟐ
  • 163. 해싱과다른탐색방법의비교 탐색 삽입 삭제 순차탐색 O(n) O(1) O(n) 이진탐색 O(Log2n) O(Log2n+n) O(Log2n+n) 이진탐색트리 균형트리 O(Log2n) O(Log2n) O(Log2n) 경사트리 O(n) O(n) O(n) 해싱 최선의경우 O(1) O(1) O(1) 최악의경우 O(n) O(n) O(n)
  • 165. 탐색(Search) 탐색이란… 여러개의자료중에서원하는자료를찾는작업 컴퓨터가가장많이하는작업중의하나 탐색을효율적으로수행하는것은매우중요 탐색키(Search key): 항목과항목을구별해주는키(key) 탐색을위해사용되는자료구조 배열, 연결리스트, 트리, 그래프등 탐색에의한문제해결 문제의해에도달하기위한탐색과정을직접수행함으로써보다포괄적이며, 자동화된해결방안 문제해결과정중에지적판단이요구되는경우탐색기법이유용 문제해결의최적의방법보다적당한방법을찾는것이쉽다
  • 166. 순차탐색(Sequential Search) 순차탐색 가장간단하고직접적인탐색방법 정렬되지않은배열의항목들을처음부터마지막까지하나씩검사하여원하는항목을찾아가는방법 int seq_search(int key, int low, int high) { int i; for(i=low; i<=high; i++) if(list[i]==key) return i; // 탐색성공 return -1; // 탐색실패 } 9 5 8 3 7 8을찾는경우 9 5 8 3 7 9 5 8 3 7 9 5 8 3 7 2를찾는경우 9 5 8 3 7 9 5 8 3 7 9 5 8 3 7 9 5 8 3 7 탐색성공 탐색실패 9≠8이므로탐색계속 5≠8이므로탐색계속 8=8이므로탐색성공 9≠2이므로탐색계속 5≠2이므로탐색계속 8≠2이므로탐색계속 3≠2이므로탐색계속 7≠2이므로탐색계속 더이상항목이없으므로실패
  • 167. 개선된순차탐색 How to ? 비교횟수를줄이기위해리스트의끝에찾고자하는키값을저장하고반복문의탈출조건을키값을찾을때까지로설정 int seq_search2(int key, int low, int high) { int i; list[high+1] = key; // 키값을찾으면종료 for(i=low; list[i] != key; i++) ; if(i==(high+1)) return -1; // 탐색실패 else return i; // 탐색성공 } 9 5 8 3 7 8 8을찾는경우 9 5 8 3 7 8 9 5 8 3 7 8 9 5 8 3 7 2 2를찾는경우 9 5 8 3 7 2 9 5 8 3 7 2 9 5 8 3 7 2 9 5 8 3 7 2 탐색성공 탐색실패 9≠8이므로탐색계속 5≠8이므로탐색계속 8=8이므로탐색성공 9≠2이므로탐색계속 5≠2이므로탐색계속 8≠2이므로탐색계속 3≠2이므로탐색계속 2=2이지만마지막항목 따라서탐색실패
  • 168. 이진탐색(Binary Search) 이진탐색 정렬된배열의탐색에적합 배열의중앙에있는값을조사하여찾고자하는항목이왼쪽또는오른쪽부분배열에있는지를알아내어탐색의범위를반으로줄임 (예) 10억명중에서이진탐색을이용하여특정한이름을탐색 이진탐색은단지30번의비교 순차탐색에서는평균5억번의비교 1 3 5 6 7 9 11 20 30 1 3 5 6 7 9 11 20 30 1 3 5 6 7 9 11 20 30 1 3 5 6 7 9 11 20 30 1 3 5 6 7 9 11 20 30 5을찾는경우 7과비교 5<7이므로앞부분만을다시탐색 5와3을비교 5>3이므로뒷부분만을다시탐색 5=5이므로탐색성공
  • 169. 이진탐색알고리즘 search_binary(list, low, high) middle ← low에서high사이의중간위치 if( 탐색값≠ list[middle] ) return TRUE; else if (탐색값< list[middle] ) return list[0]부터list[middle-1]에서의탐색; else if (탐색값> list[middle] ) return list[middle+1]부터list[high]에서의탐색; 2 6 11 13 18 20 22 27 29 30 34 38 41 42 45 47 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 2 6 11 13 18 20 22 27 29 30 34 38 41 42 45 47 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 2 6 11 13 18 20 22 27 29 30 34 38 41 42 45 47 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 2 6 11 13 18 20 22 27 29 30 34 38 41 42 45 47 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Low High middle middle Low High middle Low High middle Low High
  • 170. 색인순차탐색(indexed sequential search) 색인순차탐색 인덱스(index)라불리는테이블을사용하여탐색의효율을높이는방법 인덱스테이블은주자료리스트에서일정간격으로발췌한자료를가지고있다 주자료리스트와인덱스테이블은모두정렬되어있어야한다 3 0 15 3 67 6 3 9 15 22 31 55 67 88 91 인덱스테이블 주자료테이블 0 1 2 3 4 5 6 7 8 0 1 2
  • 171. 균형이진탐색트리 이진탐색(binary search)과이진탐색트리(binary search tree)와의차이점 이진탐색과이진탐색트리는근본적으로같은원리에의한탐색구조 이진탐색은자료들이배열에저장되어있으므로삽입과삭제가상당히힘들다. 즉자료를삽입하고삭제할때마다앞뒤의원소들을이동시켜야한다. 이진탐색트리는비교적빠른시간안에삽입과삭제를끝마칠수있는구조 삽입, 삭제가빈번히이루어진다면반드시이진탐색트리를사용하여야한다. 이진탐색트리에서의시간복잡도 균형트리: O(logn) 불균형트리: O(n), 순차탐색과동일 1 2 3 4 5 6 7 1 2 3 4 5 6 7
  • 172. AVL 트리 AVL 트리 Adelson-Velskii와Landis에의해1962년에제안 각노드에서왼쪽서브트리의높이와오른쪽서브트리의높이차이가1 이하인이진탐색트리 트리가비균형상태로되면스스로노드들을재배치하여균형상태로만든다 AVL 트리는균형트리가항상보장되기때문에탐색이O(logn)시간안에끝나게된다 균형인수(balance factor)=(왼쪽서브트리의높이-오른쪽서브트리의높이) 모든노드의균형인수가±1 이하이면AVL 트리이다 7 5 9 3 1 균형인수+2, -2 균형인수+1, -1 균형인수0 AVL 트리 Non-AVL 트리 1 0 0 7 5 9 3 1 1 0 0 2 2
  • 173. AVL 트리의연산 AVL 트리의연산 탐색연산: 이진탐색트리와동일 균형을이룬이진탐색트리에서균형상태가깨지는경우 삽입연산과삭제연산 삽입연산시에는삽입되는위치에서루트까지의경로에있는조상노드들의균형인수에영향을줄수있다. 따라서즉새로운노드의삽입후에불균형상태로변한가장가까운조상노드, 즉균형인수가±2가된가장가까운조상노드의서브트리들에대하여다시균형을잡아야한다. 균형인수+2, -2 균형인수+1, -1 균형인수0 7 5 9 3 1 1 0 0 7 5 9 3 1 1 0 0 2 2
  • 174. AVL 트리의삽입연산 균형트리로만드는방법 회전(rotation) AVL 트리에서균형이깨지는경우에는다음의4가지의경우 새로삽입된노드N로부터가장가까우면서균형인수가±2가된조상노드를A라고가정 LL 타입: N이A의왼쪽서브트리의왼쪽서브트리에삽입된다. LR 타입: N이A의왼쪽서브트리의오른쪽서브트리에삽입된다. RR 타입: N이A의오른쪽서브트리의오른쪽서브트리에삽입된다. RL 타입: N이A의오른쪽서브트리의왼쪽서브트리에삽입된다. 7 3 9 1 1 0 0 5 0 0
  • 175. 회전방법 회전종류 LL 회전: A부터N까지의경로상의노드들을오른쪽으로회전시킨다. LR 회전: A부터N까지의경로상의노드들을왼쪽-오른쪽으로회전시킨다. RR 회전: A부터N까지의경로상의노드들을왼쪽으로회전시킨다. RL 회전: A부터N까지의경로상의노드들을오른쪽-왼쪽으로회전시킨다. A B T1 T2 T3 A B T1 T2 T3 B A T2 T3 T1 일반적인LL 회전 A T4 B C T2 T3 T1 A T4 B C T2 T3 T1 삽입 LL 회전 A T4 C B T2 T1 T3 RR 회전 C B A T2 T1 T3 T4
  • 176. AVL Tree 삽입예제(1/2) 8 7 9 2 1 1 0 0 7 0 (1) 7삽입 7 8 -1 0 (2) 8삽입 7 8 -2 1 9 0 RR 회전 8 7 9 0 0 0 (3) 9삽입 (4) 2삽입 8 7 9 2 2 2 1 0 (5) 1삽입 1 0 LL 회전 8 2 9 1 7 1 0 0 0 0
  • 177. AVL Tree 삽입예제(2/2) (6) 5삽입 8 2 9 1 7 2 -1 0 0 1 5 0 LR 회전 7 2 8 1 5 9 0 0 0 0 0 -1 (7) 3삽입 7 2 8 1 5 9 1 -1 0 0 -1 3 1 (8) 6삽입 7 2 8 1 5 9 1 -1 0 0 -1 3 0 6 0 0 (9) 4삽입 7 2 8 1 5 9 2 -2 0 0 -1 3 1 6 0 -1 4 0 RL 회전 7 3 8 2 5 9 1 0 1 0 -1 4 0 6 0 0 1 0