- 자료구조는 크게 <메모리 공간 기반의 연속 방식>, <포인터 기반의 연결 방식> 으로 나뉜다.
- 배열은 이 중에서 연속 방식의 가장 기본이 되는 자료형이다.
- 그리고 배열은 데이터를 나열하고, 각 데이터를 인덱스로 접근할 수 있도록 구성한 데이터의 구조이다.
- 파이썬에서는 리스트 타입이 배열 기능 제공
2.1 파이썬에서 리스트, 딕셔너리, 튜플의 차이점
* 참고) 그럼 튜플을 굳이 왜 사용하나요 ?
보통 튜플은 요소가 절대 변경되지 않고 유지되어야 할 때 사용된다. 튜플을 만든 상태에서 요소를 변경하려면 에러가 발생하기 때문에 요소를 실수로 변경하는 경우를 방지해준다. 예를 들면, 사람들의 주민번호를 변경되지 않기 때문에 튜플로 저장할 수 있지만 핸드폰 번호는 변경되는 경우가 종종 있으므로 리스트로 저장하면 된다.
2.2. 리스트로 배열 사용
1차원 배열
2차원 배열
리스트에 데이터 추가하기 (append)
리스트 특정 인덱스에 데이터 추가하기 (insert)
리스트 정렬하기 (내림차순 : reverse)
리스트 정렬하기 (오름차순 : sort)
기존 리스트에 새로운 리스트 이어붙여서 추가하기 (extend)
리스트의 특정 데이터 개수 출력 (count)
특정 데이터의 인덱스값 출력하기 (index)
마지막 데이터값 제거하기 (pop)
특정 데이터값 제거하기 (첫 번째로 나오는 데이터 제거: remove)
정말 리스트를 포함한 시퀀스의 기능은 무수히 많기 때문에 리스트 기능 중 자주 쓰이는 것만 정리를 해봤다. 이제는 배열에 대한 문제를 풀어보도록 하자.
: 배열을 입력받아 합으로 0을 만들 수 있는 3개의 엘리먼트를 출력하라.
- 입력
nums = [-1, 0, 1, 2, -1, -4]- 출력
[ [ -1, 0, 1], [-1, -1, 2] ]
이 문제의 접근 방식은 브루트 포스와 투 포인터로 합계산을 얘기하고 싶다.
<브루트 포스로 계산>
def threeSum(self, nums: List[int]) -> List[List[int]]:
results = [] # 답을 담을 그릇
nums.sort() # 리스트를 정렬해 가장 낮은 순으로 정렬
# 브루트 포스 n^3 반복
for i in range(len(nums) -2):
# 중복된 값 건너뛰기
if i > 0 and nums[i] == nums[i - 1]:
continue
for j in range(i + 1, len(nums) - 1):
if j > i + 1 and nums[j] == nums[j - 1]:
continue
for k in range(j + 1, len(nums)):
if k > j + 1 and nums[k] == nums[k - 1]:
continue
if nums[i] + nums[j] + nums[k] == 0:
results.append((nums[i], nums[j], nums[k]))
return results
이 풀이는 리트코드에 제출하면 시간초과가 나온다. 확실히 과격한 방법이다. 다음으로는 투 포인터로 계산하는 방식이다.
< 투 포인터로 합 계산>
def threeSum(self, nums: List[int]) -> List[List[int]]:
results = [] # 답을 담을 그릇
nums.sort() # 리스트를 정렬해 가장 낮은 순으로 정렬
for i in range(len(nums) - 2):
# 중복된 값 건너뛰기
if i > 0 and nums[i] == nums[i - 1]:
continue
# 간격을 좁혀가며 합 sum 계산
left, right = i + 1, len(nums) - 1
while left < right:
sum = nums[i] + nums[left] + nums[right]
if sum < 0:
left += 1 # 작은 순으로 정렬했기 때문에 left 값을 올린다.
elif sum > 0:
right -= 1 # right 값을 내린다.
else:
# sum = 0인 경우이므로 정답 및 스킵 처리
results.append((nums[i], nums[left], nums[right]))
# left, right 값이 옮겨가면서 같은 수가 나올 수 있다. 따라서
# 같은 값이 나오면 중복되지 않게 넘겨줘야 한다.
while left < right and nums[left] == nums[left + 1]:
left += 1
while left < right and nums[right] == nums[right - 1]:
right -= 1
left += 1
right -= 1
return results
<브루트 포스> 풀이 방법보다 코드가 길지만 확실히 더 빠른 장점을 보여준다.
: n개의 페어를 이용한 min(a, b)의 합으로 만들 수 있는 가장 큰 수를 출력하라.
-입력
[1, 4, 3, 2]
-출력
4
이 문제는 오름차순 풀이, 짝수 번째 값 계산 풀이, 파이썬 다운 방식 풀이 3가지의 방식 풀이를 해보겠다.
< 오름차순 풀이>
from typing import List
def arrayPairSum(self, nums: List[int]) -> int:
sum = 0
pair = [] #
nums.sort() # 작은 순으로 정렬 ex) [1,2,3,4]
for n in nums:
# 앞에서부터 오름차순으로 페어를 만들어서 합 계산
pair.append(n)
if len(pair) == 2:
# ex) pair = [1, 2] -> min = 1
# ex) pair = [3, 4] -> min = 3
sum += min(pair)
# 물론 다 썻으면 pair를 비워줘야 한다.
pair = []
return sum
< 짝수 번째 값 계산>
from typing import List
def arrayPairSum(self, nums: List[int]) -> int:
sum = 0
nums.sort() # ex) [1,3,2,4] -> [1,2,3,4]
# enumerate는 (i:인덱스 와 n:변수안의 값)으로 뽑아주는 함수이다.
for i, n in enumerate(nums):
# 짝수 번째 값의 합 계산
# 여기서부터 이해가 힘들진 모르지만, [1,2,3,4] 리스트를
# 인덱스 0번과 2번인 짝수만 더하면 답이 나오는 것을 볼 수 있다.
if i % 2 == 0:
sum += n
return sum
< 파이썬 다운 방식 풀이>
from typing import List
def arrayPairSum(self, nums: List[int]) -> int:
return sum(sorted(nums)[::2])
# 참 어이가 없겠지만 sorted로 nums를 정렬해준 다음
# 슬라이싱으로 2칸씩 가면서 값을 더해주겠다는 코드이다.
# 이런걸 봤을 때 파이썬언어가 참 파격적인 언어인 것을 느낄 수 있다.