반응형

# 34. Find First and Last Position of Element in Sorted Array

정렬된 정수 Array 와 Target 정수값이 주어졌을 때, 해당 Target 값이 나타나는 인덱스의 시작 위치와 끝 위치를 찾는 문제.

* O(logN) 에 해결할 수 있는지.

 

이 문제는 전형적인 Binary Search 로 생각해 풀었다. Binary Search 탐색을 위해 시작/끝 인덱스를 사용하고 그 값을 찾았을 경우에 left  / right 값에 현재까지 찾은 값들 중 가장 작은, 큰 인덱스 값을 대입하도록 했다.

 

void binarySearch(vector<int>& v, int t, int s, int e, int &l, int &r) {
	if (s >= e) {
		if (s == e && v[s] == t) {
			if (l == -1 || s < l)
				l = s;
			if (r == -1 || s > r)
				r = s;
		}
		return;
	}
	int m = (s + e) / 2;
	binarySearch(v, t, s, m, l, r);
	binarySearch(v, t, m + 1, e, l, r);
}

vector<int> searchRange(vector<int>& nums, int target) {
	if (nums.empty()) {
		vector<int> ans = { -1, -1 };
		return ans;
	}

	int left, right;
	left = right = -1;
	binarySearch(nums, target, 0, nums.size() - 1, left, right);
	vector<int> ans = { left, right };
	return ans;
}

# 35. Search Insert Position

중복되지 않는 정렬된 정수형 Array 가 주어졌을 때, Target 값을 주고 그 값이 존재한다면 그 인덱스 위치를, 존재하지 않으면 그 값이 들어가야 할 인덱스 위치를 찾는 문제.

 

이 문제도 마찬가지로 전형적인 Binary Search 문제로 생각되어 풀었는데 굳이 Binary Search 를 사용하지 않고 라이브러리 함수를 사용해 문제를 해결했다. lower_bound 를 사용했지만 중복되는 값이 없기 때문에 upper_bound 를 사용했어도 결과는 같을 것으로 생각된다.

 

int searchInsert(vector<int>& nums, int target) {
	vector<int>::iterator it = lower_bound(nums.begin(), nums.end(), target);
	return (it - nums.begin());
}

# 36. Valid Sudoku

스도쿠가 주어졌을 때, 이 스도쿠가 제대로 구성되었는지 확인하는 문제

* 이 스도쿠 문제를 해결 가능한 지를 보는 것이 아니라, 현재 상태에서 오류가 없는 지만 확인.

 

단순하게 문제를 해결하는 방식으로 구현했다. 각 Row, Col, Box 별로 저장되어 있는 값을 확인해서 중복되는 값이 없는 지를 확인하도록 했다.

 

bool check_row[9][10];
bool check_col[9][10];
bool check_box[9][10];

bool isValidSudoku(vector<vector<char>>& board) {
	for (int i = 0; i < 9; i++) {
		for (int j = 0; j < 9; j++) {
			if (board[i][j] == '.')
				continue;
			int r = i, c = j, b = (i / 3) * 3 + j / 3;
			int val = board[i][j] - '0';
			if (check_row[r][val])
				return false;
			if (check_col[c][val])
				return false;
			if (check_box[b][val])
				return false;

			check_row[r][val] = check_col[c][val] = check_box[b][val] = true;
		}
	}
	return true;
}

'Algorithm > LeetCode' 카테고리의 다른 글

