지난 시간에 {}(객체 리터럴)을 이용해 만든 Deque 클래스를 배열을 활용해 만들어 보겠습니다.
배열의 크기를 지정해서 사용하는 방법으로 메모리를 덜 사용한다는 장점이 있습니다.
class Deque {
#deque;
#capacity = 0; // 배열의 크기 제한
#head = 0;
#tail = -1;
#size = 0;
constructor(capacity) {
this.#capacity = capacity;
this.#deque = new Array(capacity);
}
isEmpty() {
return this.#size === 0;
}
get size() {
return this.#size;
}
pushFront(x) {
if (this.#size < this.#capacity) {
this.#head = (this.#head - 1 + this.#capacity) % this.#capacity;
this.#deque[this.#head] = x;
this.#size++;
}
}
pushBack(x) {
if (this.#size < this.#capacity) {
this.#tail = (this.#tail + 1) % this.#capacity;
this.#deque[this.#tail] = x;
this.#size++;
}
}
popFront() {
if (this.isEmpty()) return null;
const data = this.#deque[this.#head];
this.#head = (this.#head + 1) % this.#capacity;
this.#size--;
return data;
}
popBack() {
if (this.isEmpty()) return null;
const data = this.#deque[this.#tail];
this.#tail = (this.#tail - 1 + this.#capacity) % this.#capacity;
this.#size--;
return data;
}
print() {
let ret = [];
for (let i = this.#head; i < this.#head + this.#capacity; i++) {
ret.push(this.#deque[i % this.#capacity]);
}
console.log(ret.join(" "));
}
}
▲배열을 활용한 덱
// 테스트
const deque = new Deque(6);
for(let i=1; i<6; i++) {
deque.pushBack(i);
}
deque.print(); // 1 2 3 4 5
deque.pushFront(6); deque.print(); // 6 1 2 3 4 5
deque.popBack(); // 5
deque.popFront(); // 6
deque.print(); // 1 2 3 4 5 6
잘 동작한다고 생각했는데??? 마지막에 print()를 동작시키면 1 2 3 4 가 나와야 정상입니다. 하지만 이상하게 1 2 3 4 5 6 이 나옵니다. 왜 그럴까? 하고 생각을 해봤는데... 결론은 pop을 할 때 배열에서 값을 제거하지 않기 때문입니다.
이에 두 가지 해결 방법이 있습니다.
- print 함수를 변경
- pop 함수를 변경
우선 print 함수를 변경해도 괜찮은 이유는 저희가 head와 tail 두 포인터를 활용해 값을 추가하고 삭제하기 때문에 남아있는 값은 문제가 되지 않습니다.
print() {
let ret = [];
// this.#capacity를 실제 크기인 this.#size만큼만 출력
for (let i = this.#head; i < this.#head + this.#size; i++) {
ret.push(this.#deque[i % this.#capacity]);
}
console.log(ret.join(" "));
}
그래도 찝찝함이 남아있습니다. 두 번째 방법인 pop 함수를 변경해 보겠습니다. 여기서도 여러 방법이 있습니다. 코드로 보겠습니다.
popFront() {
if (this.isEmpty()) return null;
const data = this.#deque[this.#head];
// 방법1. undefined 또는 null 값 할당하기
this.#deque[this.#head] = undefined;
this.#head = (this.#head + 1) % this.#capacity;
this.#size--;
return data;
}
popBack() {
if (this.isEmpty()) return null;
const data = this.#deque[this.#tail];
// 방법2. delete 키워드 사용하기
delete this.#deque[this.#tail];
this.#tail = (this.#tail - 1 + this.#capacity) % this.#capacity;
this.#size--;
return data;
}
코드에서는 두 가지 방법이 제시됩니다. 하지만 방법 2보다는 방법 1을 추천합니다. delete 키워드를 사용해서 배열의 요소를 삭제하는 경우 희소(sparse) 상태로 만들기 때문에 내부 동작에 이상이 생길 수 있습니다. 아래 코드로 보겠습니다.
const a = [1,2,3];
delete a[0];
a[2] = undefined;
for(let i=0; i<a.length; i++) {
console.log(a[i]);
}
// undefined 2 undefined
a.forEach(el => console.log(el));
// 2 undefined
console.log(Object.keys(a));
// ['1','2']
일반 for문을 사용하는 경우 문제없이 delete로 사라진 값이 undefined로 인식됩니다.
하지만 내장 함수의 경우 0번째 인덱스를 인식할 수가 없습니다. 그 이유는 Object.keys()로 보면 0번 키가 사라졌기 때문입니다. 그렇기 때문에 delete보다 undefined를 이용하는 것이 좋습니다.
다시 생각해 보면 배열이라는 것으로 이용할 때 객체에서 이용되는 delete 키워드를 사용하는 것은 좋지 못한 방법인 것 같습니다.
이렇게 크기를 고정시켜 메모리를 덜 사용하는 덱을 구현해 봤습니다.
'알고리즘&코테' 카테고리의 다른 글
| [자료구조 활용] Deque 이렇게도 활용할 수 있다! (0) | 2025.05.22 |
|---|---|
| [자료구조] Deque with JS 리팩토링2 (0) | 2025.05.20 |
| [자료구조] Deque with JS 리팩토링 (0) | 2025.05.15 |
| [자료구조/미완] Linked List(연결리스트) with JS (0) | 2025.05.14 |
| [자료구조] Deque with JS (0) | 2025.05.09 |