2차원배열, 버블,선택,스왑,디버깅,퀵솔트

이소연's avatar
Aug 03, 2024
2차원배열, 버블,선택,스왑,디버깅,퀵솔트
  1. 메서드(객체지향 1번째 원칙)
 
  1. 2차원 배열
 
  1. 버블정렬, 선택정렬, 삽입정렬, 퀵솔트
 
  1. 로또
 

2차원 배열

ctrl + d
행 복사 붙여넣기
notion image
누적. 우변부터 읽어서 count 선언부터 해야 함.
for문 안에 넣으면 count가 계속 0이 되서 안됨
코드 덩어리 : 모듈
 
seats[0].length : 열의 개수
seats.length : 행의 개수
println해서 해보면 됨.
 
package ex03; public class TheaterSeats { public static void main(String[] args) { //배열은 구조 변경 불가능 int[][] seats = { {0,0,0,1,1,0,0,0,0,0}, {0,0,1,1,0,0,0,0,0,0}, {0,0,0,0,0,0,1,1,1,0}, {1,1,0,0,0,0,1,1,1,0}, }; int sum = 0; int count = 0; //int row = -1; for (int row = 0; row < seats.length ; row++) { count = 0; for(int i=0; i< seats[row].length; i++){ count = count + seats[row][i]; } System.out.println(row+"번째 행의 관객수는 : " +count); sum = sum + count; } System.out.println("전체 관객수는 :" +sum); // row++; // count = 0; // for(int i=0; i< seats[row].length; i++){ // count = count + seats[row][i]; // } // System.out.println(row+"번째 행의 관객수는 : " +count); // sum = sum + count; // // row++; // count = 0; // for(int i=0; i< seats[row].length; i++){ // count = count + seats[row][i]; // } // System.out.println(row+"번째 행의 관객수는 : " +count); // sum = sum + count; // // row++; // count = 0; // for(int i=0; i< seats[row].length; i++){ // count = count + seats[row][i]; // } // System.out.println(row + "번째 행의 관객수는 : " +count); // sum = sum + count; // // row++; // count = 0; // for(int i=0; i< seats[row].length; i++){ // count = count + seats[row][i]; // } // System.out.println(row + "번째 행의 관객수는 : " +count); // sum = sum + count; // // // // System.out.println("전체 관객수는 :" +sum); } }
하나하나 단계(for문도 다 적어보고 고쳐보자)를 밟아가면서 하자.
 

버블 정렬

 
알고리즘이란 무엇일까?
컴퓨터가 따라 할 수 있도록 문제를 해결하는 절차나 방법을 자세히 설명하는 과정.
-추상적으로 일을 못 시키고 자세하게 말해줘야 함.
-함수를 이용하여 추상적으로 일을 시킬 수 있음
ex) 함수(라면 끓여와.)
 
  • 버블 정렬(bubble sort) 알고리즘의 개념 요약 서로 인접(붙어있는)한 두 원소(인덱스)를 검사하여 정렬하는 알고리즘 인접한 2개의 레코드를 비교하여 크기가 순서대로 되어 있지 않으면 서로 교환한다. 선택 정렬과 기본 개념이 유사하다. https://gmlwjd9405.github.io/2018/05/06/algorithm-bubble-sort.html
  • 버블 정렬(bubble sort) 알고리즘의 구체적인 개념 버블 정렬은 첫 번째 자료두 번째 자료를, 두 번째 자료세 번째 자료를, 세 번째와 네 번째를, … 이런 식으로 (마지막-1)번째 자료와 마지막 자료를 비교하여 교환하면서 자료를 정렬한다. https://gmlwjd9405.github.io/2018/05/06/algorithm-bubble-sort.html
ex) 5,8,2,4,3(원본 데이터)라면,
[1회전 (4바퀴)]
(1 라운드) 5, 8 비교(변화없음)
(2 라운드) 8,2비교 (5,2,8,4,3)
이후에는 변화된 (5,2,8,4,3)을 비교
(3 라운드) 8,4비교 (5,2,4,8,3)
(4 라운드) 8,4비교 (5,2,4,3,8)
4번 ⇒ 1회전
 
