인프런의 그림으로 쉽게 배우는 자료구조와 알고리즘(기본편)을 수강하고 정리한 글입니다.
자료구조
1. Array(배열)
int arr[10] = {1,2,3,4,5};
희소배열이란?
배열의 요소가 연속적으로 이어져있지 않은 배열
console.log(Object.getOwnPropertyDescriptors([1, 2, 3]));
/*
{
'0': { value: 1, writable: true, enumerable: true, configurable: true },
'1': { value: 2, writable: true, enumerable: true, configurable: true },
'2': { value: 3, writable: true, enumerable: true, configurable: true },
length: { value: 3, writable: true, enumerable: false, configurable: false }
}
*/
위의 예제를 보면 알 수 있듯이, 자바스크립트의 배열은 인덱스를 프로퍼티 키로 갖으며 length 프로퍼티를 갖는 특수한 객체이다.
자바스크립트 배열의 요소는 사실 프로퍼티 값이며 자바스크립트에서 사용되는 모든 값은 객체의 프로퍼티 값이 될 수 있으므로 어떤 타입의 값이라도 배열의 요소가 될 수 있다고 한다. 인덱스로 접근하는 배열의 구조적인 단점을 보완하기 위해 대부분의 모던 자바스크립트 엔진은 배열을 일반 객체와 구별하여 배열처럼 동작하도록 최적화 되어있다고 한다.
2. Linked List(연결리스트)
연결리스트의 추상자료형
1. printAll()
- 모든 데이터 출력
2. clear()
- 모든 데이터 제거
3. insertAt(index, data)
- 인덱스 삽입
4. insertLast(data)
- 마지막 삽입
5. deleteAt(index)
- 인덱스 삭제
6. deleteLast()
- 마지막 삭제
7. getNodeAt(index)
- 인덱스 읽기
예제코드)
class Node {
constructor(data, next = null) {
this.data = data;
this.next = next;
}
}
class LinkedList {
constructor() {
this.head = null;
this.count = 0;
}
printAll() {
let currentNode = this.head;
while (currentNode !== null) {
console.log(currentNode.data);
currentNode = currentNode.next;
}
}
clear() {
this.head = null;
this.count = 0;
}
insertAt(index, data) {
// 범위를 넘어가는 경우에 대한 예외처리
if (index > this.count || index < 0)
throw new Error("범위를 넘어갔습니다.");
let newNode = new Node(data);
if (index === 0) {
newNode.next = this.head;
this.head = newNode;
} else {
// 원하는 인덱스에 삽입
let currentNode = this.head;
// 목표인덱스 전까지 이동
for (let i = 0; i < index - 1; i++) {
currentNode = currentNode.next;
}
newNode.next = currentNode.next;
currentNode.next = newNode;
}
this.count++;
}
insertLast(data) {
this.insertAt(this.count, data);
}
deleteAt(index) {
if (index >= this.count || index < 0)
throw new Error("제거할 수 없습니다.");
let currentNode = this.head;
// head가 위치하는 node 제거할 때
if (index === 0) {
let deletedNode = this.head;
this.head = this.head.next;
this.count--;
return deletedNode;
} else {
for (let i = 0; i < index - 1; i++) {
currentNode = currentNode.next;
}
let deletedNode = currentNode.next;
currentNode.next = currentNode.next.next;
this.count--;
return deletedNode;
}
}
deleteLast() {
return this.deleteAt(this.count - 1);
}
getNodeAt(index) {
if (index >= this.count || index < 0) return;
let currentNode = this.head;
for (let i = 0; i < index; i++) {
currentNode = currentNode.next;
}
return currentNode;
}
}
export { Node, LinkedList };
3. Stack(스택)
예제코드)
import { LinkedList } from "./LinkedList.mjs";
class Stack {
constructor() {
this.list = new LinkedList();
}
push(data) {
this.list.insertAt(0, data);
}
pop() {
try {
return this.list.deleteAt(0);
} catch (e) {
return null;
}
}
// 데이터 참조하는 함수
peak() {
return this.list.getNodeAt(0);
}
isEmpty() {
return this.list.count === 0;
}
}
export { Stack };
<br/ >
4. Queue(큐)
head
와 tail
을 이용해 구현(이중 연결리스트)head
에서부터 하나씩 참조해야 하기 때문에 O(n)의 성능이지만 이중연결리스트를 이용하여 tail
을 사용하면 O(1)의 성능으로 구현 할 수 있다.class Node {
constructor(data, next = null, prev = null) {
this.data = data;
this.next = next;
this.prev = prev;
}
}
class DoublyLinkedList {
constructor() {
this.head = null;
this.tail = null;
this.count = 0;
}
printAll() {
let currentNode = this.head;
while (currentNode !== null) {
console.log(currentNode.data);
currentNode = currentNode.next;
}
}
clear() {
this.head = null;
this.count = 0;
}
insertAt(index, data) {
// 범위를 넘어가는 경우에 대한 예외처리
if (index > this.count || index < 0)
throw new Error("범위를 넘어갔습니다.");
let newNode = new Node(data);
// head에 삽입하는 경우
if (index === 0) {
newNode.next = this.head;
if (this.head !== null) {
this.head.prev = newNode;
}
this.head = newNode;
} else if (index === this.count) {
// 마지막 index에 추가하는 경우
newNode.next = null;
newNode.prev = this.tail;
this.tail.next = newNode;
} else {
// 원하는 인덱스에 삽입
let currentNode = this.head;
// 목표인덱스 전까지 이동
for (let i = 0; i < index - 1; i++) {
currentNode = currentNode.next;
}
newNode.next = currentNode.next;
newNode.prev = currentNode;
currentNode.next = newNode.next;
currentNode.next = newNode;
newNode.next.prev = newNode;
}
if (newNode.next !== null) {
// 새로 삽입한 노드가 마지막 노드이라면
this.tail = newNode;
}
this.count++;
}
insertLast(data) {
this.insertAt(this.count, data);
}
deleteAt(index) {
if (index >= this.count || index < 0)
throw new Error("제거할 수 없습니다.");
let currentNode = this.head;
// head가 위치하는 node 제거할 때
if (index === 0) {
let deletedNode = this.head;
if (this.head.next === null) {
// head의 다음 노드가 null
this.head = null;
this.tail = null;
} else {
this.head = this.head.next;
this.head.prev = null;
}
this.head = this.head.next;
this.count--;
return deletedNode;
} else if (index === this.count - 1) {
//마지막 노드를 삭제하는 경우
let deletedNode = this.tail;
this.tail.prev.next = null;
this.tail = this.tail.prev;
this.count--;
return deletedNode;
} else {
// head와 tail이 아닌 노드를 삭제할 때
for (let i = 0; i < index - 1; i++) {
currentNode = currentNode.next;
}
let deletedNode = currentNode.next;
currentNode.next.prev = currentNode;
currentNode.next = currentNode.next.next;
this.count--;
return deletedNode;
}
}
deleteLast() {
return this.deleteAt(this.count - 1);
}
getNodeAt(index) {
if (index >= this.count || index < 0) return;
let currentNode = this.head;
for (let i = 0; i < index; i++) {
currentNode = currentNode.next;
}
return currentNode;
}
}
export { Node, DoublyLinkedList };
5. Deque(덱)
head
와 tail
양쪽에서 자유롭게 할 수 있는 자료구조덱의 추상 자료형
1. printAll()
2. addFirst(data)
3. removeFirst()
4. addLast(data)
5. removeLast()
6. isEmpty()
import { DoublyLinkedList } from "./DoublyLinkedList.mjs";
class Deque {
constructor() {
this.list = new DoublyLinkedList();
}
printAll() {
this.list.printAll();
}
// O(1)
addFirst(data) {
this.list.insertAt(0, data);
}
// O(1)
removeFirst() {
this.list.deleteAt(0);
}
addLast(data) {
this.list.insertAt(this.list.count, data);
}
removeLast() {
return this.list.deleteLast();
}
isEmpty() {
return this.list.count === 0;
}
}
export { Deque };
6. Hash Table(해시 테이블)
key
와 value
로 구성되어있고, value
는 연결리스트로 이루어져 있다.해시테이블의 추상자료형
1. get(key)
- 데이터 읽기
2. set(key, value)
- 데이터 삽입
3. remove()
- 데이터 제거
import { DoublyLinkedList } from "./DoublyLinkedList.mjs";
class HashData {
constructor(key, value) {
this.key = key;
this.value = value;
}
}
class HashTable {
constructor() {
this.arr = [];
for (let i = 0; i < 10; i++) {
this.arr[i] = new DoublyLinkedList();
}
}
hashFunction(number) {
return number % 10;
}
set(key, value) {
this.arr[this.hashFunction(key)].insertAt(0, new HashData(key, value));
}
get(key) {
let currentNode = this.arr[this.hashFunction(key)].head;
while (!currentNode) {
if (currentNode.data.key === key) return currentNode.data.value;
currentNode.currentNode.next;
}
return null;
}
remove(key) {
let list = this.arr[this.hashFunction(key)];
let deletedIndex = 0;
while (!currentNode) {
if (currentNode.data.key === key) return list.deleteAt(deletedIndex);
currentNode.currentNode.next;
deletedIndex++;
}
return null;
}
}
export { HashTable };
7. Set
key
가 key
의 역할을 함과 동시에 value
가 된다. 셋의 추상자료형
1. add(data)
- 데이터 삽입
2. isContain(data)
- 데이터 체크
3. remove(data)
- 데이터 제거
4. clear()
- 셋 비우기
5. isEmpty()
- 셋이 비었는지 체크
6. printAll()
- 모든 데이터 출력
import { HashTable } from "./HashTable.mjs";
class HashSet {
constructor() {
this.hashTable = new HashTable();
}
add(data) {
if (!this.hashTable.get(data)) this.hashTable.set(data, -1);
}
isContain(data) {
return this.hashTable.get(data);
}
remove(data) {
this.hashTable.remove(data);
}
clear() {
for (let i = 0; i < this.hashTable.arr.length; i++)
this.hashTable.arr[i].clear();
}
isEmpty() {
let empty = true;
for (let i = 0; i < this.hashTable.arr.length; i++) {
if (this.hashTable.arr[i].count > 0) empty = false;
break;
}
return empty;
}
printAll() {
for (let i = 0; i < this.hashTable.arr.length; i++) {
let currentNode = this.hashTable.arr[i].head;
while (currentNode) {
console.log(currentNode.data.key);
currentNode = currentNode.next;
}
}
}
}
export { HashSet };
강의의 자료구조파트를 듣고나서 정리를 한 번 하면 더 정리가 잘 되겠다 싶어 기록을 남기게 됐다. 이해하기 쉬운 그림을 함께 보여주면서 설명을 하셔서 개념을 책으로만 접하는 것 보다 이해가 잘 됐다. 그리고 직접 자료구조를 구현해 보니까 글로만 접했을 때 보다 훨씬 구조적인 이해가 잘 되었다. 여러모로 개념을 이해하기에 좋은 강의라고 생각한다. 알고리즘 파트부분도 얼른 완강 해서 또 정리해야지!