728x90

C++로 트리 자료구조를 구현할 때에는 보통 왼쪽 자식을 가리키는 포인터, 오른쪽 자식을 가리키는 포인터, 내 데이터 이렇게 3가지 정보를 가지고 있는 Node라는 구조체를 만들어서 구현한다.

그런데 이 방법은 알고리즘 문제를 풀 때 직접 구현하기에는 너무 복잡하고 포인터를 이용하기 때문에 nullpointer 예외가 나기도 쉽다.

이진트리는 C++ STL인 map과 pair를 이용해서 쉽게 구현할 수 있다.

(만약 map을 잘 모르신다면 이 글을 먼저 보고 오는 것을 추천한다. 링크↓)

https://breakcoding.tistory.com/154

 

[C++] STL

라이브러리

map은 데이터를 key-value 형태로 저장하는 자료구조이다. http://www.cplusplus.com/reference/map/map/ map - C++ Reference difference_typea signed integral type, identical to: iterator_traits ::differen..

breakcoding.tistory.com

 

다음과 같은 이진트리가 있다고 하자.

이진 트리

 

일단 트리를 저장할 map 객체를 선언한다. 이 트리에는문자가 저장되어 있기 때문에 char 타입으로 선언했다.

pair에는 왼쪽 자식 노드와 오른쪽 자식 노드를 집어넣을 것이다.

map<char, pair<char, char>> tree;

A의 오른쪽 자식은 B, 왼쪽 자식은 C이므로 A 노드를 다음과 같이 집어넣어준다.

tree['A'] = make_pair('B', 'C');

B의 왼쪽 자식 노드는 D, 오른쪽 자식 노드는 없다. 자식이 없을 경우 .으로 표현하기로 하자. (NULL로도 표현해도 되고 내가 원하는 방식으로 표현해주면 된다. 대신 데이터로는 절대 나올 수 없는 것으로 선택해야 한다.)

tree['B'] = make_pair('D', '.');

D는 왼쪽, 오른쪽 자식이 모두 없으므로 tree['D']에는 이렇게 저장해준다.

tree['D'] = make_pair('.', '.');

이런식으로 모든 노드들을 tree라는 map에 저장해준다.

tree['C'] = make_pair('E', 'F');
tree['E'] = make_pair('.', '.');
tree['F'] = make_pair('.', 'G');
tree['G'] = make_pair('.', '.');

이렇게 노드의 개수만큼 map에 저장하는 것을 반복해주면 이진트리가 완성된다.

A 노드의 왼쪽 자식을 알고 싶으면

tree['A'].first

A의 오른쪽 자식을 알고 싶으면

tree['A'].second

이렇게 해주면 된다.

 

(pair 사용법이 낯설다면 이전 포스팅을 보고 오는 것을 추천한다. 링크↓)

https://breakcoding.tistory.com/96

 

[C++] 라이브러리 - tuple, pair

