2019년 5월 28일 화요일

Java capitalize first letter / compareTo() - 첫글자만 대문자로 String 문자열 변환, 문자열 비교




* Capitalize first letter - 첫글자만 대문자로 String 문자열 변환 / compareTo() - 문자열 사전순으로 비교



String API: https://docs.oracle.com/javase/7/docs/api/java/lang/String.html

String API 문서에서 내용 발췌
 * compareTo()
public int compareTo(String anotherString)

Compares two strings lexicographically. The comparison is based on the Unicode value of each character in the strings. The character sequence represented by this String object is compared lexicographically to the character sequence represented by the argument string. The result is a negative integer if this String object lexicographically precedes the argument string. The result is a positive integer if this String object lexicographically follows the argument string. The result is zero if the strings are equal; compareTo returns 0 exactly when the equals(Object) method would return true.


밑줄 친 부분을 요약하면, String 비교는 유니코드 값을 기반으로 하게 되는데,
A.compareTo(B) = 0 이라면, A와 B가 동일함
A.compareTo(B) < 0 이라면, A가 B보다 앞섬. 
A.compareTo(B) > 0 이라면, A가 B보다 뒤쳐짐 이다.


 * capitalize first letter
앞의 첫 문자 한개만 잘라내어 대문자로 변경해준다. 그리고 index가 1인 부분부터 마지막까지 String을 잘라내어 둘이 연결하면 된다.
A = A.substring(0, 1).toUpperCase() + A.substring(1);



* 예제코드
public class Solution {

    public static void main(String[] args) {
        
        Scanner sc=new Scanner(System.in);
        String A=sc.next();
        String B=sc.next();
        /* Enter your code here. Print output to STDOUT. */
        System.out.println(A.length() + B.length());
        String answer;
        if(A.compareTo(B) >0){ //B가 먼저이면
            answer = "Yes";
        }else{ //A가 먼저이면
            answer = "No";
        }
        System.out.println(answer);
        A = A.substring(0, 1).toUpperCase() + A.substring(1);
        B = B.substring(0, 1).toUpperCase() + B.substring(1);
        System.out.println(A + " " + B);
    }   
}

Java NumberFormat 이용하여 각 나라의 통화(currency) 출력하기




* NumberFormat 클래스를 이용하여 통화 형식 맞추기



NumberFormat API: https://docs.oracle.com/javase/8/docs/api/java/text/NumberFormat.html
Locale API: https://docs.oracle.com/javase/8/docs/api/java/util/Locale.html
IANA Language Subtag Registry: https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
 

 NumberFormat 클래스는 숫자의 parsing , 숫자의 포맷을 세팅하고  기능을 제공한다.
 각 나라의 통화들은 NumberFormat 클래스를 이용하여 표현할 수 있다.

 NumberFormat 클래스는 추상클래스이기 때문에 new로 생성할 수 없고, Calendar 클래스와 마찬가지로 getInstance() 와 같이 객체를 가져오는 메소드를 호출해야 한다.

 아래는 NumberFormat 객체를 가져오는데 Locale 국가코드를 인자로 넣어 해당 국가의 통화포멧을 설정할 수 있다. 


NumberFormat u = NumberFormat.getCurrencyInstance(Locale.US);


 Korea 통화는 Locale 클래스 안에 내장되어 있지만 인도는 Locale이 내장되어 있지 않다.
 India 같은 경우에는 Locale 클래스의 생성자를 이용하여 언어와 국가를 설정해주어야 하는데, 생성자에 들어가는 인자값에 대한 설명은 Locale class API 문서에 잘 나와있다.



country (region)
ISO 3166 alpha-2 country code or UN M.49 numeric-3 area code. You can find a full list of valid country and region codes in the IANA Language Subtag Registry (search for "Type: region"). The country (region) field is case insensitive, but Locale always canonicalizes to upper case.
Well-formed country/region values have the form [a-zA-Z]{2} | [0-9]{3}
Example: "US" (United States), "FR" (France), "029" (Caribbean)



IANA Language Subtag Registry 로 검색을 하여 인도의 subtag 값을 찾아서 넣으면 된다.

 NumberFormat i = NumberFormat.getCurrencyInstance(new Locale("en","IN"));



아래는 나라 별 통화의 기호를 출력해보는 예제이다.




public class Solution {
    
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        double payment = scanner.nextDouble();
        scanner.close();

        NumberFormat u = NumberFormat.getCurrencyInstance(Locale.US);
        NumberFormat i = NumberFormat.getCurrencyInstance(new Locale("en","IN"));
        NumberFormat c = NumberFormat.getCurrencyInstance(Locale.CHINA);
        NumberFormat f = NumberFormat.getCurrencyInstance(Locale.FRANCE);
        String us = u.format(payment);
        String india = i.format(payment);
        String china = c.format(payment);
        String france = f.format(payment);

        System.out.println("US: " + us);
        System.out.println("India: " + india);
        System.out.println("China: " + china);
        System.out.println("France: " + france);
    }
}

2019년 5월 26일 일요일

