2019년 12월 20일 금요일

Javascript 변수 선언 키워드 Let 과 Const




* let and const

let : 블럭 사이에서만 활성 되는 변수 선언 키워드. 글로벌하게 사용할 수 있는 var 키워드와는 반대이다. 스코프를 벗어나면 해당 변수를 사용할 수 없다.
또한 let 으로 선언된 변수는 동일한 이름으로 다시 let으로 선언할 수 없다. (var는 가능함)

const : 상수. 값을 레퍼런스로써 읽기만을 하기 위한 변수 선언 키워드이다.
한번 할당하면 값을 변경할 수 없다. const로 선언된 변수는 반드시초기화를 해주어야 한다.


* 문제
PI 를 상수로 선언하고 r를 입력받아, 원의 넓이와 원둘레를 구하라.


function main() {
    // Write your code here. Read input using 'readLine()' and print output using 'console.log()'.
    const PI = Math.PI;
    let r = readLine();
    // Print the area of the circle:
    console.log(r * r * PI);
    // Print the perimeter of the circle:
    console.log(2 * r * PI);
    try {    
        // Attempt to redefine the value of constant variable PI
        PI = 0;
        // Attempt to print the value of PI
        console.log(PI);
    } catch(error) {
        console.error("You correctly declared 'PI' as a constant.");
    }
}

2019년 12월 18일 수요일

Javascript 문자열에서 특정 조건 문자 한개씩 찾기






문제 : 주어진 문자열에서 모음과 자음을 찾아내어 모음을 먼저 출력한 후, 자음을 출력하시오.

주어진 문자열 : javascriptloops
출력 :
a
a
i
o
o
j
v
s
c
r
p
t
l
p
s



1. 정규표현식을 이용하여 모음과 자음을 찾아냄
function vowelsAndConsonants(s) {
    var p1 = /[aeiou]/g; //모음, g의 의미: 플래그. 문자열 내의 모든 패턴을 검색
    var p2 = /[^aeiou]/g; //자음
    var arr1 = s.match(p1); //패턴에 일치하는 결과를 배열로 만들어준다.
    var arr2 = s.match(p2);
   
   for (var i of arr1) {
       console.log(i);
   }
   for (var j of arr2) {
       console.log(j);
   }
}

2. includes 함수를 이용하여 문자 탐색.
includes는 찾을 문자를 인자로 넣어주면 된다.


function vowelsAndConsonants(s) {
    var vowel= "aeiou";
    var consonants = "";
    
   
   for (var i in s) { //문자열 그 자체를 배열처럼 사용가능하다.
       if (vowel.includes(s[i])){
           console.log(s[i]);
       }else{
           consonants+= s[i] + "\n";
       }
   }
   console.log(consonants.trim());
}

2019년 12월 16일 월요일

도수 정렬



* 도수 정렬
요소의 대소를 판단하지 않고 빠르게 정렬할 수 있는 알고리즘이다.
도수분포표를 이용하기 때문에 if 대소비교문이 필요없이 for문만으로 정렬이 가능하다.




public class fSort {
    //배열 a중 최댓값을 구하여 max로 넘겨준다.
    static void fSort(int[] a, int n, int max) {
        int[] f = new int[max + 1];
        int[] b = new int[n];

        for (int i = 0; i < n; i++) f[a[i]]++; //도수분포표 만들기
        for (int i = 1; i <= max; i++) f[i] += f[i - 1]; //누적 도수분포표 만들기
        //f 배열에 있는 값을 -- 연산자를 사용한 f 배열은 누적도수분포표가 된 상태인데
        //해당 구간의 누적개수합산을 뜻한다.
        //인덱스를 1씩 빼서 동일한 값일 경우 앞서 저장한 부분의 앞칸에 저장되게 하기 위함이다.
        for (int i = n - 1; i >= 0; i--) b[--f[a[i]]] = a[i]; 
        for (int i = 0; i < n; i++) a[i] = b[i]; //a에 b 복사하기
        
    }
}