[2회전(3바퀴)]
1) 5,2 비교(2,5,4,3,8)
2) 5,4 비교(2,4,5,3,8)
3) 5,3 비교(2,4,3,5,8)
3번 ⇒ 2회전
 
[3회전(2바퀴)]
1) 2,4비교(2,4,3,5,8)
2) 4,3비교(2,3,4,5,8)
지금의 상황에서는 운 좋게 여기서 끝났음
 
[4회전(1바퀴)]
상황에 따라 3,2,4,5,8이 되어 있을 수 있으니
1)2,3을 비교(2,3,4,5,8)
 
 
3,4,8,2,5라고 순서만 다른 경우였다면,
1) (3,4,8,2,5)
2) (3,4,8,2,5)
3) (3,4,2,8,5)
4) (3,4,2,5,8)
가장 큰 수를 끝에 둚
두번 째는 두번째 큰 수를 끝 앞에 두는 것
 
 
5,8,2,4,3 (N)
회전 수 : N-1
1회전 비교 횟수 : N-1
2회전 비교 횟수 : N-2
3회전 비교 횟수 : N-3
4회전 비교 횟수 : N-4
 
*자바 문법
상수는 대문자로
notion image
 
*스왑
notion image

* 디버깅

notion image
notion image
notion image
그 칸에 해당 하는게 맞는 지 확인 (bubbletest06)번호에 클릭 > 벌레 클릭
 
 
chat gpt 무료로 쓸 수 있음 :wrtn.ai
static으로 찾을 때는 클래스명으로 찾음
notion image
함수, 메서드
 
notion image
 
notion image

정렬의 목적

찾기 편하게
ex) 5.7.9.10.15.3
⇒ 10을 찾는 과정
첫번째부터 쭉 봄
4번만에 찾음, 그리고 뒤에 또 있을 수 있으니 끝까지 체크(full scan) ⇒효율이 n
⇒최악의 과정
 
ex2) 5.7.9.10.15.3
⇒ primary (유일)⇒ so 4번만 하면 끝
여기서는 숫자가 5,7,9,15,3,10이면 최악.
 
ex) 5.7.9.10.15.3⇒ 정렬 ⇒ 3,5,7,9,10,15
크기 [6]
6/2 =3번지 먼저 search 9니까 왼쪽방향은 안봐도 됨
⇒10,15,3 3칸에서 /2해서 … 9를 0부터 기준에서 찾음?-v
 
정렬하면 이진트리 가능, 클러스트링이 있어서 중복있어도 빠름
이진트리, 바이너리서치가 가능함.
 
⇒ 선형적으로(비례해서) 증가하지 않고 로그방식으로 증가?!(천천히 늘어남)-v
 
클러스트링 5,9,10,3,4,3,2 ->정렬 ->2,3,3,4,5,9,10 군집화 가능, search 빨라짐
notion image
💡
주말에 도전해보셈
notion image

선택 정렬

선택 정렬은 첫 번째 자료두 번째 자료부터 마지막 자료까지 차례대로 비교하여 가장 작은 값을 찾아 첫 번째에 놓고, 두 번째 자료를 세 번째 자료부터 마지막 자료까지와 차례대로 비교하여 그 중 가장 작은 값을 찾아 두 번째 위치에 놓는 과정을 반복하며 정렬을 수행한다. 1회전을 수행하고 나면 가장 작은 값의 자료가 맨 앞에 오게 되므로 그 다음 회전에서는 두 번째 자료를 가지고 비교한다. 마찬가지로 3회전에서는 세 번째 자료를 정렬한다. https://gmlwjd9405.github.io/2018/05/06/algorithm-selection-sort.html
 
5,8,2,4,3
 
기준점 p=0(place), 바뀌어야 하는 위치final i=0
5,8(0 p값,1번지 비교)
5,2(0,2) p=2
2,4(2,3)
2,3(3,4)
if(i !=p)2,8,5,4,3(i,p교환)
 
1라운드 후⇒ 5,8,2,4,3인데 ⇒ 2,8,5,4,3(i와 p교환)
교환 인덱스 p=1(place), 바뀌어야 하는 위치final i=1
(i와 p가 동일시 변경x)
 
2,8,5,4,3에서
8,5(1,2번지 비교) p=2로 변경됨
5, 4비교(2,3) p=3
4,3(3,4) p=4
if(i !=p) 2,3,5,4,8(i,p교환)
 