Java Calendar 이용하여 특정 일자 요일 구하기




* Calendar 특정일자 요일 구하기


Calendar API : https://docs.oracle.com/javase/7/docs/api/java/util/Calendar.html



Calendar 클래스를 이용하여 특정 일자를 세팅해보고 요일 문자열을 반환하는 메소드를 작성해보자.

캘린더 클래스는 추상클래스여서 new 키워드를 이용해 바로 객체를 생성할 수 없다. 아래와 같이 인스턴스를 가져오는 메소드를 통해 객체를 얻어올 수 있다.


Calendar cal = Calendar.getInstance();

cal 객체를 얻어온 후 원하는 일자를 set 메소드를 통해 값을 세팅한다.


cal.set(year, month-1, day);

Java 에서 월(month)의 시작은 $0$부터 시작하기 때문에 $6$월이라면 $-1$을 하여 $5$를 입력해야 한다.

값을 입력하고 나면 get 메소드를 이용하여 파라미터로 DAY_OF_WEEK를 넣어주면 캘린더가 해당 일자의 요일을 리턴하게 된다. 


cal.get(Calendar.DAY_OF_WEEK);

아래 코드는 특정일자를 받아서 그 일자의 요일을 찾는 메소드이다.


public static String findDay(int month, int day, int year) {
    int dayNum =0;
    String result="";
    Calendar cal = Calendar.getInstance();
    cal.set(year, month-1, day);
    
    dayNum = cal.get(Calendar.DAY_OF_WEEK);
    if(dayNum ==1) result = "SUNDAY";
    if(dayNum ==2) result = "MONDAY";
    if(dayNum ==3) result = "TUESDAY";
    if(dayNum ==4) result = "WEDNESDAY";
    if(dayNum ==5) result = "THURSDAY";
    if(dayNum ==6) result = "FRIDAY";
    if(dayNum ==7) result = "SATURDAY";
    
    return result;
}

2019년 5월 25일 토요일

Java Static Initializer Block







 Static 초기화 블럭이라고 부르는데, Static으로 선언된 변수는 메모리 공간에 한 번 올라가서 모든 객체들이 공유할 수 있는 변수이다. 
 그러한 static 변수를 초기화 할 때 단순히 값을 초기화를 할 수도 있지만 개발 과정 중에 복잡한 초기화 조건이 있을 수 있다. 
 그럴 때 Static Initializer Block을 사용한다.  
개발을 하다보면 static 변수가 초기화 될 때 어떤 조건일 경우 예외 처리를 해줘야 한다던지, 값이 상황에 따라 다르게 초기화를 해야 할 때가 있을 것이다.

문법은 단순하다.


static {
   //초기화 할 내용
}



 아래는 Static Initializer Block 사용 예제이다.
 B는 밑변, H는 높이이며 둘을 곱하여 평행사변형 면적이 나온다.
 Scanner로 B,H를 입력받는데 길이가 음수가 들어올 수 있는 상황은 막아야 한다.
고로, 길이가 음수일 때에는 flag를 false로 세팅하고 이 flag가 true 일때만 면적 계산이 되도록 한다.



public class Solution {
    static Scanner sc = new Scanner(System.in);
    static boolean flag = true;
    static int B = sc.nextInt();
    static int H = sc.nextInt();
    static {
        if(B <= 0 || H <=0){
            flag = false;
            System.out.println("java.lang.Exception: Breadth and height must be positive");
        }
    }
 public static void main(String[] args){
  if(flag){
   int area=B*H;
   System.out.print(area);
  } 
 }

}

2019년 5월 23일 목요일

Hackerrank Day 29 Bitwise AND







알고리즘 연습 사이트


Day 29:Bitwise AND



 Java D-Day 마지막 주제는 비트 연산에 대한 것이다.







* & Bitwise AND (∧)
둘 다 1이어야지 1, 둘 중 하나라도 0이라면 0이다.

* | Bitwise inclusive OR (∨)
둘 중 하나만 1이어도 1이다.

* ^ Bitwise Exclusive OR or XOR
두 개가 동일하다면 0, 두개가 다르다면 1이다.

* ~ NOT 
1이면 0으로, 0이면 1로 뒤집는 반대 연산자이다.

비트 연산자 마지막 문제는 두개의 숫자가 입력값으로 주어진다. $(N,K)$ 
집합 $S$ 의 요소들은 1~N까지이다. 
집합의 요소들끼리 & 연산을 했을 때 가장 큰 수가 나오는 것을 찾는 것이다. 다만 K보다는 작아야 한다.

K보다 작은 max 값을 찾는 부분만 주의하면 된다.


public class Solution {
    
    private static final Scanner scanner = new Scanner(System.in);

    public static void main(String[] args) {
        int t = scanner.nextInt();
        scanner.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");

        for (int tItr = 0; tItr < t; tItr++) {
            String[] nk = scanner.nextLine().split(" ");

            int n = Integer.parseInt(nk[0]);

            int k = Integer.parseInt(nk[1]);
            int max =0; 
            for(int i = 1 ; i <=n ; i++){
                for(int j=i+1 ; j <= n ; j++){
                    if((i&j)< k && (i&j)>max) {
                        max = i&j;
                    }
                }
            }
            System.out.println(max);
        }

        scanner.close();
    }
}


