---
# System prepended metadata

title: Two Pointers

---

# Two Pointers
- Two pointers 알고리즘은 Array 나 Linked List 와 같은 linear 자료구조에서 부분 수열 (subsequence)에 대한 정보를 가져올 때, $O(N^2)$이 아니라 $O(N)$ 의 시간복잡도로 가져올 수 있는 방법으로 사용이 된다.
- 구성은 $start$ 와 $end$ 이렇게 2개의 포인터를 사용하고, $start \leq end$ 라고 가정한다.  
- '슬라이딩 윈도우'라고 불리는 방법은 Two pointer 알고리즘을 사용하고, 윈도우의 크기가 어느 지점에서 일정하다고 간주한다. 
- Two pointer를 사용하는 방법에는 2가지가 있다.
    1. $start$ 와 $end$가 시작과 끝에서 움직이면서 서로 만날 때까지 이동하는 방법.
    2. $start$ 와 $end$가 하나는 더 빨리 이동하고 (at a faster pace), 다른 하나는 뒤를 따라가는 (at a slower pace) 방법.


Reverse Array (In-place)
---
- Array를 in-place 방법으로 reverse 하는 방법
- 제일 첫번째 아이템과 마지막 아이템을 swap. 그 다음 아이템과 그 다음 마지막 아이템을 swap. 반복
```python=
def reverse(self, nums, start, end):
    while start < end:
        nums[start], nums[end] = nums[end], nums[start]
        start, end = start + 1, end - 1
```
- 문제: https://leetcode.com/problems/reverse-string/

Rotate Array (In-place)
---
- Array 가 주어졌을 때 1 step 순환시키는 방법은 데이터들이 한칸씩 자리를 이동하는 것을 의미한다. K step 은 이 작업을 여러번 수행하는 것이다.
- 단순한 방법으로는 추가적인 배열을 만들어서 1 step 이동시킨 값을 저장하는 것이지만, 이 방법은 $O(N^2)$ 만큼의 시간복잡도와 추가적인 공간이 필요하다.
- 만약에 two pointer를 사용한다면 
    - 왼쪽으로 Rotate 할 때,
        1) 0 ~ end-k 까지 reverse
        2) 0 ~ end-k+1 까지 reverse
        3) 0 ~ end 까지 reverse 를 하면 step 만큼 rotate를 할 수 있다.
    - 오른쪽으로 Rotate 할 때 반대로 한다. 
        1) 0 ~ end 까지 reverse 를 하면 step
        2) 0 ~ end-k+1 까지 reverse
        3) 0 ~ end-k 까지 reverse
- 문제: https://leetcode.com/problems/rotate-array/

