여러가지 상황에서 투 포인터 학습하기

Yuno·2024년 6월 24일

Practice1 주어진 규칙으로 문자열을 정리하고 남은 문자열 반환하기

public class Practice1 {
    public static String solution(String s) {
        if (s == null || s.length() == 0) {
            return null;
        }
        int p1 = 0;
        int p2 = s.length() - 1;

        while (p1 < p2 && s.charAt(p1) == s.charAt(p2)) {
            char c = s.charAt(p2);

            while (p1 <= p2 && s.charAt(p1) == c) {
                p1++;
            }

            while (p1 <= p2 && s.charAt(p2) == c) {
                p2--;
            }
        }
        return s.substring(p1, p2 + 1);
    }

    public static void main(String[] args) {
        // Test code
        System.out.println(solution("ab"));         // ab
        System.out.println(solution("aaabbaa"));    //
        System.out.println(solution("aabcddba"));   // cdd
    }
}
  1. 입력 검사
    -문자열 s가 null 이거나 길이가 0인 경우 null을 반환

  2. 양 끝에서부터 비교
    -문자열의 양 끝에서부터 시작하여 같은 문자를 찾음. p1은 문자열의 시작부터, p2는 문자열의 끝부터 시작

  3. 문자 비교 및 제거

while (p1 < p2 && s.charAt(p1) == s.charAt(p2))

-p1 과 p2가 가리키는 문자가 같은동안 반복

char c = s.charAt(p2);

-현재 검사하는 문자를 c에 저장

while (p1 <= p2 && s.charAt(p1) == c) {
	p1++;
}

p1 이 가리키는 문자가 c와 같은동안 p1을 증가시킴. 이 과정에서 문자열의 왼쪽에서 같은 문자를 제거

while (p1 <= p2 && s.charAt(p2) == c) {
	p2--;
}

p2 가 가리키는 문자가 c와 같은동안 p2를 감소시킴. 이 과정에서 문자열의 오른쪽에서 같은 문자를 제거

  1. 결과 반환
    -제거된 문자를 제외한 부분 문자열을 반환.
    s.substring(p1, p2 + 1) 을 통해 p1 부터 p2 까지의 부분 문자열을 반환

Practice2 두 배열에서 중복되는 원소를 찾아서 배열로 반환하기

import java.util.Arrays;
import java.util.HashSet;

public class Practice2 {
    public static int[] solution(int[] nums1, int[] nums2) {
        HashSet <Integer> set = new HashSet<>();

        Arrays.sort(nums1);
        Arrays.sort(nums2);

        int p1 = 0;
        int p2 = 0;

        while (p1 < nums1.length && p2 < nums2.length) {
            if (nums1[p1] < nums2[p2]) {
                p1++;
            } else if (nums1[p1] > nums2[p2]) {
                p2++;
            } else {
                set.add(nums1[p1]);
                p1++;
                p2++;
            }
        }
        int[] result = new int[set.size()];
        int idx = 0;
        for (Integer i : set) {
            result[idx++] = i;
        }
        return result;
    }

    public static void main(String[] args) {
        // Test code
        int[] nums1 = {1, 2, 2, 1};
        int[] nums2 = {2, 2};
        System.out.println(Arrays.toString(solution(nums1, nums2)));

        nums1 = new int[]{4, 9, 5};
        nums2 = new int[]{9, 4, 9, 8, 4};
        System.out.println(Arrays.toString(solution(nums1, nums2)));

        nums1 = new int[]{1, 7, 4, 9};
        nums2 = new int[]{7, 9};
        System.out.println(Arrays.toString(solution(nums1, nums2)));
    }
}
  1. HashSet 선언
    -HashSet<Integer> set 은 중복된 값을 저장하기 위한 자료구조. HastSet은 중복을 허용하지 않고 순서를 보장하지 않는 특성을 가짐

  2. 배열 정렬
    -Arrays.sort(nums1) 과 Arrays.sort(nums2) 를 통해 각 배열을 정렬. 이는 이후 중복된 값 비교를 용이하게 함

  3. 두 배열 순회
    -p1 과 p2 인덱스를 사용하여 두 배열을 순회
    -nums1[p1] < nums2[p2] 일 경우 p1을 증가시켜 다음 원소로 이동
    -nums1[p1] > nums2[p2] 일 경우 p2를 증가시켜 다음 원소로 이동
    -nums1[p1] == nums[p2] 일 경우 두 배열에서 같은 원소를 발견했으므로 해당 원소를 HashSet에 추가하고 p1과 p2를 모두 증가시켜 다음 원소로 이동

  4. 결과 배열 생성
    -HashSet에 저장된 값을 result 배열로 옮김
    -HashSet은 중복을 허용하지 않기 때문에 중복된 값은 자동으로 제거
    -result 배열의 크기는 HashSet의 크기와 같음
    -HashSet 의 모든 값들을 result 배열로 옮기면서 인덱스 idx를 증가