힙(Heap) 정렬




*힙(heap) 정렬 

선택 정렬을 응용한 알고리즘인 힙 정렬은 힙의 특성을 이용하여 정렬을 수행한다.
힙은 부모의 값이 자식의 값보다 항상 크다는 조건을 만족하는 완전이진트리이다.
부모의 값이 자식보다 항상 작아도 힙이라고 한다.
힙은 형제의 대소관계가 정해져 있지 않아서 부분순서트리라고도 한다.


public class HeapSort {
    // 배열 요소 a[idx1]과 a[idx2]의 값을 바꿉니다.
    static void swap(int[] a, int idx1, int idx2) {
        int t = a[idx1];
        a[idx1] = a[idx2];
        a[idx2] = t;
    }

    //a[left] ~ a[right]를 힙으로 만듭니다.
    static void downHeap(int[] a, int left, int right) {
        int temp = a[left]; //root값
        int child; //큰 값을 가진 노드 인덱스
        int parent; // 부모 인덱스

        //자식과 부모를 비교하여 큰 것을 위로 올린다.
        for (parent = left; parent < (right + 1) / 2; parent = child) { 
            int cl = parent * 2 + 1; //왼쪽 자식 인덱스
            int cr = cl + 1; //오른쪽 자식 인덱스
            child = (cr <= right && a[cr] > a[cl]) ? cr : cl; //큰 값을 가진 노드를 자식에 대입
            if (temp >= a[child]) //root가 더 크다면 반복문 중지
                break;
            a[parent] = a[child]; //큰 값을 위로 올림
        }
        a[parent] = temp;
    }

    //힙 정렬
    static void heapSort(int[] a, int n) {
        for (int i = (n - 1) / 2; i >= 0; i--) //a[i] ~ a[n - 1]를 힙으로 만들기
            downHeap(a, i, n - 1);

        for (int i = n - 1; i > 0; i--) {
            swap(a, 0, i); //가장 큰 요소와 아직 정렬되지 않은 부분의 마지막 요소를 교환
            downHeap(a, 0, i - 1); //a[0] ~ a[i - 1]을 힙으로 만들기
        }
    }
}

2019년 12월 6일 금요일

병합(merge) 정렬





* 병합 정렬

정렬의 마친 두 배열을 병합하여 분할 정복법에 따라 정렬하는 알고리즘을 병합 정렬이라고 한다.



1. 정렬을 마친 두 배열을 병합하기


public class MergeArray {
    //정렬을 마친 배열 a, b를 병합하여 배열 c에 저장
    static void merge(int[] a, int na, int[] b, int nb, int[] c) {
        int pa = 0;
        int pb = 0;
        int pc = 0;

        while (pa < na && pb < nb) //작은 값을 c 배열에 저장한다.
            c[pc++] = (a[pa] <= b[pb]) ? a[pa++] : b[pb++];
        while (pa < na) //a에 남아있는 요소를 복사
            c[pc++] = a[pa++];
        while (pb < nb) //b에 남아있는 요소를 복사
            c[pc++] = a[pb++];

    }
    public static void main(String[] args) {
        Scanner stdIn = new Scanner(System.in);
        int[] a = {2, 4, 6, 11, 13};
        int[] b = {1, 2, 3, 4, 9, 16, 21};
        int[] c = new int[13];

        System.out.println("두 배열의 병합");

        merge(a, a.length, b, b.length, c); //배열 a와 b를 병합하여 c에 저장

        System.out.println("배열 a와 b를 병합하여 배열 c에 저장했습니다.");
        System.out.println("배열 a : ");
        for (int i = 0; i < a.length; i++) {
            System.out.print("a[" + i + "] = " + a[i]);
        }
        System.out.println("배열 b : ");
        for (int i = 0; i < b.length; i++) {
            System.out.print("b[" + i + "] = " + b[i]);
        }
        System.out.println("배열 c : ");
        for (int i = 0; i < a.length; i++) {
            System.out.print("c[" + i + "] = " + c[i]);
        }

    }
}