코딩을 하다가 (x, y) 이런 순서쌍이 필요할 수도 있고 세 가지 정보를 묶어서 저장해야 할 수도 있다. 이럴 때 라이브러리를 사용하면 편리하다. 2-tuple은 주로 pair라고 부른다. 튜플에는 2-tuple(pair..

breakcoding.tistory.com

 

일반적인 이진트리 구현방법인 포인터를 이용한 구조체로 트리를 구현했다면 무조건 가장 아래쪽에 있는 노드 D, 노드 E, 노드 G부터 만들어주고 위로 올라가야 한다. 따라서 그냥 이음선의 정보만 주어진다면 트리를 구현하기 위해서 위상정렬까지 해야 하는 상황이 발생한다. 하지만 이렇게 map을 이용해서 이진트리를 구현한다면 순서와 상관없이 이렇게 이음선의 정보만 map에 추가해주면 되기 때문에 트리를 쉽게 구현할 수 있다.

 

그러면 이렇게 map으로 구현한 이진트리를 전위순회, 중위순회, 후위순회 하는 방법을 알아보자.

void preorder(char node) {//전위순회
	cout << node << " "; //현재 노드의 데이터 출력
	if (m[node].first != '.') { //왼쪽 자식이 있다면
		preorder(m[node].first);
	}
	if (m[node].second != '.') { //오른쪽 자식이 있다면
		preorder(m[node].second);
	}
}
void inorder(char node) {//중위순회
	if (m[node].first != '.') { //왼쪽 자식이 있다면
		inorder(m[node].first);
	}
	cout << node << " "; //현재 노드의 데이터 출력
	if (m[node].second != '.') { //오른쪽 자식이 있다면
		inorder(m[node].second);
	}
}
void postorder(char node) { //후위순회
	if (m[node].first != '.') { //왼쪽 자식이 있다면
		postorder(m[node].first);
	}
	if (m[node].second != '.') { //오른쪽 자식이 있다면
		postorder(m[node].second);
	}
	cout << node << " "; //현재 노드의 데이터 출력
}

이렇게 재귀함수로 쉽게 순회할 수 있다. 만약 자식 노드가 없을 때에 NULL로 저장했다면

왼쪽 자식이 있는지 체크하는 부분을 m[node].first != NULL로

오른쪽 자식이 있는지 체크하는 부분을 m[node].second != NULL로 해주면 된다.

728x90
728x90

https://www.acmicpc.net/problem/9507

 

9507번: Generations of Tribbles

문제 꿍은 군대에서 진짜 할짓이 없다. 그래서 꿍만의 피보나치를 만들어보려고 한다. 기존의 피보나치는 너무 단순해서 꿍은 좀더 복잡한 피보나치를 만들어보고자 한다. 그래서 다음과 같은 피보나치를 만들었다. 꿍만의 피보나치 함수가 koong(n)이라고 할 때, n < 2 : 1 n = 2 : 2 n = 3 : 4 n > 3 : koong(n − 1) + koong(n − 2) + koong(n − 3) + koong(n − 4) 이다. 여러분도 꿍 피보나치

www.acmicpc.net

#include <iostream>
#include <vector>
using namespace std;


int main() {
	cin.tie(NULL);
	ios_base::sync_with_stdio(false);
	int testCase;
	cin >> testCase;
	long long koong[68];
	koong[0] = koong[1] = 1;
	koong[2] = 2;
	koong[3] = 4;
	for (int i = 4; i < 68; i++) {
		koong[i] = koong[i - 1] + koong[i - 2] + koong[i - 3] + koong[i - 4];
	}
	for (int t = 0; t < testCase; t++) {
		int n;
		cin >> n;
		cout << koong[n] << '\n';
	}
	return 0;
}
728x90
728x90

https://www.acmicpc.net/problem/11057

 

11057번: 오르막 수

오르막 수는 수의 자리가 오름차순을 이루는 수를 말한다. 이때, 인접한 수가 같아도 오름차순으로 친다. 예를 들어, 2234와 3678, 11119는 오르막 수이지만, 2232, 3676, 91111은 오르막 수가 아니다. 수의 길이 N이 주어졌을 때, 오르막 수의 개수를 구하는 프로그램을 작성하시오. 수는 0으로 시작할 수 있다.

www.acmicpc.net

#include <iostream>
#include <vector>
using namespace std;


int main() {
	int n;
	cin >> n;
	vector<vector<int>> count(1001,vector<int>(10));
	for (int i = 0; i < 10; i++) {
		count[0][i] = 1;
		count[1][i] = 10 - i;
	}
	for (int i = 2; i < n; i++) {
		for (int j = 0; j < 10; j++) {
			for (int k = j; k < 10; k++) {
				count[i][j] = (count[i][j] + count[i - 1][k]) % 10007;
			}
		}
	}
	int total = 0;
	for (int i = 0; i < 10; i++) {
		total = (total + count[n - 1][i]) % 10007;
	}
	cout << total;
	return 0;
}
728x90
728x90

https://www.acmicpc.net/problem/11727

 

11727번: 2×n 타일링 2

첫째 줄에 2×n 크기의 직사각형을 채우는 방법의 수를 10,007로 나눈 나머지를 출력한다.

www.acmicpc.net

규칙성을 찾으면 되는 문제이다.

#include <iostream>
using namespace std;

int main() {
	int n;
	cin >> n;
	int dp[1001];
	dp[1] = 1;
	dp[2] = 3;
	for (int i = 3; i <= n; i++) {
		dp[i] = (dp[i - 1] + 2 * dp[i - 2]) % 10007;
	}
	cout << dp[n];
	return 0;
}
728x90

'알고리즘 문제' 카테고리의 다른 글

[백준] 9507번 Generations of Tribbles  (0) 2020.02.17
[백준] 11057번 오르막 수  (0) 2020.02.17
[백준] 11052번 카드 구매하기  (0) 2020.02.17
[백준] 9465번 스티커  (0) 2020.02.17
[백준] 1431번 시리얼 번호  (0) 2020.02.15
728x90

숫자처럼 생긴 문자열을 숫자로 바꿔야 할 때가 있다. 아니면 숫자를 문자열로 바꿔야 할 때도 있을 수 있다.

그럴 때 쓰는 것이 stoi, stof, stol, stod, stold, stoll 함수이다.

이 함수들은 string 클래스에 정의되어 있기 때문에 이 함수들을 사용하려면 <string> 헤더파일을 포함해야 한다.

+stoi(s: string): int 문자열을 int로 바꾼다
+stol(s: string): long 문자열을 long으로 바꾼다.
+stof(s: string): float 문자열을 float로 바꾼다.
+stod(s: string): double 문자열을 double로 바꾼다.
+stold(s: string): long double 문자열을 long double로 바꾼다.
+stoll(s: string): long long 문자열을 long long으로 바꾼다.
#include <iostream>
#include <string>
using namespace std;


int main() {
	string s = "100";
	int a = stoi(s); //문자열 s를 정수형으로 바꿈
	cout << a + 45 << endl;
	return 0;
}

문자열일 때에는 정수와 더하기가 불가능했는데 int형으로 바꾸니 더하기가 멀쩡하게 되는 것을 볼 수 있다.

 

그렇다면 char형을 정수로 바꾸고 싶다면?

char형은 정수이기 때문에 0의 아스키코드인 48을 빼주면 된다. 0의 아스키코드를 기억하기 힘들다면 '0'을 빼주면 된다.

#include <iostream>
using namespace std;


int main() {
	char c = '9';
	int a = c - '0';
	cout << a + 10 << endl;
	return 0;
}

728x90
728x90

https://www.acmicpc.net/problem/11052

 

11052번: 카드 구매하기

첫째 줄에 민규가 구매하려고 하는 카드의 개수 N이 주어진다. (1 ≤ N ≤ 1,000) 둘째 줄에는 Pi가 P1부터 PN까지 순서대로 주어진다. (1 ≤ Pi ≤ 10,000)

www.acmicpc.net

#include <iostream>
#include <vector>
using namespace std;


int main() {
	int n;
	cin >> n;
	vector<int> v(n + 1);
	v[0] = 0;
	for (int i = 1; i <= n; i++) {
		cin >> v[i];
	}
	vector<int> dp(n + 1);
	dp[1] = v[1];
	dp[0] = v[0];
	for (int i = 1; i <= n; i++) {
		int maxDp = v[i];
		for (int j = 1; j < i; j++) {
			if (dp[j] + dp[i - j] > maxDp) maxDp = dp[j] + dp[i - j];
		}
		dp[i] = maxDp;
	}
	cout << dp[n];
	return 0;
}
728x90

'알고리즘 문제' 카테고리의 다른 글

[백준] 11057번 오르막 수  (0) 2020.02.17
[백준] 11727번 2×n 타일링 2  (0) 2020.02.17
[백준] 9465번 스티커  (0) 2020.02.17
[백준] 1431번 시리얼 번호  (0) 2020.02.15
[백준] 1100번 하얀 칸  (0) 2020.02.15
728x90

https://www.acmicpc.net/problem/9465

 

9465번: 스티커

문제 상근이의 여동생 상냥이는 문방구에서 스티커 2n개를 구매했다. 스티커는 그림 (a)와 같이 2행 n열로 배치되어 있다. 상냥이는 스티커를 이용해 책상을 꾸미려고 한다. 상냥이가 구매한 스티커의 품질은 매우 좋지 않다. 스티커 한 장을 떼면, 그 스티커와 변을 공유하는 스티커는 모두 찢어져서 사용할 수 없게 된다. 즉, 뗀 스티커의 왼쪽, 오른쪽, 위, 아래에 있는 스티커는 사용할 수 없게 된다. 모든 스티커를 붙일 수 없게된 상냥이는 각 스티커에 점

www.acmicpc.net

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;


int main() {
	int testCase;
	cin >> testCase;
	for (int t = 0; t < testCase; t++) {
		int n;
		cin >> n;
		vector<vector<int>> score(2, vector<int>(n));
		for (int i = 0; i < 2; i++) {
			for (int j = 0; j < n; j++) {
				cin >> score[i][j];
			}
		}
		vector<pair<int, int>> dp(n);
		dp[0] = make_pair(score[0][0], score[1][0]);
		if (n > 1) dp[1] = make_pair(dp[0].second + score[0][1], dp[0].first + score[1][1]);
		for (int i = 2; i < n; i++) {
			dp[i].first = max(dp[i - 1].second + score[0][i], dp[i - 2].second + score[0][i]);
			dp[i].second = max(dp[i - 1].first + score[1][i], dp[i - 2].first + score[1][i]);
		}
		cout << max(dp[n - 1].first, dp[n - 1].second) << '\n';
	}
	return 0;
}

벡터 dp의 first는 윗줄을 선택했을 때, dp의 second는 아랫줄을 선택했을 때의 최댓값을 저장하도록 했다.

728x90

'알고리즘 문제' 카테고리의 다른 글

[백준] 11727번 2×n 타일링 2  (0) 2020.02.17
[백준] 11052번 카드 구매하기  (0) 2020.02.17
[백준] 1431번 시리얼 번호  (0) 2020.02.15
[백준] 1100번 하얀 칸  (0) 2020.02.15
[백준] 3474번 교수가 된 현우  (0) 2020.02.15
728x90

람다식 안 됨

안드로이드 스튜디오에서 앱을 만드는 도중 이렇게 람다식이 지원이 안 된다고 뜬다면

 

다음과 같이 모듈 수준 gradle 파일에 들어가서

모듈 수준 gradle 파일

다음 코드를 추가한다.

compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}