여기까지 D-Day 30 Java 튜토리얼을 완료하였다. :) 
다음은 Java 를 시작할 예정이다.





Hackerrank Day 28 RegEx, Patterns, and Intro to Databases







알고리즘 연습 사이트


Day 28:RegEx, Patterns, and Intro to Databases



 정규표현식(Regular expression)은 사용하려고 할 때마다 잊어버리고 다시 찾아보게 된다. 
 따라서 복습이 필요할 때마다 이 글을 참조하기 위해 기본 개념을 정리해 둔다.


* 참고 사이트

Pattern 클래스:  https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html
Matcher 클래스: https://docs.oracle.com/javase/8/docs/api/java/util/regex/Matcher.html
정규표현식 튜토리얼:  https://docs.oracle.com/javase/tutorial/essential/regex/
정규표현식 연습사이트: 여러가지 연습할 수 있는 사이트들이 많지만 regex101.com가 가장 편리한것 같다. 내가 만든 정규식에 대해 동작원리 설명이 나와있어 학습하기 편하다.
https://regex101.com/
https://rubular.com/r/UAgzl9NxQv


* Character Classes
 일반적으로 생각하는 자바의 클래스와 같은 의미가 아니라, 한개 혹은 한 개 이상의 문자들의 집합을 의미한다. 
 Character Class라는 것은 문자 그 자체이거나 문자의 범위를 표현하는 문법이 될 수 있다. 
 예를 들면 정규표현식 a는  a 문자 하나에 매칭될 것이다.
 \d라는 것은 정규표현식 문법인데 미리 정의된 캐릭터 클래스라는 것이다. 의미는 a digit(숫자) 을 뜻한다. 
\d와 동일한 표현으로 [0-9] 역시, 0부터 9까지의 숫자중에 한 개만 일치하면 된다는 조건이다.

 점(.) 은 정규표현식 문법 중 [ ] (bracketed character class) 안에서는 일반 점(.) 문자를 의미하지만,
[ ] 밖에서는 개행문자(\n)를 제외한 아무 문자한 개를 뜻하는 Character Class이다. 
 고로 [ ] 밖에서도 일반 문자처럼 사용하고 싶다면 \(back slash, 역슬래시) 를 앞에 붙여서 escape 해야 한다.


* Quantifier(수량사)
캐릭터 클래스 뒤에 붙어서 그 캐릭터클래스가 몇 번을 반복하는지 조건을 설정 하는 문법이다.


X? : X가 1개 이거나 0개의 횟수
X+ : X가 1개 이거나 그 이상의 횟수 
X* : X가 0개 이거나 그 이상의 횟수 
X{n} : X가 정확히 n번의 횟수
X{n, } : X가 최소 n번의 횟수
X{n,m} : X가 최소 n번에서 m번 사이의 횟수 (m횟수 포함)

^ , $
 캐릭터 클래스 앞에 ^ 이 문자가 오게 되면 그 캐릭터 클래스가 첫번째 문자여야 한다는 의미이다. 
 그러나 [ ] 안에 ^ 문자가 존재한다면 not 이 된다. 
 예를 들면 ^[a].+ 혹은 ^a.+은 a로 시작하는 2개 혹은 그 이상의 문자를 찾아내지만, ^[^a].+은 a로 시작하지 않는 2개 혹은 그 이상의 문자를 찾아낸다.
 $는 바로 앞의 캐릭터 클래스로 끝나야 함을 나타낸다.

 이번 문제는 이름과 이메일 주소를 공백으로 연결하여 입력값이 주어진다. 이메일 주소는 반드시 @gmail.com 이여야 하며 이름은 20자를 넘을 수 없다. 
전체 이메일 주소 길이는 50자를 넘을 수 없다.
위 조건에 맞는 이메일 주소를 가진 이름을 오름차순으로 정렬해야 한다.  

public class Solution {
    
    private static final Scanner scanner = new Scanner(System.in);

    public static void main(String[] args) {
        int N = scanner.nextInt();
        scanner.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");
        String regexEmailId = "[a-z]{1,40}@gmail\\.com";
        String regexFirstName = "[a-z]{1,20}";
        Pattern pEmailId = Pattern.compile(regexEmailId);
        Pattern pfirstName = Pattern.compile(regexFirstName);
        List<String> resultList = new ArrayList<String>();

        for (int NItr = 0; NItr < N; NItr++) {
            String[] firstNameEmailID = scanner.nextLine().split(" ");

            String firstName = firstNameEmailID[0];

            String emailID = firstNameEmailID[1];

            Matcher firstNameM = pfirstName.matcher(firstName);
            Matcher emailM = pEmailId.matcher(emailID);

            if(!emailM.find()){
                continue;
            }
            if(firstNameM.find()){
                resultList.add(firstName);
            }
            
        }
        Collections.sort(resultList);
        for(String temp : resultList)
            System.out.println(temp);

        scanner.close();
    }
}