[LeetCode # 43, 45, 46] 문제 풀이  (0) 2021.04.01
[LeetCode # 38 ~ 40] 문제 풀이  (0) 2021.03.30
[LeetCode # 31 ~ 33] 문제 풀이  (0) 2021.03.21
[LeetCode # 26 ~ 28] 문제 풀이  (0) 2021.03.19
[LeetCode # 23 ~ 24] 문제 풀이  (0) 2021.03.19
반응형

# 31. Next Permutation

next permutation Library 함수를 구현하는 문제. 정수 배열이 주어지고, 이 배열의 다음 permutation 을 찾아야 한다.

 

이 문제는 Solution 을 참고해서 문제를 해결했는데 문제 해결 방식은 다음과 같다.

1. 배열의 뒤에서부터 탐색을 하며, 값이 처음으로 작아지는 위치 pos 를 찾는다.

2. pos + 1 부터 배열 끝까지 탐색을 하면서, pos 위치 값보다 더 작은 값의 위치 pos2 를 찾는다.

    (찾지 못한 경우 pos2 는 Last Index 로 지정한다.)

3. pos 값과 pos2 - 1 의 값을 서로 교환한다.

4. pos + 1 부터 배열의 끝까지 값을 뒤집는다.

코딩도 하고 했는데 아직도 로직이 헷갈리는 걸 보니 제대로 이해를 못한 것 같다..

 

void nextPermutation(vector<int>& nums) {
	int i, size = nums.size();
	// 일단 뒤에서부터 탐색하면서 작아지는 첫 번째 위치 탐색
	for (i = size - 1; i > 0; i--) {
		if (nums[i - 1] < nums[i]) {
            cout << nums[i - 1] << " " << nums[i] << "\n";
			// 여기서 i - 1 이 처음 작아진 위치
			for (int j = i; j < size; j++) {
				// 여기서 j 가 마지막 위치거나, j + 1 의 값이 i - 1 보다 작으면 j 랑 i - 1 값 교체
				if (j == size - 1 || nums[i - 1] >= nums[j + 1]) {
                    cout << "swap " << nums[i - 1] << " with " << nums[j] << "\n";
					swap(nums[i - 1], nums[j]);
					break;
				}
			}
			break;
		}
	}
	reverse(nums.begin() + i, nums.end());
}

# 32. Longest Valid Parentheses

괄호 '(', ')' 의 열고 닫음 관계가 올바르게 사용된 것을 찾는 문제인데, 주어진 문자열에서 가장 긴 괄호 문자열을 찾는 문제이다.

 

이 문제를 O(n) 으로 해결할 수 있을 것 같다는 생각은 들었지만, 문제 풀이 아이디어가 떠오르지 않아 솔루션을 참고했다.

스택을 사용한 방식으로 문제를 해결할 수 있었는데, 기존 문제 해결 방식과 달리 push / pop 을 통해 괄호 관계를 판별하는 걸 넘어 스택에 index 를 저장해 두었다가 중간중간 length 를 저장할 수 있도록 했다.

 

int longestValidParentheses(string s) {
    int ans = 0;
    stack<int> idxStack;
    idxStack.push(-1);
    for (int i = 0; i<s.length(); i++){
        if (s[i] == '('){
            idxStack.push(i);
        }
        else {
            idxStack.pop();
            if (idxStack.empty())
                idxStack.push(i);
            else
                ans = ans > i - idxStack.top() ? ans : i - idxStack.top();
        }
    }
    return ans;
}

# 33. Search in Rotated Sorted Array

Rotated Sorted Array 가 주어졌을 때, 그 Array 에서 원하는 값을 찾는 문제

* Rotated Sorted Array 는 이전 문제에서 풀었던, Sorted Array 에서 일정 pivot 앞과 뒤를 반전시킨 Sorted Array

 

이 문제는 Binary Search 함수를 일부 변형해 문제를 해결할 수 있었는데, 풀이는 다음과 같이 했다.

원래 Binary Search 의 경우, index 의 시작부터 끝까지 값이 증가하는 정렬된 자료구조에서 시행되기 때문에 찾고자 하는 값이 시작 index ~ 끝 index 의 값 사이에 없으면 그 구간은 탐색을 하지 않는다.

하지만 이 문제의 경우는, 시작 index 값이 끝 index 값보다 작다는 것이 보장되지 않는다.

예를 들어, [4, 5, 6, 0, 1, 2, 3] 과 같이 배열이 주어지면 [4, 5, 6, 0] 구간은 시작 index 값이 더 크게 된다.

이 때문에 탐색 조건에 한 가지를 추가할 필요가 있었는데 시작 index 값과 끝 index 값을 비교해 그 구간이 증가하는 구간인 지를 먼저 확인한 후 그 구간 내에 탐색하고자 하는 값이 없을 경우에만 구간 탐색을 포기하도록 만들었다.

그 외는 기존 Binary Search 와 동일하게 구현해서 문제를 해결했다.

 

int binarySearch(vector<int>& nums, int target, int s, int e) {
	if (s >= e) {
		if (s == e && target == nums[s])
			return s;
		return -1;
	}

	if (nums[s] < nums[e] && (nums[s] > target || target > nums[e])) {
		return -1;
	}
	int m = (s + e) / 2;
	int l = binarySearch(nums, target, s, m);
	int r = binarySearch(nums, target, m + 1, e);
	if (l != -1)
		return l;
	if (r != -1)
		return r;
	return -1;
}

int search(vector<int>& nums, int target) {
	return binarySearch(nums, target, 0, nums.size() - 1);
}

'Algorithm > LeetCode' 카테고리의 다른 글

[LeetCode # 38 ~ 40] 문제 풀이  (0) 2021.03.30
[LeetCode # 34 ~ 36] 문제 풀이  (0) 2021.03.24
[LeetCode # 26 ~ 28] 문제 풀이  (0) 2021.03.19
[LeetCode # 23 ~ 24] 문제 풀이  (0) 2021.03.19
[LeetCode # 20 ~ 22] 문제 풀이  (0) 2021.03.18
반응형

# 26. Remove Duplicates from Sorted Array

중복값이 존재하는 정렬된 정수 배열이 주어졌을 때, 모든 값들이 한 번씩만 등장하도록 만드는 문제

 

Sorted Array 를 순차적으로 탐색하면서, 바뀌는 값이 등장할 때마다 이를 배열에 저장하는 방식으로 구현했다.

 

int removeDuplicates(vector<int>& nums) {
	if (nums.size() == 0)
		return 0;
	int i, j;
	for (i = j = 1; j < nums.size(); j++) {
		if (nums[j - 1] != nums[j]) {
			nums[i++] = nums[j];
		}
	}
	return i;
}

# 27. Remove Element

정수 배열과 Target 값이 주어졌을 때, 배열에서 해당 값을 삭제하는 문제

 

위의 문제와 유사하게, 두 개의 인덱스를 가지고 탐색을 하면서 삭제하고자 하는 값이 등장하면 그 값을 제외하고 배열에 저장하는 식으로 문제를 해결했다.

 

int removeElement(vector<int>& nums, int val) {
	int i, j;
	for (i = j = 0; j < nums.size(); j++) {
		if (nums[j] == val) {
			continue;
		}
		nums[i++] = nums[j];
	}
	return i;
}

# 28. Implement strStr()

두 문자열이 주어졌을 때, 부분 문자열의 시작 위치를 찾는 문제

 

본래 문자열에서 찾고자 하는 부분 문자열의 길이만큼 Substr 을 저장하고, 슬라이딩 윈도우 방식으로 앞의 문자를 빼고 뒤의 문자를 빼면서 부분 문자열과 비교를 해서 위치를 찾는 방식으로 문제를 해결했다.

 

int strStr(string haystack, string needle) {
	if (needle.size() > haystack.size())
		return -1;
	else if (needle.size() == haystack.size() && needle != haystack)
		return -1;

	string comp;
	int i;
	comp = haystack.substr(0, needle.size());
	if (comp == needle)
		return 0;
	for (i = 1; i + needle.size() - 1 < haystack.size(); i++) {
		comp = comp.substr(1) + haystack[i + needle.size() - 1];
		if (comp == needle)
			return i;
	}
	return -1;
}

'Algorithm > LeetCode' 카테고리의 다른 글

[LeetCode # 34 ~ 36] 문제 풀이  (0) 2021.03.24
[LeetCode # 31 ~ 33] 문제 풀이  (0) 2021.03.21
[LeetCode # 23 ~ 24] 문제 풀이  (0) 2021.03.19
[LeetCode # 20 ~ 22] 문제 풀이  (0) 2021.03.18
[LeetCode # 17 ~ 19] 문제 풀이  (0) 2021.03.18
반응형

# 23. Merge k Sorted Lists

k 개의 정렬된 리스트가 주어질 때, 하나의 리스트로 이를 합치는 문제

 

리스트 별로 Pointer 를 놓고, 그 중 값이 제일 작은 노드를 찾아서 이를 연결해도 문제를 풀 수 있지만 이렇게 할 경우 k 가 커지면 하나하나 비교하는 게 시간이 오래 걸릴 것 같았다.

그래서 Counting Sort 의 방식을 사용해서, 모든 노드들이 갖는 값을 찾고 각각의 값들이 등장한 회수를 저장한 다음 그 횟수만큼 리스트에 추가하도록 구현했다.

 

int cnt[20001];

ListNode* mergeKLists(vector<ListNode*>& lists) {
	int total_cnt = 0;
	for (auto list : lists) {
		for (ListNode* tmp = list; tmp != nullptr; tmp = tmp->next) {
			cnt[tmp->val + 10000]++;
			total_cnt++;
		}
	}

	ListNode *dummy = new ListNode(0);
	ListNode *cur = dummy;

	for (int i = 0; i < 20001; i++) {
		if (total_cnt == 0)
			break;
		while (cnt[i]--) {
			cur->next = new ListNode(i - 10000);
			cur = cur->next;
			total_cnt--;
		}
	}
	return dummy->next;
}

# 24. Swap Nodes in Pairs

리스트가 주어졌을 때, 각각 인접한 노드들끼리 저장된 값을 Swap 하는 문제

 

단순히 리스트를 순차 탐색하면서 두 노드마다 한 번씩 앞뒤로 값을 바꿔주도록 만들었다.

 

void swap(ListNode* l1, ListNode* l2) {
	int tmp = l1->val;
	l1->val = l2->val;
	l2->val = tmp;
}

ListNode* swapPairs(ListNode* head) {
	bool flag = true;
	for (ListNode* tmp = head; tmp != nullptr; tmp = tmp->next) {
		if (flag && tmp->next != nullptr) {
			swap(tmp, tmp->next);
		}
		flag = !flag;
	}
	return head;
}

'Algorithm > LeetCode' 카테고리의 다른 글

[LeetCode # 31 ~ 33] 문제 풀이  (0) 2021.03.21
[LeetCode # 26 ~ 28] 문제 풀이  (0) 2021.03.19
[LeetCode # 20 ~ 22] 문제 풀이  (0) 2021.03.18
[LeetCode # 17 ~ 19] 문제 풀이  (0) 2021.03.18
[LeetCode # 14 ~ 16] 문제 풀이  (0) 2021.03.09
반응형

# 20. Valid Parentheses

괄호로 이루어진 문자열이 주어졌을 때, 이 문자열의 괄호 구조가 적절한 지 확인하는 문제

 

이 문제는 단순히 스택을 활용해서 여는 괄호면 push, 닫는 괄호면 pop 해서 확인하는 방식으로 문제를 해결했다.

bool isValid(string s) {
	stack<char> st;

	for (auto c : s) {
		if (c == '(' || c == '[' || c == '{')
			st.push(c);
		else {
			if (st.empty())
				return false;
			switch (c) {
			case ')':
				if (st.top() != '(')
					return false;
				break;
			case ']':
				if (st.top() != '[')
					return false;
				break;
			case '}':
				if (st.top() != '{')
					return false;
				break;
			}
			st.pop();
		}
	}
	if (!st.empty())
		return false;
	return true;
}

# 21. Merge Two Sorted Lists

정렬된 두 리스트가 주어졌을 때, 정렬을 유지한 상태로 두 리스트를 하나로 합치는 문제

 

간단하게 Two Pointer 사용해서 순서대로 비교하고, 하나의 리스트로 합치도록 만들었다.

ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
	ListNode dummyHead = ListNode(0);
	ListNode *tmp = &dummyHead;
	while (l1 != nullptr || l2 != nullptr) {
		if (l1 == nullptr) {
			tmp->next = l2;
			tmp = tmp->next;
			l2 = l2->next;
		}
		else if (l2 == nullptr) {
			tmp->next = l1;
			tmp = tmp->next;
			l1 = l1->next;
		}
		else {
			if (l1->val < l2->val) {
				tmp->next = l1;
				tmp = tmp->next;
				l1 = l1->next;
			}
			else {
				tmp->next = l2;
				tmp = tmp->next;
				l2 = l2->next;
			}
		}
	}
	return dummyHead.next;
}

# 22. Generate Parentheses

Parentheses 쌍의 개수 n 이 주어졌을 때, 가능한 괄호 "()" 쌍의 조합을 모두 구하는 문제

 

주어진 n 개의 여는 괄호 / 닫는 괄호의 가능한 모든 조합을 그냥 Recursive 함수로 찾도록 구현

사용된 여는 괄호 개수만큼만 닫는 괄호를 사용할 수 있도록 해 불가능한 괄호 조합은 만들지 않도록 했다.

vector<string> generateParenthesis(int n) {
	vector<string> ans;
	recursiveGenerate(ans, "", n, n);
	return ans;
}

void recursiveGenerate(vector<string>& v, string s, int l, int r) {
	if (l == 0 && r == 0) {
		v.push_back(s);
		return;
	}

	if (l > 0) {
		recursiveGenerate(v, s + "(", l - 1, r);
	}
	if (r > l) {
		recursiveGenerate(v, s + ")", l, r - 1);
	}
}

'Algorithm > LeetCode' 카테고리의 다른 글

[LeetCode # 26 ~ 28] 문제 풀이  (0) 2021.03.19
[LeetCode # 23 ~ 24] 문제 풀이  (0) 2021.03.19
[LeetCode # 17 ~ 19] 문제 풀이  (0) 2021.03.18
[LeetCode # 14 ~ 16] 문제 풀이  (0) 2021.03.09
[LeetCode # 11 ~ 13] 문제 풀이  (0) 2021.02.28
반응형

# 17. Letter Combinations of a Phone Number

각 문자가 '2-9' 정수로 이루어진 문자열이 주어졌을 때, 해당 버튼을 abc keyboard 에서 눌렀을 때 만들 수 있는 모든 경우를 구하는 문제

ex ) 2 - abc, 3 - def, ... 9 - wxyz

말 그대로 각 숫자 별로 출력 가능한 문자들이 정해져 있어 이 문자들의 조합을 만들도록 코드를 구현했다.

// 각 숫자 별로 입력 가능한 문자들 저장
string letters[10] = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};

vector<string> letterCombinations(string digits) {
    if (digits.empty()){
        return {};
    }
    vector<string> result;
    result.push_back("");

    for (auto digit : digits) {
        vector<string> tmp;
        for (auto s : result) {
            for (auto candidate : letters[digit - '0']) {
                tmp.push_back(s + candidate);
            }
        }
        result.swap(tmp);
    }
    return result;
}

# 18. 4 Sum

3 Sum 과 유사한 문제로, 정수형 배열이 주어졌을 때 a + b + c + d = target 값이 되는 a, b, c, d 의 조합을 찾는 문제

3 Sum 은 하나의 값만 정해놓고 left, right 인덱스 두 개로 탐색했지만 이 문제는 두 값을 정해놓고 left, right 인덱스 두 개로 탐색

vector<vector<int>> fourSum(vector<int>& nums, int target) {
    sort(nums.begin(), nums.end());
    vector<vector<int>> ans;

    for (int i = 0; i < nums.size(); i++) {
        int t1 = target - nums[i]; // 연산량을 줄이기 위해 앞에서 미리 계산
        for (int j = i + 1; j < nums.size(); j++) {
            int t2 = t1 - nums[j];
            int l = j + 1, r = nums.size() - 1;
            while (l < r) {
                int sum = nums[l] + nums[r];
                if (sum < t2) {
                    l++;
                }
                else if (sum > t2) {
                    r--;
                }
                else {
                    vector<int> v(4, 0);
                    v[0] = nums[i];
                    v[1] = nums[j];
                    v[2] = nums[l];
                    v[3] = nums[r];
                    ans.push_back(v);

                    while (l < r && nums[l] == v[2]){
                        l++;
                    }
                    while (l < r && nums[r] == v[3]){
                        r--;
                    }
                }
            }
            while (j < nums.size() - 1 && nums[j] == nums[j + 1]){
                j++;
            }
        }
        while (i < nums.size() - 1 && nums[i] == nums[i + 1]){
            i++;
        }
    }
    return ans;
}

# 19. Remove Nth Node From End of List

리스트가 주어졌을 때, n 번째 노드를 리스트에서 삭제하는 문제

Single Linked List 가 주어지기 때문에 단순히 뒤에서부터 n 개를 탐색할 수는 없었다.

대신 앞에서부터 탐색을 하되 총 노드의 개수를 저장하고 각각의 노드를 배열에 저장함으로써 뒤에서 n 번째 노드를 바로 찾고, 그 앞의 노드를 그 다음 노드로 연결하도록 했다.

ListNode* removeNthFromEnd(ListNode* head, int n) {
    ListNode *arr[31] = {0, }, *tmp, dummy;
    int size;
    dummy = ListNode(0, head);
    arr[0] = &dummy;

    for (tmp = head, size = 1; tmp != nullptr; tmp = tmp->next, size++) {
        arr[size] = tmp;
    }    
    if (size == 2) {
        return nullptr;
    }
    arr[size - n - 1]->next = arr[size - n + 1];
    return arr[0]->next;
}

'Algorithm > LeetCode' 카테고리의 다른 글

[LeetCode # 23 ~ 24] 문제 풀이  (0) 2021.03.19
[LeetCode # 20 ~ 22] 문제 풀이  (0) 2021.03.18
[LeetCode # 14 ~ 16] 문제 풀이  (0) 2021.03.09
[LeetCode # 11 ~ 13] 문제 풀이  (0) 2021.02.28
[LeetCode # 7 ~ 9] 문제 풀이  (0) 2021.02.26
반응형

# 14. Longest Common Prefix

String 배열이 주어졌을 때 가장 긴 Common Prefix 를 찾는 문제

 

Common Prefix 를 찾는 방법으로 생각한 것은, 우선 문자열들을 길이 순서대로 정렬해서 가장 짧은 문자열을 공통 Prefix 로 먼저 저장을 한다. 그 다음에 순서대로 탐색을 하면서, 공통 Prefix 랑 문자열 하나씩 비교를 하고 비교했을 때 서로 같은 위치까지를 Prefix 로 저장한다. 이걸 반복하다 보면, Prefix 의 길이가 점차 짧아져서 탐색 / 비교 속도가 빠를 것이라고 생각된다.

 

string longestCommonPrefix(vector<string>& strs) {
	if (strs.empty()) {
    	return "";
    }
    
    sort(strs.begin(), strs.end());
    string prefix = strs[0];
    
    for (int i = 1; i < strs.size(); i++) {
		int l;
        for (l = 0; l < prefix.length(); l++) {
        	if (prefix[l] != strs[i][l]) {
            	break;
            }
        }
        if (l == 0) {
        	return "";
        }
        prefix = prefix.substr(0, l);
    }
    return prefix;
}

# 15. 3Sum

정수 배열이 주어졌을 때, A + B + C = 0 이 되는 Unique 한 {A, B, C} 조합을 찾는 문제

 

우선, 문제에서 주어진 배열의 Length 가 최대 3000 이라서, O(n^3) 탐색을 하게 되면 무조건 시간 초과일 것 같아서, 이걸 O(n^2) 으로 탐색할 방법을 생각하게 되었다. 그래서, A + B = 0 이 되는 두 수를 찾을 때 탐색을 최소화할 수 있는 방법을 생각해 보았는데 수를 정렬해 놓고 두 수의 합에 따라서 앞뒤 인덱스를 증가/감소 시켜가며 탐색하면 O(n) 만 탐색이 가능할 것 같았다. 그래서 세 수를 찾는 문제는, 한 수를 고정해 놓고 두 수를 찾는 방식으로 하면 고정하는 수 찾기 O(n) * 나머지 두 수 찾기 O(n) 해서 O(n^2) 에 탐색이 가능할 것으로 생각해 문제를 풀었다.

 

vector<vector<int>> threeSum(vector<int>& nums) {
	sort(nums.begin(), nums.end());
    vector<vector<int>> ans;
    
    for (int i = 0; i < nums.size(); i++) {
    	if (i > 0 && nums[i] == nums[i - 1]) {
        	continue; // 중복 제거
        }
        int left = i + 1, right = nums.size() - 1; // 왼쪽, 오른쪽 Index
        while (left < right) {
        	if (right < nums.size() - 1 && nums[right] == nums[right + 1]) {
            	right--;
                continue; // 마찬가지로 오른쪽의 값이 중복될 경우 중복 제거
            }
            
            int sum = nums[i] + nums[left] + nums[right];
            if (sum > 0) {
            	right--; // sum 이 0 보다 크면 right index 감소
            }
            else if (sum < 0) {
            	left++; // sum 이 0 보다 작으면 left index 증가
            }
            else {
            	vector<int> tmp = {nums[i], nums[left], nums[right]};
                ans.push_back(tmp);
                left++;
                right--;
            }
        }
    }
    return ans;
}

# 16. 3Sum Closest

이 문제는 #15. 3Sum 문제와 유사하지만, 차이점은 A + B + C = 0 인 위의 문제와 다르게, Target 값을 정해놓고 A + B + C = Target 에 가장 가까운 합을 구하는 데 있다.

 

그래서 이 문제를 풀 때는, 위와 마찬가지로 정렬을 하고 탐색도 동일한 방식으로 진행하지만, 세 수의 조합을 저장하지 않고 Target 값과 현재 Sum 의 차를 비교해서 기존의 차이보다 더 작게 차이가 나면 Sum 을 저장하는 방식으로 문제를 풀었다.

 

int _abs(int x) {
	return (x > 0 ? x : -x);
}

int threeSumClosest(vector<int>& nums, int target) {
	int diff = INT_MAX, ans = 0;
    sort(nums.begin(), nums.end());
    
    for (int i = 0; i < nums.size(); i++) {
    	if (i > 0 && nums[i] == nums[i - 1]){
        	continue; // 중복 제거
        }
        int left = i + 1, right = nums.size() - 1;
        while (left < right) {
        	if (right < nums.size() - 1 && nums[right] == nusm[right + 1]) {
            	k--;
                continue;
            }
            
            int sum = nums[i] + nums[left] + nums[right];
            if (_abs(sum - target) < diff) {
            	diff = _abs(sum - target);
                ans = sum;
            }
            if (sum > target) {
            	right--;
            }
            else if (sum < target) {
            	left++;
            }
            else {
            	return sum;
            }
        }
    }
    return ans;
}

'Algorithm > LeetCode' 카테고리의 다른 글

[LeetCode # 20 ~ 22] 문제 풀이  (0) 2021.03.18
[LeetCode # 17 ~ 19] 문제 풀이  (0) 2021.03.18
[LeetCode # 11 ~ 13] 문제 풀이  (0) 2021.02.28
[LeetCode # 7 ~ 9] 문제 풀이  (0) 2021.02.26
<LeetCode # 4 ~ 6> 문제 풀이  (0) 2021.02.23
반응형

# 11. Container With Most Water

일렬로 세워진 n 개의 기둥들의 높이가 주어졌을 때, 두 기둥 사이에 받을 수 있는 물의 최대양을 구하는 문제

* 두 기둥 사이에 받을 수 있는 물은 (두 기둥 사이 거리 * 두 기둥 중 낮은 기둥의 높이) 로 정의

 

처음 이 문제를 봤을 때는, O(n^2) 으로 경우를 다 탐색해야 하나 생각을 했는데 이렇게 할 경우에 n 이 최대 3만이고 n^2 은 9억이니 불가능할 것이라고 생각했다. 그래서 생각한 점은, 기둥 사이 거리의 최대는 양 끝점이고, 양 끝점부터 하나씩 좁혀나가며 탐색을 하면 n 번만 탐색을 할 수 있을 것이라고 생각이 들었다.

 

#define _MIN_(X, Y) ((X) < (Y) ? (X) : (Y))

int maxArea(vector<int> &height) {
	int l = 0, r = height.size() - 1;
    int max = 0;
    while (l < r) {
    	int area = _MIN_(height[l], height[r]) * (r - l);
        max = max > area ? max : area;
        if (height[l] < height[r])
        	l++;
        else
        	r--;
    }
}

# 12. Integer to Roman

정수 N 이 주어졌을 때, 이를 Roma 표기법으로 변경하는 문제

* I = 1, V = 5, X = 10, L = 50, C = 100, D = 500, M = 1000

 

이 문제는 풀이가 떠오르지 않아서 Solution 을 참고해 풀이를 작성했다.

나는 1, 5, 10, 50, ... 각 숫자 앞자리가 1 / 5 로 달라서 이를 어떻게 처리할 지 고민을 했는데, 솔루션은 이를 가볍게 해결할 수 있는 방법을 사용했다. 10 의 승수 기준으로만 숫자를 처리하는 것이 그 방법이었는데, 4 / 9 등의 숫자가 나오면 별도의 처리가 필요하지만 그 외는 일정하게 처리가 가능한 방식이었다.

 

여기서 Map 은 각 숫자가 어떤 문자로 변환될 지를 저장하기 위해 사용되었다.

 

string intToRoman(int num) {
	map<int, char> m = {{1, 'I'}, {5, 'V'}, ..., {1000, 'M'}};
    string ans = "";
    
    for (int i = 1000; i > 0; i /= 10) {
    	int firstNum = num / i;
        
        if (firstNum == 4) {
        	ans += m[i];
            ans += m[i * 5];
        }
        else if (firstNum == 9) {
        	ans += m[i];
            ans += m[i * 10];
        }
        else {
        	if (firstNum > 4) {
            	ans += m[i * 5];
            }
            for (int k = 0; k < (firstNum % 5); k++) {
            	ans += m[i];
            }
        }
        num %= i;
    }
    return ans;
}

# 13. Roman to Integer

이 문제는 위의 # 12. 문제를 거꾸로 만드는 문제이다.

 

위의 문제는 각 자리수별로 어떤 수가 저장되어있는 지를 확인해서 그 값을 로마자로 바꿔줬는데, 이번 문제는 로마자를 보고 수로 바꿔주었다.

차이점은, 로마자의 경우 현재의 값이 하나의 독립적인 값이 아니라 다음에 나올 값의 전치 값으로 사용될 수 있기 때문에 이를 조건에 추가해서 계산하도록 만들었다.

 

마찬가지로 map 을 사용했는데 이번에는, 각 문자가 어떤 숫자로 변환될 것인지 그 값을 저장하기 위해 사용되었다.

 

int romanToInt(string s) {
	map<char, int> m = {{'M', 1000}, {'D', 500}, ..., {'I', 1}};
    int ans = 0;
    
    for (int i = 0; i < s.length(); i++) {
    	if (m[s[i + 1]] > m[s[i]]) {
        	ans -= m[s[i]];
            ans += m[s[i + 1]];
        }
        else {
        	ans += m[s[i]];
        }
    }
    return ans;
}

'Algorithm > LeetCode' 카테고리의 다른 글

[LeetCode # 17 ~ 19] 문제 풀이  (0) 2021.03.18
[LeetCode # 14 ~ 16] 문제 풀이  (0) 2021.03.09
[LeetCode # 7 ~ 9] 문제 풀이  (0) 2021.02.26
<LeetCode # 4 ~ 6> 문제 풀이  (0) 2021.02.23
<LeetCode # 1 ~ 3> 문제 풀이  (0) 2021.02.21
반응형

# 7. Reverse Integer

한 정수가 주어졌을 때, 자릿수를 뒤집은 정수를 반환하는 문제

ex) 12345 -> 54321

 

이 문제를 보고, 가장 먼저 떠올린 풀이는 정수를 한 자리씩 받아서 문자열로 변환하고, 이를 다시 정수형으로 바꾸는 거였다.

이렇게 하면, 1의 자리부터 문자열의 앞에서 넣으면 자연스럽게 뒤집을 수 있을 것이라 생각했다.

 

여기서 Solution 을 보고 참고했던 점은, 굳이 문자열로 변환했다가 다시 정수로 변환하는 과정을 거칠 필요 없이 새로운 정수에 10을 곱하고 기존 수의 1의 자리수를 새로운 정수의 1의 자리로 만들어주면 되는 점이었다.

그 다음으로 문제의 핵심은 오버플로우가 발생했을 때, 이 값을 어떻게 처리할 것인가. 였다.

 

이건 일일이 확인하는 방법이 가장 확실해 보였다. 새로운 수에 10을 곱하기 전에 조건을 확인하고 진행을 할 수 있도록 했다.

 

#define INT_MAX 2147483647
#define INT_MIN -2147483648

class Solution {
public:
	int reverse(int x) {
		int rev = 0;
		while (x != 0) {
			int pop = x % 10;
			x /= 10;
			if (rev > INT_MAX / 10 || (rev == INT_MAX / 10 && pop > 7)) return 0;
			if (rev < INT_MIN / 10 || (rev == INT_MIN / 10 && pop < -8)) return 0;
			rev = rev * 10 + pop;
		}
		return rev;
	}
};

# 8. String to Integer (atoi)

atoi 함수를 직접 구현하는 문제

 

이 때 문제의 조건은, 숫자 앞의 공백은 무시하고 '+' / '-' 는 결과값의 부호를 의미하는 것이다.

문자열에서 숫자의 조건은, 처음으로 숫자가 나와서 숫자 이외의 문자가 등장하기 전까지이고 숫자가 아닌 문자가 앞에 있는 경우 숫자로 인식하지 않는다.

추가로 오버플로우가 발생할 경우, 오버플로우가 발생하지 않는 최대(최소)값을 반환하도록 한다.

 

이 문제는, 위의 문제 풀이를 참고해서 오버플로우 조건을 검사하도록 하고, 문자열을 숫자로 변환하는 건 앞에서부터 하도록 했다.

 

class Solution {
public:
    int myAtoi(string s) {
        int idx = 0, ret = 0;
        while (s[idx] == ' ') {
            idx++;
        }

        bool negative = false;
        if (s[idx] == '-') {
            negative = true;
            idx++;
        }
        else if (s[idx] == '+')
            idx++;

        while (s[idx] >= '0' && s[idx] <= '9') {
            if (negative && (ret > INT_MAX / 10 || (ret == INT_MAX / 10 && s[idx] >= '8'))) {
                return INT_MIN;
            }
            if (!negative && (ret > INT_MAX / 10 || (ret == INT_MAX / 10 && s[idx] >= '7'))) {
                return INT_MAX;
            }
            ret = ret * 10 + (s[idx] - '0');
            idx++;
        }

        return (negative ? -ret : ret);
    }
};

# 9. Palindrome Number

주어진 정수가 Palindrome Number 인지 확인하는 문제

Palindrome Number 란, 앞뒤 자리가 대칭되는 수 ex) 12345 (X), 12321 (O)

 

처음 생각은, Palindromic String 처럼 가운데 위치를 잡고 그 위치 앞뒤로 대칭인지를 확인하는 방식이었다. 

하지만 정수는 가운데가 어디인지 한 번에 찾는 게 불가능해서 다른 방식을 찾게 되었다.

 

Solution 을 참고해 얻은 결과는, #7 번 문제를 참고해서 숫자를 뒤집는 중간에 자리수가 같거나 한자리 차이나는 순간에 두 수를 비교하면 앞뒤가 대칭인지 알 수 있다는 것이었다.

 

class Solution {
public:
	bool isPalindrome(int x) {
		if (x < 0 || (x != 0 && x % 10 == 0))
			return false;

		int rev = 0;
		while (x > rev) {
			rev = rev * 10 + x % 10;
			x /= 10;
		}

		return rev == x || x == rev / 10;
	}
};

'Algorithm > LeetCode' 카테고리의 다른 글

[LeetCode # 17 ~ 19] 문제 풀이  (0) 2021.03.18
[LeetCode # 14 ~ 16] 문제 풀이  (0) 2021.03.09
[LeetCode # 11 ~ 13] 문제 풀이  (0) 2021.02.28
<LeetCode # 4 ~ 6> 문제 풀이  (0) 2021.02.23
<LeetCode # 1 ~ 3> 문제 풀이  (0) 2021.02.21
반응형

# 4. Median of Two Sorted Arrays

정렬된 두 Array A, B 가 주어졌을 때, 중간값을 구하는 문제

* 수행시간이 O(log(m + n)) 으로 주어짐

 

우선, 내 머리로 이 문제를 풀기엔 역부족이었고 Solution 을 참고했다.

처음 생각했던 것은, 두 Array 가 정렬이 되어 있으니, Merge 를 하면 되지 않을까.. 생각을 했는데 그렇게 되면 O(m + n) 이 되므로 문제 풀이에 적합하지 않았다.

그래서 Solution 을 처음 봤는데 두 Array 를 각각 나눠서 왼쪽 부분과 오른쪽 부분을 각각 합치는데 그렇게 하면 왼쪽의 Max 값과 오른쪽의 Min 값으로 중간값을 구할 수 있다는 것이었다.

근데 여기서, 두 Array 를 나눠서 왼쪽 부분의 Max 가 오른쪽 부분의 Min 보다 작다는 보장을 할 수 있나... 라고 생각을 하던 중 Solution Source Code 를 보고 의문이 풀렸다.

이 문제를 푸는 Solution 의 핵심은 그 위치를 찾는 것이었다. 한 Array 를 기준으로 잡고 그 Array 에서 나눌 위치를 찾는 것을 이분탐색을 통해 수행하는 것, 그것이 이 문제의 해답이었다.

#define _MAX(x, y) ((x) > (y) ? (x) : (y))
#define _MIN(x, y) ((x) < (y) ? (x) : (y))

class Solution {
public:
	double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
		int m = nums1.size(), n = nums2.size();
		if (m > n) {
        	// Array 1 번을 기준으로 잡고 풀이를 진행하기 위해 Array 1 이 짧거나 같도록 유지
		}
        // Slice 위치 탐색을 위한 값 설정
		int iMin = 0, iMax = m, half = (m + n + 1) / 2;
		while (iMin <= iMax) {
        	int i, j; // i 는 Array 1 을 Slice 할 인덱스, j 는 나머지 개수를 채우기 위한 index
			if (i < iMax && nums2[j - 1] > nums1[i]) {
				// Slice 를 했는데 Array 2 의 Left Max 값이 
                // Array 1 의 Right Min 값보다 큰 경우
			}
			else if (i > iMin && nums1[i - 1] > nums2[j]) {
				// Slice 했는데 Array 1 의 Left Max 값이
                // Array 2 의 Right Min 값보다 큰 경우
			}
			else {
            	// 그 외 조건에 맞게 Median 값을 찾기

				if ((m + n) % 2 == 1)
					return (double)maxLeft;

				return (double)(maxLeft + minRight) / 2;
			}
		}
		return 0.0;
	}
};

# 5. Longest Palindromic Substring

String 이 주어지고, 이 String 의 Substring 중에 가장 긴 Palindromic String 을 찾는 문제

* Palindromic String 이란, 좌우가 대칭되는 문자열을 의미한다. ex) "abcba", "StringnirtS", ...

 

가장 먼저 떠올린 생각은, Dynamic Programming 을 활용하면.. 풀 수 있지 않을까 였는데 그 방법이 떠오르지 않아서 중심을 잡고 거기서 확장해 나가는 방법을 떠올렸다.

예를 들어, "ABCDEFG" 라는 문자열이 있으면, 'A', 'B', 'C', .. 각각의 문자를 중심으로 양 옆으로 확장했을 때 좌우 대칭이 이뤄지는 지를 확인하면 되지 않을까 라는 생각이었다.

이렇게 할 경우, Best Case 는 O(n) 의 시간 복잡도를 가질 것이고 Worst 의 경우는 O(n^2) 의 시간 복잡도를 가질 것이라 생각했다. 모든 위치에서 SubString 이 String 길이 만큼의 Palindromic 을 가질 경우가 Worst 라고 생각했지만, 그럴 경우는 거의 없을 것이고 평균적으로 O(n) 에서 크게 벗어나지 않을 것이라는 생각을 했다.

#define _MAX(x, y) ((x) > (y) ? (x) : (y))

int calculate(string &s, int l, int r) {
	while (l >= 0 && r < s.length() && s[l] == s[r]) {
    	// 기준 L, R 을 확인해서 두 문자가 서로 같으면 탐색을 계속한다.
	}
	return r - l - 1;
}

class Solution {
public:
	string longestPalindrome(string s) {
		if (s.length() < 1)
			return "";
		int st = 0, ed = 0;

		for (int i = 0; i < s.length(); i++) {
			int l1, l2; // l1 은 현재 위치 기준 1개부터 시작하고, l2 는 현재 위치 기준 2개씩 시작
			int len = _MAX(l1, l2);
			if (len > ed - st) {
				// 어디부터 시작하고 어디서 끝나는 지 st, ed 저장
			}
		}
		return s.substr(st, ed - st + 1);
	}
};

# 6. ZigZag Conversion

String 과 Row 를 주고, 지그재그로 문자열을 표현했을 때 Row 순으로 저장된 문자열을 찾는 문제

ex) String : "ABCDEFGHIJ", Row : 3
A      E       I

B  D  F  H  J

C      G

Result : "AEIBDFHJCG"

 

이 문제 역시 Solution 을 참고했다.

처음에 문제를 보고, 아! 행별로 문자를 뛰어넘는 Offset 값이 있고, 그걸 구해서 찾으면 되겠다! 라고 생각을 했는데

계산하기에 조건이 조금 귀찮아서 Solution 을 살짝 봤다.

계산하는 방식도 있긴 했지만, 그것보다 더 쉬운 방식이 있어 그걸 참고해서 구현했다.

참고한 방식은, 행별로 문자열을 저장할 공간을 만들어 두고 그냥 문자열을 순서대로 탐색하며 한 문자씩 한 행에 추가하는 방식이었다.

내가 생각한 방식보다 구현도 더 간단하고 시간적인 측면에서도 거의 차이가 없을 것 같아 이 방식을 사용했다.

#define _MIN_(X, Y) ((X) < (Y) ? (X) : (Y))

class Solution {
public:
    string convert(string s, int numRows) {
	if (numRows == 1)
		return s;
	string ret = "";
	vector<string> rows(_MIN_(numRows, s.size())); // 행별로 문자열을 저장해 둘 공간
	int curRow = 0; // 현재 어느 행에 저장할 지
	bool dir = true; // 다음 행의 위치가 어느 방향인지

	for (char c : s) {
        // 문자열 순서대로 탐색하면서 각 문자를 현재 행에 추가
        // 현재 행을 고려해 방향을 정하고, 그 방향으로 행 이동
	}
	for (string tmp : rows) {
        // 마지막에 행 순서대로 문자열 저장
	}
	return ret;
}
};

'Algorithm > LeetCode' 카테고리의 다른 글

[LeetCode # 17 ~ 19] 문제 풀이  (0) 2021.03.18
[LeetCode # 14 ~ 16] 문제 풀이  (0) 2021.03.09
[LeetCode # 11 ~ 13] 문제 풀이  (0) 2021.02.28
[LeetCode # 7 ~ 9] 문제 풀이  (0) 2021.02.26
<LeetCode # 1 ~ 3> 문제 풀이  (0) 2021.02.21

+ Recent posts