```python=
class Solution:
    def reverse(self, nums, start, end):
        while start < end:
            nums[start], nums[end] = nums[end], nums[start]
            start, end = start + 1, end - 1
        
    def rotateLeft(self, nums: List[int], k: int) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        if k is None or k <= 0:
            return
        
        k, end = k % len(nums), len(nums) - 1
        self.reverse(nums, 0, end - k)
        self.reverse(nums, end - k + 1, end)
        self.reverse(nums, 0, end)
```
![](https://i.imgur.com/7Ue7z4R.png)
- 문제: https://leetcode.com/problems/rotate-array/

연속적인 수열의 합
---
- 전체 데이터에서 연속적인 수열의 부분합이 target 값 $T$를 만족하는지 알아보는 문제다.
- 가장 단순한 방법으로는 가능한 모든 연속적인 수열을 찾으면서 그 합을 $T$와 비교하는 방법이 있는데, 이것은 시간복잡도가 $O(N^2)$이 걸린다. 
- Two pointer 방법을 사용하면 이 시간복잡도를 $O(N)$으로 줄일 수 있다. 현재까지의 수열의 합이 $T$ 보다 작으면 $end$ 포인터를 하나 증가시키고, 부분합에 $end$가 가리키는 값을 누적시킨다. 만약에 $T$보다 크면 $start$ 포인터를 하나 증가시키고, 부분합에서 $start$ 위치에 있던 값을 뺀다. 이런 방식으로 $start$와 $end$ 의 위치를 조절하면 $O(N)$ 시간 안에 답을 찾을 수 있다.

Linked List의 중간값 찾기
---
- Array의 경우에는 중간값을 찾는 방법은 중간 인덱스를 찾을 수 있다. 하지만 Singly Linked List 의 경우 전체 리스트의 길이를 모른다고 가정할 때 어떻게 하면 중간값을 찾을 수 있을까?
- 가장 단순한 방법은 $O(N)$의 시간을 소비해서 Linked List의 크기를 알아보고, 다시 한 번, 리스트를 탐색하면서 중간값을 찾을 수 있다.
- 하지만, Two pointer를 사용해서 $slowPointer$는 하나의 step을 증가시키고, 다른 하나의 포인터인 $fastPointer$는 두개의 step을 증가시킨다. 이렇게 되면 $fastPointer$가 가장 마지막 위치에 도달했을 때, $slowPointer$는 중간 위치에 도달하기 때문에 중간값을 전체 크기를 모른 상태에서도 알 수 있다.

```python=
slow = fast = head
while fast and fast.next:
    slow = slow.next
    fast = fast.next.next
return slow
```

Reverse Linked List
---
- Doubly Linked List를 사용하지 않고 Singly Linked List 를 반대로 traverse하는 방법이 있을까? 
    1. Three pointers를 사용하는 방법
    2. Two pointers를 사용하는 방법

#### Three pointers를 사용하는 방법
- Two pointers 를 사용하는 방법을 보기 전에 three pointers를 사용하는 방법을 먼저 보자. 노드를 traverse하면서 노드의 next를 prev로 업데이트하는 과정을 반복한다. 여기서는 추가적으로 3개의 포인터를 사용해서 reverse할 수 있다. 
    - $prev$: 이전 노드를 가리키는 포인터
    - $next$: 다음 노드를 가리키는 포인터
    - $curr$: 현재 노드를 가리키는 포인터
> $head$ 는 기존에 사용하고 있는 포인터이기 때문에 three pointers 라고 부른다. 
- $curr$ 노드를 $next$ 노드로 업데이트 하기 전에 $curr$ 의 next 가 $prev$ 를 가리키도록 한다.
- $prev$ 는 $curr$ 로 업데이트 한다.
- $curr$ 를 $next$ 로 업데이트 한다
- $next$ 를 $curr$ 의 next로 업데이트 한다. 
![](https://media.geeksforgeeks.org/wp-content/cdn-uploads/RGIF2.gif)

#### Two pointers를 사용하는 방법
- Two pointers 로는 $curr$ 와 $next$ 포인터만 갖고 reverse 할 수 있다.
    - $next$: 다음 노드를 가리키는 포인터
    - $curr$: 현재 노드를 가리키는 포인터
- $head$ 와 $curr$ 는 같이 이동한다고 생각한다.
- $curr$의 next를 $next$ 의 next 로 업데이트 한다.
- $next$의 next를 $curr$ 로 업데이트 한다. 
- $head$ ($curr$) 를 $next$로 업데이트 한다. 
- 한 번 반복을 하면, $next$의 next는 $curr$를 가리키게 되고, $curr$ 와 $next$ 는 다음 노드로 한 칸 이동시키는 효과가 있다. 코드를 보면 다음과 같다.
```python=
# 출처: https://www.geeksforgeeks.org/iteratively-reverse-a-linked-list-using-only-2-pointers/?ref=lbp
# Function to reverse the linked list using 2 pointers
def reverse(head_ref):
 
    current = head_ref
    next= None
    while (current.next != None) :
        next = current.next
        current.next = next.next
        next.next = (head_ref)
        head_ref = next
     
    return head_ref
```
- 문제: https://leetcode.com/problems/rotate-array/

Rotate Linked List
---
- Left 로 Rotate 하는 것은 별로 어렵지 않고, Right 로 Rotate 하는 것도 move 하는 값만 조금 조정하면 된다. 
```python=
class Solution:
    def rotateRight(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
        if not head:
            return None
        
        p = head
        size = 1
        while p.next:
            size +=1
            p = p.next
        p.next = head     
        k %= size
        
        for i in range(size-k-1):
            head = head.next
        ans = head.next 
        head.next = None
        return ans
```
- 문제: https://leetcode.com/problems/rotate-list/

참고 자료
---
1. https://github.com/WooVictory/Ready-For-Tech-Interview/blob/master/Algorithm/%ED%88%AC%ED%8F%AC%EC%9D%B8%ED%84%B0%20%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98.md
2. https://guides.codepath.com/compsci/Linked-List-Two-Pointer
3. https://leetcode.com/articles/two-pointer-technique/
4. https://www.baeldung.com/java-two-pointer-technique