2019년 5월 17일 금요일

Hackerrank Day 27 Testing







알고리즘 연습 사이트


Day 27:Testing


 단위 테스트의 목적은 개발 코드 단위(예: 함수) 가 예상대로 작동하는지 확인하는 것이다.
함수에 대해 테스트 함수 코드를 작성할 때 예상하는 모든 결과가 다뤄지기를 원할 것이다.


1. 함수가 예기치 않는 결과값을 낼 것으로 생각되는 인수를 인자로 받아본다.
2. 경계값을 인자로 넣어서 테스트한다.
3. 특정 실행 패턴에서 예외를 던질 경우에 대해서도 테스트 해야한다. 





오늘 문제는 테스트 코드를 작성해보는 것인데,
아래는 배열에서 최소값을 갖는 배열의 인덱스를 가져오는 함수이다.
 public static int minimum_index(int[] seq) {
        if (seq.length == 0) {
            throw new IllegalArgumentException("Cannot get the minimum value index from an empty sequence");
        }
        int min_idx = 0;
        for (int i = 1; i < seq.length; ++i) {
            if (seq[i] < seq[min_idx]) {
                min_idx = i;
            }
        }
        return min_idx;
    }class TestDataEmptyArray {
    public static int[] get_array() {
        return new int[]{};
    }
}

아래는 오늘 문제의 구현 코드인데 다양한 조건의 배열을 만들어서 리턴한다. 빈 배열, 유니크한 데이터 배열, 최소값을 최소 2개 이상 갖는 배열을 리턴해 본다. 
특이 케이스 상황을 임의로 만들어보는 것이다.


static class TestDataEmptyArray {
    public static int[] get_array() {
        return new int[]{};
    }
}

static class TestDataUniqueValues {
    public static int[] get_array() {
        return new int[]{1,2,3,4,5};
    }

    public static int get_expected_result() {
        return minimum_index(get_array());
    }
}

static class TestDataExactlyTwoDifferentMinimums {
    public static int[] get_array() {
         return new int[]{1,2,3,4,3,2,1};
    }

    public static int get_expected_result() {
        return minimum_index(get_array());
    }
}

실제로 테스트 할 때 위에서 생성한 배열들을 대상으로 테스트를 진행하고 특이 케이스에서 예외 처리 코드를 작성 해주면 된다. 
아래 코드는 hackerrank에 있는 코드를 가져왔다.
  
public static void TestWithEmptyArray() {
        try {
            int[] seq = TestDataEmptyArray.get_array();
            int result = minimum_index(seq);
        } catch (IllegalArgumentException e) {
            return;
        }
        throw new AssertionError("Exception wasn't thrown as expected");
    }

    public static void TestWithUniqueValues() {
        int[] seq = TestDataUniqueValues.get_array();
        if (seq.length < 2) {
            throw new AssertionError("less than 2 elements in the array");
        }

        Integer[] tmp = new Integer[seq.length];
        for (int i = 0; i < seq.length; ++i) {
            tmp[i] = Integer.valueOf(seq[i]);
        }
        if (!((new LinkedHashSet<Integer>(Arrays.asList(tmp))).size() == seq.length)) {
            throw new AssertionError("not all values are unique");
        }

        int expected_result = TestDataUniqueValues.get_expected_result();
        int result = minimum_index(seq);
        if (result != expected_result) {
            throw new AssertionError("result is different than the expected result");
        }
    }

    public static void TestWithExactlyTwoDifferentMinimums() {
        int[] seq = TestDataExactlyTwoDifferentMinimums.get_array();
        if (seq.length < 2) {
            throw new AssertionError("less than 2 elements in the array");
        }

        int[] tmp = seq.clone();
        Arrays.sort(tmp);
        if (!(tmp[0] == tmp[1] && (tmp.length == 2 || tmp[1] < tmp[2]))) {
            throw new AssertionError("there are not exactly two minimums in the array");
        }

        int expected_result = TestDataExactlyTwoDifferentMinimums.get_expected_result();
        int result = minimum_index(seq);
        if (result != expected_result) {
            throw new AssertionError("result is different than the expected result");
        }
    }




2019년 5월 15일 수요일

Hackerrank Day 26 Nested Logic







알고리즘 연습 사이트


Day 26:Nested Logic



 D-Day 문제도 거의 막바지다.


 오늘은 도서관 대출 벌금 계산하기이다.
 간단하게 설명하면 반납예정년보다 반납년이 늦으면 요금이 $10000$이다. 제때 반납했으면 물론 요금은 0이며, 달이 늦으면 $500 * (반납월-반납예정월)$ 을 계산한다.
마찬가지로 동일한 년월에서 일자만 늦는 경우에는
$15 * (반납일 - 반납예정일)$ 이다.

 가독성이 정말 떨어지지만 문제 통과는 하였다.
 코드를 작성 할 때마다 느끼는 것이지만 이 고정적인 사고 습관에서 벗어나기란 참 어렵다.


*처음작성코드
public class Solution {

    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        
        String returnDate = sc.nextLine();
        String dueDate = sc.nextLine();

