파이썬을 이용한 주식 자동매매 시스템
정렬된 배열에서 중복된 원소를 제거하는 문제를 풀었다. 코딩 인터뷰 관점에서 문제 해결 과정을 brute force 방법부터 시간, 공간 복잡도를 고려하는 과정까지 순서대로 정리하고 python/C++ 최적 Solution까지 정리해 봤다.
문제 이해
중복을 제거하는데, 순서는 그대로 유지해야 된다.
중복 제거된 후의 개수 K를 return 하고, 처음 주어진 array의 배열 크기는 그대로 두어도 된다. K 뒤로 오는 배열 안의 원소는 don't care
배열을 추가로 선언하면 안 되고 주어진 배열 안에서 수정해야 한다. O(1) extra memory
문제를 명확히 하기 위한 과정(Q&A)
생각나는 알고리즘/자료구조
      앞의 원소는 그대로 두고, 뒤에 나오는 원소는 뒤로 이동시킨다.
뒤로
      보내면 뒤쪽 index도 가지고 있어야 됨
    
앞에서부터 순회, 뒤에 교체된 index 가 K가 될 것 같다.
처음 K는 nums array length로 시작?
index i = 0부터 중복되는 count를 세면서 순회하고, K 는 N(nums array의 length)
중복되는 count만큼 K = N - count
i + 1에 i + count + 1 부터 가져옴
다음 i++ 부터 다시 순회 시작
순회하면서 중복되는 원소를 곧바로 제거하고, 뒤에 있는 원소들을 앞으로 이동시키는 작업이 불필요하다는 생각이 들었다.
O(1) 이기 때문에, 주어진 배열 안에 중복되지 않는 다음 원소의 index를 mark 할 수 있는 방법을 생각했다.
Constraints가 `-100 <= nums[i] <= 100` 이므로, 101/102/.. 로 mark 하는 방식을 생각해 봤다.
class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        start_idx = 0
        count = 0
        k = len(nums)
        for i in range(start_idx + 1, len(nums)):            
            current_num = nums[start_idx]
            if current_num == nums[i]:
                count += 1
                k -= 1
                nums[start_idx + 1] = 100 + count
            elif count > 0:
                nums[start_idx + 1] = 100 + count
                start_idx = i
                count = 0
            else:
                start_idx = i
                count = 0
        next_idx= 0
        for i in range(len(nums)):
            if next_idx >= len(nums):
                break
            if nums[next_idx] > 100:
                count = nums[next_idx] - 100                
                next_idx = next_idx + count
                if next_idx >= len(nums):
                    break
                nums[i] = nums[next_idx]                
            else:
                nums[i] = nums[next_idx]
            
            next_idx+=1
        return k
너무 복잡하게 생각해서, 시간 안에 정확히 해결하지 못했다. 유사한 답은 나왔지만 index, count 방식에서 실수가 있었다. Hint를 봤을 때 접근 방식이나 문제풀이 방법이 잘못되진 않았지만, 훨씬 더 간단하게 풀 수 있는 문제였다.
[1, 1]
우선, Solution을 확인하기 전까진 위와 같이 복잡하게 index 2개를 이용하다 보니 코너 케이스에서 계속 통과가 안됐다. Out of range error도 종종 발생했다.
몇 년 만에 알고리즘 문제를 푸는 것이라서, 연습이 많이 필요한 부분인 것 같다.
361 / 361 test cases passed.
Status: Accepted
Runtime: 126 ms
Memory Usage: 15.8 MB
Submitted: 0 minutes ago
Algorithm
Since the array is already sorted, we can keep two pointers i and j, where i is the slow-runner while j is the fast-runner. As long as nums[i]=nums[j], we increment j to skip the duplicate.
When we encounter nums[j]=nums[i], the duplicate run has ended so we must copy its value to nums[i+1]. i is then incremented and we repeat the same process again until j reaches the end of array.
nums[i]와 nums[j]가 다를 때, nums[i+1]에 nums[j]를 대입
| 
      | 
  
   0  | 
  
   1  | 
  
   1  | 
  
   1  | 
  
   2  | 
  
   3  | 
 
| 
   1  | 
  
   i  | 
  
   j  | 
  
      | 
  
      | 
  
      | 
  
      | 
 
| 
   2  | 
  
      | 
  
   i  | 
  
   j  | 
  
      | 
  
      | 
  
      | 
 
| 
   3  | 
  
      | 
  
   i  | 
  
      | 
  
   j  | 
  
      | 
  
      | 
 
| 
   4  | 
  
      | 
  
   i  | 
  
      | 
  
      | 
  
   j  | 
  
      | 
 
| 
   5  | 
  
      | 
  
      | 
  
   i  | 
  
      | 
  
      | 
  
   j  | 
 
| 
      | 
  
   0  | 
  
   1  | 
  
   2  | 
  
   3  | 
  
   2  | 
  
   3  | 
 
Solution을 보고 나니, 이렇게 간단한 것을 왜 이렇게까지 복잡하게 풀었나 싶었다.
class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        prev = nums[0]
        idx = 0
        for i in nums:
            if nums[idx] != i:
                idx+=1
                nums[idx] = i
        del nums[idx+1:] 
class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        if len(nums) == 0:
            return 0
        i = 0
        for j in range(1, len(nums)):
            if nums[j] != nums[i]:
                i += 1
                nums[i] = nums[j]
        return i + 1
public int removeDuplicates(int[] nums) {
    if (nums.length == 0) return 0;
    int i = 0;
    for (int j = 1; j < nums.length; j++) {
        if (nums[j] != nums[i]) {
            i++;
            nums[i] = nums[j];
        }
    }
    return i + 1;
}
| 
           
  | 
        
           
            Spent Time  | 
        
           
            Result  | 
      
| 
           
            1st   | 
        
           
            50min  | 
        
           
            Fail  | 
      
| 
           
            2nd  | 
        
           
            30min  | 
        
           
            Accepted  | 
      
| 
           
            3rd  | 
        
           
            -  | 
        
           
            -  | 
      
| 
           
            최적화  | 
        
           
            -  | 
        
           
            -  | 
      
| 
           
            Solution 학습  | 
        
           
            60min  | 
        
           
            -  | 
      
| 
           
            Retry Date  | 
        
           
            2022/08/29  | 
        
           
  |