[Algorithm] list vs. list[:] (가장 자주 등장한 숫자 k개)

happy tiger·2022년 7월 14일
0

Algorithm

목록 보기
2/4
post-thumbnail

코드카타 문제

풀이

max와 key를 이용한 풀이

왜 틀리지???

def top_k(nums, k):
  res = []
    for i in range(k):
      max_num = max(nums, key=nums.count)
      res.append(max_num)
      for num in nums:
        if num == max_num:
          nums.remove(num)
    return res

이 풀이를 unit tests에 돌렸더니 한 개가 빨간불 즉, 틀렸다고 표시되었다.
정민님과 계속 고민해보다가 for num in numsfor num in nums[:] 로 바꾸면 된다는 것을 깨달았다. 여기서 생긴 궁금증.. numnums[:]가 무슨 차이점이 있는 걸까?

list vs. list[:]

listlist[:]의 차이점을 알아보기 위해 아래의 간단한 코드를 만들어 실행시켜 보았다.

nums = [1,2,3,4,5]

for num in nums:
  nums.remove(num)
  print('삭제값: ', num)
print('남은값: ', nums)
nums = [1,2,3,4,5]

for num in nums[:]:
  nums.remove(num)
  print('삭제값: ', num)
print('남은값: ', nums)

위는 코드를 실행시킨 결과이다.

for num in nums:로 시작하는 첫 번째 for문부터 알아보자.
for문을 처음으로 돌릴 때의 num1이다. 그리고 nums.remove(num)으로 인해 nums==[2,3,4,5]가 된다. 그 후에 다시 돌아온 for문은 2를 가리키지 않고 3을 가리키게되어 3이 remove 된다.
왜냐하면 for a in list 는 list의 각 숫자를 기준으로 a로 뽑아내는 것이 아니라, index를 기준으로 뽑아내기 때문이다. 위의 코드와 결과창을 다시 보자. nums = [1, 2, 3, 4, 5]for문의 첫 사이클에서 인덱스 0번(1)을 remove했으므로, 그 다음 for문 사이클에선 인덱스 1번을 remove 해야한다. 첫 사이클로 인덱스 0번인 1이 삭제되어 nums==[2,3,4,5]이므로 그 다음에 삭제될 인덱스 1번은 2가 아닌 3이 된다. 고로 1 다음으로 삭제되는 값이 2가 아닌 3이 되는 것이다.

이제 for문이 돌아가는 방식은 알았다.
그런데 왜 두 번째 for 문은 정상적으로 모든 값이 삭제되는걸까?
왜냐하면 list[:]list가 아닌 list의 복사본이기 때문이다.
copy_list = list[:] 라고 할 때, list[1, 2, 3, 4, 5]에서 [6, 2, 3, 4, 5]로 바뀌어도 copy_list는 복사본에 불과하기에 원본값의 변화를 반영하지 않아 여전히 [1, 2, 3, 4, 5]이다.
즉, for문에서 삭제되는 값은 nums이지 nums의 복사본인 nums[:]가 아니다. 고로 for문에 있는 nums[:]for문의 시작부터 끝까지 [1, 2, 3, 4, 5] 를 유지하므로, for문으로 돌리며 요소의 삭제로 인해 놓치는 인덱스가 없어진다. 고로, 정상적으로 모든 값을 삭제할 수 있다.

이게 맞는거지!

def top_k(nums, k):
  res = []
    for i in range(k):
      max_num = max(nums, key=nums.count)
      res.append(max_num)
      for num in nums[:]:
        if num == max_num:
          nums.remove(num)
    return res

위의 풀이 중 max값에 해당하는 요소를 삭제하기 위한 for문을 list comprehension을 이용하여 줄일 수 있다.
아래 코드를 참고하자.

def top_k(nums, k):
  res = []
    for i in range(k):
      max_num = max(nums, key=nums.count)
      res.append(max_num)
      nums = [j for j in nums if j not in res]
    return res

최종 : sorted와 key를 이용한 풀이

def top_k(nums, k):
  	return sorted(set(nums), key=nums.count, reverse=True)[:k]

count값이 큰 순으로 nums를 나열하여 set를 적용해, 값을 하나씩만 남겨 인덱싱으로 k개를 return하는 코드이다.
set(nums)sorted할 때 key를 이용하여 nums.count를 기준으로 sorted하도록 한다.
sorted함수는 오름차순 정렬이기에, 큰 값부터 나오도록 내림차순 하기위해 reverse=True를 적용한다.
sorted의 return type은 list이므로 바로 indexing하여 return이 가능하다.

profile
호기심·끈기·성장·발전·행복·협력 ٩(๑•̀ㅂ•́)و

0개의 댓글