파이썬 알고리즘 인터뷰_해시 테이블
«파이썬 알고리즘 인터뷰 서적 내용을 정리»
문제01 해시맵 디자인
https://leetcode.com/problems/design-hashmap
다음의 기능을 제공하는 해시맵을 디자인하라.
- put(key, value) : 키, 값을 해시맵에 삽입한다. 만약 이미 존재하는 키하면 업데이트한다.
- get(key) : 키에 해당되는 값을 조회한다. 만약 키가 존재하지 않는다면 -1을 리턴한다.
- remove(key) :키에 해당하는 키,값을 해시맵에서 삭제한다.
Example 1:
MyHashMap hashMap = new MyHashMap(); hashMap.put(1, 1); hashMap.put(2, 2); hashMap.get(1); // returns 1 hashMap.get(3); // returns -1 (not found) hashMap.put(2, 1); // update the existing value hashMap.get(2); // returns 1 hashMap.remove(2); // remove the mapping for 2 hashMap.get(2); // returns -1 (not found)
- 풀이1_개별 체이닝 방식을 이용한 해시 테이블 구현
class ListNode:
def __init__(self, key=None, value=None):
self.key = key
self.value = value
self.next = None
class MyHashMap:
def __init__(self):
# 초기화
self.size = 1000
self.table = collections.defaultdict(ListNode)
def put(self, key: int, value: int) -> None:
# 제산법 사용
index = key % self.size
# 인덱스에 노드가 없다면 삽입 후 종료
if self.table[index].value is None:
self.table[index] = ListNode(key, value)
return
# 인덱스에 노드가 존재하는 경우 연결 리스트 처리(개별 체이닝)
p = self.table[index]
while p:
if p.key == key:
p.value = value
return
if p.next is None:
break
p = p.next
p.next = ListNode(key, value)
# 조회
def get(self, key: int) -> int:
index = key % self.size
if self.table[index].value is None:
return -1
# 노드가 존재할 때 일치하는 키 탐색
p = self.table[index]
while p:
if p.key == key:
return p.value
p = p.next
return -1
# 삭제
def remove(self, key: int) -> None:
index = key % self.size
if self.table[index].value is None:
return
# 인덱스의 첫 번째 노드일때 삭제 처리
p = self.table[index]
if p.key == key:
self.table[index] = ListNode() if p.next is None else p.next
return
# 연결 리스트 노드 삭제
prev = p
while p:
if p.key == key:
prev.next = p.next
return
prev, p = p, p.next
Result
Runtime : 220ms, Memory : 17.4MB
문제02 보석과 돌
https://leetcode.com/problems/jewels-and-stones
J는 보석이며, S는 갖고 있는 돌이다. S에는 보석이 몇 개나 있을까? 대소문자는 구분한다.
Example 1:
Input: jewels = "aA", stones = "aAAbbbb" Output: 3
Example 2:
Input: jewels = "z", stones = "ZZ" Output: 0
- 내 풀이 & 풀이3_Counter로 계산 생략
class Solution:
def numJewelsInStones(self, jewels: str, stones: str) -> int:
_sum = 0
cnt = collections.Counter(stones)
for j in jewels:
_sum += cnt[j]
return _sum
Result
Runtime : 32ms, Memory : 14.1MB
- 풀이1_해시 테이블을 이용한 풀이
class Solution:
def numJewelsInStones(self, jewels: str, stones: str) -> int:
freqs = {}
count = 0
# 돌(stones)의 빈도수 계산
for char in stones:
if char not in freqs:
freqs[char] = 1
else:
freqs[char] += 1
# 보석(jewels)의 빈도수 합산
for char in jewels:
if char in freqs:
count += freqs[char]
return count
Result
Runtime : 24ms, Memory : 14.3MB
- 풀이2_defaultdict를 이용한 비교 생략
class Solution:
def numJewelsInStones(self, jewels: str, stones: str) -> int:
freqs = collections.defaultdict(int)
count = 0
# 비교 없이 돌(stones)의 빈도수 계산
for char in stones:
freqs[char] += 1
# 비교 없이 보석(jewels)의 빈도수 합산
for char in jewels:
count += freqs[char]
return count
Result
Runtime : 32ms, Memory : 14.4MB
- 풀이4_파이썬다운 방식
class Solution:
def numJewelsInStones(self, jewels: str, stones: str) -> int:
return sum(s in jewels for s in stones)
Result
Runtime : 28ms, Memory : 14.3MB
문제03 중복 문자 없는 가장 긴 부분 문자열
https://leetcode.com/problems/longest-substring-without-repeating-characters
중복 문자가 없는 가장 긴 부분 문자열의 길이를 리턴하라.
Example 1:
Input: s = "abcabcbb" Output: 3 Explanation: The answer is "abc", with the length of 3.
Example 2:
Input: s = "bbbbb" Output: 1 Explanation: The answer is "b", with the length of 1.
Example 3:
Input: s = "pwwkew" Output: 3 Explanation: The answer is "wke", with the length of 3. Notice that the answer must be a substring, "pwke" is a subsequence and not a substring.
Example 4:
Input: s = "" Output: 0
- 풀이1_슬라이딩 윈도우와 투 포인터로 사이즈 조절
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
used = {}
max_length = start = 0
for index, char in enumerate(s):
# 이미 등장했던 문자라면 'start'위치 갱신
if char in used and start <= used[char]:
start = used[char] + 1
else: # 최대 부분 문자열 길이 갱신
max_length = max(max_length, index - start + 1)
# 현재 문자의 위치 삽입
used[char] = index
return max_length
Result
Runtime : 48ms, Memory : 14.3MB
문제04 상위 K 빈도 요소
https://leetcode.com/problems/top-k-frequent-elements
k번 이상 등장하는 요소를 추출하라.( = 빈도 상위 k개 요소 추출 )
Example 1:
Input: nums = [1,1,1,2,2,3], k = 2 Output: [1,2]
Example 2:
Input: nums = [1], k = 1 Output: [1]
- 내 코드
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
freqs = collections.Counter(nums)
most = freqs.most_common(k)
return [key for key, val in most]
Result
Runtime : 108ms, Memory : 18.6MB
- 풀이1_Counter를 이용한 음수 순 추출
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
freqs_heap = []
freqs = collections.Counter(nums)
# 힙에 음수로 삽입
for f in freqs:
heapq.heappush(freqs_heap, (-freqs[f], f))
topk = list()
# k번 만큼 추출, 최소 힙(Min Heap)이므로 가장 작은 음수 순으로 추출
for _ in range(k):
topk.append(heapq.heappop(freqs_heap)[1])
return topk
Result
Runtime : 92ms, Memory : 18.8MB
- 풀이2_파이썬다운 방식
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
return list(zip(*collections.Counter(nums).most_common(k)))[0]
Result
Runtime : 112ms, Memory : 18.6MB
zip() 함수
zip() 함수는 2개 이상의 시퀀스를 짧은 길이를 기준으로 일대일 대응하는 새로운 튜플 시퀀스를 만드는 역할
a = [1, 2, 3, 4]
b = ['a', 'b', 'c', 'd']
c = ['ㄱ', 'ㄴ', 'ㄷ', 'ㄹ']
list(zip(a,b,c))
# [(1, 'a', 'ㄱ'), (2, 'b', 'ㄴ'), (3, 'c', 'ㄷ'), (4, 'd', 'ㄹ')]
아스테리스크(*)
언팩(Unpack) 역할을 한다. 주로 튜플이나 리스트를 언패킹(풀어 헤치는 데) 사용한다.
fruits = ['lemon', 'pear', 'watermelon', 'tomato']
# 1) 그냥 리스트 출력1
print(fruits)
# ['lemon', 'pear', 'watermelon', 'tomato']
# 2) 그냥 리스트 출력2
print(fruits[0], fruits[1], fruits[2], fruits[3])
# lemon pear watermelon tomato
# 3) for 반복문으로 순회하여 출력
for f in fruits:
print(f, end=' ')
# lemon pear watermelon tomato
# 4) 아스테리스크(*) 사용하여 간편하게 출력
print(*fruit)
# lemon pear watermelon tomato
** 두개가 사용되는 경우는?
** 두개가 사용되는 경우는 키/값 페어를 언패킹하는데 사용됨
date_info = {'year': '2020', 'month': '01', 'day': '7'}
new_info = {**date_info, 'day': '14'}
print(new_info)
# {'year': '2020', 'month': '01', 'day': '14'}