2. 1번을 응용하여 분할 정복법에 따라 병합 정렬하기
배열을 2등분하여 앞부분과 뒷부분으로 나눈다.
앞부분과 뒷부분을 각각 정렬하고 병합한다.
여기서 재귀호출을 하게 되는데 그 앞부분과 뒷부분도 또다시 병합정렬을 이용하기 때문이다.




public class MergeSort {
    static int[] buff; //작업용 배열

    //a[left] ~ a[right] 을 재귀적으로 병합 정렬한다.
    static void __mergeSort(int[] a, int left, int right) {
        if (left < right) {
            int i;
            int center = (left + right) / 2;
            int p = 0;
            int j = 0;
            int k = left;

            __mergeSort(a, left, center); //배열의 앞부분을 병합 정렬한다.
            __mergeSort(a, center + 1, right); //배열의 뒷부분을 병합정렬한다.

            for (i = left; i <= center; i++) //버퍼에다 a[left]~a[center]를 복사한다.
                buff[p++] = a[i];

            while (i <= right && j < p) //버퍼와, a배열(center 뒷부분부터 right까지)을 서로 병합정렬한다.
                a[k++] = (buff[j] <= a[i]) ? buff[j++] : a[i++];

            while (j < p) //buff의 나머지 요소를 배열 a에 복사한다.
                a[k++] = buff[j++];
        }
    }

    //병합 정렬
    static void mergeSort(int[] a, int n) {
        buff = new int[n]; //작업용 배열 생성

        __mergeSort(a, 0, n - 1); //배열 전체를 병합 정렬한다.

        buff = null; //작업용 배열 해제
    }

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

        System.out.println("병합 정렬");
        System.out.print("요솟수:");
        int nx = stdIn.nextInt();
        int[] x = new int[nx];

        for (int i = 0; i < nx; i++) {
            System.out.print("x[" + i + "] : ");
            x[i] = stdIn.nextInt();
        }

        mergeSort(x, nx);

        System.out.println("오름차순으로 정렬했습니다.");
        for (int i = 0; i < nx; i++) {
            System.out.print("x[" + i + "]= " + x[i]);
        }

    }
}



3. 자바가 제공하는 sort 사용하기
- 기본 자료형 정렬
public class ArraySortTester {
    public static void main(String[] args) {
        Scanner stdIn = new Scanner(System.in);

        System.out.print("요솟수 : ");
        int num = stdIn.nextInt();
        int[] x = new int[num];

        for (int i = 0; i < num; i++) {
            System.out.print("x[" + i + "] :");
            x[i] = stdIn.nextInt();
        }

        //sort 메소드는 기본 자료형의 배열을 정렬한다.
        //퀵 정렬 알고리즘을 개선한 것으로 안정적이지 않다.
        //즉 배열에 같은 값이 존재하는 경우 같은 값 사이의 순서가 바뀔 수 있다.

        Arrays.sort(x);

        for (int i = 0; i < num; i++) {
            System.out.print("x[" + i + "] :");
        }
    }
}



- 클래스 객체 배열의 정렬 (병합 정렬)
클래스 객체 배열의 정렬에서 사용하는 알고리즘은 병합 정렬을 개선한 것으로 안정적인 결과가 보장된다.


아래 메소드들은 기본 오름 차순 정렬이다. static void sort(Object[] a)
static void sort(Object[] a, int fromIndex, int toIndex) : fromIndex 부터 toIndex 까지만 정렬한다.



