본문 바로가기
두들낙서/프로그래밍

[C/C++] 정수를 뒤집는 5가지 방법

by 두들낙서 2019. 6. 2.

목표는 간단하다. 입력 받은 정수를 거꾸로 뒤집은 정수를 출력하면 된다. 물론 뒤집었을 때 0으로 시작하는 경우는 0을 출력하지 않는다.

 

입력 예

1024

출력 예

4201

 

입력 예

12300

출력 예

321

 

1. 문자열 변환 후 뒤집고 정수 변환 (C++ 스타일)

가장 직관적이고, 단순한 방법이다. string 타입을 사용한다. 문자열로 변환하기 위해서는 to_string 함수를, 정수로 변환하기 위해서는 atoi 함수를 쓴다. atoi 함수는 원래 <cstdlib>에 들어 있으나 웬만한 라이브러리에 이미 include 되어 있다.

#include <iostream>
#include <string>

using namespace std;

int rev(int n) {
	string s = to_string(n);
	reverse(s.begin(), s.end());
	return atoi(s.c_str());
}

int main() {
	int n;
	cin >> n;
	cout << rev(n) << endl;
}

 

2. 문자열 변환 후 뒤집고 정수 변환 (C 스타일)

C에서는 문자열 처리가 조금 더 번거롭다. 물론 비주얼 스튜디오 컴파일러에서는 strrev라는 함수로 손쉽게 문자열을 뒤집을 수 있지만, C 표준에는 들어있지 않다. 또 atoi 함수는 표준이지만 itoa 함수는 비표준이다. 다행히 itoa는 표준인 sprintf 함수로 대체할 수 있다. 하지만 strrev 함수는 직접 구현해야 할 듯하다.

뒤집을 때는 뒤집힌 문자열을 담을 새로운 문자열 버퍼를 만들어서 원본 문자열로부터 하나씩 역순으로 대입해도 되지만, 원본 문자열이 나중에 다시 사용될 일이 없으므로 그냥 원본 문자열 자체에서 무지개식으로 골라서 각각 뒤바꾸는 방법을 썼다. (좀 TMI 같지만, 홀수인 경우도 l/2까지 하면 가운데 칸은 건드려지지 않으므로 정상 작동한다.)

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void swap(char *a, char *b) {
	char tmp = *a;
	*a = *b;
	*b = tmp;
}

int rev(int n) {
	char s[100];
	int l; // 문자열 길이

	sprintf(s, "%d", n); // 정수 n을 문자열 s로 변환 (itoa는 비표준 함수이다.)

	l = strlen(s);
	for (int i = 0; i < l / 2; i++) {
		// 무지개식으로 s[i]와 s[l-i-1]을 뒤바꾼다.
		swap(&s[i], &s[l-i-1]);
	}
	
	return atoi(s);
}

int main() {
	int n;
	scanf("%d", &n);
	printf("%d", rev(n));
}

 

 

3. 문자열 변환 후 바로 뒤집은 정수로 변환

문자열을 정수로 변환하는 코드를 작성할 줄 안다면, 문자열을 뒤집는 번거로운 작업이 굳이 필요가 없어진다. 인덱싱만 반대 방향으로 해주면 된다.

#include <stdio.h>
#include <string.h>

int rev(int n) {
	char s[100];
	int l; // 문자열 길이
	int r = 0; // 뒤집은 결과 정수

	sprintf(s, "%d", n);

	// 문자열을 정수로 변환 (인덱싱만 역순)
	l = strlen(s);
	for (int i = l - 1; i >= 0; i--) {
		r *= 10;
		r += s[i] - '0'; // s[i]를 한 자리 숫자로 변환
	}
	
	return r;
}

int main() {
	int n;
	scanf("%d", &n);
	printf("%d", rev(n));
}

 

4. 문자열 없이 수학적으로 접근

좀더 수학적으로 접근해보면, 위 예제의 s[l-1]은 n의 1의 자리, s[l-2]는 n의 10의 자리, ... 등등과 같다. 즉 s[l-i]는 n을 10으로 i-1번 나눴을 때의 1의 자리 수와 같다.

위 사실을 이용해 3번 방법을 고치면 다음과 같은 방법을 생각해낼 수 있다.

  1. n=123, r=0
  2. r에 n%10을 더한다. -> r=3
  3. n을 10으로 나누고, r에 10을 곱한다. -> n=12, r=30
  4. r에 n%10을 더한다. -> r=32
  5. n을 10으로 나누고, r에 10을 곱한다. -> n=1, r=320
  6. r에 n%10을 더한다. -> r=321
  7. n을 10으로 나눈다. -> n=0
  8. n이 0이 되면 r을 return한다.
#include <stdio.h>

int rev(int n) {
	int r = 0; // 뒤집은 결과 정수
	
	while (n != 0) {
		r *= 10;
		r += n % 10;
		n /= 10;
	}
	
	return r;
}

int main() {
	int n;
	scanf("%d", &n);
	printf("%d", rev(n));
}

 

5. 재귀로 접근

위의 수학적인 방법과 비슷하다. 그런데 다음과 같이 재귀적으로도 생각해볼 수 있다.

123을 뒤집은 수는 321인데 이 수는 12를 뒤집은 수인 21에다가 3에 100을 곱한 수인 300을 더한 것과 같다. 즉 rev(123)은 rev(123/10) + (123%10) * 100이다.

저 3이라는 숫자에다가 100을 곱할지 1000을 곱할지는 3의 자릿수에 따라 달라진다. 그럼 그걸 재귀로 어떻게 구현할까? 좀 고민을 해봤는데 특별히 좋은 답은 찾지 못했고, rad라는 전역 변수를 하나 만들어 주어 rev의 호출이 끝날 때마다 1, 10, 100, ... 이렇게 10배씩 늘어나도록 했다. 이렇게 하면 가장 나중에 재귀호출된 rev함수에서는 1을 곱하고, 그 다음에는 10을 곱하고 할 수 있다.

항상 드는 생각이지만, 함수의 호출 횟수를 세는 용도가 아니라면 재귀 함수에서 전역 변수를 수정하는 건 머리가 많이 아프다. 아무튼 코드는 다음과 같이 짰더니 결과는 잘 나온다.

#include <stdio.h>

int rad = 1;
int rev(int n) {
	if (n == 0) return 0;
	int result = rev(n / 10) + (n % 10) * rad;
	rad *= 10; // 재귀 호출이 종료될 때마다 10배씩 증가
	return result;
}

int main() {
	int n;
	scanf("%d", &n);
	printf("%d", rev(n));
}

 

댓글