        String [] returnDateArr = returnDate.split(" ");
        String [] dueDateArr = dueDate.split(" ");
        int[] iReturnDateArr =new int[3];
        int[] iDueDateArr = new int [3];

        for(int i = 0 ; i < returnDateArr.length;i++){
            iReturnDateArr[i] = Integer.parseInt(returnDateArr[i]);
        }
        for(int i = 0 ; i < dueDateArr.length;i++){
            iDueDateArr[i] = Integer.parseInt(dueDateArr[i]);
        }

        int fine = 0; 
        //0 : day , 1: month , 2 : year
        if(iReturnDateArr[2] <= iDueDateArr[2]){ //년비교
            if(iReturnDateArr[2] == iDueDateArr[2]){
                if(iReturnDateArr[1] <= iDueDateArr[1]){
                    if(iReturnDateArr[0] <= iDueDateArr[0]){
                        fine=0;
                    }else{
                        fine= 15 * (iReturnDateArr[0] - iDueDateArr[0]);
                    }
                }else{ //반납월이 늦으면
                    fine = 500 * (iReturnDateArr[1] - iDueDateArr[1]);
                }
            }else{
                fine = 0;
            }
        }else{ //반납 년도가 더 클 경우
            fine = 10000;
        }
        System.out.println(fine);

    }
}


 위 코드에서 stdin으로 입력을 받는 부분은 논외로 쳐도, 가장 큰 문제점은 쓸때 없는 if문 구절이 많이 중복되었다.
fine=0을 처리하는 조건들을 반대로 걸어 코드를 더 축약할 수 있다.


*개선버전
public class Solution {

    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        
        int rDay = sc.nextInt();
        int rMonth = sc.nextInt();
        int rYear = sc.nextInt();

        int eDay = sc.nextInt();
        int eMonth = sc.nextInt();
        int eYear = sc.nextInt();

        int fine=0;

        if(rYear < eYear){
            fine = 0;
        }else{
            if(rYear > eYear){
                fine = 10000;
            }else if(rMonth > eMonth){
                fine = 500 * (rMonth-eMonth);
            }else if(rDay > eDay){
                fine = 15 * (rDay - eDay);
            }
        }

        System.out.println(fine);
    }
}

Date 객체를 사용할 필요까지도 없고 문제 자체가 년, 월, 일만 비교하는 것이기 때문에 단순 숫자만으로 계산해도 무리가 없다.

2019년 5월 14일 화요일

Hackerrank Day 25 Running Time and Complexity







알고리즘 연습 사이트


Day 25:Running Time and Complexity

알고리즘의 복잡성이나 성능을 따져볼 때 asymptotic notation(점근 표기법)을 사용하여 나타낸다.
아무리 성능이 느린 알고리즘도 CPU가 좋다면 느린 CPU에서 성능이 좋은 알고리즘보다 빠를 수 있다.
 때문에 이러한 기계의 성능과는 독립적으로 알고리즘 그 자체에 대한 성능을 알아보기 위해 점근 표기법을 사용한다.

$f(n)$은 내가 만든 알고리즘을 나타내고 $g(n)$는 어떤 점근적으로 도달되는 함수를 나타낸다.
점근적으로 도달될 때 특징이 있는데, 내가 만든 알고리즘이 성능의 최소치보다는 항상 위이거나, 최대치보다는 항상 아래일 수 있다. 
그러한 특징을 세타나 빅오 등으로 표기하는 것이다.


1. $\Theta$ notation
세타 표기법, $f(n) = \Theta(g(n))$
즉 $f(n)$이 $g(n)$사이에 있기 때문에 $f(n) = g(n)$이라고 봐도 된다.
$ c1·g(n) ≤ f(n) ≤ c2·g(n) $ 


2. Big-Ο notation
빅 오 표기법, $f(n) = O(g(n))$
$f(n) ≤ g(n)$


3. $\Omega$ notation
빅 오메가 표기법, $f(n) = \Omega(g(n))$
$f(n) ≥ g(n)$

 오늘 문제는 소수를 찾는 알고리즘을 만들어보는 것이다. 
어떠한 $n$이 소수인지 아닌지 판별하는 방법은 찾고자하는 수를 $2$ 부터 $\sqrt n$까지 $1$씩 증가시키면서 나누면서 나누어 떨어진다면 소수가 아니고 나누어 떨어지지 않는다면 소수이다.  
 이 원리 혹은 증명은 여기서 다루지 않는다. 일단 이 공식만 알면 코드 작성은 쉽다. 
 아래 코드의 소스 알고리즘은 $O(\sqrt n)$ 의 복잡도를 가진다. 
 $g(n)$이 $\sqrt n$인데 최대 $\sqrt n$ 수까지 나누어봐야 소수인지 아닌지 알아낼 수 있기 때문이다. $\sqrt n$ 보다는 복잡도가 높아질 수 없으므로 빅 오 표기법으로 나타낼수 있다. 




public class Solution {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        //Read input from STDIN. 
        int count = sc.nextInt();
        int [] arr = new int[count];