public class SortCalendar {
    public static void main(String[] args) {
        GregorianCalendar[] x = {
                new GregorianCalendar(2017, Calendar.NOVEMBER, 1),
                new GregorianCalendar(1963, Calendar.OCTOBER, 18),
                new GregorianCalendar(1985, Calendar.APRIL, 5),
        };

        Arrays.sort(x);

        for (int i = 0; i < x.length; i++)
            System.out.printf("%04d년 %02d월 %02d일\n",
                    x[i].get(Calendar.YEAR),
                    x[i].get(Calendar.MONTH) + 1,
                    x[i].get(Calendar.DATE)
            );
    }
}


결과 출력
1963년 10월 18일
1985년 04월 05일
2017년 11월 01일


만약 정렬 시에 원하는 기준이 있을 경우 그 키를 기준으로 하는 Comparator를 구현 하면 된다. 아래 메소드들은 기본 오름 차순 정렬이다.


static void sort(T[] a, Comparator c)
static void sort(T[] a, int fromIndex, int toIndex, Comparator c) : T를 부모로 갖는 클래스이어야 한다.


public class PhyscExamSort {
    static class PhyscData {
        String name;
        int height;
        double vision;

        PhyscData(String name, int height, double vision) {
            this.name = name; this.height = height; this.vision = vision;
        }
        public String toString() {
            return name + " " + height + " " + vision;
        }

        //키 오름차순용 comparator
        //Comparator의 compare 메소드를 오버라이드하여 기준을 정함.

        static final Comparator HEIGHT_ORDER = new HeightOrderComparator();

        private static class HeightOrderComparator implements Comparator {
            @Override
            public int compare(PhyscData d1, PhyscData d2) {
                return (d1.height > d2.height) ? 1 :
                        (d1.height < d2.height) ? -1 : 0;
            }
        }
    }
    public static void main(String[] args) {
        PhyscData[] x = {
                new PhyscData("이나령", 162,0.3),
                new PhyscData("전서현", 122,1.3),
                new PhyscData("홍길동", 172,0.8),
                new PhyscData("유지훈", 182,0.9),
        };
        Arrays.sort(x, PhyscData.HEIGHT_ORDER); //HEIGHT_ORDER 를 사용하여 정렬한다.
    }
}


2019년 12월 5일 목요일

퀵(quick) 정렬






* 퀵 정렬

일반적으로 많이 사용되고 있는 아주 빠른 정렬 알고리즘.
어느 한 요소를 선택하는데 이를 피벗(pivot) 이라고 한다.
그 요소보다 작은 그룹과 큰 그룹을 나누고 각 그룹에서의 피벗 설정과 그룹 나눔을 반복하여 모든 그룹이 1개의 요소가 되면 정렬을 마친다.


1. 배열을 피벗으로 나누어보는 코드

//배열 요소 a[idx1]과 a[idx2]의 값을 바꿉니다.
static void swap(int[] a, int idx1, int idx2) {
    int t = a[idx1];
    a[idx1] = a[idx2];
    a[idx2] = t;
}

//배열을 나눕니다.
static void partition(int[] a, int n) {
    int pl = 0; //point left 왼쪽 커서
    int pr = n - 1; //point right 오른쪽 커서
    int x = a[n / 2]; //피벗(가운데 위치의 값)

    do {
        while (a[pl] < x) pl++; //왼쪽커서의 값이 피벗보다 작은경우 계속 검사
        while (a[pr] > x) pr--; //오른커서의 값이 피벗보다 큰경우 계속 검사
        if (pl <= pr)
            swap(a, pl++, pr--); // 스왑한다.
    } while (pl <= pr);

    System.out.println("피벗의 값은 " + x + "입니다.");

    System.out.println("피벗 이하의 그룹");
    for (int i = 0; i <= pl - 1; i++)
        System.out.print(a[i] + " ");
    System.out.println();

    if (pl > pr + 1) {
        System.out.println("피벗과 일치하는 그룹");
        for (int i = pr + 1; i <= pl - 1; i++)
            System.out.print(a[i] + " ");
        System.out.println();
    }
    
    System.out.println("피벗 이상의 그룹");
    for (int i = pr + 1; i < n; i++)
        System.out.print(a[i] + " ");
    System.out.println();
}




