zero-wiki Help

배열

배열 개념

배열은 같은 타입의 원소들을 효율적으로 관리할 수 있는 기본 자료형입니다. 하나의 변수 이름으로 동일한 데이터를 그룹화하여 관리할 수 있고, 인덱스라는 고유한 값을 통하여 원하는 데이터에 임의 접근할 수 있습니다.

배열 선언

배열을 선언하는 방법은 다음과 같습니다.

리터럴을 이용하는 방법

const arr = [1, 2, 3, 4, 5, 6];

배열 생성자를 이용하는 방법

const arr1 = new Array(6); // [undefined, undefined, undefined, undefined, undefined, undefined] const arr2 = [...new Array(6)].map((_, i) => i + 1); // [1, 2, 3, 4, 5, 6] const arr3 = Array.from({length: 5},(_, i) => i + 1); // [1, 2, 3, 4, 5, 6] const arr4 = new Array(6).fill(0); // [0, 0, 0, 0, 0, 0]

배열은 인덱스 0부터 시작됩니다. 만약 3번째 요소에 접근하고 싶다면 arr[2]와 같이 접근하면 됩니다.

배열과 차원

배열은 2차원 배열, 3차원 배열과 같이 다차원 배열을 사용할 때도 많습니다. 하지만 컴퓨터 메모리 구조상 메모리의 구조는 1차원이므로 2차원, 3차원 배열도 실제로는 1차원 공간에 저장합니다.

2차원 배열

2차원 배열은 1차원 배열을 확장한 것입니다.

// 2차원 배열을 리터럴로 표현 const arr = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]; // arr[2][1]에 저장된 값을 출력 console.log(arr[2][1]); // 8 // arr[2][1]에 저장된 값을 10으로 변경 arr[2][1] = 10; // 변경된 값을 출력 console.log(arr[2][1]); // 10
// 크기가 4 * 4인 배열을 선언하는 예시 const arr = [...new Array(4)].map((_, i) => new Array(4).fill(i)); // [[0, 0, 0, 0], [1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3]] console.log(arr[3][1]) // 3; console.log(arr[1][2]) // 1;

다음과 같이 표시할 수 있습니다.

i \ j

0

1

2

3

0

0

0

0

0

1

1

1

1

1

2

2

2

2

2

3

3

3

3

3

배열의 효율성

배열 연산의 시간 복잡도

배열은 임의 접근이라는 인덱스를 이용한 접근 방법으로 접근할 경우 배열의 모든 위치에 있는 데이터에 단 한 번에 접근할 수 있습니다. 따라서 시간 복잡도는 입니다. 하지만 배열에 데이터를 추가하는 경우는 어디에 저장을 하느냐에 따라서 달라집니다.

맨 뒤에 삽입할 경우

맨 뒤에 삽입할 경우 임의 접근을 통해 바로 접근할 수 있으며 데이터를 삽입해도 다른 데이터 위치에 영향을 주지 않습니다. 따라서 시간 복잡도는 입니다.

맨 앞에 삽입할 경우

맨 앞에 삽입할 경우 기존 데이터들을 뒤로 한 칸씩 밀어야 합니다. 즉, 미는 연산이 필요합니다. 만약 데이터가 4개 있다고 가정을 한다면 arr[5] = arr[4], arr[4] = arr[3]과 같이 미는 연산이 필요합니다. 따라서 시간 복잡도는 이 됩니다.

중간에 삽입할 경우

중간에 삽입할 경우 중간에 삽입할 인덱스 뒤에 있는 데이터의 개수만큼 미는 연산이 필요합니다. 따라서 밀어야 하는 데이터의 갯수가 N이라면 시간 복잡도는 이 됩니다.

배열을 선택할 때 고려할 점

데이터에 자주 접근하거나 읽어야 하는 경우 배열을 사용하면 좋은 성능을 낼 수 있습니다. 하지만 여러가지 고려사항을 생각해 보아야 합니다.

  1. 할당할 수 있는 메모리 크기를 확인해야합니다.: 운영체제마다 할당할 수 있는 메모리의 한계치는 다르지만 1차원 배열의 경우 1000만 개, 2차원 배열의 경우 3000 * 3000 크기를 최대로 생각합니다.

  2. 중간에 데이터 삽입이 많은지 확인을 해봐야합니다.: 배열은 선형 자료구조이기에 중간이나 처음에 데이터를 빈번하게 추가하거나 삭제할 경우 시간 복잡도가 높아져 시간 초과가 날 수 있습니다.

자주 활용하는 배열 기법

배열에 데이터 추가

push() 메서드를 활용하여 데이터를 추가

맨 끝에 데이터를 추가하려면 push 메소드를 사용하면 됩니다.

const arr = [1, 2, 3]; arr.push(4); console.log(arr); // [1, 2, 3, 4]

concat() 메서드로 데이터를 추가

concat() 메서드를 활용하여 배열에 든 데이터를 추가할 수도 있습니다.

let arr = [5, 6, 7]; arr = arr.concat([8, 9]) console.log(arr); // [5, 6, 7, 8, 9]

스프레드 연산자로 데이터를 추가

스프레드 연산자 ...를 이용하여 데이터를 추가할 수도 있습니다.

let arr = [1, 2, 3]; arr = [...arr, ...arr, ...[4, 5]]; console.log(arr); // [1, 2, 3, 1, 2, 3, 4, 5]