그러면 이제 오류 없이 람다식을 사용할 수 있다.

728x90
728x90

StringTokenizer는 구분자를 기준으로 문자열을 분리하고 싶을 때 클래스이다.

구분자란 무엇일까?

"양파,당근,마늘,오이,상추"라는 문자열이 있을 때 콤마(,)를 기준으로 나누고 싶다면 콤마(,)가 구분자이다.

"2020년 02월 15일"이라는 문자열이 있을 때 공백을 기준으로 나누고 싶다면 공백이 구분자이다.

"2020-02-15"라는 문자열이 있을 때 '-' 문자를 기준으로 나누고 싶다면 구분자는 '-'이다.

 

일단 문자열을 쪼개고 싶다면 StringTokenizer 객체를 생성해야 한다. StringTokenizer 객체를 생성할 때에는 나누고 싶은 문자열과 구분자를 적어야 한다. 구분자를 적어주지 않으면 공백을 기준으로 나눈다.

String s = "2020년 2월 15일";
StringTokenizer tokenizer = new StringTokenizer(s); //공백을 기준으로 문자열 s를 나누기 위한 객체

공백을 기준으로 문자열을 나누고 싶다면 이렇게 생성자의 인자로 문자열만 넣어주면 된다.

 

String s = "2020-02-15";
StringTokenizer tokenizer = new StringTokenizer(s, "-");