2. 재귀적으로 퀵 소트를 구현 (재귀호출)

static void quickSort(int[] a, int left, int right) {
    int pl = left;
    int pr = right;
    int x = a[(pl + pr) / 2]; //피벗

    do {
        while (a[pl] < x) pl++;
        while (a[pr] > x) pr--;
        if (pl <= pr)
            swap(a, pl++, pr--);
    } while (pl <= pr);

    if (left < pr) quickSort(a, left, pr); //왼쪽끝이 오른커서보다 작으면 왼쪽그룹을 계속 나눈다.
    if (pl < right) quickSort(a, pl, right); //오른끝이 왼쪽커서보다 크면 오른그룹을 계속 나눈다.
}



3. 비재귀적으로 퀵소트를 구현 (스택)

    //비재귀버전
    static void quickSort2(int[] a, int left, int right) {
        IntStack lstack = new IntStack(right - left + 1);
        IntStack rstack = new IntStack(right - left + 1);

        lstack.push(left);
        rstack.push(right);

        while (lstack.isEmpty() != true) {
            int pl = left = lstack.pop();
            int pr = right = rstack.pop();
            int x = a[(pl + pr) / 2]; //피벗

            //그룹 분할
            do {
                while (a[pl] < x) pl++;
                while (a[pr] > x) pr--;
                if (pl <= pr)
                    swap(a, pl++, pr--);
            } while (pl <= pr);

            if (left < pr) {
                // 왼쪽 그룹 범위의 인덱스를 푸시합니다.
                lstack.push(left);
                rstack.push(pr);
            }

            if (pl < right) {
                // 오른쪽 그룹 범위의 인덱스를 푸시합니다.
                lstack.push(pl);
                rstack.push(right);
            }
        }
    }




2019년 12월 4일 수요일

셸 정렬 (shell sort)





셸(shell) 정렬은 단순 삽입 정렬의 장점을 살리고 단점을 보완한 정렬 알고리즘이다.

참고: 단순삽입정렬



//기본 셸 정렬
static void shellSort(int[] a, int n) {
    //h : 요소사이의 간격을 뜻한다. h 간격의 요소를 비교하여 삽입정렬을 진행한다.
    for (int h = n / 2; h > 0; h /= 2) //h는 계속 2로 나눈 몫이 된다.  ex) h = 4, 2, 1
        for (int i = h; i < n; i++) { //i = 4, 5, 6.. 로 차례로 증가
            int j;
            int tmp = a[i]; //a[4] 값을 임시 저장한다.
            //j = 0부터 시작. a[i-h]값이 tmp보다 클때만 for문을 돈다.
            //증분은 j에서 h값을 빼준다.
            for (j = i - h; j >= 0 && a[j] > tmp; j -= h)
                a[j + h] = a[j]; //복사
            a[j + h] = tmp;
        }
}


위 코드의 문제점은 h 수열이 서로 배수가 될 경우 동일한 요소들끼리만 섞인다는 점이다.


//셸 정렬 개선 버전
//h값 수열을 서로 배수가 되지 않도록 h값을 변경
static void shellSort2(int[] a, int n) {
    int h;
    //h의 초기값을 미리 계산해놓는다.
    //초기값은 너무 크면 의미가 없으므로 배열의 요솟수 n을 9로 나눈 몫을 넘지 않도록 정한다.
    //그리고 h간격을 3배한 후 1을 더하면 요소가 충분히 섞일수 있도록 하는 초기값을 구할 수 있다.
    for (h = 1; h < n / 9; h = h * 3 + 1)
        ;
    
    //h를 3으로 나눈 몫을 가지고 간격이 점차 줄어들어
    for ( ; h > 0; h /= 3) //마지막엔 h가 1이 남게 될 것이다.
        for (int i = h; i < n; i++) {
            int j;
            int tmp = a[i];
            for (j = i - h; j >= 0 && a[j] > tmp; j -= h)
                a[j + h] = a[j];
            a[j + h] = tmp;
        }

}