Practice3 문자열 내의 단어들을 거꾸로 뒤집고 공백 제거하기

public class Practice3 {
    public static String solution(String s) {
        if (s == null) {
            return null;
        }

        if (s.length() < 2) {
            return s;
        }

        s = removeSpaces(s);
        char[] cArr = s.toCharArray();
        reverseString(cArr, 0, s.length() - 1);
        reverseWords(cArr, s.length());

        return new String(cArr);
    }

    public static String removeSpaces(String s) {
        int p1 = 0;
        int p2 = 0;

        char[] cArr = s.toCharArray();
        int length = s.length();

        while (p2 < length) {
            while (p2 < length && cArr[p2] == ' ') {
                p2++;
            }

            while (p2 < length && cArr[p2] != ' ') {
                cArr[p1++] = cArr[p2++];
            }

            while (p2 < length && cArr[p2] == ' ') {
                p2++;
            }

            if (p2 < length) {
                cArr[p1++] = ' ';
            }
        }
        return new String(cArr).substring(0, p1);
    }
    public static void reverseString(char[] cArr, int i, int j) {
        while (i < j) {
            char tmp = cArr[i];
            cArr[i++] = cArr[j];
            cArr[j--] = tmp;
        }
    }

    public static void reverseWords(char[] cArr, int length) {
        int p1 = 0;
        int p2 = 0;

        while (p1 < length) {
            while (p1 < p2 || p1 < length && cArr[p1] == ' ') {
                p1++;
            }

            while (p2 < p1 || p2 < length && cArr[p2] != ' ') {
                p2++;
            }

            reverseString(cArr, p1, p2 - 1);
        }
    }

    public static void main(String[] args) {
        // Test code
        System.out.println(solution("the sky is blue"));
        System.out.println(solution("  hello      java    "));

    }
}

removeSpaces 메서드

pulbic static String removeSpaces(String s) {
	int p1 = 0;
    int p2 = 0;
    
    char[] cArr = s.toCharArr();
    int length = s.length();
    
    while(p2 < length) {
    	// 공백을 스킵
    	while (p2 < length && cArr[p2] = ' ') {
        	p2++;
        }
        // 단어를 복사
        while (p2 < length && cArr[p2] != ' ') {
        	cArr[p1++] = cArr[p2++];
        }
        // 다음 공백 이전에 공백 추가
        while (p2 < length && cArr[p2] == ' ') {
        	p2++;
        }
        
        if (p2 < length) {
        	cArr[p1++] = ' ';
        }
    }
    return new String(cArr).substring(0, p1);
}

-removeSpaces 메서드는 문자열 s를 받아 공백을 제거한 새로운 문자열을 반환
-cArr 배열은 입력 문자열 s를 문자 배열로 변환한 것
-p1과 p2는 포인터 변수로, p1은 결과 문자열에 쓰일 인덱스를, p2는 입력 문자열을 탐색하는 인덱스를 나타냄
-첫번째 while 루프는 입력 문자열에서 공백을 스킵
-두번째 while 루프는 공백이 아닌 단어를 복사
-세번째 while 루프는 다시 공백을 스킵
-공백을 추가하는 부분은 마지막 단어 이후의 공백을 처리하기 위함
-최종적으로 new String(cArr).subString(0, p1) 을 통해 결과 문자열을 만들어 반환

reverseString 메서드

public static void reverseString(char[] cArr, int i, int j) {
	while (i < j) {
    	char tmp = cArr[i];
        cArr[i++] = cArr[j];
        cArr[j--] = tmp;
    }
}

-reverseString 메서드는 문자 배열 cArr에서 인덱스 i 부터 j 까지의 문자를 역순으로 뒤집음
-i는 시작 인덱스 j는 끝 인덱스를 나타냄

reverseWords 메서드

public static void reverseWords(char[] cArr, int length) {
	int p1 = 0;
    int p2 = 0;
    
    while (p1 < length) {
    	while (p1 < p2 || p1 < length && cArr[p1] == ' ') {
        	p1++;
        }
        
        while (p2 < p1 || p2 < length && cArr[p2] != ' ') {
        	p2++;
        }
        
        reverseString(cArr, p1, p2 - 1);
    }
}

-reverseWords 메서드는 문자 배열 cArr에서 단어들을 뒤집음
-p1과 p2는 단어의 시작과 끝 인덱스를 나타내며, 공백을 건너뛰는데 사용됨
-첫번째 while 루프는 공백을 스킵하고, 두번째 while 루프는 단어를 찾음
-reverseString 메서드를 호출하여 단어를 뒤집음

solutuion 메서드