        for(int i = 0 ; i < count;i++){
            arr[i] = sc.nextInt();
        }

        for(int k= 0 ; k < count ; k++){
          
            if(isPrime(arr[k])){
                System.out.println("Prime");
            }else{
                System.out.println("Not prime");
            }
        }
    }

    public static boolean isPrime(int n){
        if(n == 1){
            return false;
        }
        if(n == 2){
            return true;
        }
        for (int i = 2; i<=Math.sqrt(n); i++) {
            if (n % i == 0) {
                return false;
            }
        }
        return true;
    }
}

2019년 5월 13일 월요일

Hackerrank Day 24 More Linked Lists







알고리즘 연습 사이트


Day 24:More Linked Lists



 Linked List 관련 3번째 문제이다. 
자료구조 관련 문제는 역시 어렵지만 풀고나면 재밌다. 

 이 문제의 포인트는 노드들의 첫 시작 주소인 head 참조값을 잘 갖고 있다가 반환해야 한다는 것이다.

 
 다음 노드의 참조 값을 변수로 가지고 있는 노드들 리스트의 head 노드의 참조값이 메소드 인자로 주어지고, 이 노드 리스트의 data 변수에 있는 값 중 중복된 값을 제거하라는 문제이다. 
 다만 항상 data가 오름차순으로 정렬된 노드 리스트가 주어진다.




class Node{
 int data;
 Node next;
 Node(int d){
        data=d;
        next=null;
    }
 
}
public static Node removeDuplicates(Node head) {
    if(head == null) //head가 null일 때 처리
        return null;
    Node s = head; // s 변수에 head 참조 값을 저장한다.
    while(s.next != null){//while안에서 s변수를 이용하여 노드리스트들을 조작한다.
        if(s.data == s.next.data)//현재 노드의 data값과 다음노드의 데이터값이 일치하면
            s.next = s.next.next; //현재 노드 next 변수에 노드 하나를 건너뛴 다음의 주소값을 저장한다.
        else // 중복되지 않으면 
            s = s.next; //다음 노드 참조값을 s에 대입한다.
    } 
    return head;
}


 return을 할 때는 s 참조 변수가 아닌 head 변수를 반드시 리턴해야한다. 
 잘 생각해보면 s는 while문이 돌면서 값이 계속 변경되고 있어서 head값은 이미 잃어버린 상태이다. 
결국 모든 노드리스트들의 첫 시작 지점 주소 값인 head 를 리턴하면 될 것이다.



2019년 5월 9일 목요일

Hackerrank Day 23 BST Level-Order Traversal







알고리즘 연습 사이트


Day 23:BST Level-Order Traversal



트리를 접근하는 순서에는 여러가지가 있다.


 1. InOrder Traversal
 일반적인 순서대로 트리의 노드를 접근한다. 
left-root-right 순서이다.

 2. PostOrder Traversal
 Post라는 말에서 유추할 수 있듯이 root 노드를 마지막에 두는 순서를 말한다. left-right-root 순서이다.

 3. PreOrder Traversal(DFS)
 PreOrder는 root노드를 가장 먼저 접근한다. 
root-left-right.  Depth-first-search라고도 한다.

 4. Level-Order Traversal(BFS)
 트리의 레벨 순서대로 검색한다. breath-first-search라고도 한다.

예시를 보면 위 4가지 방법을 어떤 순서로 진행되는지 이해하기 쉽다. 
아래 링크에서 이미지를 가져왔다.

https://www.hackerrank.com/challenges/30-binary-trees/tutorial
BinaryTree.png
hackerrank-Binary Tree
  • InOrder: 1 2 3 4 5 6 7
  • PostOrder: 1 3 2 5 7 6 4
  • PreOrder: 4 2 1 3 6 5 7
  • Level-Order: 4 2 6 1 3 5 7



이번 문제는 Binary search tree에서 Level-Order Traversal 방식대로 데이터를 꺼내와서 출력해야 한다.
이 문제를 해결하기 위해 Queue를 이용하면 매우 쉽게 문제가 해결된다. 
Day 18:Queues and Stacks 글 참조


 대표적인 Queue를 구현하는 클래스인 LinkedList 객체를 생성하고, 이 list에 2진탐색트리인 root 노드를 add한다.
 poll 메소드를 사용하였는데 poll은 리스트에 저장된 요소를 가져오면서 그 요소를 list 내에서 삭제해 준다. 
그리고 tmpNode에 현재 꺼내온 노드의 참조 주소를 저장하여 현재 노드의 left, right 자식이 존재하는지 체크하고 존재한다면 list에 add한다.  
add 할 때 주의점은 왼쪽에서 오른쪽 순으로 출력될 수 있도록 왼쪽부터 넣어야한다. 
 list가 비어있지 않는 동안은 계속 while문이 돌면서 data 값을 출력하게 된다.
 while문의 마지막에서 peek 메소드를 써 봤는데, 그다음 list에 요소가 존재할 때에만 data 간에 띄어쓰기를 넣어서 표출하기 위해서이다. 
 peek은 poll과 다르게 요소를 삭제 하지 않으므로 자식이 있다면 다음 반복을 탈 수 있게 된다.