교환 인덱스 p=2(place), 바뀌어야 하는 위치final i=2
final i = 2(해당위치변경), p=2(교환 인덱스)
5,4 (2,3) p=3
4,8(3,4)
if(i !=p) 2,3,4,5,8
 
final i = 3(해당위치변경), p=3(교환 인덱스)
5,4 (3,4)
if(i !=p) x
 
*이 과정에서 공식, 규칙 find
 

버블정렬 연습문제_내가 연습
{1,2,3,4,5,6,7,8,9}
1회전]
(1,2) 비교 ⇒ 변화x
(2,3) 비교 ⇒ 변화x
(3,4) 비교 ⇒ 변화x
(4,5) 비교 ⇒ 변화x
(5,6) 비교 ⇒ 변화x
(6,7) 비교 ⇒ 변화x
(7,8) 비교 ⇒ 변화x
(8,9) 비교 ⇒ 변화x
⇒{1,2,3,4,5,6,7,8, 9} 9확정
 
2회전]
(1,2) 비교 ⇒ 변화x
(2,3) 비교 ⇒ 변화x
(3,4) 비교 ⇒ 변화x
(4,5) 비교 ⇒ 변화x
(5,6) 비교 ⇒ 변화x
(6,7) 비교 ⇒ 변화x
(7,8) 비교 ⇒ 변화x
⇒{1,2,3,4,5,6,7, 8, 9} 8확정
 
3회전]
(1,2) 비교 ⇒ 변화x
(2,3) 비교 ⇒ 변화x
(3,4) 비교 ⇒ 변화x
(4,5) 비교 ⇒ 변화x
(5,6) 비교 ⇒ 변화x
(6,7) 비교 ⇒ 변화x
⇒{1,2,3,4,5,6, 7, 8,9} 7확정
8회전(8+7+6+5+4+3+2+1) ⇒ 총 36번
find rule ⇒ 숫자가 9개(n)면 n-1회전, n!번?-v
 
정렬의 목적 ⇒ 검색
검색의 기본 ⇒ 풀스캔
풀스캔이 좋은가?최악인가?
 

이진검색