public static String solution(String s) {
	if (s == null) {
    	return null;
    }
    
    if (s.length() < 2) {
    	return s;
    }
    
    s = removeSpaces(s);
    char[] cArr = s.toCharArray();
    reverseString(cArr, 0, s.length() - 1);
    reverseWords(cArr, s.length());
    
    return new String(cArr);
}

-solution 메서드는 입력 문자열 s 를 받아서 공백을 제거하고 단어들을 뒤집은 결과를 반환
-입력 문자열이 null이면 null을 반환하고, 길이가 1 이하이면 그대로 반환
-removeSpaces 메서드를 통해 공백을 제거한 문자열을 얻고, 이를 문자 배열로 변환하여 단어들을 뒤집고 다시 조합

Practice4 세 수의 합이 0이되는 모든 조합 리스트에 저장하기

import java.util.ArrayList;
import java.util.Arrays;

public class Practice4 {
    public static ArrayList<ArrayList<Integer>> solution(int[] nums) {
        Arrays.sort(nums);
        ArrayList<ArrayList<Integer>> result = new ArrayList<>();

        for (int i = 0; i < nums.length - 2; i++) {
            if (i == 0 || i > 0 && nums[i] != nums[i - 1]) {
                int p1 = i + 1;
                int p2 = nums.length - 1;
                int sum = 0 - nums[i];

                while (p1 < p2) {
                    if (nums[p1] + nums[p2] == sum) {
                        result.add(new ArrayList<>(Arrays.asList(nums[i], nums[p1], nums[p2])));

                        while (p1 < p2 && nums[p1] == nums[p1 + 1]) {
                            p1++;
                        }
                        while (p1 < p2 && nums[p2] == nums[p2 - 1]) {
                            p2--;
                        }
                        p1++;
                        p2--;
                    } else if (nums[p1] + nums[p2] < sum) {
                        p1++;
                    } else {
                        p2--;
                    }
                }
            }
        }
        return result;
    }

    public static void main(String[] args) {
        // Test code
        int[] nums = {-1, 0, 1, 2, -1, -4};     // [[-1, -1, 2], [-1, 0, 1]]
        System.out.println(solution(nums));

        nums = new int[] {1, -7, 6, 3, 5, 2};   // [[-7, 1, 6], [-7, 2, 5]]
        System.out.println(solution(nums));
    }
}

solution 메서드

public static ArrayList<ArrayList<Integer>> solution(int[] nums) {
	Arrays.sort(nums);
    ArrayList<ArrayList<Integer>> result = new ArrayList<>();
    
    for (int i = 0; i < nums.length - 2; i++) {
    	if (i == 0 || i > 0 && nums[i] != nums[i - 1]) {
        	int p1 = i + 1;
            int p2 = nums.length - 1;
            int sum = 0 - nums[i];
            
            while (p1 < p2) {
            	if (nums[p1] + nums[p2] == sum) {
                	result.add(new ArrayList<>(Arrays.asList(nums[i], nums[p1], nums[p2])));
                    
                    while (p1 < p2 && nums[p1] == nums[p1 + 1]) {
                    	p1++;
                    }
                    while (p1 < p2 && nums[p2] == nums[p2 - 1]) {
                    	p2--;
                    }
                    p1++;
                    p2--;
                } else if (nums[p1] + nums[p2] < sum) {
                	p1++;
                } else {
                	p2--;
                }
            }
        }
    }
    return result;
}
  1. 배열 정렬
    -Arrays.sort(nums) 를 통해 입력 배열 nums를 오름차순으로 정렬

2.세 수의 합이 0이되는 조합 찾기
-for 루프에서 i는 배열을 순회하며 첫 번째 수로 사용될 인덱스를 나타냄.
nums.length - 2 까지만 반복하는 이유는 마지막 두 개의 수는 이미 앞서 검사된 경우이기 때문
-if (i == 0 || i > 0 && nums[i] != nums[i - 1]) 중복된 숫자를 처리하기 위해 이전 값과 비교하여 같지 않은 경우에만 처리

  1. 투 포인터 사용
    -p1과 p2는 각각 두 번째와 세 번째 수의 인덱스를 가리키며, 처음에는 첫 번째 수의 다음과 마지막 수를 가리킴
    -while (p1 < p2) 루프에서 두 번째 수 nums[p1] 과 세번째 수 nums[p2]의 합을 계산
    -nums[p1] + nums[p2] == sum 인 경우에는 세 수의 합이 0이 되므로 결과 리스트 result에 추가
    -중복된 값은 건너뛰기 위해 while 루프로 중복을 제거
    -nums[p1] + nums[p2] < sum 인 경우 p1을 증가시켜 합을 크게 만들고, 반대로 p2를 감소시켜 합을 작게 만듬

  2. 결과반환
    -모든 경우의 수를 찾은 후에 result 리스트를 반환

profile
Hello World

0개의 댓글