개발자로서 현장에서 일하면서 새로 접하는 기술들이나 알게된 정보 등을 정리하기 위한 블로그입니다. 운 좋게 미국에서 큰 회사들의 프로젝트에서 컬설턴트로 일하고 있어서 새로운 기술들을 접할 기회가 많이 있습니다. 미국의 IT 프로젝트에서 사용되는 툴들에 대해 많은 분들과 정보를 공유하고 싶습니다.
public class DuplicateCharacters { public static void main(String[] args) { String string1 = "Find duplicated letters in a String."; int count;
//Converts given string into character array char string[] = string1.toCharArray();
System.out.println("Duplicate characters in a given string: "); //Counts each character present in the string for(int i = 0; i <string.length; i++) { count = 1; for(int j = i+1; j <string.length; j++) { if(string[i] == string[j] && string[i] != ' ') { count++; //Set string[j] to 0 to avoid printing visited character string[j] = '0'; } } //A character is considered as duplicate if count is greater than 1 if(count > 1 && string[i] != '0') System.out.println(string[i]); } } }
하나 하나 분석해 보겠다.
public class DuplicateCharacters { // 클래스 이름은 DuplicateCharacters 이다. public static void main(String[] args) { // 실행 하기 위해 main 메소드를 사용한다. String string1 = "Find duplicated letters in a String."; // 이게 입력 되는 스트링이다. int count; // 중복 되는 글자가 몇개나 있는지 담을 인티저 변수이다.
//Converts given string into character array char string[] = string1.toCharArray(); // 스트링을 어레이로 변환한다.
System.out.println("Duplicate characters in a given string: "); // 입력 값을 프린트 한다. //Counts each character present in the string for(int i = 0; i <string.length; i++) { // 배열내 값의 갯수 즉 스페이스를 포함한 글자의 갯수 만큼 for 문을 돌린다. count = 1; // count를 1로 선언한다. for(int j = i+1; j <string.length; j++) { // 배열 내 값의 갯수 보다 한개 적은 숫자 만큼 for 문을 돌린다. 이중 루프이다. if(string[i] == string[j] && string[i] != ' ') { // 만약에 스페이스가 아닌데 같은 글자가 있으면 이 if 문을 실행한다. count++; // 같은 글자가 있다면 count를 하나 증가 시킨다. //Set string[j] to 0 to avoid printing visited character string[j] = '0'; // 해당 되는 같은 글자는 0으로 바꾼다. } } //A character is considered as duplicate if count is greater than 1 if(count > 1 && string[i] != '0') // count가 1보다 크고 값이 0이 아니면 if문을 출력한다. System.out.println(string[i]); // 해당 값을 출력한다. } } }
코드를 조금 바꿔 봤다.
출력 값을 리스트에 담아서 이 리스트를 출력 하도록 수정했다.
소스 코드는 아래와 같다.
수정한 부분만 주석을 달았다.
import java.util.List; // List 를 사용하기 위해 import 함 import java.util.ArrayList; // ArrayList를 사용하기 위해 import 함
public class DuplicateCharacters { public static void main(String[] args) { String string1 = "Find duplicated letters in a String."; int count;
//Converts given string into character array char string[] = string1.toCharArray(); List<Character> answer = new ArrayList(); // 출력 될 값을 담을 ArrayList를 선언 함. 이름은 answer
System.out.println("Duplicate characters in a given string: "); System.out.println(); //Counts each character present in the string for(int i = 0; i <string.length; i++) { count = 1; for(int j = i+1; j <string.length; j++) { if(string[i] == string[j] && string[i] != ' ') { count++; //Set string[j] to 0 to avoid printing visited character string[j] = '0'; } } //A character is considered as duplicate if count is greater than 1 if(count > 1 && string[i] != '0') { answer.add(string[i]); // 출력 값을 answer 에 담는다. } }
answer.forEach(System.out::println); // forEach를 사용해 ArrayList answer의 값을 출력한다.
요즘 Leetcode에서 문자열 reverse 하는 문제를 풀어 봤는데 주말에 한번 더 연습을 해 보았다. 내가 만든 코드와 결과는 아래와 같다.
코드를 잘 살펴 보자.
class reverse { // 클래스 이름은 reverse이다. public static void main(String[] args) { // 실행을 하기 위해 main() 메소드를 사용했다. String a = "Hello World"; // 테스트 할 스트링이다. char[] b = a.toCharArray(); // 리버스 하기 위해 스트링을 char[] 데이터 타입으로 변환했다.
int left = 0; // left는 0으로 선언 int right = b.length-1; // right 는 b[] 의 맨 마지막 자리로 선언
while(left < right) { // left가 right보다 작을 때 까지만 while문을 실행한다. char temp = b[left]; // 임시로 입력값의 맨 앞자리 값을 temp에 담는다. b[left++] = b[right]; // b의 left 자리에 right 자리값을 넣는다. b[right--] = temp; // 임시로 저장했던 left 자리의 값을 right에 넣는다. // left++ 와 right—의 의미는 이전 글 참조 }
for(int i = 0; i < b.length; i++){ // for문을 돌려 b[]의 값들을 출력한다. System.out.println(b[i]); }
이번 문제는 문제 자체를 이해하기가 좀 어렵다. 문제를 잘 해석해 보면… 우선 Example 1 의 인풋과 아웃풋을 보자
abcdefg 와 2가 인풋이다. 여기서 요구하는 사항은 두번째 글자를 첫번째 글짜와 Reverse 하라는 거다. 그래서 아웃풋을 보면 abcd가 bacd 가 되었다. 그 다음 요구는 각각의 2k 마다 이 일을 하라는 거다. 여기서 인풋 k = 2이기 때문에 2 * 2 = 4 마다 이 일을 하라는 거다.
즉 abcd는 일을 다 끝냈고 그 다음에 남은건 efg 이니까 여기서 두번째 글자와 첫번째 글자의 위치를 바꾸라는 거다.
아래 스크립트로 원하는 답을 얻을 수 있다.
class Solution { public String reverseStr(String s, int k) { char[] a = s.toCharArray(); for (int start = 0; start < a.length; start += 2 * k) { int i = start, j = Math.min(start + k - 1, a.length - 1); while (i < j) { char tmp = a[i]; a[i++] = a[j]; a[j--] = tmp; } } return new String(a); } }
한줄 한줄 분석해 보자.
class Solution { ==> 클래스 이름은 Solution이다. public String reverseStr(String s, int k) { ==> 메소드 이름은 reverseStr이고 입력값으로 스트링 s , int k 를 받는다. return 값은 String이다. char[] a = s.toCharArray(); ==> 입력받은 스트링을 toCharArray()를 이용해서 배열로 만들고 변수 a 에 할당한다. for (int start = 0; start < a.length; start += 2 * k) { ==> 루프를 만들어 start가 배열 아이템 갯수 보다 작을 때 까지 돌린다. start는 초기 값이 0이고 루프 한번 돌면 start 값은 start + 2 * k 만큼 증가한다. int i = start, j = Math.min(start + k - 1, a.length - 1); ==> inti 에는 start 값이 대입된다. Int j 에는 start + k 값과 a.length - 1 값 중 작은 값이 대입 된다. ==> while (i < j) { ==> i 가 j 보다 작을 때 까지만 while문을 돌린다. 즉 2*k 블럭이 더이상 없으면 while문은 종료한다. char tmp = a[i]; ==> tmp에 배열의 첫번째 값을 대입한다. a[i++] = a[j]; ==> a 의 첫번째 값에 j의 값을 대입한다. 그리고 i 에 1을 더해 준다. a[j--] = tmp; ==> 배열 맨 마지막에 첫번째 값을 대입한다. } } ==> 이건 for 와 while이 있는 이중 루프이다. ==> start와 i와 j가 어떻게 증가하는지 이해 해야 한다. ==> start는 k 가 2인경우 0, 4, 8, 12 이렇게 증가한다. k가 3인 경우는 0, 6, 12, 18 이렇게 증가한다. ==> i 는 매번 루프를 돌 때무다 start가 대입 되고 while을 돌 때는 1씩 증가한다. ==> j 는 start + k 와 a.length -1 중 작은 값이 대입 되고 while을 돌 때 마다 1씩 증가한다. return new String(a); ==> 배열 a를 스트링 형으로 바꿔서 return 한다. } }
입력값을 위와 같이 바꿔 봤다.
결과 값을 보면 세번째 값인 c가 맨 앞에 왔다. 그 다음 블럭은 ghijkl 이다. 두번째 블럭 세번째 값인 i 가 맨 앞으로 왔다. 세번째 블럭은 mn 이다. 여기서 두번째 값인 n이 앞으로 왔다. 이건 요구 조건 충족이 되는건가? 하여간 Expected 와 결과값이 같으니까 통과 하긴 했는데… 왜 두번째 값인 n이 앞으로 왔는지 모르겠다. 입력값은 3이니까 세번째 값이 와야 되는데… 그런데 세번째 값이 없으니…. 맨 마지막 값이 앞으로 와 버렸다. 로직상 이렇게 작동하는건 맞긴 한데… 이게 요구 조건에 충족되는 건지는 모르겠다.
파이썬 코드는 아래와 같다.
class Solution(object): def reverseStr(self, s, k): a = list(s) for i in xrange(0, len(a), 2*k): a[i:i+k] = reversed(a[i:i+k]) return "".join(a)
오늘의 Leetcode 문제는 118번 Pascal’s Triangle 이다. 난이도는 Easy. 전혀 Easy 하지 않은 것 같은데. 아래가 풀어야 할 문제이다.
우선 패턴을 분석하면 첫줄은 숫자가 한개이고 그 값은 1이다. 두번째 줄은 숫자가 두개이고 그 값은 1과 1이다. 세번째 줄은 숫자가 세개이고 그 값은 1, 2,1 이다. 여기서 2는 윗줄의 1과 1을 더한 것이다. 네번째 줄은 숫자가 네개이고 그 값은 1, 3, 3, 1 이다. 첫번째 3은 윗줄의 첫번째 값 1과 두번째 값 2을 더한 값이다. 두번째 3은 윗줄의 두번째 값 2와 세번째 값 1을 더한 값이다. 다섯번째 줄은 숫자가 다섯개이고 그 값은 1, 4, 6, 4, 1 이다. 첫번째 4는 윗줄의 첫번쨰 숫자 1과 두번째 숫자 3을 더한 값이다. 그 다음 6은 윗줄의 두번째 숫자 3과 세번째 숫자 3을 더한 값이다. 그 다음 4는 윗줄의 세번째 숫자 3과 네번째 숫자 1을 더한 값이다.
이제 패턴을 알 것 같다. 1. 줄이 늘어날 수록 숫자의 갯수는 1씩 증가한다. 첫번째 줄은 한개의 숫자가 있고 그 값은 1이다. 2. 맨 처음 값과 맨 마지막 값은 각각 1이다. 3. 나머지 값들은 윗줄의 (col -1) + 윗줄의 col 값이다. 여기서 col은 해당 숫자가 그 줄의 몇번째에 있는지를 나타내는 것이다.
class Solution { public List<List<Integer>> generate(int numRows) { List<List<Integer>> triangle = new ArrayList<>(); triangle.add(new ArrayList<>()); triangle.get(0).add(1); for(int rowNum =1; rowNum < numRows; rowNum++) { List<Integer> currRow = new ArrayList<>(); List<Integer> prevRow = triangle.get(rowNum -1); currRow.add(1); for (int col = 1; col < prevRow.size(); col++) { currRow.add(prevRow.get(col-1) + prevRow.get(col)); } currRow.add(1); triangle.add(currRow); } return triangle; } }
한줄 한줄 분석해 보자.
class Solution { ==> 클래스 이름은 Solution이다. public List<List<Integer>> generate(int numRows) { ==> 메소드 이름은 generate이다. ==> input 값으로 numRows를 받는다. ==> return 값은 List<List<Integer>>이다. 배열 갯수를 마음대로 변경할 수 있도록 하기 위해서 List를 사용한다. ==> Expected Output 값이 이중 배열이기 때문에 List 안에 또 List를 넣었다. 배열내 값의 데이터 타입은 Integer이다.
List<List<Integer>> triangle = new ArrayList<>(); ==> 결과 값을 저장할 변수를 만든다. 이 변수 이름은 triangle이다. ==> List를 사용하기 위해서는 ArrayList, LinkedList, Vector, Stack 클래스들로 인스턴스 화 할 수 있다. ==> 이 중에서 ArrayList를 생성해서 인스턴스 화 시켰다.
triangle.add(new ArrayList<>()); ==> 이중 배열이기 때문에 triangle안에 ArrayList를 add 했다.
triangle.get(0).add(1); ==> triangle의 첫 줄에는 값 1을 hardcoding으로 집어 넣었다.
for(int rowNum =1; rowNum < numRows; rowNum++) { ==> 결과 값을 만들기 위해 for문을 만들었다. 이 메소드의 input 값인 numRows가 결과 값의 줄의 갯수를 의미하는 것이니, 그 갯수만큼 줄을 만들기 위해서 numRows만큼만 for 문을 돌린다.
List<Integer> currRow = new ArrayList<>(); ==> 현재의 줄을 ArrayList로 인스턴스 화 한다. 이름은 currRow
List<Integer> prevRow = triangle.get(rowNum -1); ==> 계산을 해서 현재 Row의 각 컬럼의 값을 얻기 위해서 이전 줄의 값을 가져와야 한다. prevRow에 이전 줄의 값들을 저장한다.
currRow.add(1); ==> 현재 줄의 첫번째 값은 항상 1이다. 이 값을 하드코딩으로 집어 넣었다.
for (int col = 1; col < prevRow.size(); col++) { ==> 현재 줄의 첫번째 값과 마지막 값을 제외한 중간에 위치한 컬럼의 값을 구하기 위해 for문을 만들었다. ==> 이전 줄의 size보다 한개 작은 숫자 만큼 돌린다. currRow.add(prevRow.get(col-1) + prevRow.get(col)); ==> 이 코드가 핵심이다. ==> 현재 줄의 현재 컬럼에 이전줄의 현재 컬럼 -1 의 값 + 이전줄의 현재 컬럼 값을 넣어 준다. } currRow.add(1); ==> 현재 줄의 맨 마지막에는 값 1을 하드코딩으로 넣어 준다.
triangle.add(currRow); ==> triangle에 현재 줄의 값을 add 한다. }
return triangle; ==> 이중 for 문에서 구한 결과 값을 리턴한다. } }
이렇게 하면 원하는 결과를 얻을 수 있다. 참고로 첫번째 for문은 결과 값이 몇줄이 될 것인지 정하는 것이고, 두번째 for 문은 각 줄에 값들을 구하기 위해 만들어진 것이다.
약간 다른 방법으로는 아래와 같이 할 수도 있다.
class Solution { public List<List<Integer>> generate(int numRows) { List<List<Integer>> triangle = new ArrayList<List<Integer>>();
// Base case; first row is always [1]. triangle.add(new ArrayList<>()); triangle.get(0).add(1);
for (int rowNum = 1; rowNum < numRows; rowNum++) { List<Integer> row = new ArrayList<>(); List<Integer> prevRow = triangle.get(rowNum-1);
// The first row element is always 1. row.add(1); ==> 여기 까지는 위와 같다.
// Each triangle element (other than the first and last of each row) // is equal to the sum of the elements above-and-to-the-left and // above-and-to-the-right. for (int j = 1; j < rowNum; j++) { ==> 위 코드와 다른 것인 이 한 줄이다. ==> 두번째 for문을 rowNum보다 작을 때까지 돌린다. ==> rowNum은 두번째 줄이 1이다. (위 에서 첫번쨰 줄에는 1을 하드 코딩했고 첫번째 for문에서 그 다음 줄을 rowNum = 1 을 했기 때문이다.) ==> 그리고 두번째 for 문에서는 이 rowNum보다 작을 때까지 for문을 돌린다. ==> 그러니까 실질 적으로는 두번째 에서는 줄 -2 만큼 돌리게 된다. ==> 즉 4번째 줄은 4-2 이니까 두개의 값만 구하게 된다. ==> 요구 조건을 충족 시키는 방법이다. 위 코드와는 조금 다르지만 결과 값은 똑 같다. row.add(prevRow.get(j-1) + prevRow.get(j)); }
// The last row element is always 1. row.add(1); triangle.add(row); } return triangle; } }
for row_num in range(num_rows): # The first and last row elements are always 1. row = [None for _ in range(row_num + 1)] row[0], row[-1] = 1, 1
# Each triangle element is equal to the sum of the elements # above-and-to-the-left and above-and-to-the-right. for j in range(1, len(row) - 1): row[j] = triangle[row_num - 1][j - 1] + triangle[row_num - 1][j]
서브 프라임 모기지 시기에 미국에 왔다. 당시 수퍼에서는 신용카드를 사용하는 사람을 하나도 볼 수가 없었다. 그만큼 어려웠었나보다. 그런 시기에 시작한 나의 이민 생활도 쉬울리가 없었다. 나의 이민 생활이 잘 진행 되도록 도와준 고마운 회사 Capgemini. 지난 5월이 이 회사 10주년이 되는 기념일 이었다. 그리고 7월 1일 해고 통지를 받았다. 헤어짐에 마음은 아프지만… 고마운 회사다.
Getting old is cumulating sadnesses. 나이가 든다는 것은 슬픔이 쌓인다는 거야.
A while ago, another great sadness piled up. 얼마전 또 하나의 큰 슬픔을 쌓았어.
I broke up with a company I worked for for over 10 years. 10년 넘게 같이 했던 회사와 이별을 했지.
Basically, parting is all sad. 기본적으로 이별은 모두 슬퍼.
I've been saying goodbye to every moment for over 50 years. 나는 매 순간들과의 이별을 50년 넘게 하고 있어.
It is said that the years pass when separation accumulates. 그 이별 들이 쌓이는 것을 세월이 흐른다고 하지.
The accumulation of that sorrow is called age. 그 슬픔들이 쌓인 것을 나이라고 하지.
The breakup we broke up not long ago made 10 years of memories. 얼마전에 한 이별은 10년의 추억을 만들었어.
I am happy to have all of you who shine beautifully in the midst of sad memories. 슬픈 기억들 속에서 아름답게 반짝 이는 당신들이 있어서 행복해.
Thanks all 모두 고마워.
Thank you for making the 10 years of my life beautiful. 내 인생의 10년을 아름답게 만들어 줘서……