unshift() 메서드를 활용하여 데이터를 추가

배열의 맨 앞에 데이터를 추가할 수 있습니다. 원래 시간 복잡도는 이 걸리지만, 자바스크립트 엔진이 최적화를 하여 이보다 더 적은 시간 복잡도로 처리합니다.

const arr = [1, 2, 3]; arr.unshift(4); console.log(arr); // [4, 1, 2, 3]

splice() 메서드로 데이터를 추가

배열 중간에 데이터를 추가하기 위해서는 splice() 메서드를 사용해야 합니다.

사용 법은 다음과 같습니다.

array.splice(startIndex, deleteCount, newItem); const arr = [0, 1, 2, 3, 4]; arr.splice(2, 0, 5); console.log(arr); // [0, 1, 5, 2, 3, 4]

고차 함수를 이용하여 데이터에 특정 연산 적용

map, filter, reduce와 같이 유용한 고차함수를 사용하여 기존 배열에 기반하여 새로운 배열을 만드는 것이 가능합니다. 고차 함수를 이용할 경우 기존 반복문, 조건문을 이용한 복잡한 로직을 대체할 수 있습니다.

배열에 제곱 연산 적용 예 (map)

배열의 모든 데이터에 제곱 연산을 적용하려면 다음과 같이 작성할 수 있습니다.

const numbers = [2, 4, 5, 10, 20]; const squares = numbers.map(num => num * num); // [4, 16, 25, 100, 400]

map 메소드를 활용하면 배열 내의 각 데이터를 변경할 수 있습니다.

짝수 필터링의 예 (filter)

filter 메소드를 활용하면 원하는 조건에 해당하는 값만 남긴 배열을 만들 수 있습니다.

const numbers = [2, 4, 7, 9, 10]; const evens = numbers.filter(num => num % 2 === 0); // [2, 4, 10]

전체 합의 예 (reduce)

reduce 메소드를 활용하면 배열 전체 데이터를 하나로 합칠 수 있습니다.

const numbers = [10, 20, 30, 40, 50]; const sum = numbers.reduce((a, b) => a + b, 0); // 150

reduce 메소드는 앞의 map이나 filter와 약간 다릅니다. 해당 함수의 인자로는 (value, index, array) 와 같이 각각 현재 바로보고 있는 값, 순번, 배열의 주소를 나타냈지만 reduce는 다릅니다.

reduce는 다음과 같이 인자를 생각할 수 있습니다. (prev, next) prev에는 이전 값을 담고있고 next는 다음 값을 바라보고 있습니다. 때문에 순회를 할 때 다음과 같이 변화하게 됩니다.

  1. prev = 0, next = 10 (초기 값 0이 prev에 들어가게 됩니다.)

  2. prev = 10, next = 20 (10이 이전 값으로 합쳐져 있으며, 다음 값 20을 바라봅니다.)

  3. prev = 30, next = 30 (30이 이전 값으로 합쳐져 있으며, 다음 값 30을 바라봅니다.)

  4. prev = 60, next = 40 (60이 이전 값으로 합쳐져 있으며, 다음 값 40을 바라봅니다.)

  5. prev = 100, next = 50 (100이 이전 값으로 합쳐져 있으며, 다음 값 50을 바라봅니다.)

  6. prev = 150 (종료)

몸풀기 문제

배열 정렬하기

배열을 정렬을 할 때 다음과 같이 정렬을 하는 실수를 많이 하곤 합니다.

const number = [1, 100, 2, 50, 300, 27]; number.sort(); console.log(number); // [1, 100, 2, 27, 300, 50]

원하는 결과는 1, 2, 27, 50, 100, 300으로 숫자가 낮을 순으로 높은 순으로 나오기를 기대하였습니다. 하지만 이는 자바스크립트의 sort 메소드를 생각하지 않고 진행한 것입니다.

위와 같이 정렬하는 성격이 있으므로 100 다음 2가 들어오는 것입니다. 맨앞에 문자를 비교하여 1과 2를 비교하였을때 2가 더 크므로 100보다 2가 뒤에 오게 되는 것입니다.

따라서 숫자에 대해 정렬을 하려면 sort 메서드에 익명 함수로 조건을 전달해야 합니다.

number.sort((a, b) => a - b);

이런식으로 조건을 전달하여 정렬한다면 문자열로 형변환하여 비교를 하지 않을 것이고 때문에 원하는 오름차순대로 배열을 재배치하게 됩니다.

만약 map, filter와 같이 기존 배열을 내버려둔채로 진행을 하고 싶다면 toSorted() 메서드를 이용하면 되지만 ECMA2023에 추가된 기능으로 sort 기능을 사용하는 것이 좋습니다.

sort 함수는 의 시간복잡도를 활용하기에 적극 활용하는 것을 추천합니다.

배열 제어하기

만약 중복된 값을 제거하고 싶을 때 앞서 설명에서 봤듯이 Set 자료형을 사용하면 됩니다.

const uniqueArr = [...new Set(arr)]

만약 반복문을 통해 일일이 데이터를 확인하여 중복값을 제거한다면 알고리즘의 시간 복잡도는 로 성능이 좋지 않습니다.

Set을 활용한 중복 원소를 제거하는데 걸리는 시간은 입니다. 만약 다시 정렬까지 한다면 시간 복잡도는 이므로 최종 시간 복잡도는 입니다.

Last modified: 07 August 2024