정렬이 되어있는 상태에서 개수/2
package ex03.test; public class BinaryTest01 { public static void main(String[] args) { //이진 검색 => 반드시 정렬이 되어 있어야 한다. int[] arr = {1,2,3,4,5,6,7,8,9}; // 9(갯수)/2=4 =>4번지 정중앙 find -> 8을 찾고자 함 //target =8 //0~8번지비교 // mid = N/2= 4(index 위치값) -> arr[4]=값 5 //if(8 ==5) -> mid 위치에 타겟이 있다. //if(8>5) 참-> // 5번지[mid+1]부터 8번지까지 비교 //mid = 7=arr[7]->값 8 //if(8 ==5) -> mid 위치에 타겟이 있다. //if(8>8) } }
package ex03.test; public class BinaryTest01 { public static void main(String[] args) { // 이진 검색 => 반드시 정렬이 되어 있어야 한다. int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9}; // 9 / 2 = 4 // target = 8 // start=0 ~ end = 8 // // mid = N/2 = 4 -> arr[4] -> 값 5 // // if(8==5) -> mid 위치에 타겟이 있다. // // if(8>5) 참 // // // // start=5 (mid+1) ~ end=8 // // mid = 7 = arr[7] -> 값 8 // // if(8==8) -> mid 위치에 타겟이 있다. // // if(8>8) // // // // target = 3 // // // start=0 ~ end = 8 // // mid = N/2 = 4 -> arr[4] -> 값 5 // // if(3==5) -> mid 위치에 타겟이 있다. // // if(3>5) 거짓 // // // // start=0 ~ end = mid-1 // // mid = 7 = arr[7] -> 값 8 // // if(8==8) -> mid 위치에 타겟이 있다. // // if(8>8) } }
  • 시간복잡도(최악으로 검색하는 횟수) : 풀스캔,선형적으로 늘어남
수가 늘어나면 너무 큰 수는 int로는 부족할 수 있다. 그래서 28승 = 256 216승 = 65536 24승 = 16 => 수를 8승, 16승, 4승으로 나타내면 편함=> log사용함 ex> 250000승을 log를 이용하여 간단하게 표현할 수 있다.
시간복잡도는 log 밑이 2 log 2의 n
  • 이진트리는 값들이 늘어나도 시간복잡도가 선형적으로 늘지 않음. 검색횟수가 완전 적고(O(logN)) 장점이다.
ex) 데이터 1억번.. 만번.. (풀 스캔 O(N)보다)
log2(42억 9천) = 32번 안에
//정렬이 되있든 안되있든 풀 스캔을 하면
 
* 이 정도는 외우자! (단위를 쉽게 알수 있게) 2^8 = 256 2^10 = 1024 2^16 = 65,536 2^32 =429천~~~
notion image
notion image
 
데이터가 많을 수록 풀스캔보다 이진트리가 유리
적을 때는 풀스캔이 유리-v
package ex03; public class BinarySearch { public static void main(String[] args) { // N = 21 // 시간복잡도 log2(N) -> log2(21) -> 4.xx (max. 뭘 찾는 지에 따라 최대 5번(올림 시킴)만에 찾는 다는 이야기 함.) // 이진 검색 => 반드시 정렬이 되어 있어야 한다. // 16 / 2*2*2*2*2 -> logn -> log17 int[] arr = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}; int N = arr.length; final int target = 3; int start = 0; int end = N - 1; int mid; int round = 1; while (true) { // 1 회전 mid = start + ((end - start) / 2); // 기대값 7 System.out.println("mid: " + mid); if (arr[mid] == target) { System.out.println(round + "회전 : " + target + "값은 " + mid + "번지에 있습니다"); break; } if (arr[mid] < target) { // 기대값 start 5, end 8 start = mid + 1; } if (arr[mid] > target) { end = mid - 1; } System.out.println(round + "회전 start : " + start); System.out.println(round + "회전 end : " + end); round++; } System.out.println("시간복잡도 : " + (Math.log(N) / Math.log(2))); } }
💡
자료를 2개를 찾을 때(예를 들어, 3,5를 찾을 때는)는 위의 경우 시간 복잡도가 5+5=10 / 3 개는 15, / 4 개는 20/ 5 개는 25 걸림. 근데 풀 스캔은 21번이니까 5개 자료 찾을 때는 풀 스캔이 유리. (필요한 데이터가 전체 데이터의 15%미만인 경우 (랜덤 액세스가)(바이너리가)(이진서치) 유리함. )
(랜덤액세스 vs 풀스캔)
풀스캔 처음부터 보는 것
 
 
 
 

퀵솔트

퀵 정렬은 이름 그대로 매우 빠르게 정렬을 수행하는 알고리즘이다. 기본적으로 분할정복의 성격을 가지고 있고 실제로도 많이 사용되는 대표적인 정렬 알고리즘이다.
 

Algorithm Concept

퀵 정렬은 다음과 같은 과정으로 정렬을 진행한다. 정렬은 오름차순으로 진행한다고 가정하자.
  1. 주어진 배열에서 하나의 요소를 선택하고 이를 pivot(피벗) 으로 삼는다.
  1. 배열 내부의 모든 값을 검사하면서 피벗 값보다 작은 값들은 왼쪽에, 큰 값들은 오른쪽에 배치한다.
  1. 이렇게 하면 배열이 두 부분으로 나뉜다. 나뉜 이 두 개의 배열에서 각각 새로운 피벗을 만들어서 두개의 배열로 다시 쪼개어 준다.
  1. 더 이상 배열을 쪼갤 수 없을 때까지 진행한다.
이 과정은 분할 정복의 원리를 이용한 것이다. 피벗을 중심으로 문제를 분할 하고, 피벗을 기준으로 해서 작은 값과 큰 값을 나열하는 정복 과정을 거친 뒤, 모든 결과를 결합 해서 큰 전체 문제를 해결한다.

Example

퀵소트 알고리즘이 어떻게 동작하는지 자세히 알아보자.
notion image

Phase 1

notion image
일단 피봇을 선정하고 피벗보다 더 작은값과 더 큰 값을 탐색할 low 와 high 를 피벗을 제외한 배열의 양끝점에 둔다.

Phase 2

notion image
먼저 low를 계속 움직이면서 배열 요소를 검사해보자. 한칸 이동했을 때 low는 배열요소 9를 만나게 되는데, 이 값은 피벗값인 5보다 큰 값이다. low의 뒤에는 항상 피벗보다 작은 값이 있어야 하기 때문에 여기서 일단 멈춘다.

Phase 3

notion image
low가 멈추면 high를 움직인다. high를 한칸 옮기면 배열 요소 1에 도착하게 되는데 이 값은 피벗값인 5보다 작은 값이기 때문에 high의 영역에 있으면 안된다. high 도 여기서 일단 정지시킨다.

Phase 4

notion image
low 와 high가 모두 중단되면 중단된 위치를 먼저 확인해본다. 만약 low 와 high가 서로 위치가 역전된 상태가 아니라면 중단된 지점에 있던 두 요소를 교환한다. 따라서 1과 9가 교환되었다.

Phase 5

notion image
다시 low를 이동시킨다. 이번에도 역시 확인한 배열요소가 피벗보다 큰 값을 가지고 있기 때문에 중단한다.

Phase 6

notion image
이제 high 를 이동시키자. high도 새로 검사한 배열의 요소가 피벗보다 작은 값을 가지고 있기 때문에 현 위치에서 중단한다.

Phase 7

notion image
low 와 high 가 아직 교차하지 않기 때문에 두 배열 요소를 교환한다.

Phase 8

notion image
low가 한번 더 이동하면 피벗보다 큰 값을 만나기 때문에 다시 또 중단하게 된다.
high도 한번 더 이동하면 피벗보다 작은 값을 만나기 때문에 중단하게 된다.
이번 경우에는 low와 high의 위치가 역전됐기 때문에 교환되지 않고 정렬을 끝낸다.

Phase 9

notion image
1차로 정렬이 끝나면 피벗으로 설정된 배열요소를 high가 가르키는 배열요소와 교환해준다. 이제 피벗을 중심으로 왼쪽과 오른쪽에 피벗보다 작고 큰 값들이 위치했음을 확인할 수 있다.

More...

피벗을 중심으로 나뉘어진 두 그룹은 두 개의 배열처럼 취급되어서 위에서 거쳤던 과정을 똑같이 거칠 수 있다. low가 만들어낸 그룹의 피벗은 3이 될 것이고, high 가 만들어낸 그룹의 피벗은 7이 될 것이다. 이 작업을 분할된 배열의 길이가 1이나 0이 될 때까지 계속 반복한다.
 

implement

#include <stdio.h> int partition (int arr[], int p, int r){ int low, high; int pivot = arr[p]; // pivot 값 설정 low = p + 1; // low 는 pivot의 바로 다음 위치에서부터 high = r; // high는 전달된 끝지점 while(low <= high){ while(arr[low] < pivot) low++; // pivot 보다 작은 값이 나올때마다 이동 while(arr[high] > pivot) high--; // pivot 보다 큰 값이 나올때마다 이동 if (low <= high){ // low와 high 가 중단된 지점이 서로 위치가 역전된 지점이 아니라면 int temp = arr[low]; // low 와 high 의 값 변경 arr[low] = arr[high]; arr[high] = temp; } } // 피벗과 high 위치 교환 int temp = arr[p]; arr[p] = arr[high]; arr[high] = temp; return high; // 피벗 위치 반환 } void quick_sort(int arr[], int left, int right){ if (left < right){ int pivot = partition(arr, left, right); quick_sort(arr, left, pivot-1); // 피벗을 기준으로 왼쪽 배열 정렬 quick_sort(arr, pivot+1, right); // 피벗 기준으로 오른쪽 배열 정렬 } } int main (){ int arr [] = {3, 2, 1, 5, 7, 9, 6}; quick_sort(arr, 0, sizeof(arr)/sizeof(arr[0])-1); for (int i = 0 ; i < sizeof(arr)/sizeof(arr[0]) ; i++){ printf("%d ", arr[i]); } } 5 신고하기 구독하기이 글은 본 저작자 표시, 비영리 규칙 하에 배포할 수 있습니다. 자세한 내용은 Creative Commons 라이선스를 확인하세요. Creative Commons본 저작자 표시비영리
 
참고자료)
Share article

Coding's note