특정 문자열을 기준으로 나누고 싶다면 문자열과 구분자를 생성자의 인자로 넣어주면 된다.

 

이렇게 StringTokenizer 객체를 생성하고 나면 문자열을 쪼개기 위한 준비작업은 다 끝났다.

이제 문자열을 나누면 된다.

 

일단 StringTokenizer 클래스의 메소드부터 살펴보자.

int countTokens() 남아있는 토큰의 개수를 반환한다.
boolean hasMoreTokens() 남아있는 토큰이 있으면 true, 더 이상 토큰이 없으면 false
String nextToken() 토큰을 꺼내온다.

https://docs.oracle.com/javase/10/docs/api/java/util/StringTokenizer.html

 

StringTokenizer (Java SE 10 & JDK 10 )

Constructs a string tokenizer for the specified string. All characters in the delim argument are the delimiters for separating tokens. If the returnDelims flag is true, then the delimiter characters are also returned as tokens. Each delimiter is returned a

docs.oracle.com

이 메소드들의 사용법은 직접 코드를 보면서 배우는 것이 가장 빠를 거라고 생각한다.

import java.util.StringTokenizer;

public class StringTokenizerTest {
    public static void main(String[] args) {
        String s = "2020-02-15";
        StringTokenizer tokenizer = new StringTokenizer(s, "-");
        System.out.println("토큰의 개수: " + tokenizer.countTokens());
        while(tokenizer.hasMoreTokens()) {
            System.out.println(tokenizer.nextToken());
            System.out.println("토큰의 개수: " + tokenizer.countTokens());
        }
    }
}

 

 

 

 

