# 원본 리스트 (내부에 리스트가 포함됨)
original_list = [1, [2, 3], 4]
# 깊은 복사 함수 정의
def deep_copy(obj):
if isinstance(obj, list):
# 리스트의 경우 요소를 새 리스트로 복사
new_list = []
for item in obj:
new_list.append(deep_copy(item)) # 재귀적으로 요소를 복사
return new_list
else:
# 리스트가 아닌 경우 (기본 자료형 등), 그대로 반환
return obj
# 깊은 복사 수행
deep_copy_list = deep_copy(original_list)
# 원본과 복사본 출력
print("Original list:", original_list)
print("Deep copy:", deep_copy_list)
# 리스트 요소 변경 (깊이 1)
original_list[1][0] = 'A'
# 변경 후 출력
print("After deep copy, original list:", original_list)
print("After deep copy, deep copy:", deep_copy_list)
결과 설명:
deep_copy() 함수는 재귀적으로 리스트의 요소를 탐색하면서 모든 요소를 새로운 객체로 복사합니다.
이 함수는 리스트를 인자로 받고, 리스트 내부의 모든 요소가 기본 자료형이 될 때까지 재귀적으로 호출하여 복사를 수행합니다.
original_list의 [2, 3] 부분은 새로운 리스트로 복사되어 deep_copy_list와 독립적인 객체가 됩니다.
따라서 original_list를 변경해도 deep_copy_list에는 어떠한 영향도 미치지 않습니다.
실행 결과:
Original list: [1, [2, 3], 4]
Deep copy: [1, [2, 3], 4]
After deep copy, original list: [1, ['A', 3], 4]
After deep copy, deep copy: [1, [2, 3], 4]
이 예제에서는 copy 모듈을 사용하지 않고도 깊은 복사를 수행하는 간단한 방법을 구현했습니다. 하지만 파이썬의 copy.deepcopy() 함수는 이러한 복잡한 작업을 대신 처리해 주기 때문에 실제 프로덕션 환경에서는 copy 모듈을 사용하는 것이 좋습니다.
파이썬에서 객체를 복사할 때 얕은 복사(shallow copy)와 깊은 복사(deep copy)의 개념이 중요합니다. 이 두 가지 복사 방법은 객체의 중첩 구조와 상호작용 방식에 따라 다릅니다.
얕은 복사 (Shallow Copy)
얕은 복사는 객체를 복사할 때, 원본 객체의 요소들을 새로운 객체에 복사하지만, 요소들이 참조하는 객체들은 원본과 동일한 객체를 참조합니다. 즉, 복사된 객체와 원본 객체는 같은 객체를 공유하게 됩니다.
파이썬에서는 copy 모듈의 copy() 함수나 객체의 copy() 메서드를 사용하여 얕은 복사를 수행할 수 있습니다.
import copy
# 원본 리스트
original_list = [1, [2, 3], 4]
# 얕은 복사
shallow_copy = copy.copy(original_list)
# 원본과 복사본 출력
print("Original list:", original_list)
print("Shallow copy:", shallow_copy)
# 리스트 요소 변경 (깊이 1)
original_list[1][0] = 'A'
# 변경 후 출력
print("After shallow copy, original list:", original_list)
print("After shallow copy, shallow copy:", shallow_copy)
결과 설명:
original_list는 [1, [2, 3], 4]로 초기화됩니다.
shallow_copy는 copy.copy(original_list)를 통해 얕은 복사가 수행됩니다.
original_list의 두 번째 요소인 [2, 3]는 내부 리스트입니다.
original_list[1][0]을 'A'로 변경하면, original_list와 shallow_copy 모두에 영향을 미칩니다.
이는 얕은 복사에서는 내부 리스트가 동일한 객체를 참조하기 때문에 발생하는 현상입니다.
실행 결과:
Original list: [1, [2, 3], 4]
Shallow copy: [1, [2, 3], 4]
After shallow copy, original list: [1, ['A', 3], 4]
After shallow copy, shallow copy: [1, ['A', 3], 4]
깊은 복사 (Deep Copy)
깊은 복사는 객체와 그 객체가 참조하는 모든 객체들까지 완전히 새로운 객체로 복사하는 방법입니다. 따라서 원본 객체와 복사된 객체는 완전히 독립적인 객체가 됩니다.
깊은 복사는 copy 모듈의 deepcopy() 함수나 객체의 copy.deepcopy() 메서드를 사용하여 수행할 수 있습니다.
import copy
# 원본 리스트
original_list = [1, [2, 3], 4]
# 깊은 복사
deep_copy = copy.deepcopy(original_list)
# 원본과 복사본 출력
print("Original list:", original_list)
print("Deep copy:", deep_copy)
# 리스트 요소 변경 (깊이 1)
original_list[1][0] = 'A'
# 변경 후 출력
print("After deep copy, original list:", original_list)
print("After deep copy, deep copy:", deep_copy)
결과 설명:
original_list는 [1, [2, 3], 4]로 초기화됩니다.
deep_copy는 copy.deepcopy(original_list)를 통해 깊은 복사가 수행됩니다.
original_list[1][0]을 'A'로 변경하더라도, deep_copy에는 어떠한 영향도 미치지 않습니다.
이는 깊은 복사에서는 내부 리스트까지 새로운 객체로 복사하기 때문에 발생하는 현상입니다.
실행 결과:
Original list: [1, [2, 3], 4]
Deep copy: [1, [2, 3], 4]
After deep copy, original list: [1, ['A', 3], 4]
After deep copy, deep copy: [1, [2, 3], 4]
요약
얕은 복사는 객체의 내용을 새로운 객체에 복사하지만, 내부 객체들은 원본과 동일한 객체를 참조합니다.
깊은 복사는 객체와 그 객체가 참조하는 모든 객체들까지 새로운 객체로 복사하여 완전히 독립적인 복사본을 생성합니다.
객체의 중첩 구조가 복잡할 때는 깊은 복사를 사용하여 예기치 않은 부작용을 방지하는 것이 좋습니다.
이때 시간초과로 인한 실패가 많은데 해결하기 위해서는 더이상 플레이어가 없을때(n==0)는 계산 없이 바로 실패율을 0으로 만든다.
def solution(N, stages):
answer = []
n = len(stages)
for i in range(N):
if n==0:#플레이어가 없을때 계산없이 0으로 추가함
answer.append(0)
else:
c = stages.count(i+1)
answer.append(c/n)
n -= c
answer = sorted(range(1,N+1), key=lambda k: answer[k-1], reverse=True)
return answer
실행결과
테스트 1 〉
통과 (0.01ms, 9.99MB)
테스트 2 〉
통과 (0.26ms, 10.1MB)
테스트 3 〉
통과 (80.91ms, 10.2MB)
테스트 4 〉
통과 (434.18ms, 10.7MB)
테스트 5 〉
통과 (1687.00ms, 15.1MB)
테스트 6 〉
통과 (0.83ms, 10.4MB)
테스트 7 〉
통과 (11.96ms, 10.3MB)
테스트 8 〉
통과 (385.14ms, 10.8MB)
테스트 9 〉
통과 (1593.72ms, 14.9MB)
테스트 10 〉
통과 (149.76ms, 10.9MB)
테스트 11 〉
통과 (444.06ms, 10.8MB)
테스트 12 〉
통과 (450.53ms, 11.1MB)
테스트 13 〉
통과 (514.34ms, 11.3MB)
테스트 14 〉
통과 (0.04ms, 10.1MB)
테스트 15 〉
통과 (13.92ms, 10.7MB)
테스트 16 〉
통과 (5.73ms, 10.4MB)
테스트 17 〉
통과 (17.65ms, 10.5MB)
테스트 18 〉
통과 (6.00ms, 10.3MB)
테스트 19 〉
통과 (1.26ms, 10.2MB)
테스트 20 〉
통과 (20.84ms, 10.4MB)
테스트 21 〉
통과 (18.46ms, 10.8MB)
테스트 22 〉
통과 (1373.85ms, 18.3MB)
테스트 23 〉
통과 (10.45ms, 11.6MB)
테스트 24 〉
통과 (62.01ms, 11.6MB)
테스트 25 〉
통과 (0.01ms, 10.2MB)
테스트 26 〉
통과 (0.01ms, 10.1MB)
테스트 27 〉
통과 (0.01ms, 10.1MB)
테스트 3, 4, 8, 9 등 몇몇 테스트의 경우 큰 시간이 필요하다. 그래서 좀 더 효율적으로 시간을 줄이기 위해서 아래와 같이 count() 함수를 사용하지 않고 미리 계산된 결과만 불러오도록 수정하였다.
def solution(N, stages):
answer = []
n = len(stages)
temp =[0]*N #count 를 미리 계산
for v in stages:
if v<=N:
temp[v-1] += 1
for i in range(N):
if n==0:
answer.append(0)
else:
c = temp[i] # count 값만 호출함
answer.append(c/n)
n -= c
answer = sorted(range(1,N+1), key=lambda k: answer[k-1], reverse=True)#리스트 인덱스 정렬
return answer
실행결과: 아래와 같이 시간이 많이 단축됨을 확인할 수 있다.
테스트 1 〉
통과 (0.01ms, 10.1MB)
테스트 2 〉
통과 (0.18ms, 10.1MB)
테스트 3 〉
통과 (1.14ms, 10.3MB)
테스트 4 〉
통과 (9.74ms, 10.8MB)
테스트 5 〉
통과 (20.49ms, 14.8MB)
테스트 6 〉
통과 (0.12ms, 10.3MB)
테스트 7 〉
통과 (0.84ms, 10.3MB)
테스트 8 〉
통과 (9.54ms, 10.8MB)
테스트 9 〉
통과 (20.53ms, 15MB)
테스트 10 〉
통과 (10.58ms, 10.8MB)
테스트 11 〉
통과 (9.36ms, 10.9MB)
테스트 12 〉
통과 (14.67ms, 11.3MB)
테스트 13 〉
통과 (20.32ms, 11.3MB)
테스트 14 〉
통과 (0.02ms, 10.2MB)
테스트 15 〉
통과 (4.08ms, 10.5MB)
테스트 16 〉
통과 (3.23ms, 10.3MB)
테스트 17 〉
통과 (6.43ms, 10.4MB)
테스트 18 〉
통과 (3.26ms, 10.4MB)
테스트 19 〉
통과 (0.64ms, 10.2MB)
테스트 20 〉
통과 (5.68ms, 10.3MB)
테스트 21 〉
통과 (19.67ms, 10.8MB)
테스트 22 〉
통과 (21.01ms, 18.3MB)
테스트 23 〉
통과 (19.02ms, 11.7MB)
테스트 24 〉
통과 (18.49ms, 11.6MB)
테스트 25 〉
통과 (0.01ms, 10.1MB)
테스트 26 〉
통과 (0.01ms, 10.1MB)
테스트 27 〉
통과 (0.01ms, 10.1MB)
dictionary가 list 보다 데이터 접근이 빠르다고 하여 아래와같이 answer 타입을 list 에서 dictionary 로 변경하여 속도를 확인하였다.
def solution(N, stages):
answer = {}
n = len(stages)
temp =[0]*N
for v in stages:
if v<=N:
temp[v-1] += 1
for i in range(N):
if n==0:
answer[i+1]= 0
else:
c = temp[i]
answer[i+1]= c/n
n -= c
answer = sorted(answer, key=lambda k: answer[k], reverse=True)
return answer
실행결과: 처리 과정은 저장후 정렬이 대부분이며, 저장된 데이터를 찾는 경우가 작다. 그래서 아래와 같이 결과가 비슷하거나 데이터에 따라 약간 차이가 있음을 확인할 수 있다.
a = [-2, -5, 3, 1, -10]
b = sorted(a,key=lambda x: -(x*2))
print(b)
각 값들이 -(x*2) 가 적용되어 [4, 10, -6, -2, 20] 로 되고 다시 오름차순으로 정렬되어 아래와 같이 실행결과가 나타남
- 실행결과
[3, 1, -2, -5, -10]
3) 사용자 정의 객체 리스트를 속성에 따라 정렬하기
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
people = [Person("Alice", 25), Person("Bob", 30), Person("Charlie", 20)]
sorted_people = sorted(people, key=lambda x: x.age)
print([person.name for person in sorted_people])