👩💻배경
코테 문제 풀이 중 파이썬으로 풀이를하면
heapq
라는 내장 라이브러리를 활용해 최소힙을 이용해 쉽게 문제풀이를 할 수 있지만, 안타깝게도 js에는 최소힙을 만들어줘야한다.
또deque
를 통해 쉽게 double ended queue 자료구조를 쉽게 활용해서popleft()
를 통해 O(1)시간으로 덱의 맨 앞요소를 제거할 수도 있지만popleft()
와 같이 시간복잡도를 확 줄이는 방식 말고 class로 deque를 구현해야 O(1)을 만족하는 듯 싶다.
js자체가 브라우저에서 돌아가는 lightweight언어라서 성능최적화 자체를 무시할 경우가 많지만 코테라는 특수성 등 특정 환경에서는 비용적인 측면에서 최적화를 하는 습관화하는게 좋다고 판단하여, 이 기회에 정리를 해봤다.
const arr = ['A', 'B', 'C', 'D', 'E', 'F'];
arr
에서 arr[2]
에 접근(Access)
할 때 어떤 일들이 일어나나?
❌ arr라는 배열을 0번째 인덱스부터 순회하면서 2번째 인덱스 읽기? O(n)
?
⭕ 인덱스로 접근할 때는 물리적 메모리 주소와 논리적 메모리 주소 영역이 동일하므로 일일히 배열을 순회하는 것이 아니라 바로 인덱스에 해당되는 주소로 값을 가져올 수 있다. O(1)
!
배열값을 수정하는 것
arr[2]='G'
또한 O(1)
! : 인덱스는 주소값인데, 'G'라는 값을 해당 주소에다가 할당해주는 것이므로 접근하기(O(1)
)이고 할당해주기(O(1)
)로 시간복잡도는 O(1)
! findIndex('C')
, ES5로는 indexOf('C')
에 해당되는 함수는 arr에서 특정 값이 있는 인덱스값을 찾고 싶을 때 쓴다.O(n)
시간복잡도를 지니는데, 0번 인덱스부터 시작해서 구할는 인자 'C'의 인덱스를 찾을 때까지 순회한다.O(1)
const arr = ['A', 'B', 'C', 'D', 'E', 'F'];
arr.push('G');
console.log(arr); /*['A', 'B', 'C', 'D', 'E', 'F','G']*/
O(n)
const arr = ['A', 'B', 'C', 'D', 'E', 'F'];
arr.unshift('G');
console.log(arr); /*['G','A', 'B', 'C', 'D', 'E', 'F']*/
arr[0]
의 주소값 자리의 값에 'G'를 할당하고, 다음 arr[1]
의 주소값 자리에는 이전에 인덱스 0이었던 'A'값이 들어감.O(n)
)주고 값을 할당(O(1)
)해줘야하므로 시간 복잡도는 O(n)
O(1)
const arr = ['A', 'B', 'C', 'D', 'E', 'F'];
console.log(arr.pop()); //'F'
console.log(arr); /*['A', 'B', 'C', 'D', 'E']*/
O(n)
unshift()
함수에서 맨 앞에 요소를 추가해줬을 때 뒤의 인덱스들을 하나씩 밀어줘야하는 상황과 동일const arr = ['A', 'B', 'C', 'D', 'E', 'F'];
console.log(arr.shift()); //'A'
console.log(arr); /*['B', 'C', 'D', 'E','F']*/
indexOf()
와 같이)에 삭제/삽입하므로 특정 인덱스에 접근하기 위해 시간복잡도는 O(n)
const arr = ['A', , 'B', ,'C', 'D', ,'E', 'F'];
console.log(arr.splice(1,2)); // [empty,'B']
console.log(arr); // ['A', empty, 'C', 'D', empty, 'E', 'F']
this
의 length 속성을 읽어서 integer 키의 속성을 length속성에 맞게 업데이트해줌const arrayLike = {
length: 3,
unrelated: "foo",
0: 5,
2: 4,
};
console.log(Array.prototype.splice.call(arrayLike, 0, 1, 2, 3));
// [ 5 ]
console.log(arrayLike); //0인 키값부터 1개 삭제하고 해당 키에 차례대로 2, 3 넣기 0의 값은 5 👉 2 , 1 👉 2 , 3 👉 4
// { '0': 2, '1': 3, '3': 4, length: 4, unrelated: 'foo' }
splice(start) // start인덱스부터 요소 삭제
splice(start, deleteCount) // start인덱스부터 deleteCount개의 요소 삭제
splice(start, deleteCount, item0) // start인덱스부터 deleteCount개의 요소 삭제하고 start위치에 item0 삽입
splice(start, deleteCount, item0, item1) // start인덱스부터 deleteCount개의 요소 삭제하고 start위치에 item0, item1 삽입
splice(start, deleteCount, item0, item1, /* … ,*/ itemN) // start인덱스부터 deleteCount개의 요소 삭제하고 start위치에 item1, /* … ,*/ itemN 삽입
start
: 필수⭕인 인자.start+array.length
)deleteCount
: 필수❌ 인자.start
위치부터 샂게할 element의 개수start
보다 값이 크거나 같으면 start위치부터 끝까지 모든 요소들을 삭제Infinity
를 전달해야 함 (undefined
전달 시 0으로 변환됨)item0,...,itemN
: 필수❌ 인자. 없으면 삭제만 함.O(N * log N)
: quicksort 알고리즘을 사용함!O(n)
const arr1 = ['A', 'B', 'C', 'D', 'E', 'F'];
const arr2 = arr1.concat('G'); //['A', 'B', 'C', 'D', 'E', 'F','G']
O(n+m)
const arr1 = ['A', 'B', 'C', 'D', 'E', 'F'];
const arr2 = arr1.concat('G');
const arr3 = arr1.concat(arr2);
console.log(arr3);//['A', 'B', 'C', 'D', 'E', 'F','A', 'B', 'C', 'D', 'E', 'F','G']
😊 마치면서
- 물론 시간복잡도도 줄이면서 깔끔한 코드가 best case
- But 어쩔 수 없이 내장함수를 통해서 코드길이를 단축시키거나 깔끔하게 하기 위해서는 시간복잡도를 좀 희생해야할 경우도 있고, 반대로 시간복잡도가 worst case인 경우 문제가 될 경우에는 깔끔한 코드를 포기해야할 때도 있는 것같다.
시간복잡도 함수명 O(1) at() , entries() , every() , isArray() , keys() , pop() , push() , shift() , values() , with() O(n) concat() , copyWithin() , fill() , filter() , find() , findIndex() , findLast() , findLastIndex() , flat() , flatMap() , forEach() , from() , fromAsync() , group() , groupToMap() , includes() , indexOf() , join() , lastIndexOf() , map() , of() , reduce() , reduceRight() , reverse() , slice() , some() , splice() , toLocaleString() , toReversed() , toSpliced() , toString() , unshift() O(n log n) , linear time on average sort() , toSorted()