import java.util.StringTokenizer;

public class StringTokenizerTest {
    public static void main(String[] args) {
        String s = "안녕? 여기는 feelcoding 블로그야";
        StringTokenizer tokenizer = new StringTokenizer(s);
        System.out.println("토큰의 개수: " + tokenizer.countTokens());
        while(tokenizer.hasMoreTokens()) {
            System.out.println(tokenizer.nextToken());
        }
    }
}

 

위 코드의 실행 결과

 

 

 

import java.util.StringTokenizer;

public class StringTokenizerTest {
    public static void main(String[] args) {
        String s = "2020-02-15";
        StringTokenizer tokenizer = new StringTokenizer(s, "-");
        System.out.println("토큰의 개수: " + tokenizer.countTokens());
        while(tokenizer.hasMoreTokens()) {
            System.out.println(tokenizer.nextToken());
        }
    }
}

 

위 코드의 실행 결과

 

 

 

import java.util.StringTokenizer;

public class StringTokenizerTest {
    public static void main(String[] args) {
        String s = "2020-02-15";
        StringTokenizer tokenizer = new StringTokenizer(s, "-");
        System.out.println("토큰의 개수: " + tokenizer.countTokens());
        while(tokenizer.hasMoreTokens()) {
            System.out.println(tokenizer.nextToken());
            System.out.println("토큰의 개수: " + tokenizer.countTokens());
        }
        tokenizer.nextToken(); //다 소모한 객체에서 토큰을 뽑아내려고 시도. 예외 발생
    }
}

StringTokenizer 객체는 소모적이라서 한 번 사용하면 다시 사용할 수 없다.

다시 사용하고 싶다면 객체를 새로 만들어야 한다. 이미 소모한 객체에서 토큰을 꺼내려고 하면 다음과 같이 NoSuchElementException 예외가 발생한다.

 

위 코드의 실행결과. 예외가 발생한다.

 

13번째 줄에서 예외가 발생했다고 하는데 그것은 마지막 코드인 tokenizer.nextToken();이다.

따라서 nextToken() 메소드를 사용하기 전에는 반드시 if(객체.hasMoreTokens()) 또는 if(객체.countTokens() > 0)으로 체크해보고 토큰을 뽑아야 한다.

728x90
728x90

https://www.acmicpc.net/problem/1431

 

1431번: 시리얼 번호

첫째 줄에 기타의 개수 N이 주어진다. N은 1,000보다 작거나 같다. 둘째 줄부터 N개의 줄에 시리얼 번호가 하나씩 주어진다. 시리얼 번호의 길이는 최대 50이고, 알파벳 대문자 또는 숫자로만 이루어져 있다. 시리얼 번호는 중복되지 않는다.

www.acmicpc.net

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;

bool cmp(string a, string b) {
	if (a.size() != b.size()) {
		return a.size() < b.size();
	}
	else {
		int sumOfA = 0;
		int sumOfB = 0;
		for (int i = 0; i < a.size(); i++) {
			if (a[i] < 58) {
				sumOfA += (a[i] - '0');
			}
		}
		for (int i = 0; i < b.size(); i++) {
			if (b[i] < 58) {
				sumOfB += (b[i] - '0');
			}
		}
		if (sumOfA != sumOfB) {
			return sumOfA < sumOfB; //sumOfB
		}
		else {
			return a < b;
		}
	}
}

int main() {
	cin.tie(NULL);
	ios_base::sync_with_stdio(false);
	int n;
	cin >> n;
	vector<string> v(n);
	for (int i = 0; i < n; i++) {
		cin >> v[i];
	}
	sort(v.begin(), v.end(), cmp);
	for (int i = 0; i < n; i++)
		cout << v[i] << '\n';
	return 0;
}
728x90

'알고리즘 문제' 카테고리의 다른 글

[백준] 11052번 카드 구매하기  (0) 2020.02.17
[백준] 9465번 스티커  (0) 2020.02.17
[백준] 1100번 하얀 칸  (0) 2020.02.15
[백준] 3474번 교수가 된 현우  (0) 2020.02.15
[백준] 10816번 숫자 카드 2  (0) 2020.02.13

+ Recent posts