2019년 12월 3일 화요일

버블/단순 선택/단순 삽입 정렬





정렬에서 사용하는 swap 메소드는 아래와 같이 구현하면 된다.

// a[idx1]와 a[idx2]의 값을 바꿉니다.
    static void swap(int[] a, int idx1, int idx2) {
        int t = a[idx1];
        a[idx1] = a[idx2];
        a[idx2] = t;
    }



1. 버블(Bubble) 정렬


    //length가 n 인 배열 a 을 버블정렬한다.
    static void bubbleSort(int[] a, int n) {
        //정렬을 마친 부분을 저장한다.
        int k = 0; // a[k]보다 앞쪽은 정렬은 마친 상태

        while (k < n - 1) {
            int last = n - 1; // 마지막으로 요소를 교환한 위치
            for (int j = n - 1; j > k; j--) //
                if (a[j - 1] > a[j]) {
                    swap(a, j - 1, j);
                    last = j; 
                }
            k = last; 
        }
    }

    // 양방향 버블 정렬(셰이커 정렬)
    static void shakerSort(int[] a, int n) {
        int left = 0;
        int right = n - 1;
        int last = right;

        while (left < right) {
            for (int j = right; j > left; j--) {
                if (a[j - 1] > a[j]) {
                    swap(a, j - 1, j);
                    last = j;
                }
            }
            left = last;

            for (int j = left; j < right; j++) {
                if (a[j] > a[j + 1]) {
                    swap(a, j, j + 1);
                    last = j;
                }
            }
            right = last;
        }
    }


2. 단순 선택 정렬
가장 작은 요소부터 선택해 알맞은 위치로 옮겨서 순서대로 정렬하는 알고리즘이다.


// 1. 가장 작은 요소의 값을 선택.
// 2. 아직 정렬하지 않은 부분의 첫번째 요소를 교환한다.
static void selectionSort(int[] a, int n) {
    for (int i = 0; i < n - 1; i++) {
        int min = i;
        for (int j = i + 1; j < n; j++)
            if (a[min] > a[j]) //최소값의 인덱스를 for 루프를 통해 찾았으면
                min = j; //해당 j(인덱스)를 min 에 저장
        swap(a, i, min); //아직 정렬되지 않은 부분과 min을 교환한다.

    }
}


3. 단순 삽입 정렬
선택한 요소를 그보다 더 앞쪽의 알맞은 위치에 삽입하는 작업을 반복하여 정렬하는 알고리즘이다.

public class InsertionSort {
    //단순 삽입 정렬
    static void insertionSort(int[] a, int n) {
        for(int i = 1; i < n; i++) {
            int j;
            int tmp = a[i]; //2번째 요소부터 선택하고 임시저장
            //tmp와 배열의 값을 하나씩 비교하여 앞이 더 크면 그 값을 뒤로 복사한다.
            for (j = i; j > 0 && a[j - 1] > tmp ; j--)
                a[j] = a[j - 1];
            //a[j - 1] <= tmp 인 경우 for 문을 탈출
            a[j] = tmp; // j인덱스 자리에 tml 값을 삽입
        }
    }

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

        System.out.println("단순 삽입 정렬");
        System.out.print("요솟수:");
        int nx = stdIn.nextInt();
        int[] x = new int[nx];

        for (int i = 0; i < nx; i++) {
            System.out.print("x[" + i + "] : ");
            x[i] = stdIn.nextInt();
        }

        insertionSort(x, nx); //배열 x를 단순 삽입 정렬 합니다.

        System.out.println("오른차순으로 정렬했습니다.");
        for (int i = 0; i < nx; i++) {
            System.out.print("x[" + i + "]= " + x[i]);
        }
    }
}