Insertion Sort
손안의 카드를 정렬하는 방법과 유사하다. 새로운 카드를 기존의 정렬된 카드 사이의 올바른 자리를 찾아 삽입하는 방식이다. 새로 삽입될 카드의 수만큼 반복하게 되면 전체 카드의 정렬이 완성된다. 자료 배열의 모든 요소를 앞에서부터 차례대로 이미 정렬된 배열 부분과 비교 하여, 자신의 위치를 찾아 삽입함으로써 정렬을 완성하는 알고리즘이다. 매 순서마다 해당 원소를 삽입할 수 있는 위치를 찾아 해당 위치에 넣는다. 이렇게 정렬을 1회전 수행할 때마다 데이터를 하나씩 늘리면서 전체 배열과 비교해 다음 정렬을 수행하게 된다. 따라서 삽입 정렬의 시간복잡도는 이다.
아래와 같은 배열을 삽입 정렬로 정렬한다고 가정해 보자.
0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
7 | 2 | 3 | 9 | 28 | 11 |
한 장의 카드인 7만 있다고 생각한 뒤, 새로운 카드인 2을 넣는다고 생각해 보자. 2의 올바른 자리는 7 앞이기 때문에 교환이 일어난다.
0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
2 | 7 | 3 | 9 | 28 | 11 |
새로운 카드인 3을 추가하면 3의 올바른 위치는 2와 7사이이다. 따라서 7과 교환한다.
0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
2 | 3 | 7 | 9 | 28 | 11 |
새로운 카드인 9를 추가하면 9의 올바른 위치는 7 뒤이다. 따라서 올바르게 위치해 있다.
0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
2 | 3 | 7 | 9 | 28 | 11 |
새로운 카드인 28를 추가하면 28의 올바른 위치는 9 뒤이다. 따라서 올바르게 위치해 있다.
0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
2 | 3 | 7 | 9 | 28 | 11 |
새로운 카드인 11을 추가하면 28의 올바른 위치는 9와 28 사이이다. 따라서 28과 교환해 준다.
0 | 1 | 2 | 3 | 5 | 4 |
---|---|---|---|---|---|
2 | 3 | 7 | 9 | 11 | 28 |
public class InsertionSort01 {
public static void main(String[] args) {
int[] arr = {6, 4, 1, 7, 8, 9, 3};
for (int i = 1; i < arr.length; i++) {
for (int j = i; j > 0 ; j--) {
if (arr[j] < arr[j - 1]) {
int temp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = temp;
}
}
}
System.out.println(Arrays.toString(arr));
}
}
이때 퍼포먼스 튜닝을 위해 교환이 이미 일어난 loop라면 beak을 통해 종료시면 된다.
public class InsertionSort01 {
public static void main(String[] args) {
int[] arr = {6, 4, 1, 7, 8, 9, 3};
for (int i = 1; i < arr.length; i++) {
for (int j = i; j > 0 ; j--) {
if (arr[j] < arr[j - 1]) {
int temp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = temp;
} else { break;
}
}
}
System.out.println(Arrays.toString(arr));
}
}
삽입 정렬을 자바 코드 답게 OOP로 리팩토링하면 다음과 같다.
public class InsertionSort01 {
public int[] sort(int[] arr) {
for (int i = 1; i < arr.length; i++) {
for (int j = i; j > 0 ; j--) {
if (arr[j] < arr[j - 1]) {
int temp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = temp;
} else { break;
}
}
}
return arr;
}
public static void main(String[] args) {
int[] arr = {6, 4, 1, 7, 8, 9, 3};
InsertionSort01 insertionSort = new InsertionSort01();
arr = insertionSort.sort(arr);
System.out.println(Arrays.toString(arr));
}
}
삽입 정렬에서 내림차순 기능 추가를 위해 파라미터로 isAscending 변수를 하나 추가해 준다. 그리고 비교 연산이 있는 부분에 삼항연산자를 넣어 boolean의 값에 따라 정렬이 완성되도록 한다.
public class InsertionSort01 {
public int[] sort(int[] arr, boolean isAscending) {
for (int i = 1; i < arr.length; i++) {
for (int j = i; j > 0 ; j--) {
if (isAscending? (arr[j] - arr[j - 1] < 0) : (arr[j] - arr[j - 1] > 0)) {
int temp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = temp;
} else { break;
}
}
}
return arr;
}
public static void main(String[] args) {
int[] arr = {6, 4, 1, 7, 8, 9, 3};
InsertionSort01 insertionSort = new InsertionSort01();
arr = insertionSort.sort(arr, true);
System.out.println(Arrays.toString(arr));
arr = insertionSort.sort(arr, false);
System.out.println(Arrays.toString(arr));
}
}
이때, 사용자가 내림차순인지 오름차순인지 지정하지 않고 정렬을 호출할지도 모른다. C에서는 이런 문제를 default parameter로 해결하였지만 JAVA에서는 Overloading을 사용한다.
public class InsertionSort01 {
public int[] sort(int[] arr, boolean isAscending) {
for (int i = 1; i < arr.length; i++) {
for (int j = i; j > 0 ; j--) {
if (isAscending? (arr[j] - arr[j - 1] < 0) : (arr[j] - arr[j - 1] > 0)) {
int temp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = temp;
} else { break;
}
}
}
return arr;
}
public int[] sort(int[] arr) {
return sort(arr, true);
}
public static void main(String[] args) {
int[] arr = {6, 4, 1, 7, 8, 9, 3};
InsertionSort01 insertionSort = new InsertionSort01();
arr = insertionSort.sort(arr, true);
System.out.println(Arrays.toString(arr));
arr = insertionSort.sort(arr, false);
System.out.println(Arrays.toString(arr));
}
}
Selection Sort
손안의 카드를 정렬하는 방법과 유사하다. 새로운 카드를 기존의 정렬된 카드 사이의 올바른 자리를 찾아 삽입하는 방식이다. 새로 삽입될 카드의 수만큼 반복하게 되면 전체 카드의 정렬이 완성된다. 자료 배열의 모든 요소를 앞에서부터 차례대로 이미 정렬된 배열 부분과 비교 하여, 자신의 위치를 찾아 삽입함으로써 정렬을 완성하는 알고리즘이다. 매 순서마다 해당 원소를 삽입할 수 있는 위치를 찾아 해당 위치에 넣는다. 이렇게 정렬을 1회전 수행할 때마다 데이터를 하나씩 늘리면서 전체 배열과 비교해 다음 정렬을 수행하게 된다. 따라서 삽입 정렬의 시간복잡도는 이다.
아래와 같은 배열을 선택 정렬로 정렬한다고 가정해 보자.
0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
7 | 2 | 3 | 9 | 28 | 11 |
0번 index에 와야 할 최소값은 2이기 때문에 둘을 교환한다.
0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
2 | 7 | 3 | 9 | 28 | 11 |
1번 index에 와야 할 다음 최소값은 3이기 때문에 둘을 교환한다.
0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
2 | 3 | 7 | 9 | 28 | 11 |
2번 index에 와야 할 다음 최소값은 7이기 때문에 교환이 일어나지 않는다.
0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
2 | 3 | 7 | 9 | 28 | 11 |
3번 index에 와야 할 다음 최소값은 9이기 때문에 교환이 일어나지 않는다.
0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
2 | 3 | 7 | 9 | 28 | 11 |
4번 index에 와야 할 다음 최소값은 11이기 때문에 교환이 일어난다.
0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
2 | 3 | 7 | 9 | 11 | 28 |
public class SelectionSort01 {
public static void main(String[] args) {
int[] arr = {55, 64, 21, 643, 23, 84};
for (int i = 0; i < arr.length; i++) {
int targetValue = arr[i];
for (int j = i; j < arr.length; j++) {
if(targetValue > arr[j]) {
targetValue = arr[j];
arr[j] = arr[i];
}
}
arr[i] = targetValue;
}
System.out.println(Arrays.toString(arr));
}
}
Container은 Image를 담아서 실행시키는 파일이다. 따라서 Image를 삭제하기 위해서는 해당 이미지를 실행 중인 Container의 상태를 종료로 바꾸고 삭제해야 한다.
docker stop <container name or container ID>
docker container ls
docker image ls
-a 옵션이 있으면 데몬에서 실행되는 모든 리스트까지 확인
docker container ls -a
docker image ls -a
docker ps
docker --help
docker container rm <Contaniner ID>
docker image rm <Imgae ID>
-f 옵션이 있으면 강제로라도 삭제
docker container prune
docker image prune -a
-f 옵션이 있으면 강제로라도 삭제
image와 container 모두 삭제
docker system prune -a
linux에서 cron scheduling을 통해 한 시간마다 docker prune을 실행하도록 바꿔 주거나 하는 작업들을 실행할 수 있다.
Amazon EC2 서비스는 사용에 따라 비용이 다르게 발생한다. 비용을 절약하기 위해 서버를 효율적으로 사용하는 것이 중요하고, 서버가 안정적으로 운영되기 위해서는 부하가 걸리는 지점이 없는지 관리하고 감독하는 일이 필요하다. 좋은 자원 관리는 서비스의 속도 향상에도 기여하기 때문에 서버의 부하를 최소화하도록 이를 관리하며 운영해야 한다.
Resource monitoring은 시스템에서 사용되는 자원(리소스)의 사용량을 모니터링하는 프로세스입니다. 이는 CPU, 메모리, 디스크, 네트워크 및 기타 자원 사용량을 추적하고 관리하는 것을 의미합니다.
Resource monitoring은 다양한 시나리오에서 사용됩니다. 예를 들어, 시스템 관리자는 서버에서 발생하는 과부하를 파악하고 문제를 해결하기 위해 CPU 및 메모리 사용량을 모니터링합니다. 또한, 개발자는 자신이 작성한 애플리케이션이 시스템 리소스를 효율적으로 사용하는지 확인하기 위해 자원 모니터링 도구를 사용할 수 있습니다.
Resource monitoring은 시스템의 안정성과 성능을 유지하기 위해 중요합니다. 자원 사용량이 과도하게 높아지면 시스템이 다운될 수 있기 때문입니다. 따라서 시스템 관리자나 개발자는 시스템의 자원 사용량을 모니터링하여 시스템의 안정성을 유지하고 사용자에게 최상의 성능을 제공해야 합니다.
자원 모니터링 도구에는 다양한 종류가 있습니다. 일부 도구는 터미널에서 작동하며 다른 도구는 GUI를 사용합니다. 예를 들어, Linux에서는 top, htop, atop, Glances, Nagios 등의 도구를 사용할 수 있습니다. 각 도구는 특정한 목적에 맞게 설계되었으며 사용자가 선택할 수 있도록 다양한 기능을 제공합니다.
Glances
Glances는 Python으로 작성된 무료 및 오픈 소스 시스템 모니터링 도구다. Glances는 터미널 기반의 인터페이스를 제공하며, CPU, 메모리, 디스크, 네트워크 등의 시스템 자원 사용량을 실시간으로 모니터링할 수 있다.
glances를 설치하는 명령어는 다음과 같다.
sudo apt install glances
glances를 실행하는 명령어는 다음과 같으며 실행하면 이렇게 시스템 모니터링이 가능하다.
glances
리눅스 커맨드에서도 용량을 확인할 수 있다. df는 disk filesystem의 약자로 파일 시스템 별로 사용 중인 디스크 공간을 보여 준다. df -h 는 “human readable”을 의미하며 사람이 읽기 쉬운 형태로 변환해서 보여 준다.
df -h
du 명령어는 dicsk usage의 약자로 특정 디렉토리나 파일이 사용 중인 디스크 공간을 보여 준다. 기징 간단한 확인 방법은 du -sh로 s는 “summary”를 의미하며 이는 디렉토리에 있는 각 파일의 디렉토리의 디스크 사용량을 보여 준다
du -sh *
현재 디렉토리의 서브 디렉토리의 용량
du .
하위 디렉토리 용량 확인
du -sh ./*
하위 디렉토리 용량 확인하고 정렬
du -sh ./* | sort -nr
Springboot example 프로젝트를 하나 만든다.
프로젝트를 업로드할 깃허브 리포지토리를 만든다.
springboot project를 git 에 업로드해 준다.
ec2에서 git clone을 한다.
java jdk를 설치해 준다.
sudo apt update
sudo apt install openjdk-17-jdk
java -version
Dockerfile을 작성한 뒤 git에 push해 준다. 이때 아래의 4개 파일은 무조건 올라가야 한다.
프로젝트 폴더로 가서 올린 Dockerfile을 pull 해 준다.
gradle build를 실행한다.
sh gradlew build
project / build / libs 폴더에 가면 springboot app이 보인다.
명령어를 통해 springboot project 실행하면 8080 포트를 열었다고 나온다.
java -jar <프로젝트이름>-SNAPSHOT.jar
Amazon EC2의 보안 그룹에서 8080 포트를 열어 준다.
이후 8080 포트로 접속하면 잘 연결된 것을 확인할 수 있다.
git에서 clone받은 파일 중 Dockerfile에 쓰여진 대로 이미지를 build하여 Container 위에 띄울 수 있다.
docker build -t <이미지 이름> .
포트포워딩을 통해 포트 지정 후 이미지 실행하기
docker run -p 8081:8080 -d <이미지 이름>