Skip to content

[SunaDu] Week 1 #628

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Dec 9, 2024
23 changes: 23 additions & 0 deletions contains-duplicate/dusunax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'''
# Leetcode 217. Contains Duplicate

use set to store distinct elements 🗂️

## Time and Space Complexity

```
TC: O(n)
SC: O(n)
```

### TC is O(n):
- iterating through the list just once to convert it to a set.

### SC is O(n):
- creating a set to store the distinct elements of the list.
'''

class Solution:
def containsDuplicate(self, nums: List[int]) -> bool:
return len(nums) != len(set(nums))

44 changes: 44 additions & 0 deletions house-robber/dusunax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
'''
# Leetcode 198. House Robber

use **dynamic programming** to solve this problem. (bottom-up approach) 🧩

choose bottom-up approach for less space complexity.

## DP relation

```
dp[i] = max(dp[i - 1], dp[i - 2] + nums[i])
```

- **dp[i - 1]:** skip and take the value from the previous house
- **dp[i - 2]:** rob the current house, add its value to the maximum money from two houses before

## Time and Space Complexity

```
TC: O(n)
SC: O(n)
```

### TC is O(n):
- iterating through the list just once to calculate the maximum money. = O(n)

### SC is O(n):
- using a list to store the maximum money at each house. = O(n)

'''

class Solution:
def rob(self, nums: List[int]) -> int:
if len(nums) == 1:
return nums[0]

dp = [0] * len(nums)
dp[0] = nums[0]
dp[1] = max(nums[0], nums[1])

for i in range(2, len(nums)):
dp[i] = max(dp[i - 1], dp[i - 2] + nums[i])

return dp[-1]
38 changes: 38 additions & 0 deletions longest-consecutive-sequence/dusunax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
'''
# Leetcode 128. Longest Consecutive Sequence

keep time complexity O(n) by iterating through the set and accessing elements in O(1) time. ⚖️

## Time and Space Complexity
```
TC: O(n)
SC: O(n)
```

### TC is O(n):
- iterating through the set. O(n)
- accessing elements in the set. O(1)
- while loop incrementing `current_num` while `current_num + 1 in nums_set`. O(1)

### SC is O(n):
- creating a set from the list of numbers. O(n)
'''

class Solution:
def longestConsecutive(self, nums: List[int]) -> int:
nums_set = set(nums) # O(n)
longest_sequence = 0

for num in nums_set: # O(n)
if (num - 1) not in nums_set:
current_num = num
current_sequence = 1

while current_num + 1 in nums_set: # O(1)
current_num += 1
current_sequence += 1

longest_sequence = max(current_sequence, longest_sequence)

return longest_sequence

69 changes: 69 additions & 0 deletions top-k-frequent-elements/dusunax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
'''
# Leetcode 347. Top K Frequent Elements

use **Counter** to count the frequency of each element in the list 🚀

- **solution 1**: use **sorted()** to sort the elements by their frequency in descending order.
- **solution 2**: use **bucket sort** to sort the elements by their frequency in descending order. (efficient!)

## Time and Space Complexity

### solution 1: topKFrequent()

```
TC: O(n log n)
SC: O(n)
```

#### TC is O(n log n):
- iterating through the list just once to count the frequency of each element. = O(n)
- sorting the elements by their frequency in descending order. = O(n log n)

#### SC is O(n):
- using a Counter to store the frequency of each element. = O(n)
- sorted() creates a new list that holds the elements of frequency_map. = O(n)
- result list that holds the top k frequent elements. = O(k)

### solution 2: topKFrequentBucketSort()

```
TC: O(n)
SC: O(n)
```

#### TC is O(n):
- iterating through the list just once to count the frequency of each element. = O(n)
- creating **buckets** to store the elements by their frequency. = O(n)
- iterating through the buckets in reverse order to get only the top k frequent elements. = O(n)

#### SC is O(n):
- using a Counter to store the frequency of each element. = O(n)
- using buckets to store the elements by their frequency. = O(n)
- result list that holds only the top k frequent elements. = O(k)
'''

class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
frequency_map = Counter(nums) # TC: O(n), SC: O(n)
sorted_frequencies = sorted(frequency_map.items(), key=lambda x: x[1], reverse=True) # TC: O(n log n), SC: O(n)

result = [] # SC: O(k)
for e in sorted_frequencies: # TC: O(k)
result.append(e[0])

return result[0:k]
Comment on lines +45 to +54
Copy link
Contributor

@changchanghwang changchanghwang Dec 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 이런식으로 풀었는데
기본적으로 보통 언어들에서는 Quick sort를 기반으로한 정렬을 하기 때문에 O(nlogn) 입니다.

Bucket sort를 이용하면 시간적으로 조금 더 효율적으로 풀 수 있을 것 같습니다!

제가 python을 잘 모르겠어서 js로 풀이 공유드립니다!

const input = [1, 1, 1, 2, 2, 3];

function topKFrequent(nums, k) {
  const count = new Map();
  nums.forEach((num) => {
    count.set(num, (count.get(num) || 0) + 1);
  }); // { 1 => 3, 2 => 2, 3 => 1 }

  const frequent = Array.from({ length: nums.length + 1 }, () => []); // [[], [], [], [], ...] 처럼 0부터 nums.length까지 빈 배열을 생성

  for (const [num, freq] of count) {
    frequent[freq].push(num);
  } // [[], [3], [2], [1], ...] 처럼 빈 배열에 빈도수에 해당하는 숫자를 넣음

  const result = [];

  // index가 빈도수, value가 숫자이기 때문에 뒤부터 돌아야 빈도수가 높은 숫자부터 result를 넣을 수 있다.
  for (let i = frequent.length - 1; i >= 0; i--) {
    // 빈도수가 높은 숫자부터 result에 넣음
    for (const num of frequent[i]) {
      result.push(num);
      // k개만큼 result에 넣으면 종료
      if (result.length === k) {
        return result;
      }
    }
  }

  return result;
}
console.log(topKFrequent(input, 2));

가독성은 좀 구리지만... 성능적으로는 아주 큰수로 갈때 이득을 볼 수 있습니다!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

코멘트 감사합니다!
bucket sort 방식을 추가했어요 👉 3dff920


def topKFrequentBucketSort(self, nums: List[int], k: int) -> List[int]:
frequency_map = Counter(nums)
n = len(nums)
buckets = [[] for _ in range(n + 1)]

for num, freq in frequency_map.items():
buckets[freq].append(num)

result = []
for i in range(len(buckets) - 1, 0, -1):
for num in buckets[i]:
result.append(num)
if len(result) == k:
return result
31 changes: 31 additions & 0 deletions valid-palindrome/dusunax.py
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

파이썬 라이브러리와 문법은 정말.. 알고리즘 풀기에 행복한 수준이네요 ㅎㅎ

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ㅎㅎㅎㅎ 공감합니다

Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
'''
# Leetcode 125. Valid Palindrome

use `isalnum()` to filter out non-alphanumeric characters 🔍

## Time and Space Complexity

```
TC: O(n)
SC: O(n)
```

### TC is O(n):
- iterating through the string just once to filter out non-alphanumeric characters. O(n)

### SC is O(n):
- `s.lower()` creates a new string. O(n)
- creating a new string `converted_s` to store the filtered characters. O(n)
- `converted_s[::-1]` creates a new reversed string. O(n)
'''

class Solution:
def isPalindrome(self, s: str) -> bool:
if s == " ":
return True

s = s.lower()
converted_s = ''.join(c for c in s if c.isalnum())

return converted_s == converted_s[::-1]

Loading