static void levelOrder(Node root){
      LinkedList<Node> list = new LinkedList<Node>();

      list.add(root);
      while(!list.isEmpty()){
          Node tmpNode = list.poll();
          System.out.print(tmpNode.data);
          
          if(tmpNode.left != null){
              list.add(tmpNode.left);
          }
          if(tmpNode.right != null){
              list.add(tmpNode.right);
          }
          if(list.peek() != null)
            System.out.print(" ");
      }
}

터미널 화면을 녹화하여 동영상으로 만들기 (Asciinema)




asciinema : https://asciinema.org/


asciinema를 이용하면 터미널에서 어떠한 타이핑 작업 내용을 화면 그 자체로 녹화를 할 수 있어 굉장히 유용하다.
또한 asciinema가 터미널 화면을 녹화 후, 자동으로 웹 링크 동영상까지 만들어주기 때문에 동영상을 찍어서 직접 업로드를 하는 번거로운 작업이 없다.  

 이 asciinema 여러모로 응용할 수가 있는데, 
 녹화된 콘솔 화면 자체를 보여줌으로써 동영상을 보고 이해하고 따라할 수 있는 가이드용으로 쓸 수 있다.  
 즉 솔루션 설치 과정이나 간단히 터미널에서 하는 작업들을 녹화하여 IT 업무상의 고객 응대 혹은 작업기록용으로도 쓰면 좋을 것 같다.



* 설치 환경

CentOS 6 에서 설치를 진행하였다.
또한 asciinema 설치하기 앞서 python3이 필요하다.
python3버전이 이미 설치되어 있다면 이 과정을 건너뛰고 asciinema 만 설치하면 된다.


(1) centos 6 epel repository 설치
yum으로 epel을 설치한다.
yum install -y http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm

(2) yum python 설치
yum install -y python3 python34-devel python34-setuptools   


(3) python 설치 확인
python3 을 입력하면 아래와 같이 보이면 성공이다.

# python3
Python 3.4.10 (default, Apr  8 2019, 02:18:20) 
[GCC 4.4.7 20120313 (Red Hat 4.4.7-23)] on linux>
Type "help", "copyright", "credits" or "license" for more information.
>>> 

(4) pip3 설치
# cd /usr/lib/python3.4/site-packages/ 
# ll
합계 24
drwxr-xr-x 2 root root 4096 2019-05-09 09:43 __pycache__
drwxr-xr-x 3 root root 4096 2019-05-09 09:43 _markerlib
-rw-r--r-- 1 root root  126 2016-01-31 02:43 easy_install.py
drwxr-xr-x 5 root root 4096 2019-05-09 09:43 pkg_resources
drwxr-xr-x 5 root root 4096 2019-05-09 09:43 setuptools
drwxr-xr-x 2 root root 4096 2019-05-09 09:43 setuptools-19.6.2-py3.4.egg-info
 
# python3 easy_install.py pip



(5) asciinema 설치
# pip3 install asciinema
DEPRECATION: Python 3.4 support has been deprecated. pip 19.1 will be the last one supporting it. Please upgrade your Python as Python 3.4 won't be maintained after March 2019 (cf PEP 429).
Collecting asciinema
  Downloading https://files.pythonhosted.org/packages/a7/71/771c859795e02c71c187546f34f7535487b97425bc1dad1e5f6ad2651357/asciinema-2.0.2.tar.gz
Installing collected packages: asciinema
  Running setup.py install for asciinema ... done

Successfully installed asciinema-2.0.2

여기까지 asciinema 설치가 완료되었다.

* asciinema 로 녹화하기
터미널 창에 단지 asciinema rec만 치면 그 후부터 녹화가 시작된다.


# asciinema rec
asciinema needs an ASCII or UTF-8 character encoding to run. Check the output of `locale` command.
혹시 위와 같이 UTF-8이 필요하다고 나오는 경우 아래와 같이 입력한다. 

# export LANG=ko_KR.utf-8



# asciinema rec
asciinema: recording asciicast to /tmp/tmpcyzurpjo-ascii.cast
asciinema: press <ctrl-d> or type "exit" when you're done


asciinema rec 명령어를 친 이후부터 녹화가 진행되고 있는 것이니 녹화하고자하는 명령어등을 입력해본다.


녹화를 끝내고 싶다면 exit 명령어를 입력한다.
exit를 입력하고 asciinema.org에 업로드를 하고 싶다면 enter를 치면 업로드가 되면서 녹화한 동영상의 웹 url이 보이게 된다.
asciinema에 계정이 없이 동영상을 만들었다면 웹에 업로드한 영상은 7일까지만 보관이 된다고 하니 동영상을 웹에 보존하고 싶다면 asciinema에 계정을 만들자.

# exit
exit
asciinema: recording finished
asciinema: press <enter> to upload to asciinema.org, <ctrl-c> to save locally

View the recording at:

    https://asciinema.org/a/xxx 녹화한 동영상의 주소.

