-> Deque는 이런 특성이 혼합되어 있음.
양방향으로 열려있는 구조로, queue와 외형적으로 비슷한 구조이나 한 방향인 queue와 다름. queue나 stack과 달리 LIFO나 FIFO와 같은 순서에 구속되지 않음.
Deque는 양쪽으로 데이터를 추가하고 삭제할 수 있어서 stack과 queue를 구현할 수 있음.추가와 삭제를 양쪽에서 제어할 수 있어서 여러가지 형태로 사용됨.
---------------------------
-> insert
<- delete delete ->
---------------------------
데이터 추가의 방향이 정해진 상태가 됨. 왼쪽으로 삭제하는 형태는 stack과 같고 오른쪽으로 삭제하는 형태는 queue와 같음.
데이터의 추가는 양쪽에서 가능하지만, 삭제는 한 방향에서만 가능하게 구현한다면 아래와 같은 구조가 됨.
---------------------------
-> insert insert <-
<- delete
---------------------------
데이터 삭제의 방향이 정해진 상태가 됨. 이때 왼쪽에서 추가하는 형태는 stack과 같고 오른쪽에서 추가하는 형태는 queue와 같음.
두 예와 같이 양방향의 추가 또는 삭제를 제한하여 상황에 맞는 자료구조를 구현할 수 있음.
-----
3 // 3추가
-----
L R
-----------
2 | 3 // 왼쪽에 2추가
-----------
L R
-----------------
2 | 3 | 4 // 오른쪽에 4추가
-----------------
L R
-----------------------
1 | 2 | 3 | 4 // 왼쪽에 1추가
-----------------------
L R
Stack과 Queue에서 추가할 데이터나 삭제할 데이터의 인덱스 정보를 가지고 있듯이 Deque도 양쪽의 추가 삭제할 데이터의 인덱스 정보를 가지고 있어서 양쪽 끝의 데이터에 접근과 추가, 삭제가 용이함
Deque는 양방향 끝의 인덱스 정보를 가지고 있음. 앙방향의 데이터가 아닌 중간에 있는 데이터에 접근하려고 할 때 양쪽 끝 이외의 인덱스 정보가 없어서 접근할 수 없음. 이러한 구조로 deque는 임의의 인덱스만 추가 삭제하는 것은 불가능함.
Linked List 자료구조는 선형으로 그룹화된 데이터의 집합으로 데이터와 다음 데이터의 주소를 포함하고 있는 하나의 노드가 선형으로 연결된 자료구조임. 한글로는 연결 리스트
라고도 함.
예. 배열(Array)은 교실에 있는 반 학생들이 일렬로 놓여있는 의자에 번호순으로 앉혀 놓은 상태임. 의자의 위치만으로도 빠르게 학생을 찾을 수 있음. Linked List 자료구조는 반 전체 학생 이름이 적힌 종이로 제비를 뽑아 마니또를 정한 상태와 같음. 첫 번째 학생부터 마지막 학생까지 마니또를 알기 위해서는 다음 마니또가 누구인지 한명 한명 물어보고 알아내야 함. 학생은 제비에 적힌 자신의 마니또밖에 모르기 때문임.
Linked List는 배열과 같이 선형으로 이루어진 자료구조임. 배열과 비교하면 쉽게 이해할 수 있음.
메모리에 표시한 링크드 리스트와 배열
배열은 메모리 순서가 정해져 있어서 값을 추가하거나 삭제할 때 메모리에 재할당을 해야 하지만 Linked List는 순서가 지정되지 않은 특성 때문에 데이터를 담은 노드를 어디에도 손쉽게 추가하거나 삭제할 수 있음.
배열은 추가하거나 삭제하는 경우 때에 따라 시간 복잡도가 많이 걸림.
배열은 중간에 데이터를 추가하고 싶을 때 제일 마지막 인덱스를 새로 만들어서 삽입하고자 하는 인덱스까지 모든 데이터를 한 칸씩 이동한 다음에 원하는 인덱스의 값을 추가할 수 있음. 가장 마지막 인덱스에 데이터를 추가할 때 가장 O(1)의 빠른 시간 복잡도를 보이지만 -번 인덱스에 데이터를 추가하는 경우 모든 데이터를 오른쪽으로 옮겨야 하므로 최악의 경우 O(n)의 시간 복잡도를 가짐.
삭제는 추가의 반대 과정으로 동작함. 배열은 마지막 요소의 수정으 ㄴ빠르나 가장 앞의 요소 수정은 가장 오래 걸리게 됨.
Linked List의 노드에는 데이터와 다음 노드의 메모리 주소가 함께 들어가 있음.
위 그림의 Linked List에서 데이터 추가해보기.
1. 7 → 2 → 5 → 3 으로 연결되어있음.
2. 7은 2를 가리키고, 2는 5를 가리키고, 3은 마지막에 있어서 아무것도 가리키고 있지 않음.
3. 이 상태에서 2와 5 사이에 9를 추가하려고 함. 여기서 추가하는 방법은 2는 9를 가리키게 바꾸고 9는 5를 가리키게 바꾸면 7 → 2 → 9 → 5 → 3 으로 Linked List에 9를 추가할 수 있음.
위 그림의 Linked List에서 데이터 삭제해보기.
1. 9를 삭제하려고 함.
2. 9는 5를 가리키고 있지만, 5를 가리키지 않고 null을 가리키게 바꿈.
3. 2는 9를 가리키고 있지만, 9를 가리키지 않고 5를 가리키게 바꿈.
4. 그렇게 되면 7 → 2 → 5 → 3으로 9를 Linked List에서 삭제할 수 있음. 삭제도 다음 노드를 가리키는 메모리 주소만 수정하면 됨.
이렇게 Linked List는 어떤 위치에 노드를 추가하거나 삭제해도 O(1)의 빠른 시간 복잡도를 보여줌.
배열과 비교해보자면, 배열은 연속적으로 이어져 있어 주소의 계산이 쉬워서 쉽게 접근할 수 있음. 하지만 Linked List의 노드는 메모리에 흩어져 있어서 특정 노드로 쉽게 접근할 수 없음.
이런 식으로 배열은 물리적 메모리에 연속적으로 할당되어 있어서 주소에 접근하는것이 쉬움. 다음 메모리 주소에도 배열의 값이 있을 것이기 때문. 배열은 인덱스값을 계산하기가 쉬우므로 어느 위치의 인덱스인지 관계없이 데이터에 접근하는데 O(1)의 시간복잡도를 가짐.
Linked List의 첫 번째 노드를 head node라고 함. 순회하기 전까지는 head node의 정보만 알고 있음. 필요한 값이 있는지 확인하려면 head node에 연결된 다음 노드로 접근해야 하고, 접근한 노드에 원하는 값이 없다면 다시 다음 노드로 이동해야 함. Linked List의 구조가 위와 같이 보이지만, 실제로는 아래 같은 정보가 주어져 있음.
head node의 값은 7이고 뒤로 붙은 노드의 개수가 3개인 리스트를 말할 뿐, 그 뒤의 요소 정보를 전혀 알 수 없음. 3개의 요소 중 마지막 요소에는 무엇이 있는지 확인하려면 head node에서 출발하여 마지막 노드까지 전부 순회해야 5라는 것을 알게 됨. head node에 있는 값은 O(1)의 시간 복잡도를 가지지만 마지막 요소의 값에 접근하기 위해서는 요소의 개수만큼 순회해야 하기 때문에 최악의 경우로 O(n)의 시간복잡도를 가짐.
hash table이란 해시함수(hash function)를 사용하여 변환한 해시(hash)를 색인(index)으로 삼아 키(key)와 데이터(value)를 저장하는 자료구조.
예. 핸드폰 단축번호 설정
김코딩, 010-1234-5678, 단축번호:1 이라는 사람에게 전화를 걸기 위해 미리 사용자가 저장한 단축번호: 1을 누르면 010-1234-5678 번호로 바로 통화할 수 있음.
이처럼 필요한 키(key)를 해시함수를 사용해 별도의 해시(hash)로 바꿔주고, 해당하는 데이터(value)를 함께 저장하는 자료구조.
hash table에서 저장, 삭제, 검색 과정은 모두 평균적으로 O(1)의 시간복잡도를 가지고 있어 데이터를 다루는 작업이 매우 빠르다는 장점이 있음. 다만 해시 충돌이 발생할 수가 있고 데이터가 저장되기 전에 저장공간을 미리 만들어놔야하기 때문에 공간 효율성이 떨어짐. 또한 해시 함수(hash function)의 의존도가 높음. 이 말은 곧, 해시 함수(hash function)가 복잡하다면, 해시(hash)값을 만들어내는데 많은 시간이 소요됨.
hash table에서 저장, 삭제, 검색하기 위해서는 해시 함수(hash function)에 키(key)값을 넣어 해시(hash)값을 만들게 됨. 이후 만ㄷ르어진 해시(hash)값과 이맃하는 색인(index)을 찾아 저장하거나 삭제, 검색함.
기본적으로 해당 작업들의 시간 복잡도는 O(1)임.
해시함수(hash function)를 거쳐 해시값을 찾아내는데 걸리는 과정은 고려하지 않음. 그러나 해시충돌이 발생할 경우 저장소의 모든 색인(삽입) 혹은 데이터(삭제, 검색)를 찾아봐야 하기 때문에 O(n)이 됨.
Division Method
를 적용할 수 있음.개방 연결법이란 해시 충돌이 발생하면 다른 색인에 해당 자료를 삽입하는 방식. 여러 가지 방법이 존재하지만 대표적으로 3가지가 존재함.
분리 연결법이란 동일한 색인의 데이터에 대해 연결리스트(linked list), 트리(red-black tree)등의 자료구조를 활용해 데이터의 주소를 저장하는 방법
위의 그림처럼 저장소의 동일한 버킷의 데이터에 연결리스트(linked list), 트리(red-black tree)등의 자료구조를 사용해서 충돌이 일어난 데이터를 저장하는 방식. 이 방식의 경우 구현이 간단하며, 데이터를 쉽게 삭제할 수 있다는 장점이 있음. 하지만 중복으로 저장된 데이터가 많아지면 동일한 버킷에 연결되는 데이터가 많아져서 검색의 효율성이 감소한다는 단점이 있음.
저장소에 크기가 작다면 불필요한 메모리 사용을 줄일 수 있지만, 해시 충돌이 발생하며 개방 연결법이나 분리 연결법을 사용해도 성능상 손실이 발생함. 실제 Java에서 사용되는 HashMap이라는 자료구조는 매치된 key-value 데이터 개수가 일정 이상이 된다면 (저장소의 75% 이상 사용) 저장소의 크기를 두 배로 늘리게 됨. 이 방식으로 해시 충돌로 인한 성능이 감소하는 문제를 어느 정도 해결이 가능.
heap tree는 트리 구조로 구현된 자료구조. 일반적인 트리 구조와는 다르게, heap tree는 우선순위에 따라서 빠르게 자료를 검색할 수 있는 구조 임.
예. 응급실에서 환자가 들어올 때 환자가 들어온 순서와 달리 조금 더 빠른 치료가 필요한 환자부터 치료를 시작하게 되는 것
heap tree는 느슨한 정렬 구조로 구현 되어 있음: 부모 노드의 값은 자식의 값보다 항상 크거나 항상 작게 정렬되어 있지만 자식 노드끼리의 값의 크기에 따라 좌우 위치는 정렬하지 않기 때문에 느슨한 정렬 구조라고 표현함.
완전 이진 트리: 단순히 최대값, 최소값을 찾기 위해서는 완전 이진 트리로 구성할 필요는 없지만, 삽입/삭제 시 성능을 위해 완전 이진 트리로 구현하게 됨.
중복된 값 저장: 일반적인 이진 탐색 트리와 다르게 중복된 값을 저장할 수 있음. 단순히 최댓값/최솟값을 찾아내기 위한 구조이기 때문.
최대 힙/ 최소 힙: 루트 노드에 가장 큰 값이 위치하며 자식 노드로 내려갈수록 작은 값이 위치하게 구현한다면 최대 힙이 되고, 루트 노드에 가장 값이 위치하며 자식 노드로 내려갈수록 큰 값이 위치하게 구현한다면 최소 힙이 됨.