알고리즘/알고리즘 노트

[자료구조]

산을좋아한라쯔 2016. 7. 27. 15:08
반응형

1. 스택

2. 큐

3. 덱(Deque)

4. 링크드 리스트

5. 상호배타적 집합(disjoint set)

6. 세그먼트 트리

 

--------------------------------

1. 스택

- FILO (First In Last Out). 

- stl의 stack 클래스 사용하면 간단 사용 가능.

   push, top, pop, empty, size

- 알고리즘 문제풀이에 사용되는 스택은, 배열로 쉽게  구현해도 됨 (사용될 최대 크기가 결정되어 있기에)

  int st[1001],top=0;

  st[++top] = 1; //push(1)

  int a = st[top]; //top()

  top--;  //pop

  st[top--]; //top() and pop()

  if(top==0) //empty

 

2. 큐

- FIFO (First In First Out)

- stl의 queue클래스 사용하면 간단 사용 가능

  push, front, pop, size, empty /

- push는 rear쪽으로, pop은 front쪽으로. 

 

- 알고리즘 문제 풀이용 큐는 쉽게 구현가능. 그러나, 스택보다는 복잡(front와 rear의 포인터가 마냥 증가하지 않도록 조치 필요)

#define SIZE 1000

int q[SIZE], s = 0, e = 0;

void push(int a) {

          e = (e + 1) % SIZE;         

          q[e] = a;         

}

 

int front() {

         if (s == e) return -1;

         return q[s];

}

 

void pop() {

         if (s == e) return;

         s = (s + 1) % SIZE;

}

 

int size() {

         return e - s;

}

 

3. 덱(Deque)

- Double ended queue

- stl의 deque사용하면 간단

  push_front, push_back, pop_front, pop_back, front, back, size, empty, at

 (인덱스로 접근가능한 at 함수 있는게 특이)

 

- 문제풀이에서 직접 구현할 때는, 데이터크기의 두 배정도로 배열 잡아두고, 가운데를 중심으로해서, rear 인덱스는 증가, front인덱스는 감소하게해서 구현,

 

4. 링크드 리스트

양방향 Linked List를 구현해보면, 단방향은 그대로 해결

 

구조체 정의

struct List{

List* prev;

List* next:

int num;

}

 

기본 구조

- head가 존재. 초기상태는,

  . head->num=1

  . head->prev = head->next = head  //자기자신을 가리킴. head 하나만 있는 상태

- head->prev는 항상 last(맨 마지막 노드)를 가리킴

- last->next는 항상 head를 가리킴

 

insert(x)

- 맨 마지막 last 다음에 삽입. 이 때 last는 head->prev 가 가리키고 있음

- 삽입시 해줘야 할 것은,

1)신규 노드 생성: f = new List(); //f: fresh

2)num 지정: f->num = x

3)신규노드의 prev 지정: f->prev = head->prev; //head->prev는 삽입되기 전 맨 마지막 노드를 가리키고 있음

4)신규노드의 next를 head로 지정: f->next=head

5)last였던 노드의 next 갱신: head->prev->next = f

6)헤드의 prev 갱신: head->prev=f

 

삽입과정을 그림으로 보면 다음과 같다. (1,2가 있는 상태에서 3이 삽입될 경우)

 

 

 

LIST *head=NULL;

         for (int i = 1; i <= N; i++) {

                  if (i == 1) {

                           head = new LIST();

                           head->number = i;

                           head->next = head;

                           head->prev = head;

                           continue;

                  }

 

                  LIST *fresh = new LIST();

                  fresh->prev = head->prev;

                  fresh->next = head;

                  fresh->number = i;

                  head->prev->next = fresh;

                  head->prev = fresh;

         }

 

 

 

삭제

헤드를 삭제해 나가는 방법

1)헤드를 가리키는 임시 노드 생성: List* tmp = head;

2)last->prev 갱신: head->prev->next = head->next

3)head바로 다음에 있던 노드의 prev 갱신: head->next->prev=head->prev

 

 

 

         for (int i = 1; i <= N; i++) {

                  printf("%d ", head->number);

 

                  LIST *tmp = head;

                  if (i != N) {

                           head->prev->next = head->next;

                           head->next->prev = head->prev;

                           head = head->prev;

                           for (int j = 1; j <= K; j++) {

                                   head = head->next;

                           }

                  }

                  delete tmp;

         } 

 

5. 상호배타적 집합(disjoint set)

3가지 기능으로 구성됨: init, find, merge

1. init

//모든 배열값에 대해 각기 다른 값 배정

for(int i=0;i<=N;++i) p[i]=i;

 

2. find(a)

//parent값이 자기 자신과 같으면, 그것을 리턴

if(p[a]==a) return a;

 

//경로 압축을 해야 속도 보장됨

p[a] = find(p[a]); //새로 찾은 값들을 root 바로 밑에 붙여서, 경로를 줄임

return p[a];

 

3. merge(a,b)

int pa = find(a);

int pb = find(b);

p[pa] = pb;

 

6. 세그먼트 트리

주어진 값들에 대해 최소/최대/합 을 log(n)시간에 구해준다.

그럴러면, 주어진 값들을 가지고 특정 구조로 만들어둬야 한다. 트리 구조로.

 

트리구조를 만들 때 아래 특성을 이용해서 만듬.

  - 2진트리의 부모의 인덱스가 i이면, 왼쪽 자식은 2*i, 오른쪽 자식은 2*i+1이 됨

    아래 트리에서 보면 6 = 2*3, 7=2*3+1

    1

2                  3

4        5         6        7

 

3가지 연산: 1)init  2)query 3)update 

 

*구간 합 Tree 예

-주어진 값: arr[1..n]

-트리 배열: tree[1..4n]  //n에 가장 가까운 2의 멱승으로 올림한 값이 정확. 대충은 4n

1)init: 주어진 배열을 가지고 tree를 형성해 둔다.

int init(node, s,e)

if(s==e) return tree[node]=arr[s]

int m = (s+e)/2

return tree[node] = init(2*node,s,m) + init(2*node+1,m+1,e) 

//만약 min tree라면 min( init(2*node,s,m), init(2*node+1,m+1,e)

 

2)query

ll query(node, s,e,i,j) //i~j까지의 합

if(i>e || j<s) return 0 //1. 범위 밖이면.

if(i<=s && j>=e) return tree[node] //2. i~j가 node가 표현하는 집합을 완전히 포함

int m = (s+e)/2

return query(2*node,s,m,i,j)  + query(2*node+1,m+1,e,i,j) //3. 왼쪽 재귀 + 오른쪽 재귀

 

3)update

void update(node,s,e,i,diff) //diff:i노드에 diff만큼 더한다. diff를 한 이유는, 관련노드들을 diff만큼 더해야기 때문. 

if(i<s || i>e) return;        //1. 범위 밖

tree[node] = tree[node] + diff; //2. if(s==e)이면 갱신만. 즉, leaf까지 내려온경우.

if(s != e)                    //3. if(s!=e)이면, 자신도 갱신하고, 자식 노드들도 갱신

int m = (s+e) / 2;

update(2*node,s,m,i,diff)

update(2*node+1,m+1,e,i,diff)

 

 

 

 

-끝-

 

 

 

 

 

 

반응형

'알고리즘 > 알고리즘 노트' 카테고리의 다른 글

[다이나믹]-문제1  (0) 2016.07.30
[문자열]  (0) 2016.07.28
[다이나믹]타일채우기 문제들  (0) 2016.07.26
[다이나믹]  (0) 2016.07.26
[기하]  (0) 2016.07.25