선택 정렬은 첫 번째 자료를 두 번째 자료부터 마지막 자료까지 차례대로 비교하여 가장 작은 값을 찾아 첫 번째에 놓고, 두 번째 자료를 세 번째 자료부터 마지막 자료까지와 차례대로 비교하여 그 중 가장 작은 값을 찾아 두 번째 위치에 놓는 과정을 반복하며 정렬을 수행한다.
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 classBinaryTest01 {
public staticvoidmain(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 classBinaryTest01 {
public staticvoidmain(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로는 부족할 수 있다.
그래서
2의 8승 = 2562의 16승 = 655362의 4승 = 16
=> 수를 8승, 16승, 4승으로 나타내면 편함=> log사용함
ex> 2의 50000승을 log를 이용하여 간단하게 표현할 수 있다.
* 이 정도는 외우자! (단위를 쉽게 알수 있게)
2^8 = 2562^10 = 10242^16 = 65,5362^32 =42억 9천~~~
데이터가 많을 수록 풀스캔보다 이진트리가 유리
적을 때는 풀스캔이 유리-v
package ex03;
public classBinarySearch {
public staticvoidmain(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); // 기대값 7System.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
퀵 정렬은 다음과 같은 과정으로 정렬을 진행한다. 정렬은 오름차순으로 진행한다고 가정하자.
주어진 배열에서 하나의 요소를 선택하고 이를 pivot(피벗) 으로 삼는다.
배열 내부의 모든 값을 검사하면서 피벗 값보다 작은 값들은 왼쪽에, 큰 값들은 오른쪽에 배치한다.
이렇게 하면 배열이 두 부분으로 나뉜다. 나뉜 이 두 개의 배열에서 각각 새로운 피벗을 만들어서 두개의 배열로 다시 쪼개어 준다.
더 이상 배열을 쪼갤 수 없을 때까지 진행한다.
이 과정은 분할 정복의 원리를 이용한 것이다. 피벗을 중심으로 문제를 분할 하고, 피벗을 기준으로 해서 작은 값과 큰 값을 나열하는 정복 과정을 거친 뒤, 모든 결과를 결합 해서 큰 전체 문제를 해결한다.
Example
퀵소트 알고리즘이 어떻게 동작하는지 자세히 알아보자.
Phase 1
일단 피봇을 선정하고 피벗보다 더 작은값과 더 큰 값을 탐색할 low 와 high 를 피벗을 제외한 배열의 양끝점에 둔다.
Phase 2
먼저 low를 계속 움직이면서 배열 요소를 검사해보자. 한칸 이동했을 때 low는 배열요소 9를 만나게 되는데, 이 값은 피벗값인 5보다 큰 값이다. low의 뒤에는 항상 피벗보다 작은 값이 있어야 하기 때문에 여기서 일단 멈춘다.
Phase 3
low가 멈추면 high를 움직인다. high를 한칸 옮기면 배열 요소 1에 도착하게 되는데 이 값은 피벗값인 5보다 작은 값이기 때문에 high의 영역에 있으면 안된다. high 도 여기서 일단 정지시킨다.
Phase 4
low 와 high가 모두 중단되면 중단된 위치를 먼저 확인해본다. 만약 low 와 high가 서로 위치가 역전된 상태가 아니라면 중단된 지점에 있던 두 요소를 교환한다. 따라서 1과 9가 교환되었다.
Phase 5
다시 low를 이동시킨다. 이번에도 역시 확인한 배열요소가 피벗보다 큰 값을 가지고 있기 때문에 중단한다.
Phase 6
이제 high 를 이동시키자. high도 새로 검사한 배열의 요소가 피벗보다 작은 값을 가지고 있기 때문에 현 위치에서 중단한다.
Phase 7
low 와 high 가 아직 교차하지 않기 때문에 두 배열 요소를 교환한다.
Phase 8
low가 한번 더 이동하면 피벗보다 큰 값을 만나기 때문에 다시 또 중단하게 된다.
high도 한번 더 이동하면 피벗보다 작은 값을 만나기 때문에 중단하게 된다.
이번 경우에는 low와 high의 위치가 역전됐기 때문에 교환되지 않고 정렬을 끝낸다.
Phase 9
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; // 피벗 위치 반환
}
voidquick_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
신고하기
구독하기이 글은 본 저작자 표시, 비영리 규칙 하에 배포할 수 있습니다. 자세한 내용은 CreativeCommons 라이선스를 확인하세요.
CreativeCommons본 저작자 표시비영리
퀵소트 퀵 정렬은 이름 그대로 매우 빠르게 정렬을 수행하는 알고리즘이다. 기본적으로 분할정복의 성격을 가지고 있고 실제로도 많이 사용되는 대표적인 정렬 알고리즘이다. 동시에 자료구조 수강생들을 괴롭게 하는 친구 중 한명이긴 하지만.. Algorithm Concept 퀵 정렬은 다음과 같은 과정으로 정렬을 진행한다. 정렬은 오름차순으로 진행한다고 가정하자. 주어진 배열에서 하나의 요소를 선택하고 이를 pivot(피벗) 으로 삼는다. 배열 내부의 모든 값을 검사하면서 피벗 값보다 작은 값들은 왼쪽에, 큰 값들은 오른쪽에 배치한다. 이렇게 하면 배열이 두 부분으로 나뉜다. 나뉜 이 두 개의 배열에서 각각 새로운 피벗을 만들어서 두개의 배열로 다시 쪼개어 준다. 더 이상 배열을 쪼갤 수 없을 때까지 진행한다. ..