This installation of asciinema recorder hasn't been linked to any asciinema.org
account. All unclaimed recordings (from unknown installations like this one)
are automatically archived 7 days after upload.


If you want to preserve all recordings made on this machine, connect this
installation with asciinema.org account by opening the following link:


터미널에서 직접 동영상을 재생할 수도 있다.
또한 동영상이 재생되는 도중에 영상의 텍스트를 복사할 수도 있다.
asciinema usage는 아래의 링크에서 자세히 나와있다. https://asciinema.org/docs/usage


* asciinema.org 계정 생성
asciinema 웹에서 계정을 이메일 생성한다.
그리고 asciinema를 설치한 서버에서 asciinema auth 명령을 입력한다.


# asciinema auth 
Open the following URL in a web browser to link your install ID with your asciinema.org user account:

https://asciinema.org/connect/xxxx

This will associate all recordings uploaded from this machine (past and future ones) to your account, and allow you to manage them (change title/theme, delete) at asciinema.org.


중간에 url로 접속해보면 내 계정과 동영상들이 연결되어 웹에서 보이게 될 것이다.




2019년 5월 8일 수요일

Hackerrank Day 22 Binary Search Trees







알고리즘 연습 사이트


Day 22:Binary Search Trees



Binary Trees.png
2진 트리(Binary Trees)

 2진 트리의 가장 깊은 노드까지의 거리 (height)를 계산하는 메소드를 구현한다. 
즉 root 노드로부터 가장 먼 자식 노드까지의 거리를 구하는 것이다. 
 2진 트리는 right, left 노드 2개를 갖거나 둘 중의 하나의 자식만 갖거나 아예 자식이 존재하지 않는다.(leaf)

 오늘 문제의 키 포인트는 인자로 들어오는 root노드의 자식 노드를 검사할 때, right, left 자식 노드가 각각 존재하는지 한번에 체크를 해야한다.

 각 사이드의 자식 노드가 존재한다면 해당 높이변수에 1을 더하고 그의 하위자식노드를 인자로 넘겨주어 재귀호출을 한다. 
 주의깊게 살펴봐야 할 부분은 return 부분인데 이곳에서 max height 을 처리한다.

 자식노드가 몇 개가 달려있건간에 자식노드가 존재하지 않을 때까지 재귀호출 되어 결국 마지막 leaf노드에 도달하였을 때 right과 left중 큰 쪽의 height 을 리턴한다. (깊이가 깊은 것이 2개가 있다고 해도 어쨌든 둘중의 하나를 리턴한다.) 그러면 재귀호출이 된 지점으로 반환되면서 결국 가장 최초의 getHeight 메소드 호출의 리턴에서 가장 큰 height값을 구할 수 있다.

 2진 트리가 어떻게 구성되고 특징이 무엇인지 이해하여야 풀 수 있었던 문제였다.




public static int getHeight(Node root){
        int heightLeft = 0;
        int heightRight = 0;
        
        if(root.left !=null){
            heightLeft = 1 + getHeight(root.left);
        }
        if(root.right !=null){
            heightRight = 1 + getHeight(root.right);
        }
        return heightLeft > heightRight ? heightLeft : heightRight; 
}

2019년 5월 7일 화요일

Hackerrank Day 21 Generics







알고리즘 연습 사이트


Day 21:Generics



Generics 참고 URL - oracle API 
Generic Types
Generic Methods
Type Inference



배열의 요소들을 출력하는 printArray 메소드를 제네릭을 이용하여 구현한다.
Printer 클래스의 인스턴스를 만들 때 데이터 타입을 지정해야한다. Class Printer가 제네릭으로 선언되어 있기 때문이다.
class안에 T로 되어있는 부분들은 인스턴스 생성시 지정했던 데이터 타입으로 전부 변환된다.


아래 코드를 보면 이해하기가 쉽다.



class Printer <T> { //class 선언 시 제네릭으로 데이터형을 선언한다.

    /**
    *    Method Name: printArray
    *    Print each element of the generic array on a new line. Do not return anything.
    *    @param A generic array
    **/
    
    // Write your code here

    void printArray(T[] array){ //다양한 형태의 데이터 타입 배열을 인자로 받아들일 수 있다.
        for(T temp : array){
            System.out.println(temp);
        }
    }
}
public class Generics {
    
    public static void main(String args[]){
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        Integer[] intArray = new Integer[n];
        for (int i = 0; i < n; i++) {
            intArray[i] = scanner.nextInt();
        }

        n = scanner.nextInt();
        String[] stringArray = new String[n];
        for (int i = 0; i < n; i++) {
            stringArray[i] = scanner.next();
        }
        
        Printer<Integer> intPrinter = new Printer<Integer>(); //T가 Integer가 됨
        Printer<String> stringPrinter = new Printer<String>(); //T가 String이 됨

        intPrinter.printArray( intArray  );
        stringPrinter.printArray( stringArray );
        if(Printer.class.getDeclaredMethods().length > 1){
            System.out.println("The Printer class should only have 1 method named printArray.");
        }
    } 
}